From 833c049313864044ad2d61dd2804e2bc8cf085b3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:35:51 +0200 Subject: [PATCH 001/215] set up Vercel for front and backend --- backend/package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/package.json b/backend/package.json index 08f29f2448..290d526563 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,8 +3,9 @@ "version": "1.0.0", "description": "Server part of final project", "scripts": { - "start": "babel-node server.js", - "dev": "nodemon server.js --exec babel-node" + "dev": "vercel dev", + "build": "tsc", + "start": "node dist/index.js" }, "author": "", "license": "ISC", @@ -17,4 +18,4 @@ "mongoose": "^8.4.0", "nodemon": "^3.0.1" } -} \ No newline at end of file +} From bf4f8d6957dd4ef1df774c229718e99aa994fd58 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:53:02 +0200 Subject: [PATCH 002/215] install runtime and dev dependecies in backend --- backend/package.json | 26 +++++++++++++++---------- backend/tsconfig.json | 44 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 backend/tsconfig.json diff --git a/backend/package.json b/backend/package.json index 290d526563..a5e1c01b58 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,21 +1,27 @@ { - "name": "project-final-backend", + "name": "bada-backend", "version": "1.0.0", - "description": "Server part of final project", + "type": "module", "scripts": { "dev": "vercel dev", "build": "tsc", "start": "node dist/index.js" }, - "author": "", - "license": "ISC", "dependencies": { - "@babel/core": "^7.17.9", - "@babel/node": "^7.16.8", - "@babel/preset-env": "^7.16.11", + "bcrypt": "^6.0.0", "cors": "^2.8.5", - "express": "^4.17.3", - "mongoose": "^8.4.0", - "nodemon": "^3.0.1" + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "mongoose": "^8.18.1", + "serverless-http": "^4.0.0", + "swagger-ui-express": "^5.0.1", + "zod": "^4.1.5" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", + "@types/node": "^20.19.13", + "typescript": "^5.9.2" } } diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000000..cec4a3a4be --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,44 @@ +{ + // Visit https://aka.ms/tsconfig to read more about this file + "compilerOptions": { + // File Layout + // "rootDir": "./src", + // "outDir": "./dist", + + // Environment Settings + // See also https://aka.ms/tsconfig/module + "module": "nodenext", + "target": "esnext", + "types": [], + // For nodejs: + // "lib": ["esnext"], + // "types": ["node"], + // and npm install -D @types/node + + // Other Outputs + "sourceMap": true, + "declaration": true, + "declarationMap": true, + + // Stricter Typechecking Options + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + + // Style Options + // "noImplicitReturns": true, + // "noImplicitOverride": true, + // "noUnusedLocals": true, + // "noUnusedParameters": true, + // "noFallthroughCasesInSwitch": true, + // "noPropertyAccessFromIndexSignature": true, + + // Recommended Options + "strict": true, + "jsx": "react-jsx", + "verbatimModuleSyntax": true, + "isolatedModules": true, + "noUncheckedSideEffectImports": true, + "moduleDetection": "force", + "skipLibCheck": true, + } +} From e08b6b86fa8b312b8252173185694cee2572f0d3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:01:48 +0200 Subject: [PATCH 003/215] create src and routes folders in backend and edit tsconfig.json --- backend/routes/health.ts | 12 ++++++++ backend/src/index.ts | 47 ++++++++++++++++++++++++++++++++ backend/tsconfig.json | 59 ++++++++++++++++------------------------ 3 files changed, 83 insertions(+), 35 deletions(-) create mode 100644 backend/routes/health.ts create mode 100644 backend/src/index.ts diff --git a/backend/routes/health.ts b/backend/routes/health.ts new file mode 100644 index 0000000000..3a8234835f --- /dev/null +++ b/backend/routes/health.ts @@ -0,0 +1,12 @@ +import { Router } from "express"; + +export const healthRouter = Router(); + +// Express 5 supports async handlers out of the box +healthRouter.get("/health", async (_req, res) => { + res.json({ + ok: true, + env: process.env.NODE_ENV ?? "development", + timestamp: new Date().toISOString(), + }); +}); diff --git a/backend/src/index.ts b/backend/src/index.ts new file mode 100644 index 0000000000..ef3dd2f44c --- /dev/null +++ b/backend/src/index.ts @@ -0,0 +1,47 @@ +import express from "express"; +import cors from "cors"; +import serverless from "serverless-http"; + +// Routers +import { healthRouter } from "./routes/health.js"; + +const app = express(); + +// CORS — allow only your deployed frontend (you can comma-separate multiple origins) +const allowed = (process.env.ALLOWED_ORIGIN ?? "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); +app.use( + cors({ + origin: allowed.length ? allowed : true, // during local dev if empty, allow all + credentials: true, + }) +); + +// Body parsing +app.use(express.json()); + +// Mount routes under /api +app.use("/api", healthRouter); + +// Simple not-found handler +app.use((_req, res) => { + res.status(404).json({ error: "NotFound" }); +}); + +// Centralized error handler (Express 5 catches async errors) +app.use( + ( + err: unknown, + _req: express.Request, + res: express.Response, + _next: express.NextFunction + ) => { + console.error("[ERROR]", err); + res.status(500).json({ error: "InternalServerError" }); + } +); + +// Export serverless handler for Vercel +export default serverless(app); diff --git a/backend/tsconfig.json b/backend/tsconfig.json index cec4a3a4be..0c26458366 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -1,44 +1,33 @@ { - // Visit https://aka.ms/tsconfig to read more about this file "compilerOptions": { - // File Layout - // "rootDir": "./src", - // "outDir": "./dist", + /* Output */ + "outDir": "dist", + "rootDir": "src", - // Environment Settings - // See also https://aka.ms/tsconfig/module - "module": "nodenext", - "target": "esnext", - "types": [], - // For nodejs: - // "lib": ["esnext"], - // "types": ["node"], - // and npm install -D @types/node - - // Other Outputs - "sourceMap": true, - "declaration": true, - "declarationMap": true, + /* Language and Environment */ + "target": "ES2022", // modern JS output + "module": "ESNext", // use modern ESM + "moduleResolution": "NodeNext", // proper module resolution for Node + ESM + "esModuleInterop": true, // allow default imports from CJS + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true, - // Stricter Typechecking Options + /* Type Checking */ + "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, - // Style Options - // "noImplicitReturns": true, - // "noImplicitOverride": true, - // "noUnusedLocals": true, - // "noUnusedParameters": true, - // "noFallthroughCasesInSwitch": true, - // "noPropertyAccessFromIndexSignature": true, + /* Source Maps */ + "sourceMap": true, + "declaration": true, + "declarationMap": true, - // Recommended Options - "strict": true, - "jsx": "react-jsx", - "verbatimModuleSyntax": true, - "isolatedModules": true, - "noUncheckedSideEffectImports": true, - "moduleDetection": "force", - "skipLibCheck": true, - } + /* Types */ + "types": ["node"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] } From 65f594f89d17b428a9f3985f0d36d03334c57b42 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:09:53 +0200 Subject: [PATCH 004/215] setup TS + basic health endpoint --- backend/dist/index.d.ts | 4 ++++ backend/dist/index.d.ts.map | 1 + backend/dist/index.js | 31 +++++++++++++++++++++++++++++ backend/dist/index.js.map | 1 + backend/dist/routes/health.d.ts | 2 ++ backend/dist/routes/health.d.ts.map | 1 + backend/dist/routes/health.js | 10 ++++++++++ backend/dist/routes/health.js.map | 1 + backend/package.json | 1 + backend/{ => src}/routes/health.ts | 1 - backend/tsconfig.json | 14 ++++++------- 11 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 backend/dist/index.d.ts create mode 100644 backend/dist/index.d.ts.map create mode 100644 backend/dist/index.js create mode 100644 backend/dist/index.js.map create mode 100644 backend/dist/routes/health.d.ts create mode 100644 backend/dist/routes/health.d.ts.map create mode 100644 backend/dist/routes/health.js create mode 100644 backend/dist/routes/health.js.map rename backend/{ => src}/routes/health.ts (82%) diff --git a/backend/dist/index.d.ts b/backend/dist/index.d.ts new file mode 100644 index 0000000000..41a92ac86c --- /dev/null +++ b/backend/dist/index.d.ts @@ -0,0 +1,4 @@ +import serverless from "serverless-http"; +declare const _default: serverless.Handler; +export default _default; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/index.d.ts.map b/backend/dist/index.d.ts.map new file mode 100644 index 0000000000..f2747608d5 --- /dev/null +++ b/backend/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,UAAU,MAAM,iBAAiB,CAAC;;AA4CzC,wBAA+B"} \ No newline at end of file diff --git a/backend/dist/index.js b/backend/dist/index.js new file mode 100644 index 0000000000..47e8b865d2 --- /dev/null +++ b/backend/dist/index.js @@ -0,0 +1,31 @@ +import express from "express"; +import cors from "cors"; +import serverless from "serverless-http"; +// Routers +import { healthRouter } from "./routes/health.js"; +const app = express(); +// CORS — allow only your deployed frontend (you can comma-separate multiple origins) +const allowed = (process.env.ALLOWED_ORIGIN ?? "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); +app.use(cors({ + origin: allowed.length ? allowed : true, // during local dev if empty, allow all + credentials: true, +})); +// Body parsing +app.use(express.json()); +// Mount routes under /api +app.use("/api", healthRouter); +// Simple not-found handler +app.use((_req, res) => { + res.status(404).json({ error: "NotFound" }); +}); +// Centralized error handler (Express 5 catches async errors) +app.use((err, _req, res, _next) => { + console.error("[ERROR]", err); + res.status(500).json({ error: "InternalServerError" }); +}); +// Export serverless handler for Vercel +export default serverless(app); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/index.js.map b/backend/dist/index.js.map new file mode 100644 index 0000000000..71aa48d684 --- /dev/null +++ b/backend/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,qFAAqF;AACrF,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;KAC/C,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACnB,GAAG,CAAC,GAAG,CACL,IAAI,CAAC;IACH,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,uCAAuC;IAChF,WAAW,EAAE,IAAI;CAClB,CAAC,CACH,CAAC;AAEF,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,0BAA0B;AAC1B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE9B,2BAA2B;AAC3B,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,6DAA6D;AAC7D,GAAG,CAAC,GAAG,CACL,CACE,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B,EAC3B,EAAE;IACF,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;AACzD,CAAC,CACF,CAAC;AAEF,uCAAuC;AACvC,eAAe,UAAU,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/health.d.ts b/backend/dist/routes/health.d.ts new file mode 100644 index 0000000000..7358e505fa --- /dev/null +++ b/backend/dist/routes/health.d.ts @@ -0,0 +1,2 @@ +export declare const healthRouter: import("express-serve-static-core").Router; +//# sourceMappingURL=health.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/health.d.ts.map b/backend/dist/routes/health.d.ts.map new file mode 100644 index 0000000000..62c17ca00b --- /dev/null +++ b/backend/dist/routes/health.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/health.js b/backend/dist/routes/health.js new file mode 100644 index 0000000000..5b4eb11a90 --- /dev/null +++ b/backend/dist/routes/health.js @@ -0,0 +1,10 @@ +import { Router } from "express"; +export const healthRouter = Router(); +healthRouter.get("/health", async (_req, res) => { + res.json({ + ok: true, + env: process.env.NODE_ENV ?? "development", + timestamp: new Date().toISOString(), + }); +}); +//# sourceMappingURL=health.js.map \ No newline at end of file diff --git a/backend/dist/routes/health.js.map b/backend/dist/routes/health.js.map new file mode 100644 index 0000000000..4e7138dfa9 --- /dev/null +++ b/backend/dist/routes/health.js.map @@ -0,0 +1 @@ +{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC;AAErC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IAC9C,GAAG,CAAC,IAAI,CAAC;QACP,EAAE,EAAE,IAAI;QACR,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;QAC1C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index a5e1c01b58..7002bd59fe 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@types/bcrypt": "^5.0.2", + "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20.19.13", diff --git a/backend/routes/health.ts b/backend/src/routes/health.ts similarity index 82% rename from backend/routes/health.ts rename to backend/src/routes/health.ts index 3a8234835f..e43e2c06e7 100644 --- a/backend/routes/health.ts +++ b/backend/src/routes/health.ts @@ -2,7 +2,6 @@ import { Router } from "express"; export const healthRouter = Router(); -// Express 5 supports async handlers out of the box healthRouter.get("/health", async (_req, res) => { res.json({ ok: true, diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 0c26458366..5ee860e330 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -5,14 +5,14 @@ "rootDir": "src", /* Language and Environment */ - "target": "ES2022", // modern JS output - "module": "ESNext", // use modern ESM - "moduleResolution": "NodeNext", // proper module resolution for Node + ESM - "esModuleInterop": true, // allow default imports from CJS - "forceConsistentCasingInFileNames": true, + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "resolveJsonModule": true, + "forceConsistentCasingInFileNames": true, "isolatedModules": true, - "allowSyntheticDefaultImports": true, "skipLibCheck": true, /* Type Checking */ @@ -20,7 +20,7 @@ "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true, - /* Source Maps */ + /* Source Maps & Declarations */ "sourceMap": true, "declaration": true, "declarationMap": true, From 2184b768699cc6b7cba2708a202535099d7dde5a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:26:49 +0200 Subject: [PATCH 005/215] add vercel.json to route traffic to Express app --- backend/src/routes/health.ts | 8 +++++--- backend/vercel.json | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 backend/vercel.json diff --git a/backend/src/routes/health.ts b/backend/src/routes/health.ts index e43e2c06e7..d247168cf9 100644 --- a/backend/src/routes/health.ts +++ b/backend/src/routes/health.ts @@ -1,11 +1,13 @@ -import { Router } from "express"; +import { Router, Request, Response } from "express"; -export const healthRouter = Router(); +const router = Router(); -healthRouter.get("/health", async (_req, res) => { +router.get("/health", async (_req: Request, res: Response) => { res.json({ ok: true, env: process.env.NODE_ENV ?? "development", timestamp: new Date().toISOString(), }); }); + +export { router as healthRouter }; diff --git a/backend/vercel.json b/backend/vercel.json new file mode 100644 index 0000000000..674c42f3fb --- /dev/null +++ b/backend/vercel.json @@ -0,0 +1,4 @@ +{ + "builds": [{ "src": "src/index.ts", "use": "@vercel/node" }], + "routes": [{ "src": "/(.*)", "dest": "src/index.ts" }] +} From ac659c31b11596f61e1ddaaf085b336557ac64f7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:30:47 +0200 Subject: [PATCH 006/215] edit vercel.json in backend to test health route --- backend/vercel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/vercel.json b/backend/vercel.json index 674c42f3fb..0ecf1b1b1d 100644 --- a/backend/vercel.json +++ b/backend/vercel.json @@ -1,4 +1,4 @@ { "builds": [{ "src": "src/index.ts", "use": "@vercel/node" }], - "routes": [{ "src": "/(.*)", "dest": "src/index.ts" }] + "routes": [{ "src": "/api/(.*)", "dest": "src/index.ts" }] } From e3edf9d22f1c300988f995d4d5a38f5f55b5b266 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:34:14 +0200 Subject: [PATCH 007/215] add temporary direct health routhe to rule out router import issues --- backend/src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/index.ts b/backend/src/index.ts index ef3dd2f44c..c93fe8f67e 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -25,6 +25,10 @@ app.use(express.json()); // Mount routes under /api app.use("/api", healthRouter); +app.get("/api/health-direct", (_req, res) => { + res.json({ ok: true, via: "direct" }); +}); // for quick testing without router + // Simple not-found handler app.use((_req, res) => { res.status(404).json({ error: "NotFound" }); From 72f2bd0d71ac551269e7aaa0c4511f7460825a42 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:40:38 +0200 Subject: [PATCH 008/215] update health router with a log --- backend/src/index.ts | 2 ++ backend/src/routes/health.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/backend/src/index.ts b/backend/src/index.ts index c93fe8f67e..85e0417c85 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,3 +1,5 @@ +console.log("Server booted at", new Date().toISOString()); + import express from "express"; import cors from "cors"; import serverless from "serverless-http"; diff --git a/backend/src/routes/health.ts b/backend/src/routes/health.ts index d247168cf9..19d5a570eb 100644 --- a/backend/src/routes/health.ts +++ b/backend/src/routes/health.ts @@ -3,6 +3,7 @@ import { Router, Request, Response } from "express"; const router = Router(); router.get("/health", async (_req: Request, res: Response) => { + console.log("Hit /api/health"); // <-- add this res.json({ ok: true, env: process.env.NODE_ENV ?? "development", From f9752b86471c7b0f52b8afd12a3a422f2fb4f70a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:47:55 +0200 Subject: [PATCH 009/215] chore(backend): move Express handler to /api/index.ts for Vercel routing --- backend/api/index.ts | 53 ++++++++++++++++++++++++++++++++++++ backend/src/routes/health.ts | 2 +- backend/vercel.json | 4 --- 3 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 backend/api/index.ts delete mode 100644 backend/vercel.json diff --git a/backend/api/index.ts b/backend/api/index.ts new file mode 100644 index 0000000000..ca3816f833 --- /dev/null +++ b/backend/api/index.ts @@ -0,0 +1,53 @@ +// backend/api/index.ts +console.log("Server booted at", new Date().toISOString()); + +import express from "express"; +import cors from "cors"; +import serverless from "serverless-http"; + +import { healthRouter } from "../src/routes/health.js"; + +const app = express(); + +const allowed = (process.env.ALLOWED_ORIGIN ?? "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + +app.use( + cors({ + origin: allowed.length ? allowed : true, + credentials: true, + }) +); + +app.use(express.json()); + +// Mount routes under /api (the function path is /api, so this becomes /api/health) +app.use("/api", healthRouter); + +// Temporary direct route for sanity: +app.get("/api/health-direct", (_req, res) => { + console.log("Hit /api/health-direct"); + res.json({ ok: true, via: "direct" }); +}); + +// 404 +app.use((_req, res) => { + res.status(404).json({ error: "NotFound" }); +}); + +// Error handler +app.use( + ( + err: unknown, + _req: express.Request, + res: express.Response, + _next: express.NextFunction + ) => { + console.error("[ERROR]", err); + res.status(500).json({ error: "InternalServerError" }); + } +); + +export default serverless(app); diff --git a/backend/src/routes/health.ts b/backend/src/routes/health.ts index 19d5a570eb..4b11f1801e 100644 --- a/backend/src/routes/health.ts +++ b/backend/src/routes/health.ts @@ -3,7 +3,7 @@ import { Router, Request, Response } from "express"; const router = Router(); router.get("/health", async (_req: Request, res: Response) => { - console.log("Hit /api/health"); // <-- add this + console.log("Hit /api/health"); res.json({ ok: true, env: process.env.NODE_ENV ?? "development", diff --git a/backend/vercel.json b/backend/vercel.json deleted file mode 100644 index 0ecf1b1b1d..0000000000 --- a/backend/vercel.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "builds": [{ "src": "src/index.ts", "use": "@vercel/node" }], - "routes": [{ "src": "/api/(.*)", "dest": "src/index.ts" }] -} From 2c2f5f9196474915987630b665747847a0ca0ab1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:51:11 +0200 Subject: [PATCH 010/215] fix(backend): mount router at root inside /api function --- backend/api/index.ts | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/backend/api/index.ts b/backend/api/index.ts index ca3816f833..c53415886f 100644 --- a/backend/api/index.ts +++ b/backend/api/index.ts @@ -1,10 +1,8 @@ -// backend/api/index.ts console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; import serverless from "serverless-http"; - import { healthRouter } from "../src/routes/health.js"; const app = express(); @@ -23,31 +21,21 @@ app.use( app.use(express.json()); -// Mount routes under /api (the function path is /api, so this becomes /api/health) -app.use("/api", healthRouter); +// IMPORTANT: the function is already mounted at /api +// so we mount our router at the root ("") here +app.use(healthRouter); -// Temporary direct route for sanity: -app.get("/api/health-direct", (_req, res) => { - console.log("Hit /api/health-direct"); +// Temporary direct route for sanity (now at /api/health-direct) +app.get("/health-direct", (_req, res) => { + console.log("Hit /health-direct"); res.json({ ok: true, via: "direct" }); }); -// 404 -app.use((_req, res) => { - res.status(404).json({ error: "NotFound" }); +// 404 + error handlers +app.use((_req, res) => res.status(404).json({ error: "NotFound" })); +app.use((err: unknown, _req: any, res: any, _next: any) => { + console.error("[ERROR]", err); + res.status(500).json({ error: "InternalServerError" }); }); -// Error handler -app.use( - ( - err: unknown, - _req: express.Request, - res: express.Response, - _next: express.NextFunction - ) => { - console.error("[ERROR]", err); - res.status(500).json({ error: "InternalServerError" }); - } -); - export default serverless(app); From bbc12c63c2f34d4d2c3eb844f502378660539d36 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 10 Sep 2025 18:56:16 +0200 Subject: [PATCH 011/215] fix(backend): route /api/* to Express app via vercel.json and export app directly --- backend/api/index.ts | 41 ----------------------------------------- backend/src/index.ts | 26 ++++++++++++-------------- backend/vercel.json | 4 ++++ 3 files changed, 16 insertions(+), 55 deletions(-) delete mode 100644 backend/api/index.ts create mode 100644 backend/vercel.json diff --git a/backend/api/index.ts b/backend/api/index.ts deleted file mode 100644 index c53415886f..0000000000 --- a/backend/api/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -console.log("Server booted at", new Date().toISOString()); - -import express from "express"; -import cors from "cors"; -import serverless from "serverless-http"; -import { healthRouter } from "../src/routes/health.js"; - -const app = express(); - -const allowed = (process.env.ALLOWED_ORIGIN ?? "") - .split(",") - .map((s) => s.trim()) - .filter(Boolean); - -app.use( - cors({ - origin: allowed.length ? allowed : true, - credentials: true, - }) -); - -app.use(express.json()); - -// IMPORTANT: the function is already mounted at /api -// so we mount our router at the root ("") here -app.use(healthRouter); - -// Temporary direct route for sanity (now at /api/health-direct) -app.get("/health-direct", (_req, res) => { - console.log("Hit /health-direct"); - res.json({ ok: true, via: "direct" }); -}); - -// 404 + error handlers -app.use((_req, res) => res.status(404).json({ error: "NotFound" })); -app.use((err: unknown, _req: any, res: any, _next: any) => { - console.error("[ERROR]", err); - res.status(500).json({ error: "InternalServerError" }); -}); - -export default serverless(app); diff --git a/backend/src/index.ts b/backend/src/index.ts index 85e0417c85..249b283ed7 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,41 +2,39 @@ console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; -import serverless from "serverless-http"; +// remove serverless-http +// import serverless from "serverless-http"; -// Routers import { healthRouter } from "./routes/health.js"; const app = express(); -// CORS — allow only your deployed frontend (you can comma-separate multiple origins) const allowed = (process.env.ALLOWED_ORIGIN ?? "") .split(",") .map((s) => s.trim()) .filter(Boolean); + app.use( cors({ - origin: allowed.length ? allowed : true, // during local dev if empty, allow all + origin: allowed.length ? allowed : true, credentials: true, }) ); -// Body parsing app.use(express.json()); -// Mount routes under /api +// NOTE: Vercel routes /api/* TO this handler. +// So keep the mount at /api so /api/health resolves correctly. app.use("/api", healthRouter); +// temporary direct test app.get("/api/health-direct", (_req, res) => { + console.log("Hit /api/health-direct"); res.json({ ok: true, via: "direct" }); -}); // for quick testing without router - -// Simple not-found handler -app.use((_req, res) => { - res.status(404).json({ error: "NotFound" }); }); -// Centralized error handler (Express 5 catches async errors) +// 404 + error handlers +app.use((_req, res) => res.status(404).json({ error: "NotFound" })); app.use( ( err: unknown, @@ -49,5 +47,5 @@ app.use( } ); -// Export serverless handler for Vercel -export default serverless(app); +// ✅ Export the Express app itself. Vercel's Node runtime can call it directly. +export default app; diff --git a/backend/vercel.json b/backend/vercel.json new file mode 100644 index 0000000000..0ecf1b1b1d --- /dev/null +++ b/backend/vercel.json @@ -0,0 +1,4 @@ +{ + "builds": [{ "src": "src/index.ts", "use": "@vercel/node" }], + "routes": [{ "src": "/api/(.*)", "dest": "src/index.ts" }] +} From 8b75be29cb10fe246dd6ce7e51f6c5f6800cb7ae Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 10:17:29 +0200 Subject: [PATCH 012/215] clean project from using serverlesshttp and some more tidy-ups, also updated to node 22 --- backend/package.json | 4 +++- backend/src/index.ts | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/package.json b/backend/package.json index 7002bd59fe..d66062703d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,6 @@ "express": "^5.1.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.18.1", - "serverless-http": "^4.0.0", "swagger-ui-express": "^5.0.1", "zod": "^4.1.5" }, @@ -24,5 +23,8 @@ "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20.19.13", "typescript": "^5.9.2" + }, + "engines": { + "node": "22.x" } } diff --git a/backend/src/index.ts b/backend/src/index.ts index 249b283ed7..3f683a08f9 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,8 +2,6 @@ console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; -// remove serverless-http -// import serverless from "serverless-http"; import { healthRouter } from "./routes/health.js"; From 4f5c1976d3bf58ea03a37ec3d643caae75e05f8b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 13:58:40 +0200 Subject: [PATCH 013/215] add db-check route to backend API --- backend/src/index.ts | 13 ++++++++----- backend/src/routes/dbCheck.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 backend/src/routes/dbCheck.ts diff --git a/backend/src/index.ts b/backend/src/index.ts index 3f683a08f9..f2315952f1 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -4,9 +4,12 @@ import express from "express"; import cors from "cors"; import { healthRouter } from "./routes/health.js"; +import { dbCheckRouter } from "./routes/dbCheck.js"; +// 1) Create app first const app = express(); +// 2) Global middleware (before routers) const allowed = (process.env.ALLOWED_ORIGIN ?? "") .split(",") .map((s) => s.trim()) @@ -21,17 +24,17 @@ app.use( app.use(express.json()); -// NOTE: Vercel routes /api/* TO this handler. -// So keep the mount at /api so /api/health resolves correctly. +// 3) Routers (mounted under /api) app.use("/api", healthRouter); +app.use("/api", dbCheckRouter); -// temporary direct test +// 4) Temporary direct test app.get("/api/health-direct", (_req, res) => { console.log("Hit /api/health-direct"); res.json({ ok: true, via: "direct" }); }); -// 404 + error handlers +// 5) 404 + error handlers app.use((_req, res) => res.status(404).json({ error: "NotFound" })); app.use( ( @@ -45,5 +48,5 @@ app.use( } ); -// ✅ Export the Express app itself. Vercel's Node runtime can call it directly. +// 6) Export the Express app for Vercel export default app; diff --git a/backend/src/routes/dbCheck.ts b/backend/src/routes/dbCheck.ts new file mode 100644 index 0000000000..6c0315376c --- /dev/null +++ b/backend/src/routes/dbCheck.ts @@ -0,0 +1,27 @@ +import { Router, Request, Response } from "express"; +import mongoose from "mongoose"; + +export const dbCheckRouter = Router(); + +dbCheckRouter.get("/db-check", async (_req: Request, res: Response) => { + try { + const state = mongoose.connection.readyState; + // 0 = disconnected, 1 = connected, 2 = connecting, 3 = disconnecting + + res.json({ + ok: state === 1, + state, + message: + state === 1 + ? "MongoDB is connected" + : state === 2 + ? "MongoDB is connecting..." + : state === 3 + ? "MongoDB is disconnecting..." + : "MongoDB is disconnected", + }); + } catch (err) { + console.error("DB check error:", err); + res.status(500).json({ ok: false, error: "DB check failed" }); + } +}); From 542bcf783f228ac3a504bea6d9e3dcdfe7d3d122 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 14:06:06 +0200 Subject: [PATCH 014/215] fix(api): ensure /db-check forces connectDB before reporting --- backend/src/index.ts | 6 +++++- backend/src/lib/db.ts | 22 ++++++++++++++++++++++ backend/src/routes/dbCheck.ts | 6 ++++-- 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 backend/src/lib/db.ts diff --git a/backend/src/index.ts b/backend/src/index.ts index f2315952f1..c4c2979377 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,9 +2,13 @@ console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; - import { healthRouter } from "./routes/health.js"; import { dbCheckRouter } from "./routes/dbCheck.js"; +import { connectDB } from "./lib/db.js"; + +connectDB() + .then(() => console.log("Mongo connected")) + .catch((e) => console.error("Mongo error", e)); // 1) Create app first const app = express(); diff --git a/backend/src/lib/db.ts b/backend/src/lib/db.ts new file mode 100644 index 0000000000..cdc28df22a --- /dev/null +++ b/backend/src/lib/db.ts @@ -0,0 +1,22 @@ +import mongoose from "mongoose"; + +let cached = (global as any)._mongoose as + | { conn: typeof mongoose | null; promise: Promise | null } + | undefined; + +if (!cached) { + cached = (global as any)._mongoose = { conn: null, promise: null }; +} + +export async function connectDB() { + if (cached!.conn) return cached!.conn; + + if (!cached!.promise) { + const uri = process.env.MONGODB_URI; + if (!uri) throw new Error("MONGODB_URI missing"); + cached!.promise = mongoose.connect(uri).then((m) => m); + } + + cached!.conn = await cached!.promise; + return cached!.conn; +} diff --git a/backend/src/routes/dbCheck.ts b/backend/src/routes/dbCheck.ts index 6c0315376c..4893c87614 100644 --- a/backend/src/routes/dbCheck.ts +++ b/backend/src/routes/dbCheck.ts @@ -1,12 +1,14 @@ import { Router, Request, Response } from "express"; import mongoose from "mongoose"; +import { connectDB } from "../lib/db.js"; export const dbCheckRouter = Router(); dbCheckRouter.get("/db-check", async (_req: Request, res: Response) => { try { - const state = mongoose.connection.readyState; - // 0 = disconnected, 1 = connected, 2 = connecting, 3 = disconnecting + // Ensure we attempt a connection before reporting + await connectDB(); + const state = mongoose.connection.readyState; // 0=disconnected, 1=connected, 2=connecting, 3=disconnecting res.json({ ok: state === 1, From 7d453c5e2c0e853b9273cb59481e615e375bf21a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:14:09 +0200 Subject: [PATCH 015/215] fix(types): strongly type User mongoose model to avoid union overloads --- backend/src/middleware/auth.ts | 30 ++++++++++++++++ backend/src/models/User.ts | 16 +++++++++ backend/src/routes/auth.ts | 63 ++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 backend/src/middleware/auth.ts create mode 100644 backend/src/models/User.ts create mode 100644 backend/src/routes/auth.ts diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts new file mode 100644 index 0000000000..85da6bed09 --- /dev/null +++ b/backend/src/middleware/auth.ts @@ -0,0 +1,30 @@ +import { Request, Response, NextFunction } from "express"; +import jwt from "jsonwebtoken"; + +export interface AuthedRequest extends Request { + user?: { sub: string; email: string }; +} + +export function requireAuth( + req: AuthedRequest, + res: Response, + next: NextFunction +) { + try { + const header = req.headers.authorization; // "Bearer " + if (!header) return res.status(401).json({ error: "NoAuthHeader" }); + + const [type, token] = header.split(" "); + if (type !== "Bearer" || !token) + return res.status(401).json({ error: "InvalidAuthHeader" }); + + const payload = jwt.verify(token, process.env.JWT_SECRET!) as { + sub: string; + email: string; + }; + req.user = payload; + next(); + } catch { + return res.status(401).json({ error: "InvalidToken" }); + } +} diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts new file mode 100644 index 0000000000..f1d4faac8e --- /dev/null +++ b/backend/src/models/User.ts @@ -0,0 +1,16 @@ +import { Schema, model, models, InferSchemaType, Model } from "mongoose"; + +const userSchema = new Schema( + { + email: { type: String, unique: true, required: true, index: true }, + passwordHash: { type: String, required: true }, + }, + { timestamps: true } +); + +// Document type inferred from schema +export type UserDoc = InferSchemaType; + +// Single, strongly-typed Model (no union) +export const User: Model = + (models.User as Model) ?? model("User", userSchema); diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts new file mode 100644 index 0000000000..630dd993ff --- /dev/null +++ b/backend/src/routes/auth.ts @@ -0,0 +1,63 @@ +import { Router } from "express"; +import { z } from "zod"; +import bcrypt from "bcrypt"; +import jwt from "jsonwebtoken"; +import { User } from "../models/User.js"; + +export const authRouter = Router(); + +const zCreds = z.object({ + email: z.string().email(), + password: z.string().min(8, "Password must be at least 8 characters"), +}); + +// POST /api/auth/register +authRouter.post("/register", async (req, res) => { + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + + const { email, password } = parsed.data; + + const exists = await User.findOne({ email }); + if (exists) return res.status(409).json({ error: "EmailInUse" }); + + const passwordHash = await bcrypt.hash(password, 12); + await User.create({ email, passwordHash }); + + return res.status(201).json({ ok: true }); +}); + +// POST /api/auth/login +authRouter.post("/login", async (req, res) => { + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + + const { email, password } = parsed.data; + + const user = await User.findOne({ email }); + if (!user) return res.status(401).json({ error: "InvalidCredentials" }); + + const ok = await bcrypt.compare(password, user.passwordHash); + if (!ok) return res.status(401).json({ error: "InvalidCredentials" }); + + const token = jwt.sign( + { sub: String(user._id), email }, + process.env.JWT_SECRET!, + { expiresIn: "7d" } + ); + return res.json({ token }); +}); + +// GET /api/auth/me (protected example) +authRouter.get("/me", async (req, res) => { + // This endpoint is public by default; we'll mount it behind requireAuth in index.ts + res.json({ + message: "You reached /api/auth/me but auth middleware decides access.", + }); +}); From 7cd340ba1560fbf4c03ddbcd40228cf8747ebc21 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:33:51 +0200 Subject: [PATCH 016/215] feat(auth): mount /api/auth routes and add protected example --- backend/src/index.ts | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index c4c2979377..0d249fdee8 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -2,18 +2,15 @@ console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; + import { healthRouter } from "./routes/health.js"; import { dbCheckRouter } from "./routes/dbCheck.js"; -import { connectDB } from "./lib/db.js"; - -connectDB() - .then(() => console.log("Mongo connected")) - .catch((e) => console.error("Mongo error", e)); +import { authRouter } from "./routes/auth.js"; +import { requireAuth } from "./middleware/auth.js"; -// 1) Create app first const app = express(); -// 2) Global middleware (before routers) +// --- Global middleware --- const allowed = (process.env.ALLOWED_ORIGIN ?? "") .split(",") .map((s) => s.trim()) @@ -21,24 +18,37 @@ const allowed = (process.env.ALLOWED_ORIGIN ?? "") app.use( cors({ - origin: allowed.length ? allowed : true, + origin: allowed.length ? allowed : true, // during local dev if empty, allow all credentials: true, }) ); app.use(express.json()); -// 3) Routers (mounted under /api) +// --- Public routes (mounted under /api) --- app.use("/api", healthRouter); app.use("/api", dbCheckRouter); -// 4) Temporary direct test +// 🔐 Auth routes (public endpoints: /register, /login) +app.use("/api/auth", authRouter); + +// 🔒 Example protected endpoints (require a Bearer token) +app.get("/api/protected/ping", requireAuth, (req, res) => { + res.json({ ok: true, user: (req as any).user }); +}); + +// To protect /api/auth/me this can also be used +app.get("/api/auth/me", requireAuth, (req, res) => { + res.json({ user: (req as any).user }); +}); + +// Temporary direct test app.get("/api/health-direct", (_req, res) => { console.log("Hit /api/health-direct"); res.json({ ok: true, via: "direct" }); }); -// 5) 404 + error handlers +// --- 404 + error handlers --- app.use((_req, res) => res.status(404).json({ error: "NotFound" })); app.use( ( @@ -52,5 +62,4 @@ app.use( } ); -// 6) Export the Express app for Vercel export default app; From 128bf0292682965cbb5b72ca7c1372203e15ddd1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:38:50 +0200 Subject: [PATCH 017/215] add a quick ping route inside the auth router --- backend/src/routes/auth.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 630dd993ff..3db07ee8e8 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -6,6 +6,8 @@ import { User } from "../models/User.js"; export const authRouter = Router(); +authRouter.get("/ping", (_req, res) => res.json({ ok: true, from: "auth" })); + const zCreds = z.object({ email: z.string().email(), password: z.string().min(8, "Password must be at least 8 characters"), From c0eddbe2f856ffe14312f186b365c1fec3ae11d8 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:48:26 +0200 Subject: [PATCH 018/215] fix(mongoose): use mongoose.models via default export for ESM runtime --- backend/src/models/User.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index f1d4faac8e..1e72380f84 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -1,4 +1,4 @@ -import { Schema, model, models, InferSchemaType, Model } from "mongoose"; +import mongoose, { Schema, model, InferSchemaType, Model } from "mongoose"; const userSchema = new Schema( { @@ -8,9 +8,10 @@ const userSchema = new Schema( { timestamps: true } ); -// Document type inferred from schema +// Infer the document shape from the schema export type UserDoc = InferSchemaType; -// Single, strongly-typed Model (no union) +// Always return a single, strongly-typed Model (no union) export const User: Model = - (models.User as Model) ?? model("User", userSchema); + (mongoose.models.User as Model) ?? + model("User", userSchema); From d7fae205d0c13a390dc08c7da3b55575357de198 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:54:10 +0200 Subject: [PATCH 019/215] fix(auth): switch to bcryptjs for serverless compatibility --- backend/package.json | 2 +- backend/src/routes/auth.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index d66062703d..43eea84492 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,7 +8,7 @@ "start": "node dist/index.js" }, "dependencies": { - "bcrypt": "^6.0.0", + "bcryptjs": "^3.0.2", "cors": "^2.8.5", "express": "^5.1.0", "jsonwebtoken": "^9.0.2", diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 3db07ee8e8..8971e56545 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -1,6 +1,6 @@ import { Router } from "express"; import { z } from "zod"; -import bcrypt from "bcrypt"; +import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import { User } from "../models/User.js"; From 70bdaa7e8f173aeeae03b4f25f9859e820d1417f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:01:56 +0200 Subject: [PATCH 020/215] fix(auth): ensure Mongo connect before queries and add error logging --- backend/src/index.ts | 5 +++ backend/src/routes/auth.ts | 75 +++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 0d249fdee8..e506498645 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -7,6 +7,11 @@ import { healthRouter } from "./routes/health.js"; import { dbCheckRouter } from "./routes/dbCheck.js"; import { authRouter } from "./routes/auth.js"; import { requireAuth } from "./middleware/auth.js"; +import { connectDB } from "./lib/db.js"; + +connectDB() + .then(() => console.log("Mongo connected")) + .catch((e) => console.error("Mongo error", e)); const app = express(); diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index 8971e56545..b2a0eacd61 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import bcrypt from "bcryptjs"; import jwt from "jsonwebtoken"; import { User } from "../models/User.js"; +import { connectDB } from "../lib/db.js"; export const authRouter = Router(); @@ -15,51 +16,57 @@ const zCreds = z.object({ // POST /api/auth/register authRouter.post("/register", async (req, res) => { - const parsed = zCreds.safeParse(req.body); - if (!parsed.success) - return res - .status(400) - .json({ error: "InvalidBody", details: parsed.error.flatten() }); + try { + await connectDB(); - const { email, password } = parsed.data; + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); - const exists = await User.findOne({ email }); - if (exists) return res.status(409).json({ error: "EmailInUse" }); + const { email, password } = parsed.data; - const passwordHash = await bcrypt.hash(password, 12); - await User.create({ email, passwordHash }); + const exists = await User.findOne({ email }); + if (exists) return res.status(409).json({ error: "EmailInUse" }); - return res.status(201).json({ ok: true }); + const passwordHash = await bcrypt.hash(password, 12); + await User.create({ email, passwordHash }); + + return res.status(201).json({ ok: true }); + } catch (err) { + console.error("REGISTER_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } }); // POST /api/auth/login authRouter.post("/login", async (req, res) => { - const parsed = zCreds.safeParse(req.body); - if (!parsed.success) - return res - .status(400) - .json({ error: "InvalidBody", details: parsed.error.flatten() }); + try { + await connectDB(); - const { email, password } = parsed.data; + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); - const user = await User.findOne({ email }); - if (!user) return res.status(401).json({ error: "InvalidCredentials" }); + const { email, password } = parsed.data; - const ok = await bcrypt.compare(password, user.passwordHash); - if (!ok) return res.status(401).json({ error: "InvalidCredentials" }); + const user = await User.findOne({ email }); + if (!user) return res.status(401).json({ error: "InvalidCredentials" }); - const token = jwt.sign( - { sub: String(user._id), email }, - process.env.JWT_SECRET!, - { expiresIn: "7d" } - ); - return res.json({ token }); -}); + const ok = await bcrypt.compare(password, user.passwordHash); + if (!ok) return res.status(401).json({ error: "InvalidCredentials" }); -// GET /api/auth/me (protected example) -authRouter.get("/me", async (req, res) => { - // This endpoint is public by default; we'll mount it behind requireAuth in index.ts - res.json({ - message: "You reached /api/auth/me but auth middleware decides access.", - }); + const token = jwt.sign( + { sub: String(user._id), email }, + process.env.JWT_SECRET!, + { expiresIn: "7d" } + ); + return res.json({ token }); + } catch (err) { + console.error("LOGIN_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } }); From 3ad679909fcf52cabdd16fa4d81abf7aff255430 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 21:36:07 +0200 Subject: [PATCH 021/215] feat(favorites): add Favorite model and protected CRUD routes --- backend/src/index.ts | 2 + backend/src/models/Favorite.ts | 25 +++++++++++ backend/src/routes/favorites.ts | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 backend/src/models/Favorite.ts create mode 100644 backend/src/routes/favorites.ts diff --git a/backend/src/index.ts b/backend/src/index.ts index e506498645..79df085783 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -8,6 +8,7 @@ import { dbCheckRouter } from "./routes/dbCheck.js"; import { authRouter } from "./routes/auth.js"; import { requireAuth } from "./middleware/auth.js"; import { connectDB } from "./lib/db.js"; +import { favoritesRouter } from "./routes/favorites.js"; connectDB() .then(() => console.log("Mongo connected")) @@ -33,6 +34,7 @@ app.use(express.json()); // --- Public routes (mounted under /api) --- app.use("/api", healthRouter); app.use("/api", dbCheckRouter); +app.use("/api", favoritesRouter); // 🔐 Auth routes (public endpoints: /register, /login) app.use("/api/auth", authRouter); diff --git a/backend/src/models/Favorite.ts b/backend/src/models/Favorite.ts new file mode 100644 index 0000000000..5815e62f97 --- /dev/null +++ b/backend/src/models/Favorite.ts @@ -0,0 +1,25 @@ +import mongoose, { Schema, model, InferSchemaType, Model } from "mongoose"; + +const favoriteSchema = new Schema( + { + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + index: true, + }, + beachId: { type: String, required: true }, // HaV beach identifier (string) + note: { type: String, default: "" }, // optional user note + order: { type: Number, default: 0 }, // for drag & drop later + }, + { timestamps: true } +); + +// Prevent duplicates: one user can favorite each beach only once +favoriteSchema.index({ userId: 1, beachId: 1 }, { unique: true }); + +export type FavoriteDoc = InferSchemaType; + +export const Favorite: Model = + (mongoose.models.Favorite as Model) ?? + model("Favorite", favoriteSchema); diff --git a/backend/src/routes/favorites.ts b/backend/src/routes/favorites.ts new file mode 100644 index 0000000000..b207a187ce --- /dev/null +++ b/backend/src/routes/favorites.ts @@ -0,0 +1,80 @@ +import { Router } from "express"; +import { z } from "zod"; +import { Favorite } from "../models/Favorite.js"; +import { requireAuth, AuthedRequest } from "../middleware/auth.js"; +import { connectDB } from "../lib/db.js"; + +export const favoritesRouter = Router(); + +const zCreate = z.object({ + beachId: z.string().min(1), + note: z.string().max(500).optional(), +}); + +favoritesRouter.use(async (_req, _res, next) => { + // ensure DB connection for every favorites op + try { + await connectDB(); + next(); + } catch (e) { + next(e); + } +}); + +// GET /api/favorites -> list current user's favorites +favoritesRouter.get( + "/favorites", + requireAuth, + async (req: AuthedRequest, res) => { + const userId = req.user!.sub; + const items = await Favorite.find({ userId }) + .sort({ order: 1, createdAt: -1 }) + .lean(); + res.json(items); + } +); + +// POST /api/favorites -> add a favorite +favoritesRouter.post( + "/favorites", + requireAuth, + async (req: AuthedRequest, res) => { + const parsed = zCreate.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + + const { beachId, note } = parsed.data; + const userId = req.user!.sub; + + try { + const created = await Favorite.create({ + userId, + beachId, + note: note ?? "", + }); + return res.status(201).json(created); + } catch (err: any) { + // Handle duplicate key (already favorited) + if (err?.code === 11000) { + return res.status(409).json({ error: "AlreadyFavorited" }); + } + throw err; + } + } +); + +// DELETE /api/favorites/:id -> remove a favorite by its _id +favoritesRouter.delete( + "/favorites/:id", + requireAuth, + async (req: AuthedRequest, res) => { + const userId = req.user!.sub; + const { id } = req.params; + + const deleted = await Favorite.findOneAndDelete({ _id: id, userId }); + if (!deleted) return res.status(404).json({ error: "NotFound" }); + res.json({ ok: true }); + } +); From 9cd2592588eb73f9fd82ce7ca752e8ea2ea01a16 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 11 Sep 2025 23:09:09 +0200 Subject: [PATCH 022/215] chore(favorites): fix TS handler types (cast req to AuthedRequest) and switch to z.treeifyError --- backend/src/middleware/auth.ts | 10 ++-- backend/src/routes/favorites.ts | 95 ++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 50 deletions(-) diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts index 85da6bed09..6c0a21ba11 100644 --- a/backend/src/middleware/auth.ts +++ b/backend/src/middleware/auth.ts @@ -2,14 +2,10 @@ import { Request, Response, NextFunction } from "express"; import jwt from "jsonwebtoken"; export interface AuthedRequest extends Request { - user?: { sub: string; email: string }; + user: { sub: string; email: string }; // ← non-optional } -export function requireAuth( - req: AuthedRequest, - res: Response, - next: NextFunction -) { +export function requireAuth(req: Request, res: Response, next: NextFunction) { try { const header = req.headers.authorization; // "Bearer " if (!header) return res.status(401).json({ error: "NoAuthHeader" }); @@ -22,7 +18,7 @@ export function requireAuth( sub: string; email: string; }; - req.user = payload; + (req as AuthedRequest).user = payload; // set strongly-typed user next(); } catch { return res.status(401).json({ error: "InvalidToken" }); diff --git a/backend/src/routes/favorites.ts b/backend/src/routes/favorites.ts index b207a187ce..7a07108ec5 100644 --- a/backend/src/routes/favorites.ts +++ b/backend/src/routes/favorites.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import { Favorite } from "../models/Favorite.js"; import { requireAuth, AuthedRequest } from "../middleware/auth.js"; import { connectDB } from "../lib/db.js"; +import mongoose from "mongoose"; export const favoritesRouter = Router(); @@ -22,58 +23,66 @@ favoritesRouter.use(async (_req, _res, next) => { }); // GET /api/favorites -> list current user's favorites -favoritesRouter.get( - "/favorites", - requireAuth, - async (req: AuthedRequest, res) => { - const userId = req.user!.sub; - const items = await Favorite.find({ userId }) - .sort({ order: 1, createdAt: -1 }) - .lean(); - res.json(items); - } -); +favoritesRouter.get("/favorites", requireAuth, async (req, res) => { + const userId = (req as AuthedRequest).user.sub; + const items = await Favorite.find({ userId }) + .sort({ order: 1, createdAt: -1 }) + .lean(); + res.json(items); +}); -// POST /api/favorites -> add a favorite -favoritesRouter.post( - "/favorites", - requireAuth, - async (req: AuthedRequest, res) => { - const parsed = zCreate.safeParse(req.body); - if (!parsed.success) - return res - .status(400) - .json({ error: "InvalidBody", details: parsed.error.flatten() }); +// POST /api/favorites +favoritesRouter.post("/favorites", requireAuth, async (req, res) => { + const parsed = zCreate.safeParse(req.body); + if (!parsed.success) { + return res + .status(400) + .json({ error: "InvalidBody", details: z.treeifyError(parsed.error) }); + } - const { beachId, note } = parsed.data; - const userId = req.user!.sub; + const { beachId, note } = parsed.data; + const userId = (req as AuthedRequest).user.sub; - try { - const created = await Favorite.create({ - userId, - beachId, - note: note ?? "", - }); - return res.status(201).json(created); - } catch (err: any) { - // Handle duplicate key (already favorited) - if (err?.code === 11000) { - return res.status(409).json({ error: "AlreadyFavorited" }); - } - throw err; + try { + const created = await Favorite.create({ + userId, + beachId, + note: note ?? "", + }); + return res.status(201).json(created); + } catch (err: any) { + if (err?.code === 11000) { + return res.status(409).json({ error: "AlreadyFavorited" }); } + throw err; } -); +}); + +// DELETE /api/favorites/:id +favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { + const userId = (req as AuthedRequest).user.sub; + + const id = req.params.id as string | undefined; + if (!id) return res.status(400).json({ error: "MissingId" }); + + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ error: "InvalidId" }); + } + + const deleted = await Favorite.findOneAndDelete({ _id: id, userId }); + if (!deleted) return res.status(404).json({ error: "NotFound" }); + res.json({ ok: true }); +}); -// DELETE /api/favorites/:id -> remove a favorite by its _id +// DELETE /api/favorites/by-beach/:beachId favoritesRouter.delete( - "/favorites/:id", + "/favorites/by-beach/:beachId", requireAuth, - async (req: AuthedRequest, res) => { - const userId = req.user!.sub; - const { id } = req.params; + async (req, res) => { + const userId = (req as AuthedRequest).user.sub; + const { beachId } = req.params; - const deleted = await Favorite.findOneAndDelete({ _id: id, userId }); + const deleted = await Favorite.findOneAndDelete({ beachId, userId }); if (!deleted) return res.status(404).json({ error: "NotFound" }); res.json({ ok: true }); } From 6206ce5ac0637691b1d9a9966bdd88d582063678 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 15:55:55 +0200 Subject: [PATCH 023/215] add memory cache + fetch wrapper --- backend/src/lib/cache.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 backend/src/lib/cache.ts diff --git a/backend/src/lib/cache.ts b/backend/src/lib/cache.ts new file mode 100644 index 0000000000..adf5f140e6 --- /dev/null +++ b/backend/src/lib/cache.ts @@ -0,0 +1,23 @@ +type Entry = { value: T; expiry: number }; + +export class SimpleCache { + private store = new Map>(); + + constructor(private defaultTtlMs = 60_000) {} // default 60s + + get(key: string): T | undefined { + const hit = this.store.get(key); + if (!hit) return; + if (Date.now() > hit.expiry) { + this.store.delete(key); + return; + } + return hit.value as T; + } + + set(key: string, value: T, ttlMs = this.defaultTtlMs) { + this.store.set(key, { value, expiry: Date.now() + ttlMs }); + } +} + +export const cache = new SimpleCache(60_000); // 60s default From dbf25c25e7737540d77a00fd8a57d004527d36be Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:04:56 +0200 Subject: [PATCH 024/215] add hav.ts --- backend/src/lib/hav.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 backend/src/lib/hav.ts diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts new file mode 100644 index 0000000000..801c4045bb --- /dev/null +++ b/backend/src/lib/hav.ts @@ -0,0 +1,34 @@ +import { cache } from "./cache.js"; + +const HAV_BASE_URL = process.env.HAV_BASE_URL!; +const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; + +// Helper to build a cache key +function key(path: string) { + return `hav:${path}`; +} + +// Generic fetch wrapper with caching +export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { + const k = key(path); + const cached = cache.get(k); + if (cached) { + return cached; // ✅ serve from memory if not expired + } + + const url = `${HAV_BASE_URL}${path}`; + const res = await fetch(url, { + headers: { + "User-Agent": HAV_USER_AGENT, + Accept: "application/json", + }, + }); + + if (!res.ok) { + throw new Error(`HaV API error: ${res.status} ${res.statusText}`); + } + + const data = await res.json(); + cache.set(k, data, ttlMs); // ✅ cache result + return data; +} From a89c2d4a7e0b3729bc2f5ca5b4cbdca4acf79444 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:07:14 +0200 Subject: [PATCH 025/215] update tsconfig in backend to use node18 features and include DOM types --- backend/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 5ee860e330..fafc27f38f 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -14,6 +14,7 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "skipLibCheck": true, + "lib": ["ES2022", "DOM"], /* Type Checking */ "strict": true, From fea7d346ca326b714943d142abf07427009671ad Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:10:15 +0200 Subject: [PATCH 026/215] chore(backend): relax Node engine to >=18 for local + Vercel compatibility --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 43eea84492..2123a0ef93 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,6 @@ "typescript": "^5.9.2" }, "engines": { - "node": "22.x" + "node": ">=18" } } From 3d25d5fab386d5647b0391cf7251da449ca85ad0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:11:28 +0200 Subject: [PATCH 027/215] chore(backend): relax Node engine to >=18 and remove unused @types/bcrypt --- backend/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 2123a0ef93..6dfe9244a0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,7 +17,6 @@ "zod": "^4.1.5" }, "devDependencies": { - "@types/bcrypt": "^5.0.2", "@types/cors": "^2.8.19", "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", From bdc3169d75d8a46c38d7d71d7affbbecca44605c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:19:05 +0200 Subject: [PATCH 028/215] feat(backend): add /api/beaches routes to proxy HaV API --- backend/src/index.ts | 2 ++ backend/src/routes/beaches.ts | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 backend/src/routes/beaches.ts diff --git a/backend/src/index.ts b/backend/src/index.ts index 79df085783..5fe1772818 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -9,6 +9,7 @@ import { authRouter } from "./routes/auth.js"; import { requireAuth } from "./middleware/auth.js"; import { connectDB } from "./lib/db.js"; import { favoritesRouter } from "./routes/favorites.js"; +import { beachesRouter } from "./routes/beaches.js"; connectDB() .then(() => console.log("Mongo connected")) @@ -35,6 +36,7 @@ app.use(express.json()); app.use("/api", healthRouter); app.use("/api", dbCheckRouter); app.use("/api", favoritesRouter); +app.use("/api", beachesRouter); // 🔐 Auth routes (public endpoints: /register, /login) app.use("/api/auth", authRouter); diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts new file mode 100644 index 0000000000..0a86442f7e --- /dev/null +++ b/backend/src/routes/beaches.ts @@ -0,0 +1,25 @@ +import { Router } from "express"; +import { havGet } from "../lib/hav.js"; + +export const beachesRouter = Router(); + +// GET /api/beaches → list all beaches (cached) +beachesRouter.get("/beaches", async (_req, res, next) => { + try { + const data = await havGet("/BathingSites"); + res.json(data); + } catch (err) { + next(err); + } +}); + +// GET /api/beaches/:id → single beach by id +beachesRouter.get("/beaches/:id", async (req, res, next) => { + try { + const { id } = req.params; + const data = await havGet(`/BathingSites/${id}`); + res.json(data); + } catch (err) { + next(err); + } +}); From 219c55b250ee0f4306ec5d3ecc29eaf9b1ebdd26 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 16:42:10 +0200 Subject: [PATCH 029/215] feat(backend): wire /api/beaches to HaV feature & detail endpoints --- backend/src/routes/beaches.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts index 0a86442f7e..cdbbeb736b 100644 --- a/backend/src/routes/beaches.ts +++ b/backend/src/routes/beaches.ts @@ -3,21 +3,33 @@ import { havGet } from "../lib/hav.js"; export const beachesRouter = Router(); -// GET /api/beaches → list all beaches (cached) +/** + * GET /api/beaches + * Proxies HaV "feature" endpoint and returns (likely) GeoJSON. + * I keep it as a thin proxy for now; later I can normalize + filter. + */ beachesRouter.get("/beaches", async (_req, res, next) => { try { - const data = await havGet("/BathingSites"); + // Easiest JSON form; if upstream returns XML, will switch to explicit WFS params. + const data = await havGet("/feature/?format=json", 5 * 60 * 1000); // cache 5 min res.json(data); } catch (err) { next(err); } }); -// GET /api/beaches/:id → single beach by id +/** + * GET /api/beaches/:id + * Proxies HaV detail endpoint for a single bathing site by ID. + * Example ID: SE0441273000000001 + */ beachesRouter.get("/beaches/:id", async (req, res, next) => { try { const { id } = req.params; - const data = await havGet(`/BathingSites/${id}`); + const data = await havGet( + `/detail/${encodeURIComponent(id)}`, + 5 * 60 * 1000 + ); res.json(data); } catch (err) { next(err); From ff99d9e4ff3f18b30f70ca4d95d0ccf56c1c576d Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:44:25 +0200 Subject: [PATCH 030/215] feat(frontend): add types folder and beaches.ts with GeoJSON types + mappers --- frontend/src/types/beaches.ts | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 frontend/src/types/beaches.ts diff --git a/frontend/src/types/beaches.ts b/frontend/src/types/beaches.ts new file mode 100644 index 0000000000..0f88063dc2 --- /dev/null +++ b/frontend/src/types/beaches.ts @@ -0,0 +1,70 @@ +// GeoJSON + domain types for beaches + +export type LngLat = [number, number]; // GeoJSON order: [lon, lat] + +// --- GeoJSON (list) --- +export interface BeachFeatureProperties { + NUTSKOD: string; // e.g. "SE0441273000000001" (stable beach ID) + NAMN: string; // e.g. "Hökarängsbadet, Drevviken" + KMN_NAMN?: string; // municipality (optional) +} + +export interface BeachFeature { + type: "Feature"; + id?: string; + geometry: { type: "Point"; coordinates: LngLat }; + geometry_name?: string; + properties: BeachFeatureProperties; +} + +export interface BeachFeatureCollection { + type: "FeatureCollection"; + features: BeachFeature[]; +} + +// Simplified list item for UI +export interface BeachSummary { + id: string; // NUTSKOD + name: string; // NAMN + municipality?: string; // KMN_NAMN + lat: number; + lng: number; +} + +export function featureToSummary(f: BeachFeature): BeachSummary | null { + const [lng, lat] = f.geometry?.coordinates ?? []; + if (typeof lat !== "number" || typeof lng !== "number") return null; + return { + id: f.properties.NUTSKOD, + name: f.properties.NAMN, + municipality: f.properties.KMN_NAMN, + lat, + lng, + }; +} + +// --- Detail shape (plain JSON from /detail/:id) --- +export interface BeachQualityRating { + qualityRating: number; // 1..4 + qualityRatingText: string; // "Utmärkt kvalitet" | "Bra kvalitet" | ... + ratingYear: number; +} + +export interface BeachDetail { + algalText?: string; + algalValue?: number; + bathInformation?: string; + classification?: number; // 1..4 (current year) + classificationText?: string; // e.g. "Bra kvalitet" + classificationYear?: number; + contactMail?: string; + contactPhone?: string; + contactUrl?: string; + dissuasion?: string[]; + euMotive?: string; + euType?: boolean; + locationArea?: string; // municipality/area + locationName?: string; // beach name + nutsCode: string; // stable id (same as NUTSKOD) + qualityRating?: BeachQualityRating[]; +} From 9891798920bae1e0a7ffaf6e6349b9969c3f76fb Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:34:17 +0200 Subject: [PATCH 031/215] feat(frontend): add beaches types and setup VITE_API_BASE in .env --- frontend/src/api/beaches.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 frontend/src/api/beaches.ts diff --git a/frontend/src/api/beaches.ts b/frontend/src/api/beaches.ts new file mode 100644 index 0000000000..e0f4fc8179 --- /dev/null +++ b/frontend/src/api/beaches.ts @@ -0,0 +1,25 @@ +import { + BeachSummary, + BeachDetail, + mapFeatureToBeachSummary, +} from "../types/beaches"; + +// Helper: fetch JSON with error handling +async function apiGet(path: string): Promise { + const res = await fetch(`${import.meta.env.VITE_BACKEND_URL}${path}`); + if (!res.ok) { + throw new Error(`API error ${res.status}`); + } + return res.json() as Promise; +} + +// List all beaches (summary) +export async function fetchBeaches(): Promise { + const data = await apiGet<{ type: string; features: any[] }>("/api/beaches"); + return data.features.map(mapFeatureToBeachSummary); +} + +// Get a single beach detail +export async function fetchBeach(id: string): Promise { + return apiGet(`/api/beaches/${id}`); +} From 299feea01a9e09c5e5de47fd5ea6fa6987e18c57 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:36:13 +0200 Subject: [PATCH 032/215] feat(frontend): add beaches quality label utils --- frontend/src/utils/quality.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 frontend/src/utils/quality.ts diff --git a/frontend/src/utils/quality.ts b/frontend/src/utils/quality.ts new file mode 100644 index 0000000000..cef2a3b65c --- /dev/null +++ b/frontend/src/utils/quality.ts @@ -0,0 +1,21 @@ +/** + * HaV uses numeric classification codes: + * 1 = Utmärkt kvalitet / Excellent quality + * 2 = Bra kvalitet / Good quality + * 3 = Tillfredsställande kvalitet / Sufficient quality + * 4 = Dålig kvalitet / Poor quality + */ +export function getQualityLabel( + code: number, + lang: "sv" | "en" = "sv" +): string { + const map: Record = { + 1: { sv: "Utmärkt kvalitet", en: "Excellent quality" }, + 2: { sv: "Bra kvalitet", en: "Good quality" }, + 3: { sv: "Tillfredsställande kvalitet", en: "Sufficient quality" }, + 4: { sv: "Dålig kvalitet", en: "Poor quality" }, + }; + const entry = map[code]; + if (!entry) return lang === "sv" ? "Okänd" : "Unknown"; + return entry[lang]; +} From 069a876a135e14d80de9fbb3348c04ad4bafe43e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:48:27 +0200 Subject: [PATCH 033/215] chore(frontend): standardize on VITE_API_BASE and remove VITE_BACKEND_URL --- frontend/src/api/beaches.ts | 17 +++++++++++------ frontend/src/hooks/useBeaches.ts | 21 +++++++++++++++++++++ frontend/src/vite-env.d.ts | 9 +++++++++ package.json | 5 ++++- 4 files changed, 45 insertions(+), 7 deletions(-) create mode 100644 frontend/src/hooks/useBeaches.ts create mode 100644 frontend/src/vite-env.d.ts diff --git a/frontend/src/api/beaches.ts b/frontend/src/api/beaches.ts index e0f4fc8179..3bbc7e8991 100644 --- a/frontend/src/api/beaches.ts +++ b/frontend/src/api/beaches.ts @@ -1,25 +1,30 @@ +// frontend/src/api/beaches.ts import { BeachSummary, BeachDetail, - mapFeatureToBeachSummary, + BeachFeatureCollection, + featureToSummary, } from "../types/beaches"; // Helper: fetch JSON with error handling async function apiGet(path: string): Promise { - const res = await fetch(`${import.meta.env.VITE_BACKEND_URL}${path}`); + const base = import.meta.env.VITE_API_BASE; // e.g. https://bada-backend.vercel.app/api + const res = await fetch(`${base}${path}`); if (!res.ok) { throw new Error(`API error ${res.status}`); } return res.json() as Promise; } -// List all beaches (summary) +// List all beaches → map GeoJSON features to BeachSummary[] export async function fetchBeaches(): Promise { - const data = await apiGet<{ type: string; features: any[] }>("/api/beaches"); - return data.features.map(mapFeatureToBeachSummary); + const data = await apiGet("/beaches"); + return data.features + .map(featureToSummary) + .filter((x): x is BeachSummary => x !== null); } // Get a single beach detail export async function fetchBeach(id: string): Promise { - return apiGet(`/api/beaches/${id}`); + return apiGet(`/beaches/${id}`); } diff --git a/frontend/src/hooks/useBeaches.ts b/frontend/src/hooks/useBeaches.ts new file mode 100644 index 0000000000..03ef42e153 --- /dev/null +++ b/frontend/src/hooks/useBeaches.ts @@ -0,0 +1,21 @@ +// frontend/src/hooks/useBeaches.ts +import { useQuery } from "@tanstack/react-query"; +import { fetchBeaches, fetchBeach } from "../api/beaches"; +import type { BeachSummary, BeachDetail } from "../types/beaches"; + +export function useBeaches() { + return useQuery({ + queryKey: ["beaches"], + queryFn: fetchBeaches, + staleTime: 5 * 60 * 1000, + }); +} + +export function useBeachDetail(id: string | undefined) { + return useQuery({ + queryKey: ["beach", id], + enabled: !!id, + queryFn: () => fetchBeach(id!), + staleTime: 5 * 60 * 1000, + }); +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000000..03e8021d01 --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_API_BASE: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/package.json b/package.json index 680d190772..6c42e4be67 100644 --- a/package.json +++ b/package.json @@ -3,5 +3,8 @@ "version": "1.0.0", "scripts": { "postinstall": "npm install --prefix backend" + }, + "dependencies": { + "@tanstack/react-query": "^5.87.4" } -} \ No newline at end of file +} From e161abe56731ce4790fb6c9f19605cdb0a2640c7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sat, 13 Sep 2025 23:54:52 +0200 Subject: [PATCH 034/215] chore(frontend): add TS + Vite types, env typing, and fix beaches API imports --- frontend/package.json | 7 ++++--- frontend/src/api/beaches.ts | 9 +++------ frontend/src/vite-env.d.ts | 1 - frontend/tsconfig.json | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 frontend/tsconfig.json diff --git a/frontend/package.json b/frontend/package.json index 7b2747e949..67dabc8b8d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,13 +14,14 @@ "react-dom": "^18.2.0" }, "devDependencies": { - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", + "@types/react": "^18.3.24", + "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^4.0.3", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", - "vite": "^6.3.5" + "typescript": "^5.9.2", + "vite": "^6.3.6" } } diff --git a/frontend/src/api/beaches.ts b/frontend/src/api/beaches.ts index 3bbc7e8991..182aae9b4f 100644 --- a/frontend/src/api/beaches.ts +++ b/frontend/src/api/beaches.ts @@ -1,4 +1,3 @@ -// frontend/src/api/beaches.ts import { BeachSummary, BeachDetail, @@ -10,13 +9,11 @@ import { async function apiGet(path: string): Promise { const base = import.meta.env.VITE_API_BASE; // e.g. https://bada-backend.vercel.app/api const res = await fetch(`${base}${path}`); - if (!res.ok) { - throw new Error(`API error ${res.status}`); - } + if (!res.ok) throw new Error(`API error ${res.status}`); return res.json() as Promise; } -// List all beaches → map GeoJSON features to BeachSummary[] +// List all beaches → BeachSummary[] export async function fetchBeaches(): Promise { const data = await apiGet("/beaches"); return data.features @@ -24,7 +21,7 @@ export async function fetchBeaches(): Promise { .filter((x): x is BeachSummary => x !== null); } -// Get a single beach detail +// One beach detail export async function fetchBeach(id: string): Promise { return apiGet(`/beaches/${id}`); } diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts index 03e8021d01..03b33fd07f 100644 --- a/frontend/src/vite-env.d.ts +++ b/frontend/src/vite-env.d.ts @@ -3,7 +3,6 @@ interface ImportMetaEnv { readonly VITE_API_BASE: string; } - interface ImportMeta { readonly env: ImportMetaEnv; } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000000..9c029057f5 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "strict": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "skipLibCheck": true, + "lib": ["ES2022", "DOM"], + "types": ["vite/client"] + }, + "include": ["src"] +} From 93e843b2fc2b14b3845429aaa3b987135bcacb01 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 00:03:31 +0200 Subject: [PATCH 035/215] chore(frontend): wrap app with React Query provider --- frontend/src/main.jsx | 10 ---------- frontend/src/main.tsx | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 10 deletions(-) delete mode 100644 frontend/src/main.jsx create mode 100644 frontend/src/main.tsx diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx deleted file mode 100644 index 51294f3998..0000000000 --- a/frontend/src/main.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import { App } from "./App.jsx"; -import "./index.css"; - -ReactDOM.createRoot(document.getElementById("root")).render( - - - -); diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000000..a0e5e9778f --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,14 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +const queryClient = new QueryClient(); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + +); From 46c22edf23adaa68dcf5427a2c0d022b929fafe5 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 00:20:50 +0200 Subject: [PATCH 036/215] feat(frontend): add BeachesList component and render it in App --- frontend/src/App.jsx | 8 ------- frontend/src/App.tsx | 5 +++++ frontend/src/components/BeachesList.tsx | 30 +++++++++++++++++++++++++ frontend/src/main.tsx | 2 +- 4 files changed, 36 insertions(+), 9 deletions(-) delete mode 100644 frontend/src/App.jsx create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/components/BeachesList.tsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx deleted file mode 100644 index 0a24275e6e..0000000000 --- a/frontend/src/App.jsx +++ /dev/null @@ -1,8 +0,0 @@ -export const App = () => { - - return ( - <> -

Welcome to Final Project!

- - ); -}; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000000..be48a03fc0 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,5 @@ +import BeachesList from "./components/BeachesList"; + +export default function App() { + return ; +} diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx new file mode 100644 index 0000000000..f3bc4996b3 --- /dev/null +++ b/frontend/src/components/BeachesList.tsx @@ -0,0 +1,30 @@ +import type { BeachSummary } from "../types/beaches"; +import { useBeaches } from "../hooks/useBeaches"; + +export default function BeachesList() { + const { data, isLoading, error } = useBeaches(); + + if (isLoading) return

Loading beaches…

; + if (error) return

Could not load beaches.

; + + // data is BeachSummary[] | undefined + const items: BeachSummary[] = data ?? []; + + return ( +
+

Beaches ({items.length})

+
    + {items.map((b: BeachSummary) => ( +
  • + {b.name} + {b.municipality ? ` — ${b.municipality}` : ""} + + {" "} + ({b.lat.toFixed(4)}, {b.lng.toFixed(4)}) + +
  • + ))} +
+
+ ); +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index a0e5e9778f..5425b9d73d 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,6 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; +import App from "./App"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); From 3c741d582f6650760c31a8b180cb08c9964a75a6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 00:27:29 +0200 Subject: [PATCH 037/215] fix(frontend): add @tanstack/react-query dependency --- frontend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/package.json b/frontend/package.json index 67dabc8b8d..21a8cecd4b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@tanstack/react-query": "^5.87.4", "react": "^18.2.0", "react-dom": "^18.2.0" }, From 3a59de3a61645f8fb847a7df0faff77c131ab6e3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:33:16 +0200 Subject: [PATCH 038/215] chore(frontend): add react-router-dom --- frontend/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 21a8cecd4b..6685488d59 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "dependencies": { "@tanstack/react-query": "^5.87.4", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^7.9.1" }, "devDependencies": { "@types/react": "^18.3.24", From cdd155c29308083a9d015732587980c6b4bd5cb5 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:34:19 +0200 Subject: [PATCH 039/215] chore(frontend): wrap app with BrowserRouter --- frontend/src/main.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 5425b9d73d..613abac3aa 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,14 +1,17 @@ import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; - import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { BrowserRouter } from "react-router-dom"; + const queryClient = new QueryClient(); ReactDOM.createRoot(document.getElementById("root")!).render( - + + + ); From 705c12cfa48e8f13e68640150842f92b6c49500a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:39:24 +0200 Subject: [PATCH 040/215] feat(frontend): add routes for list and beach detail --- frontend/src/App.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index be48a03fc0..c5720ae098 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,5 +1,12 @@ +import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; +import BeachDetailPage from "./pages/BeachDetailPage"; export default function App() { - return ; + return ( + + } /> + } /> + + ); } From 4eb0f770a64e0307362813856520083183d9094c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:40:43 +0200 Subject: [PATCH 041/215] feat(frontend): link beach items to detail route --- frontend/src/components/BeachesList.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index f3bc4996b3..b0d4d5c7d8 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,5 +1,6 @@ import type { BeachSummary } from "../types/beaches"; import { useBeaches } from "../hooks/useBeaches"; +import { Link } from "react-router-dom"; export default function BeachesList() { const { data, isLoading, error } = useBeaches(); @@ -7,19 +8,19 @@ export default function BeachesList() { if (isLoading) return

Loading beaches…

; if (error) return

Could not load beaches.

; - // data is BeachSummary[] | undefined const items: BeachSummary[] = data ?? []; return (

Beaches ({items.length})

    - {items.map((b: BeachSummary) => ( + {items.map((b) => (
  • - {b.name} - {b.municipality ? ` — ${b.municipality}` : ""} + + {b.name} + {b.municipality ? ` — ${b.municipality}` : ""} + {" "} - {" "} ({b.lat.toFixed(4)}, {b.lng.toFixed(4)})
  • From 37f4c82eb1e48799361bf66bbb7ee14dabaf0656 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:41:52 +0200 Subject: [PATCH 042/215] feat(frontend): add deriveClassificationCode helper --- frontend/src/utils/quality.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/frontend/src/utils/quality.ts b/frontend/src/utils/quality.ts index cef2a3b65c..cda4f3029f 100644 --- a/frontend/src/utils/quality.ts +++ b/frontend/src/utils/quality.ts @@ -19,3 +19,27 @@ export function getQualityLabel( if (!entry) return lang === "sv" ? "Okänd" : "Unknown"; return entry[lang]; } + +// Map detail object to a normalized classification code +export function deriveClassificationCode(detail: { + classification?: number; + qualityRating?: { qualityRating?: number; ratingYear?: number }[]; +}): "excellent" | "good" | "sufficient" | "poor" | "unknown" { + const numeric = + detail.classification ?? + detail.qualityRating?.find((q) => typeof q.qualityRating === "number") + ?.qualityRating; + + switch (numeric) { + case 1: + return "excellent"; + case 2: + return "good"; + case 3: + return "sufficient"; + case 4: + return "poor"; + default: + return "unknown"; + } +} From 50f024a52d5d7e9bfb434502fa83af63ae87ffec Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:43:50 +0200 Subject: [PATCH 043/215] feat(frontend): add BeachDetailPanel --- frontend/src/components/BeachDetailPanel.tsx | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 frontend/src/components/BeachDetailPanel.tsx diff --git a/frontend/src/components/BeachDetailPanel.tsx b/frontend/src/components/BeachDetailPanel.tsx new file mode 100644 index 0000000000..1260668774 --- /dev/null +++ b/frontend/src/components/BeachDetailPanel.tsx @@ -0,0 +1,37 @@ +import { useBeachDetail } from "../hooks/useBeaches"; +import { deriveClassificationCode, getQualityLabel } from "../utils/quality"; + +export default function BeachDetailPanel({ + id, + lang = "sv", +}: { + id: string; + lang?: "sv" | "en"; +}) { + const { data, isLoading, error } = useBeachDetail(id); + + if (isLoading) return

    Loading beach…

    ; + if (error || !data) return

    Could not load beach.

    ; + + const code = deriveClassificationCode(data); + const qualityText = getQualityLabel(data.classification ?? 0, lang); + + return ( +
    +

    {data.locationName}

    +
    + {lang === "sv" ? "Vattenkvalitet" : "Water quality"}:{" "} + {qualityText} +
    + {data.classificationYear && ( +
    + {lang === "sv" ? "Senaste år" : "Latest year"}:{" "} + {data.classificationYear} +
    + )} + {data.bathInformation && ( +

    {data.bathInformation}

    + )} +
    + ); +} From c8b068fe84dbad1c11bb3cf05c970c015e87a928 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:46:19 +0200 Subject: [PATCH 044/215] feat(frontend): add BeachDetailPage --- frontend/src/App.tsx | 2 +- frontend/src/components/BeachDetailPage.tsx | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/BeachDetailPage.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c5720ae098..d7e6d73cec 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,6 @@ import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; -import BeachDetailPage from "./pages/BeachDetailPage"; +import BeachDetailPage from "./components/BeachDetailPage"; export default function App() { return ( diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx new file mode 100644 index 0000000000..bb69a5080d --- /dev/null +++ b/frontend/src/components/BeachDetailPage.tsx @@ -0,0 +1,16 @@ +import { useParams, Link } from "react-router-dom"; +import BeachDetailPanel from "../components/BeachDetailPanel"; + +export default function BeachDetailPage() { + const { id } = useParams<{ id: string }>(); + if (!id) return

    Missing beach id.

    ; + + return ( +
    +

    + ← Back to list +

    + +
    + ); +} From 76983ef6764b2f4e1e2ad6cdda10765fed7b133d Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 11:53:14 +0200 Subject: [PATCH 045/215] chore(frontend): add i18next + react-i18next --- frontend/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index 6685488d59..954413c9de 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,8 +11,10 @@ }, "dependencies": { "@tanstack/react-query": "^5.87.4", + "i18next": "^25.5.2", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^15.7.3", "react-router-dom": "^7.9.1" }, "devDependencies": { From 945cf8878a67c2ff466e844a328d59986aeff3f7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 17:50:06 +0200 Subject: [PATCH 046/215] feat(frontend): add sv translation files --- frontend/src/locales/sv/common.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 frontend/src/locales/sv/common.json diff --git a/frontend/src/locales/sv/common.json b/frontend/src/locales/sv/common.json new file mode 100644 index 0000000000..05aabe9130 --- /dev/null +++ b/frontend/src/locales/sv/common.json @@ -0,0 +1,19 @@ +{ + "appTitle": "BADA", + "beaches": "Badplatser", + "loadingBeaches": "Laddar badplatser…", + "loadError": "Kunde inte ladda data.", + "back": "Tillbaka", + "waterQuality": "Vattenkvalitet", + "latestYear": "Senaste år", + "coordinates": "Koordinater", + "municipality": "Kommun", + "classification": { + "excellent": "Utmärkt kvalitet", + "good": "Bra kvalitet", + "sufficient": "Tillfredsställande kvalitet", + "poor": "Dålig kvalitet", + "unknown": "Okänd" + }, + "sourceTextNote": "Källtext på svenska" +} From 86512b7fa93761575f26fdf310ec9dab0ff7b9c8 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 17:50:45 +0200 Subject: [PATCH 047/215] feat(frontend): add english translation files --- frontend/src/locales/en/common.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 frontend/src/locales/en/common.json diff --git a/frontend/src/locales/en/common.json b/frontend/src/locales/en/common.json new file mode 100644 index 0000000000..b309d87b64 --- /dev/null +++ b/frontend/src/locales/en/common.json @@ -0,0 +1,19 @@ +{ + "appTitle": "BADA", + "beaches": "Beaches", + "loadingBeaches": "Loading beaches…", + "loadError": "Could not load data.", + "back": "Back", + "waterQuality": "Water quality", + "latestYear": "Latest year", + "coordinates": "Coordinates", + "municipality": "Municipality", + "classification": { + "excellent": "Excellent", + "good": "Good", + "sufficient": "Sufficient", + "poor": "Poor", + "unknown": "Unknown" + }, + "sourceTextNote": "Source text in Swedish" +} From 809d3d36218a319d5d09fb5938148e33b7dad03a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 17:52:36 +0200 Subject: [PATCH 048/215] chore(frontend): initialize i18next and load sv/en resources --- frontend/src/i18n.ts | 23 +++++++++++++++++++++++ frontend/src/main.tsx | 1 + 2 files changed, 24 insertions(+) create mode 100644 frontend/src/i18n.ts diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts new file mode 100644 index 0000000000..89b0aa5577 --- /dev/null +++ b/frontend/src/i18n.ts @@ -0,0 +1,23 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; + +import sv from "./locales/sv/common.json"; +import en from "./locales/en/common.json"; + +const saved = localStorage.getItem("lang"); +const fallbackLng = "sv"; +const lng = saved || fallbackLng; + +i18n.use(initReactI18next).init({ + resources: { + sv: { common: sv }, + en: { common: en }, + }, + lng, + fallbackLng, + ns: ["common"], + defaultNS: "common", + interpolation: { escapeValue: false }, +}); + +export default i18n; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 613abac3aa..65ce9238dc 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client"; import App from "./App"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter } from "react-router-dom"; +import "./i18n"; const queryClient = new QueryClient(); From 2d6b9050fe7e7a95983ed38273553b327f59223e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:13:20 +0200 Subject: [PATCH 049/215] feat(frontend): add LanguageSwitcher --- frontend/src/components/LanguageSwitcher.tsx | 30 ++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 frontend/src/components/LanguageSwitcher.tsx diff --git a/frontend/src/components/LanguageSwitcher.tsx b/frontend/src/components/LanguageSwitcher.tsx new file mode 100644 index 0000000000..285d8d916c --- /dev/null +++ b/frontend/src/components/LanguageSwitcher.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from "react-i18next"; + +export default function LanguageSwitcher() { + const { i18n } = useTranslation(); + const current = i18n.language.startsWith("en") ? "en" : "sv"; + + function setLang(lang: "sv" | "en") { + i18n.changeLanguage(lang); + localStorage.setItem("lang", lang); + } + + return ( +
    + + +
    + ); +} From b4afeee8180218b3d9b1d5bcf88693ea367696f0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:14:23 +0200 Subject: [PATCH 050/215] feat(frontend): render LanguageSwitcher in App --- frontend/src/App.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d7e6d73cec..991edfbe46 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,12 +1,16 @@ import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; import BeachDetailPage from "./components/BeachDetailPage"; +import LanguageSwitcher from "./components/LanguageSwitcher"; export default function App() { return ( - - } /> - } /> - + <> + + + } /> + } /> + + ); } From db91d9e9c0dd2eaa2d16ba22b7b9f829af1b3375 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:23:45 +0200 Subject: [PATCH 051/215] feat(frontend): add useTranslation in BeachesList --- frontend/src/components/BeachesList.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index b0d4d5c7d8..2cb2663689 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,18 +1,22 @@ import type { BeachSummary } from "../types/beaches"; import { useBeaches } from "../hooks/useBeaches"; import { Link } from "react-router-dom"; +import { useTranslation } from "react-i18next"; export default function BeachesList() { + const { t } = useTranslation(); const { data, isLoading, error } = useBeaches(); - if (isLoading) return

    Loading beaches…

    ; - if (error) return

    Could not load beaches.

    ; + if (isLoading) return

    {t("loadingBeaches")}

    ; + if (error) return

    {t("loadError")}

    ; const items: BeachSummary[] = data ?? []; return (
    -

    Beaches ({items.length})

    +

    + {t("beaches")} ({items.length}) +

      {items.map((b) => (
    • @@ -21,7 +25,7 @@ export default function BeachesList() { {b.municipality ? ` — ${b.municipality}` : ""} {" "} - ({b.lat.toFixed(4)}, {b.lng.toFixed(4)}) + {t("coordinates")}: {b.lat.toFixed(4)}, {b.lng.toFixed(4)}
    • ))} From 7a4d1fb67eac6d8f9c742624f7f7f594d4ec042b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:24:47 +0200 Subject: [PATCH 052/215] feat(frontend): add useTranslation in BeachDetailPanel --- frontend/src/components/BeachDetailPanel.tsx | 42 ++++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/frontend/src/components/BeachDetailPanel.tsx b/frontend/src/components/BeachDetailPanel.tsx index 1260668774..c135ec1ed3 100644 --- a/frontend/src/components/BeachDetailPanel.tsx +++ b/frontend/src/components/BeachDetailPanel.tsx @@ -1,36 +1,44 @@ import { useBeachDetail } from "../hooks/useBeaches"; -import { deriveClassificationCode, getQualityLabel } from "../utils/quality"; +import { deriveClassificationCode } from "../utils/quality"; +import { useTranslation } from "react-i18next"; -export default function BeachDetailPanel({ - id, - lang = "sv", -}: { - id: string; - lang?: "sv" | "en"; -}) { +export default function BeachDetailPanel({ id }: { id: string }) { + const { t, i18n } = useTranslation(); const { data, isLoading, error } = useBeachDetail(id); - if (isLoading) return

      Loading beach…

      ; - if (error || !data) return

      Could not load beach.

      ; + if (isLoading) return

      {t("loadingBeaches")}

      ; + if (error || !data) return

      {t("loadError")}

      ; - const code = deriveClassificationCode(data); - const qualityText = getQualityLabel(data.classification ?? 0, lang); + const code = deriveClassificationCode(data); // "excellent" | "good" | ... + const qualityText = t(`classification.${code}`); return (

      {data.locationName}

      - {lang === "sv" ? "Vattenkvalitet" : "Water quality"}:{" "} - {qualityText} + {t("waterQuality")}: {qualityText}
      {data.classificationYear && (
      - {lang === "sv" ? "Senaste år" : "Latest year"}:{" "} - {data.classificationYear} + {t("latestYear")}: {data.classificationYear}
      )} {data.bathInformation && ( -

      {data.bathInformation}

      +

      + {data.bathInformation} + {i18n.language.startsWith("en") && ( + + {t("sourceTextNote")} + + )} +

      )}
      ); From b80d2b5296796e6e66f0f0ca28688135e5803cf3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 18:25:22 +0200 Subject: [PATCH 053/215] feat(frontend): add useTranslation in BeachDetailPage --- frontend/src/components/BeachDetailPage.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index bb69a5080d..12aaeb74ab 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -1,14 +1,16 @@ import { useParams, Link } from "react-router-dom"; -import BeachDetailPanel from "../components/BeachDetailPanel"; +import BeachDetailPanel from "./BeachDetailPanel"; +import { useTranslation } from "react-i18next"; export default function BeachDetailPage() { + const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); - if (!id) return

      Missing beach id.

      ; + if (!id) return

      {t("loadError")}

      ; return (

      - ← Back to list + ← {t("back")}

      From 1f69de3c29e8692fb33039588a96df0568c7c6f7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:22:55 +0200 Subject: [PATCH 054/215] chore(frontend): install Tailwind, PostCSS, and Autoprefixer --- frontend/package.json | 3 +++ frontend/tailwind.config.js | 0 2 files changed, 3 insertions(+) create mode 100644 frontend/tailwind.config.js diff --git a/frontend/package.json b/frontend/package.json index 954413c9de..874607af32 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,10 +21,13 @@ "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^4.0.3", + "autoprefixer": "^10.4.21", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.13", "typescript": "^5.9.2", "vite": "^6.3.6" } diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000000..e69de29bb2 From 6a04903a46ea6f53ef6ed86c05f95a1656374691 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:28:35 +0200 Subject: [PATCH 055/215] chore(frontend): add PostCSS config for Tailwind --- frontend/postcss.config.cjs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 frontend/postcss.config.cjs diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs new file mode 100644 index 0000000000..12a703d900 --- /dev/null +++ b/frontend/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; From 23bff03f1ffe47b80c824581643b591084cda0e3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:31:54 +0200 Subject: [PATCH 056/215] chore(frontend): add Tailwind config with fonts, colors, and spacing --- frontend/tailwind.config.js | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index e69de29bb2..bf2cdf813e 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -0,0 +1,52 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + darkMode: "class", + content: ["./index.html", "./src/**/*.{ts,tsx,js,jsx}"], + theme: { + extend: { + letterSpacing: { + // Photoshop tracking -75 → -0.075em + "tight-075": "-0.075em", + }, + lineHeight: { + // 32px on 16px base + 32: "2rem", + }, + fontFamily: { + serif: ['"Spectral"', "ui-serif", "Georgia", "serif"], + sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"], + }, + colors: { + sand: { 50: "#f7f4ee", 100: "#efebe4", 200: "#e6e0d6" }, + ink: { 900: "#1d1a1a", 950: "#161011" }, + card: { light: "#ffffff", dark: "#1b1617" }, + line: "#ded6cb", + lineDark: "#2a2224", + accent: { + teal: "#1e6a73", + tealBg: "#e2f1f3", + cyan: "#2a7f97", + cyanBg: "#e4f2f7", + gold: "#a9823a", + goldBg: "#f6efe2", + red: "#bb3a3a", + redBg: "#f9e7e7", + // dark tints + tealBgD: "#163c40", + cyanBgD: "#163a47", + goldBgD: "#3d2f16", + redBgD: "#4a1c1c", + }, + }, + boxShadow: { + soft: "0 1px 2px rgba(0,0,0,.06), 0 8px 24px rgba(0,0,0,.06)", + softDark: "0 1px 2px rgba(0,0,0,.35), 0 8px 24px rgba(0,0,0,.35)", + }, + borderRadius: { card: "12px" }, + }, + }, + plugins: [], +}; + +export default config; From c87198370fd14babe84facad30925dd74c3e7765 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:42:35 +0200 Subject: [PATCH 057/215] chore(frontend): add Tailwind base styles and global CSS --- frontend/src/index.css | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontend/src/index.css b/frontend/src/index.css index e69de29bb2..8362e8b55b 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -0,0 +1,22 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* Global defaults */ +@layer base { + html, + body, + #root { + height: 100%; + } + body { + @apply bg-sand-100 text-ink-900 dark:bg-ink-950 dark:text-sand-100 antialiased; + } +} + +/* Little badge helper */ +@layer components { + .badge { + @apply inline-flex items-center px-2.5 py-1 rounded-full text-sm font-medium; + } +} From 797669c67d0aa9a2c28c4d3dd7aed6ad39e93f34 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:43:49 +0200 Subject: [PATCH 058/215] chore(frontend): import Tailwind CSS in main entry --- frontend/src/main.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 65ce9238dc..46282a8cfe 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -4,6 +4,7 @@ import App from "./App"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter } from "react-router-dom"; import "./i18n"; +import "./index.css"; const queryClient = new QueryClient(); From 98c5436cb3138b07fdda0a0b2893c46524c5f4f6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 21:44:43 +0200 Subject: [PATCH 059/215] chore(frontend): add Google Fonts (Inter & Spectral) in index.html --- frontend/index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/index.html b/frontend/index.html index 664410b5b9..ad78c7f718 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,13 @@ - Technigo React Vite Boiler Plate + BADA + + +
      From 0b2d97fceb6a7de61eb5c3f068bb3946fa7492a9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:20:49 +0200 Subject: [PATCH 060/215] change tailwind config from js to ts --- frontend/{tailwind.config.js => tailwind.config.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/{tailwind.config.js => tailwind.config.ts} (100%) diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.ts similarity index 100% rename from frontend/tailwind.config.js rename to frontend/tailwind.config.ts From 6f0988e4b1d3b4ef3d01477d1ed44c4ae97d4e07 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:30:57 +0200 Subject: [PATCH 061/215] chore(frontend): add @tailwindcss/postcss for Tailwind v4 --- frontend/package.json | 1 + frontend/src/main.tsx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index 874607af32..5833ed2a24 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "react-router-dom": "^7.9.1" }, "devDependencies": { + "@tailwindcss/postcss": "^4.1.13", "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^4.0.3", diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 46282a8cfe..c522c085f1 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -12,6 +12,10 @@ ReactDOM.createRoot(document.getElementById("root")!).render( + {/* 🔴 TEMP: Tailwind smoke test */} +
      + Tailwind OK +
      From b351f985ecefd50349cdad6ace991f4347a99e6a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:31:19 +0200 Subject: [PATCH 062/215] chore(frontend): switch PostCSS plugin to @tailwindcss/postcss --- frontend/postcss.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs index 12a703d900..de8ec7142c 100644 --- a/frontend/postcss.config.cjs +++ b/frontend/postcss.config.cjs @@ -1,6 +1,6 @@ module.exports = { plugins: { - tailwindcss: {}, + "@tailwindcss/postcss": {}, autoprefixer: {}, }, }; From e157a4b48f01c3a3231e9778edd45bb27111d780 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:36:05 +0200 Subject: [PATCH 063/215] chore(frontend): simplify index.css to Tailwind v4 import only --- frontend/src/index.css | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 8362e8b55b..093d467f22 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,22 +1,2 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* Global defaults */ -@layer base { - html, - body, - #root { - height: 100%; - } - body { - @apply bg-sand-100 text-ink-900 dark:bg-ink-950 dark:text-sand-100 antialiased; - } -} - -/* Little badge helper */ -@layer components { - .badge { - @apply inline-flex items-center px-2.5 py-1 rounded-full text-sm font-medium; - } -} +@import "tailwindcss"; +/* I'll add custom layers + colors after I see styles appear */ From 0abc42582ad1a188beae26856366b8819990dff0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:39:02 +0200 Subject: [PATCH 064/215] feat(frontend): edit tailwind config ts file for setting up actual UI --- frontend/tailwind.config.ts | 52 +++++++++++-------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index bf2cdf813e..da8e21454d 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -1,51 +1,29 @@ import type { Config } from "tailwindcss"; const config: Config = { - darkMode: "class", - content: ["./index.html", "./src/**/*.{ts,tsx,js,jsx}"], + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], theme: { extend: { - letterSpacing: { - // Photoshop tracking -75 → -0.075em - "tight-075": "-0.075em", - }, - lineHeight: { - // 32px on 16px base - 32: "2rem", - }, - fontFamily: { - serif: ['"Spectral"', "ui-serif", "Georgia", "serif"], - sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"], - }, colors: { - sand: { 50: "#f7f4ee", 100: "#efebe4", 200: "#e6e0d6" }, - ink: { 900: "#1d1a1a", 950: "#161011" }, - card: { light: "#ffffff", dark: "#1b1617" }, - line: "#ded6cb", - lineDark: "#2a2224", - accent: { - teal: "#1e6a73", - tealBg: "#e2f1f3", - cyan: "#2a7f97", - cyanBg: "#e4f2f7", - gold: "#a9823a", - goldBg: "#f6efe2", - red: "#bb3a3a", - redBg: "#f9e7e7", - // dark tints - tealBgD: "#163c40", - cyanBgD: "#163a47", - goldBgD: "#3d2f16", - redBgD: "#4a1c1c", + sand: { + 100: "#f5f0e8", // light beige background + }, + ink: { + 900: "#1a1a1a", // near black for text + 950: "#0d0d0d", // darkest background for dark mode + }, + quality: { + excellent: "#0077b6", // blue + poor: "#d62828", // red }, }, - boxShadow: { - soft: "0 1px 2px rgba(0,0,0,.06), 0 8px 24px rgba(0,0,0,.06)", - softDark: "0 1px 2px rgba(0,0,0,.35), 0 8px 24px rgba(0,0,0,.35)", + fontFamily: { + spectral: ["Spectral", "serif"], + inter: ["Inter", "sans-serif"], }, - borderRadius: { card: "12px" }, }, }, + darkMode: "class", plugins: [], }; From c9942c4c35b4a32301e6a827923c3be54ac3afe1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:40:04 +0200 Subject: [PATCH 065/215] feat(frontend): restore index css file with UI layers --- frontend/src/index.css | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 093d467f22..5be17c1a9d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,2 +1,18 @@ @import "tailwindcss"; -/* I'll add custom layers + colors after I see styles appear */ + +@layer base { + html, + body, + #root { + height: 100%; + } + body { + @apply bg-sand-100 text-ink-900 dark:bg-ink-950 dark:text-sand-100 antialiased; + } +} + +@layer components { + .badge { + @apply inline-flex items-center px-2.5 py-1 rounded-full text-sm font-medium; + } +} From 7b3c35faa359bd9321a31ac5c2e3e92974141e0e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:41:53 +0200 Subject: [PATCH 066/215] feat(frontend): edit fonts links --- frontend/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/index.html b/frontend/index.html index ad78c7f718..1d218ec2f1 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,7 +8,7 @@ From b989f1d37b0254e5da1b251a2fef80da530a3620 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:49:39 +0200 Subject: [PATCH 067/215] feat(frontend): add accessible light/dark theme tokens + fonts in tailwind config ts file --- frontend/tailwind.config.ts | 42 +++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index da8e21454d..56b2365067 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -2,28 +2,48 @@ import type { Config } from "tailwindcss"; const config: Config = { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + darkMode: "class", // toggle by adding/removing `class="dark"` on theme: { extend: { + // Semantic color tokens (use these in your components) colors: { - sand: { - 100: "#f5f0e8", // light beige background - }, - ink: { - 900: "#1a1a1a", // near black for text - 950: "#0d0d0d", // darkest background for dark mode - }, + surface: "rgb(var(--color-surface) / )", + surfaceMuted: "rgb(var(--color-surface-muted) / )", + ink: "rgb(var(--color-ink) / )", + inkMuted: "rgb(var(--color-ink-muted) / )", + border: "rgb(var(--color-border) / )", + accent: "rgb(var(--color-accent) / )", // links/buttons + badge: "rgb(var(--color-badge) / )", + + // Quality/status (picked for AA contrast on both themes) quality: { - excellent: "#0077b6", // blue - poor: "#d62828", // red + excellent: "#0B6FB8", // accessible blue + good: "#147A53", // accessible green + sufficient: "#8A6F00", // amber with contrast + poor: "#C43131", // accessible red + unknown: "#6B7280", // neutral gray }, }, fontFamily: { spectral: ["Spectral", "serif"], - inter: ["Inter", "sans-serif"], + inter: ["Inter", "system-ui", "sans-serif"], + }, + letterSpacing: { + // Title “BADA” tracking ≈ -0.06em looks like your mockup + tighter2: "-0.06em", + }, + lineHeight: { + snugPlus: "1.2", // for the 2-line subtitle fitting next to the logo + }, + boxShadow: { + card: "0 1px 2px rgba(0,0,0,0.05), 0 8px 24px rgba(0,0,0,0.06)", + cardDark: "0 1px 2px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.35)", + }, + borderRadius: { + xl2: "1rem", }, }, }, - darkMode: "class", plugins: [], }; From c72805f5f524a0a8cd227639b835e9052087bbfd Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:50:45 +0200 Subject: [PATCH 068/215] feat(frontend): add accessible light/dark theme tokens + fonts in index css file --- frontend/src/index.css | 68 +++++++++++++++++++++++++++++++++++-- frontend/tailwind.config.ts | 4 +-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 5be17c1a9d..129451ff17 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,18 +1,82 @@ @import "tailwindcss"; +/* Theme variables — LIGHT */ +:root { + /* Light background ~ your beige (#efebe4), nudged for AA on white cards/text */ + --color-surface: 239 235 228; /* #efebe4 */ + --color-surface-muted: 246 242 236; /* slightly lighter for containers */ + --color-ink: 26 26 26; /* near-black text */ + --color-ink-muted: 100 104 108; /* secondary text */ + --color-border: 205 200 192; /* subtle lines (#cdc8c0) */ + --color-accent: 10 90 130; /* accessible blue for links/buttons */ + --color-badge: 230 228 220; /* pill bg on light */ +} + +/* Theme variables — DARK (#161011 base, adjusted) */ +.dark { + --color-surface: 22 16 17; /* #161011 */ + --color-surface-muted: 30 24 25; /* card bg */ + --color-ink: 240 237 230; /* sand-ish text for dark */ + --color-ink-muted: 184 178 170; /* secondary text in dark */ + --color-border: 64 56 58; /* separators on dark */ + --color-accent: 94 165 210; /* lighter blue for dark contrast */ + --color-badge: 48 42 44; /* pill bg on dark */ +} + +/* Base defaults */ @layer base { html, body, #root { height: 100%; } + body { - @apply bg-sand-100 text-ink-900 dark:bg-ink-950 dark:text-sand-100 antialiased; + @apply bg-surface text-ink antialiased; + } + + a { + @apply text-accent underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded-sm; + } + + /* Headings: Spectral; content: Inter */ + h1, + h2, + h3, + h4 { + @apply font-spectral text-ink; + } + p, + li, + small, + input, + button, + label { + @apply font-inter; } } +/* Components/utilities I can reuse */ @layer components { + .card { + @apply bg-surface-muted rounded-xl2 shadow-card dark:shadow-cardDark border border-border; + } .badge { - @apply inline-flex items-center px-2.5 py-1 rounded-full text-sm font-medium; + @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium bg-badge text-ink; + } + .kpi-excellent { + color: theme(colors.quality.excellent); + } + .kpi-good { + color: theme(colors.quality.good); + } + .kpi-sufficient { + color: theme(colors.quality.sufficient); + } + .kpi-poor { + color: theme(colors.quality.poor); + } + .kpi-unknown { + color: theme(colors.quality.unknown); } } diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 56b2365067..06b86ca8e6 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -5,7 +5,7 @@ const config: Config = { darkMode: "class", // toggle by adding/removing `class="dark"` on theme: { extend: { - // Semantic color tokens (use these in your components) + // Semantic color tokens mapped to CSS variables for light/dark mode colors: { surface: "rgb(var(--color-surface) / )", surfaceMuted: "rgb(var(--color-surface-muted) / )", @@ -29,7 +29,7 @@ const config: Config = { inter: ["Inter", "system-ui", "sans-serif"], }, letterSpacing: { - // Title “BADA” tracking ≈ -0.06em looks like your mockup + // Title “BADA” tracking ≈ -0.06em looks like the UI mockup tighter2: "-0.06em", }, lineHeight: { From b0fca576b1b58cc7f69b99243ab18e69ec716de6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:51:42 +0200 Subject: [PATCH 069/215] feat(frontend): update fonts links in index html file --- frontend/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/index.html b/frontend/index.html index 1d218ec2f1..e94d2ce651 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,7 +8,7 @@ From d0abd1867deed6f930d03c0827d9bb21d4ac6009 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 22:55:37 +0200 Subject: [PATCH 070/215] feat(frontend): integrate Header into App layout while keeping Routes + LanguageSwitcher --- frontend/src/App.tsx | 20 +++++++++------ frontend/src/components/Header.tsx | 39 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/Header.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 991edfbe46..fd68e8517a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,15 +2,21 @@ import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; import BeachDetailPage from "./components/BeachDetailPage"; import LanguageSwitcher from "./components/LanguageSwitcher"; +import Header from "./components/Header"; export default function App() { return ( - <> - - - } /> - } /> - - +
      +
      +
      + {/* Keep language switcher (you may later move it into the menu) */} + + + + } /> + } /> + +
      +
      ); } diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx new file mode 100644 index 0000000000..62f80ab88b --- /dev/null +++ b/frontend/src/components/Header.tsx @@ -0,0 +1,39 @@ +import React from "react"; + +export function Header() { + return ( +
      +
      + {/* Left: Hamburger */} + + + {/* Center: Title + subtitle */} +
      +
      + BADA +
      +
      + EU beaches +
      + in Sweden +
      +
      + + {/* Right: User */} + +
      +
      + ); +} + +export default Header; From d5a503f811224fb5278aa9cc526df90563bdb99a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:05:02 +0200 Subject: [PATCH 071/215] chore(frontend): ensure Tailwind theme includes quality colors + semantic tokens --- frontend/tailwind.config.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 06b86ca8e6..99e4818bee 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -2,26 +2,26 @@ import type { Config } from "tailwindcss"; const config: Config = { content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - darkMode: "class", // toggle by adding/removing `class="dark"` on + darkMode: "class", theme: { extend: { - // Semantic color tokens mapped to CSS variables for light/dark mode colors: { + // Semantic tokens from CSS variables surface: "rgb(var(--color-surface) / )", surfaceMuted: "rgb(var(--color-surface-muted) / )", ink: "rgb(var(--color-ink) / )", inkMuted: "rgb(var(--color-ink-muted) / )", border: "rgb(var(--color-border) / )", - accent: "rgb(var(--color-accent) / )", // links/buttons + accent: "rgb(var(--color-accent) / )", badge: "rgb(var(--color-badge) / )", - // Quality/status (picked for AA contrast on both themes) + // Status/quality colors (will generate utilities like text-quality-excellent) quality: { - excellent: "#0B6FB8", // accessible blue - good: "#147A53", // accessible green - sufficient: "#8A6F00", // amber with contrast - poor: "#C43131", // accessible red - unknown: "#6B7280", // neutral gray + excellent: "#0B6FB8", + good: "#147A53", + sufficient: "#8A6F00", + poor: "#C43131", + unknown: "#6B7280", }, }, fontFamily: { @@ -29,11 +29,10 @@ const config: Config = { inter: ["Inter", "system-ui", "sans-serif"], }, letterSpacing: { - // Title “BADA” tracking ≈ -0.06em looks like the UI mockup tighter2: "-0.06em", }, lineHeight: { - snugPlus: "1.2", // for the 2-line subtitle fitting next to the logo + snugPlus: "1.2", }, boxShadow: { card: "0 1px 2px rgba(0,0,0,0.05), 0 8px 24px rgba(0,0,0,0.06)", From a07a3c05d26e677e439fde2498dcde087917cdf9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:05:45 +0200 Subject: [PATCH 072/215] fix(frontend): replace theme() usages with utilities and keep @apply inside layers --- frontend/src/index.css | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 129451ff17..bb9917b967 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -2,28 +2,26 @@ /* Theme variables — LIGHT */ :root { - /* Light background ~ your beige (#efebe4), nudged for AA on white cards/text */ --color-surface: 239 235 228; /* #efebe4 */ - --color-surface-muted: 246 242 236; /* slightly lighter for containers */ - --color-ink: 26 26 26; /* near-black text */ - --color-ink-muted: 100 104 108; /* secondary text */ - --color-border: 205 200 192; /* subtle lines (#cdc8c0) */ - --color-accent: 10 90 130; /* accessible blue for links/buttons */ - --color-badge: 230 228 220; /* pill bg on light */ + --color-surface-muted: 246 242 236; + --color-ink: 26 26 26; + --color-ink-muted: 100 104 108; + --color-border: 205 200 192; /* #cdc8c0 */ + --color-accent: 10 90 130; + --color-badge: 230 228 220; } -/* Theme variables — DARK (#161011 base, adjusted) */ +/* Theme variables — DARK */ .dark { --color-surface: 22 16 17; /* #161011 */ - --color-surface-muted: 30 24 25; /* card bg */ - --color-ink: 240 237 230; /* sand-ish text for dark */ - --color-ink-muted: 184 178 170; /* secondary text in dark */ - --color-border: 64 56 58; /* separators on dark */ - --color-accent: 94 165 210; /* lighter blue for dark contrast */ - --color-badge: 48 42 44; /* pill bg on dark */ + --color-surface-muted: 30 24 25; + --color-ink: 240 237 230; + --color-ink-muted: 184 178 170; + --color-border: 64 56 58; + --color-accent: 94 165 210; + --color-badge: 48 42 44; } -/* Base defaults */ @layer base { html, body, @@ -56,7 +54,6 @@ } } -/* Components/utilities I can reuse */ @layer components { .card { @apply bg-surface-muted rounded-xl2 shadow-card dark:shadow-cardDark border border-border; @@ -64,19 +61,20 @@ .badge { @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium bg-badge text-ink; } + /* KPI helpers using generated utilities */ .kpi-excellent { - color: theme(colors.quality.excellent); + @apply text-quality-excellent; } .kpi-good { - color: theme(colors.quality.good); + @apply text-quality-good; } .kpi-sufficient { - color: theme(colors.quality.sufficient); + @apply text-quality-sufficient; } .kpi-poor { - color: theme(colors.quality.poor); + @apply text-quality-poor; } .kpi-unknown { - color: theme(colors.quality.unknown); + @apply text-quality-unknown; } } From d4e5fabb4430a5e133480e812801821355fd590f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:28:46 +0200 Subject: [PATCH 073/215] =?UTF-8?q?chore(frontend):=20switch=20to=20Tailwi?= =?UTF-8?q?nd=20v4=20tokens=20=E2=80=94=20define=20palette/fonts=20in=20@t?= =?UTF-8?q?heme=20and=20remove=20legacy=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/postcss.config.cjs | 1 - frontend/src/App.tsx | 8 ++-- frontend/src/index.css | 84 ++++++++++++++++++++----------------- frontend/tailwind.config.ts | 49 ---------------------- package.json | 7 ++++ 5 files changed, 58 insertions(+), 91 deletions(-) delete mode 100644 frontend/tailwind.config.ts diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs index de8ec7142c..483f378543 100644 --- a/frontend/postcss.config.cjs +++ b/frontend/postcss.config.cjs @@ -1,6 +1,5 @@ module.exports = { plugins: { "@tailwindcss/postcss": {}, - autoprefixer: {}, }, }; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fd68e8517a..c53094c7c7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,12 +6,14 @@ import Header from "./components/Header"; export default function App() { return ( -
      +
      + + {/* TEMP: Tailwind smoke test */} +
      Tailwind OK
      +
      - {/* Keep language switcher (you may later move it into the menu) */} - } /> } /> diff --git a/frontend/src/index.css b/frontend/src/index.css index bb9917b967..948b0293ec 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,27 +1,40 @@ @import "tailwindcss"; -/* Theme variables — LIGHT */ -:root { - --color-surface: 239 235 228; /* #efebe4 */ - --color-surface-muted: 246 242 236; - --color-ink: 26 26 26; - --color-ink-muted: 100 104 108; - --color-border: 205 200 192; /* #cdc8c0 */ - --color-accent: 10 90 130; - --color-badge: 230 228 220; +/* Design tokens (Tailwind v4) */ +@theme { + /* Light theme */ + --color-surface: #efebe4; + --color-surface-muted: #f6f2ec; + --color-ink: #1a1a1a; + --color-ink-muted: #64686c; + --color-border: #cdc8c0; + --color-accent: #0a5a82; + --color-badge: #e6e4dc; + + /* Quality colors */ + --color-quality-excellent: #1f6fb2; + --color-quality-good: #2b8a3e; + --color-quality-sufficient: #b08900; + --color-quality-poor: #c0392b; + --color-quality-unknown: #6b7280; + + /* Fonts */ + --font-spectral: "Spectral", serif; + --font-inter: "Inter", system-ui, sans-serif; } -/* Theme variables — DARK */ +/* Dark theme overrides */ .dark { - --color-surface: 22 16 17; /* #161011 */ - --color-surface-muted: 30 24 25; - --color-ink: 240 237 230; - --color-ink-muted: 184 178 170; - --color-border: 64 56 58; - --color-accent: 94 165 210; - --color-badge: 48 42 44; + --color-surface: #161011; + --color-surface-muted: #1e1819; + --color-ink: #f0ede6; + --color-ink-muted: #b8b2aa; + --color-border: #40383a; + --color-accent: #5ea5d2; + --color-badge: #302a2c; } +/* Base defaults */ @layer base { html, body, @@ -30,51 +43,46 @@ } body { - @apply bg-surface text-ink antialiased; - } - - a { - @apply text-accent underline-offset-2 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 rounded-sm; + @apply bg-surface text-ink antialiased font-inter; } - /* Headings: Spectral; content: Inter */ h1, h2, h3, h4 { @apply font-spectral text-ink; } - p, - li, - small, - input, - button, - label { - @apply font-inter; + + a { + @apply text-accent underline-offset-2 hover:underline + focus-visible:outline-none focus-visible:ring-2 + focus-visible:ring-accent/50 rounded-sm; } } +/* Reusable bits */ @layer components { .card { - @apply bg-surface-muted rounded-xl2 shadow-card dark:shadow-cardDark border border-border; + @apply bg-surface-muted rounded-2xl shadow border border-border; } .badge { - @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-medium bg-badge text-ink; + @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full + text-xs font-medium bg-badge text-ink; } - /* KPI helpers using generated utilities */ + .kpi-excellent { - @apply text-quality-excellent; + color: var(--color-quality-excellent); } .kpi-good { - @apply text-quality-good; + color: var(--color-quality-good); } .kpi-sufficient { - @apply text-quality-sufficient; + color: var(--color-quality-sufficient); } .kpi-poor { - @apply text-quality-poor; + color: var(--color-quality-poor); } .kpi-unknown { - @apply text-quality-unknown; + color: var(--color-quality-unknown); } } diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts deleted file mode 100644 index 99e4818bee..0000000000 --- a/frontend/tailwind.config.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { Config } from "tailwindcss"; - -const config: Config = { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - darkMode: "class", - theme: { - extend: { - colors: { - // Semantic tokens from CSS variables - surface: "rgb(var(--color-surface) / )", - surfaceMuted: "rgb(var(--color-surface-muted) / )", - ink: "rgb(var(--color-ink) / )", - inkMuted: "rgb(var(--color-ink-muted) / )", - border: "rgb(var(--color-border) / )", - accent: "rgb(var(--color-accent) / )", - badge: "rgb(var(--color-badge) / )", - - // Status/quality colors (will generate utilities like text-quality-excellent) - quality: { - excellent: "#0B6FB8", - good: "#147A53", - sufficient: "#8A6F00", - poor: "#C43131", - unknown: "#6B7280", - }, - }, - fontFamily: { - spectral: ["Spectral", "serif"], - inter: ["Inter", "system-ui", "sans-serif"], - }, - letterSpacing: { - tighter2: "-0.06em", - }, - lineHeight: { - snugPlus: "1.2", - }, - boxShadow: { - card: "0 1px 2px rgba(0,0,0,0.05), 0 8px 24px rgba(0,0,0,0.06)", - cardDark: "0 1px 2px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.35)", - }, - borderRadius: { - xl2: "1rem", - }, - }, - }, - plugins: [], -}; - -export default config; diff --git a/package.json b/package.json index 6c42e4be67..a3a70fcad1 100644 --- a/package.json +++ b/package.json @@ -6,5 +6,12 @@ }, "dependencies": { "@tanstack/react-query": "^5.87.4" + }, + "devDependencies": { + "autoprefixer": "^10.4.21", + "init": "^0.1.2", + "npx": "^10.2.2", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.13" } } From bf15636d9ef9a7ffc9a6e257b1df81f8f1123d35 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:33:28 +0200 Subject: [PATCH 074/215] feat(frontend): add accessible Header with brand, menus, language slot and dark-mode toggle --- frontend/src/components/Header.tsx | 224 +++++++++++++++++++++++++---- 1 file changed, 196 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 62f80ab88b..0ab91a9cab 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,39 +1,207 @@ -import React from "react"; +import { useEffect, useRef, useState } from "react"; +import { Link } from "react-router-dom"; + +/** Simple outside-click hook so the menus close on blur */ +function useOutsideClose(onClose: () => void) { + const ref = useRef(null); + useEffect(() => { + function handler(e: MouseEvent) { + if (ref.current && !ref.current.contains(e.target as Node)) onClose(); + } + document.addEventListener("mousedown", handler); + return () => document.removeEventListener("mousedown", handler); + }, [onClose]); + return ref; +} + +/** Dark mode toggle — flips the `dark` class on */ +function useDarkMode() { + const [isDark, setIsDark] = useState(() => + document.documentElement.classList.contains("dark") + ); + useEffect(() => { + if (isDark) document.documentElement.classList.add("dark"); + else document.documentElement.classList.remove("dark"); + }, [isDark]); + return { isDark, setIsDark }; +} + +type HeaderProps = { + /** Optional: render a language switcher control I already have */ + languageSwitcher?: React.ReactNode; + /** Is the user authenticated? (for conditional user menu) */ + authed?: boolean; +}; + +export default function Header({ languageSwitcher, authed }: HeaderProps) { + const [menuOpen, setMenuOpen] = useState(false); + const [userOpen, setUserOpen] = useState(false); + const menuRef = useOutsideClose(() => setMenuOpen(false)); + const userRef = useOutsideClose(() => setUserOpen(false)); + const { isDark, setIsDark } = useDarkMode(); -export function Header() { return ( -
      -
      +
      +
      {/* Left: Hamburger */} - - - {/* Center: Title + subtitle */} -
      -
      - BADA -
      -
      - EU beaches -
      - in Sweden +
      + + + {/* Flyout menu */} + {menuOpen && ( +
      +
      Menu
      +
      +
      + Language + {languageSwitcher ?? ( + SV / EN + )} +
      + What is an EU Beach? + About BADA + Terms of Use + Contact +
      + )} +
      + + {/* Center: Brand block */} +
      +
      + {/* Title BADA (Spectral, tight tracking) */} + + BADA + + {/* Subtitle (Inter, two-line, leading to match height visually) */} +
      + EU Beaches +
      + in Sweden +
      - {/* Right: User */} - + {/* Right: User menu */} +
      + + + {userOpen && ( +
      +
      + {authed ? "KONTO / USER" : "USER"} +
      +
      + {!authed ? ( + <> + Sign in + Register + + ) : ( + <> + Favourite beaches + Profile + Settings + + )} + + {authed && ( + <> +
      + Log out + + )} +
      + )} +
      +
      + + {/* Optional: Search + “Use current location” bar lives just under header */} + {/* I’ll wire these as actual components later */} +
      +
      + + +
      ); } -export default Header; +function MenuLink({ to, children }: { to: string; children: React.ReactNode }) { + return ( + + {children} + + ); +} From f62a4684a285ddb086f49e58dfb20f1c2407a37b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:50:31 +0200 Subject: [PATCH 075/215] chore(backend): remove global Mongo connect; connect only in auth/favorites --- backend/src/index.ts | 5 ----- frontend/src/api/beaches.ts | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 5fe1772818..c3e04a4075 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -7,14 +7,9 @@ import { healthRouter } from "./routes/health.js"; import { dbCheckRouter } from "./routes/dbCheck.js"; import { authRouter } from "./routes/auth.js"; import { requireAuth } from "./middleware/auth.js"; -import { connectDB } from "./lib/db.js"; import { favoritesRouter } from "./routes/favorites.js"; import { beachesRouter } from "./routes/beaches.js"; -connectDB() - .then(() => console.log("Mongo connected")) - .catch((e) => console.error("Mongo error", e)); - const app = express(); // --- Global middleware --- diff --git a/frontend/src/api/beaches.ts b/frontend/src/api/beaches.ts index 182aae9b4f..d7ab4c3a67 100644 --- a/frontend/src/api/beaches.ts +++ b/frontend/src/api/beaches.ts @@ -25,3 +25,6 @@ export async function fetchBeaches(): Promise { export async function fetchBeach(id: string): Promise { return apiGet(`/beaches/${id}`); } + +console.log("API_BASE =", import.meta.env.VITE_API_BASE); +// For debugging; remove later From e68aaac693893402d5cc2e07ec6d5f7eedf3b0b7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 14 Sep 2025 23:56:14 +0200 Subject: [PATCH 076/215] chore(backend): harden Mongo connect (fail-fast, no buffer, cached global) --- backend/src/lib/db.ts | 51 +++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/backend/src/lib/db.ts b/backend/src/lib/db.ts index cdc28df22a..b56d3e7942 100644 --- a/backend/src/lib/db.ts +++ b/backend/src/lib/db.ts @@ -1,22 +1,49 @@ import mongoose from "mongoose"; -let cached = (global as any)._mongoose as - | { conn: typeof mongoose | null; promise: Promise | null } - | undefined; - -if (!cached) { - cached = (global as any)._mongoose = { conn: null, promise: null }; -} +// Cache the connection across hot-reloads / serverless invocations +type Cached = { + conn: typeof mongoose | null; + promise: Promise | null; +}; +let cached: Cached = (global as any)._mongooseCached ?? { + conn: null, + promise: null, +}; +(global as any)._mongooseCached = cached; export async function connectDB() { - if (cached!.conn) return cached!.conn; + if (cached.conn) return cached.conn; - if (!cached!.promise) { + if (!cached.promise) { const uri = process.env.MONGODB_URI; if (!uri) throw new Error("MONGODB_URI missing"); - cached!.promise = mongoose.connect(uri).then((m) => m); + + mongoose.set("strictQuery", true); + mongoose.set("bufferCommands", false); + + cached.promise = mongoose + .connect(uri, { serverSelectionTimeoutMS: 5000 }) + .then((m) => { + cached.conn = m; + + // 🔴 Optional debug hooks (remove later) + mongoose.connection.on("connected", () => + console.log("[mongo] connected") + ); + mongoose.connection.on("error", (e) => + console.error("[mongo] error", e) + ); + mongoose.connection.on("disconnected", () => + console.warn("[mongo] disconnected") + ); + + return m; + }) + .catch((err) => { + cached.promise = null; + throw err; + }); } - cached!.conn = await cached!.promise; - return cached!.conn; + return cached.promise; } From c87b2ed3cb15a7b9aba082b86363d141e4b6640e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:00:36 +0200 Subject: [PATCH 077/215] feat(frontend): add haversine distance helper (utils/geo) --- frontend/src/utils/geo.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 frontend/src/utils/geo.ts diff --git a/frontend/src/utils/geo.ts b/frontend/src/utils/geo.ts new file mode 100644 index 0000000000..53c845eea7 --- /dev/null +++ b/frontend/src/utils/geo.ts @@ -0,0 +1,21 @@ +export function distanceKm( + a: { lat: number; lon: number }, + b: { lat: number; lon: number } +): number { + const R = 6371; // km + const dLat = ((b.lat - a.lat) * Math.PI) / 180; + const dLon = ((b.lon - a.lon) * Math.PI) / 180; + const lat1 = (a.lat * Math.PI) / 180; + const lat2 = (b.lat * Math.PI) / 180; + + const s = + Math.sin(dLat / 2) ** 2 + + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2; + + return 2 * R * Math.asin(Math.min(1, Math.sqrt(s))); +} + +export function formatKm(km: number): string { + if (km < 1) return `${Math.round(km * 1000)} m`; + return `${km.toFixed(km < 10 ? 1 : 0)} km`; +} From d0ccb7976b2c0183368c6055843761a736fbce6c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:01:26 +0200 Subject: [PATCH 078/215] feat(frontend): add on-demand geolocation hook --- frontend/src/hooks/useGeolocation.ts | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 frontend/src/hooks/useGeolocation.ts diff --git a/frontend/src/hooks/useGeolocation.ts b/frontend/src/hooks/useGeolocation.ts new file mode 100644 index 0000000000..8819d3f6d5 --- /dev/null +++ b/frontend/src/hooks/useGeolocation.ts @@ -0,0 +1,39 @@ +import { useCallback, useState } from "react"; + +type Coords = { lat: number; lon: number }; + +export function useGeolocation() { + const [coords, setCoords] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const request = useCallback(() => { + if (!("geolocation" in navigator)) { + setError("Geolocation not supported"); + return; + } + setLoading(true); + setError(null); + + navigator.geolocation.getCurrentPosition( + (pos) => { + setLoading(false); + setCoords({ + lat: pos.coords.latitude, + lon: pos.coords.longitude, + }); + }, + (err) => { + setLoading(false); + setError(err.message || "Failed to get location"); + }, + { + enableHighAccuracy: true, + timeout: 10_000, + maximumAge: 60_000, + } + ); + }, []); + + return { coords, loading, error, request }; +} From 6fe627d330066a8859bae4c281d949f2448a4a3e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:09:44 +0200 Subject: [PATCH 079/215] =?UTF-8?q?fix(frontend):=20standardize=20on=20lon?= =?UTF-8?q?=20(keep=20lng=20alias)=20and=20update=20feature=E2=86=92summar?= =?UTF-8?q?y=20mapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/BeachesList.tsx | 100 ++++++++++++++++++------ frontend/src/types/beaches.ts | 37 ++++++--- 2 files changed, 102 insertions(+), 35 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 2cb2663689..747178c6bf 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,32 +1,88 @@ -import type { BeachSummary } from "../types/beaches"; -import { useBeaches } from "../hooks/useBeaches"; +import { useEffect, useMemo } from "react"; +import { fetchBeaches } from "../api/beaches"; +import { BeachSummary } from "../types/beaches"; +import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; -import { useTranslation } from "react-i18next"; +import { useGeolocation } from "../hooks/useGeolocation"; +import { distanceKm, formatKm } from "../utils/geo"; export default function BeachesList() { - const { t } = useTranslation(); - const { data, isLoading, error } = useBeaches(); + const { data, isLoading, isError, error, refetch } = useQuery({ + queryKey: ["beaches"], + queryFn: fetchBeaches, + staleTime: 5 * 60 * 1000, + }); - if (isLoading) return

      {t("loadingBeaches")}

      ; - if (error) return

      {t("loadError")}

      ; + const { + coords, + loading: geoLoading, + error: geoError, + request, + } = useGeolocation(); - const items: BeachSummary[] = data ?? []; + // Re-sort by proximity when coords arrive (no re-fetch needed) + const items = useMemo(() => { + if (!data) return [] as BeachSummary[]; + if (!coords) return data; + + const withDist = data.map((b) => { + const km = distanceKm( + { lat: coords.lat, lon: coords.lon }, + { lat: b.lat, lon: b.lon } + ); + return { ...b, _distanceKm: km } as BeachSummary & { + _distanceKm: number; + }; + }); + + return withDist.sort((a, b) => a._distanceKm - b._distanceKm); + }, [data, coords]); + + // optional: ensure we refetch once on mount in case cache is empty + useEffect(() => { + if (!data && !isLoading) refetch(); + }, [data, isLoading, refetch]); + + if (isLoading) return

      Loading beaches…

      ; + if (isError) + return

      Could not load beaches. {(error as Error)?.message}

      ; return ( -
      -

      - {t("beaches")} ({items.length}) -

      -
        - {items.map((b) => ( -
      • - - {b.name} - {b.municipality ? ` — ${b.municipality}` : ""} - {" "} - - {t("coordinates")}: {b.lat.toFixed(4)}, {b.lng.toFixed(4)} - +
        +
        + + + {coords && ( + + Using your location for proximity. + + )} + {geoError && {geoError}} +
        + +
          + {items.slice(0, 50).map((b) => ( +
        • + +
          +

          {b.name}

          + {/* distance pill if available */} + {"_distanceKm" in b && ( + + {formatKm((b as any)._distanceKm)} + + )} +
          +

          + {b.municipality} — {b.lat.toFixed(4)}, {b.lon.toFixed(4)} +

          +
        • ))}
        diff --git a/frontend/src/types/beaches.ts b/frontend/src/types/beaches.ts index 0f88063dc2..6c6e69400d 100644 --- a/frontend/src/types/beaches.ts +++ b/frontend/src/types/beaches.ts @@ -4,17 +4,17 @@ export type LngLat = [number, number]; // GeoJSON order: [lon, lat] // --- GeoJSON (list) --- export interface BeachFeatureProperties { - NUTSKOD: string; // e.g. "SE0441273000000001" (stable beach ID) - NAMN: string; // e.g. "Hökarängsbadet, Drevviken" + NUTSKOD: string; // stable beach ID + NAMN: string; // name KMN_NAMN?: string; // municipality (optional) } export interface BeachFeature { type: "Feature"; id?: string; - geometry: { type: "Point"; coordinates: LngLat }; + geometry?: { type: "Point"; coordinates: LngLat }; geometry_name?: string; - properties: BeachFeatureProperties; + properties?: BeachFeatureProperties; } export interface BeachFeatureCollection { @@ -22,24 +22,35 @@ export interface BeachFeatureCollection { features: BeachFeature[]; } -// Simplified list item for UI +// --- Simplified list item for UI --- export interface BeachSummary { id: string; // NUTSKOD name: string; // NAMN - municipality?: string; // KMN_NAMN + municipality: string; // KMN_NAMN (empty string if missing) lat: number; - lng: number; + lon: number; // canonical longitude + lng?: number; // temporary alias (same as lon) to avoid breaking older code } +/** + * Map a GeoJSON feature → BeachSummary. + * Returns null if coordinates are missing/invalid. + */ export function featureToSummary(f: BeachFeature): BeachSummary | null { - const [lng, lat] = f.geometry?.coordinates ?? []; - if (typeof lat !== "number" || typeof lng !== "number") return null; + const props = f.properties; + const coords = f.geometry?.coordinates; + if (!props || !coords) return null; + + const [lon, lat] = coords; + if (typeof lat !== "number" || typeof lon !== "number") return null; + return { - id: f.properties.NUTSKOD, - name: f.properties.NAMN, - municipality: f.properties.KMN_NAMN, + id: props.NUTSKOD ?? f.id ?? "", + name: props.NAMN ?? "Okänd", + municipality: props.KMN_NAMN ?? "", lat, - lng, + lon, + lng: lon, // alias for any code still using `lng` }; } From f3c5c9e8c5c587f01b0caca0e6978726868b298d Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:19:28 +0200 Subject: [PATCH 080/215] delete tailwind test in App.tsx --- frontend/src/App.tsx | 4 ---- frontend/src/components/Header.tsx | 3 --- 2 files changed, 7 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index c53094c7c7..d69193de37 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -8,10 +8,6 @@ export default function App() { return (
        - - {/* TEMP: Tailwind smoke test */} -
        Tailwind OK
        -
        diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 0ab91a9cab..528ff5a3a0 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -185,9 +185,6 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { placeholder="Search beaches…" className="flex-1 rounded-2xl border border-border bg-surface-muted px-3 py-2 focus:outline-none focus:ring-2 focus:ring-accent/40" /> -
      From 8b3e084eee052a5ee00d03d51f8d195ead3253ad Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:19:56 +0200 Subject: [PATCH 081/215] delete tailwind smoke test in main.tsx --- frontend/src/main.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index c522c085f1..46282a8cfe 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -12,10 +12,6 @@ ReactDOM.createRoot(document.getElementById("root")!).render( - {/* 🔴 TEMP: Tailwind smoke test */} -
      - Tailwind OK -
      From 8c7c3106726ea0332d86505754c2dc9d9977ec95 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:30:15 +0200 Subject: [PATCH 082/215] feat(frontend): style BeachesList as cards (custom palette, Spectral headings, distance pill) + a11y states --- frontend/src/components/BeachesList.tsx | 108 +++++++++++++++++++----- 1 file changed, 85 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 747178c6bf..884b3ee97d 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -22,7 +22,7 @@ export default function BeachesList() { // Re-sort by proximity when coords arrive (no re-fetch needed) const items = useMemo(() => { - if (!data) return [] as BeachSummary[]; + if (!data) return [] as (BeachSummary & { _distanceKm?: number })[]; if (!coords) return data; const withDist = data.map((b) => { @@ -35,7 +35,7 @@ export default function BeachesList() { }; }); - return withDist.sort((a, b) => a._distanceKm - b._distanceKm); + return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); }, [data, coords]); // optional: ensure we refetch once on mount in case cache is empty @@ -43,45 +43,97 @@ export default function BeachesList() { if (!data && !isLoading) refetch(); }, [data, isLoading, refetch]); - if (isLoading) return

      Loading beaches…

      ; - if (isError) - return

      Could not load beaches. {(error as Error)?.message}

      ; + /* ---------- STATES ---------- */ + if (isLoading) { + return ( +
      +
      + + + +
      + ); + } + + if (isError) { + return ( +
      +

      Could not load beaches.

      +

      + {(error as Error)?.message ?? "Please try again."} +

      + +
      + ); + } + + if (!items.length) { + return ( +
      +

      No beaches found.

      +

      + Try refreshing or adjusting your filters. +

      +
      + ); + } + /* ---------- UI ---------- */ return (
      + {/* Location action + status */}
      - {coords && ( - - Using your location for proximity. - - )} - {geoError && {geoError}} +
      + {coords && ( + + Using your location for proximity. + + )} + {geoError && {geoError}} +
      + {/* List */}
        {items.slice(0, 50).map((b) => ( -
      • - -
        -

        {b.name}

        - {/* distance pill if available */} - {"_distanceKm" in b && ( - - {formatKm((b as any)._distanceKm)} +
      • + +
        +
        +

        + {b.name} +

        +

        + {b.municipality ?? "—"} • {b.lat.toFixed(4)},{" "} + {b.lon.toFixed(4)} +

        +
        + + {"_distanceKm" in b && b._distanceKm !== undefined && ( + + {formatKm(b._distanceKm)} )}
        -

        - {b.municipality} — {b.lat.toFixed(4)}, {b.lon.toFixed(4)} -

      • ))} @@ -89,3 +141,13 @@ export default function BeachesList() {
      ); } + +/** small skeleton block for loading state */ +function CardSkeleton() { + return ( +
      +
      +
      +
      + ); +} From de5104fc564a104aeb9d05f1e491955898321cf7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:33:49 +0200 Subject: [PATCH 083/215] =?UTF-8?q?chore(frontend):=20keep=20BeachesList?= =?UTF-8?q?=20=E2=80=93=20distance=20guard=20&=20skeletons=20already=20in?= =?UTF-8?q?=20place?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/BeachesList.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 884b3ee97d..cc02fcb15a 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -21,8 +21,8 @@ export default function BeachesList() { } = useGeolocation(); // Re-sort by proximity when coords arrive (no re-fetch needed) - const items = useMemo(() => { - if (!data) return [] as (BeachSummary & { _distanceKm?: number })[]; + const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { + if (!data) return []; if (!coords) return data; const withDist = data.map((b) => { @@ -30,9 +30,7 @@ export default function BeachesList() { { lat: coords.lat, lon: coords.lon }, { lat: b.lat, lon: b.lon } ); - return { ...b, _distanceKm: km } as BeachSummary & { - _distanceKm: number; - }; + return { ...b, _distanceKm: km }; }); return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); From 8a931c6d55d824959fc743c4a1bbea161ae9fe0f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:35:55 +0200 Subject: [PATCH 084/215] chore(frontend): minor Tailwind component polish (card shadow helper) --- frontend/src/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index 948b0293ec..b0f09d0d5f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -63,7 +63,7 @@ /* Reusable bits */ @layer components { .card { - @apply bg-surface-muted rounded-2xl shadow border border-border; + @apply bg-surface-muted rounded-2xl border border-border shadow-card dark:shadow-cardDark; } .badge { @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full From 9ea064c785f4714592b7b517f4e45f07cd4373f8 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:53:08 +0200 Subject: [PATCH 085/215] style(frontend): restore v4 @theme tokens and base styles in index.css --- frontend/src/index.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/index.css b/frontend/src/index.css index b0f09d0d5f..948b0293ec 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -63,7 +63,7 @@ /* Reusable bits */ @layer components { .card { - @apply bg-surface-muted rounded-2xl border border-border shadow-card dark:shadow-cardDark; + @apply bg-surface-muted rounded-2xl shadow border border-border; } .badge { @apply inline-flex items-center gap-1 px-2.5 py-1 rounded-full From c966aa503fcec7f1b5697124d3a94a235edc7c61 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 00:56:02 +0200 Subject: [PATCH 086/215] feat(frontend): style Beaches list as cards + proximity pill, skeleton + error states --- frontend/src/components/BeachesList.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index cc02fcb15a..295be45eb9 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,8 +1,10 @@ +// frontend/src/components/BeachesList.tsx import { useEffect, useMemo } from "react"; -import { fetchBeaches } from "../api/beaches"; -import { BeachSummary } from "../types/beaches"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; + +import { fetchBeaches } from "../api/beaches"; +import { BeachSummary } from "../types/beaches"; import { useGeolocation } from "../hooks/useGeolocation"; import { distanceKm, formatKm } from "../utils/geo"; @@ -20,7 +22,7 @@ export default function BeachesList() { request, } = useGeolocation(); - // Re-sort by proximity when coords arrive (no re-fetch needed) + // Derive sorted items (nearest first when we have coords) const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { if (!data) return []; if (!coords) return data; @@ -36,7 +38,7 @@ export default function BeachesList() { return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); }, [data, coords]); - // optional: ensure we refetch once on mount in case cache is empty + // Refetch once on mount if cache was empty useEffect(() => { if (!data && !isLoading) refetch(); }, [data, isLoading, refetch]); @@ -90,7 +92,7 @@ export default function BeachesList() { {/* Location action + status */}
      - {/* List */} + {/* Card list */}
        {items.slice(0, 50).map((b) => (
      • From 7721fc6604f66dbbe5e3b8e71c4a8b5c5654d46b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:02:05 +0200 Subject: [PATCH 087/215] feat(frontend): show water quality + latest rating on Beach detail, add error/skeleton states --- frontend/src/components/BeachDetailPage.tsx | 191 +++++++++++++++++++- 1 file changed, 182 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index 12aaeb74ab..29836c25e9 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -1,18 +1,191 @@ +// frontend/src/components/BeachDetailPage.tsx import { useParams, Link } from "react-router-dom"; -import BeachDetailPanel from "./BeachDetailPanel"; -import { useTranslation } from "react-i18next"; +import { useQuery } from "@tanstack/react-query"; +import { fetchBeach } from "../api/beaches"; +import type { BeachDetail } from "../types/beaches"; +import { formatDate } from "../utils/format"; + +// Map quality to CSS class (uses the tokens defined in index.css) +function qualityClass(q?: number | string) { + // Prefer numeric classification 1..4 if present + if (typeof q === "number") { + switch (q) { + case 1: + return "kpi-excellent"; + case 2: + return "kpi-good"; + case 3: + return "kpi-sufficient"; + case 4: + return "kpi-poor"; + default: + return "kpi-unknown"; + } + } + // Fallback by text (covers Swedish/English labels from API) + const t = String(q ?? "").toLowerCase(); + if (t.includes("utmärkt") || t.includes("excellent")) return "kpi-excellent"; + if (t.includes("bra") || t.includes("good")) return "kpi-good"; + if (t.includes("tillfreds") || t.includes("sufficient")) + return "kpi-sufficient"; + if (t.includes("dålig") || t.includes("poor")) return "kpi-poor"; + return "kpi-unknown"; +} export default function BeachDetailPage() { - const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); - if (!id) return

        {t("loadError")}

        ; + const { data, isLoading, isError, error, refetch } = useQuery({ + queryKey: ["beach", id], + enabled: !!id, + queryFn: () => fetchBeach(id!), + staleTime: 5 * 60 * 1000, + }); + + if (isLoading) { + return ( +
        + +
        +
        +
        +
        +
        +
        + ); + } + + if (isError || !data) { + return ( +
        + +
        +

        Could not load beach.

        +

        + {(error as Error)?.message ?? "Please try again."} +

        + +
        +
        + ); + } + + // Pull primary fields + const name = data.locationName ?? data.nutsCode; + const area = data.locationArea ?? "—"; + const qualityNum = data.classification; // 1..4 (current) + const qualityText = data.classificationText ?? ""; // e.g. "Bra kvalitet" + const year = data.classificationYear; + + // Most recent rating in the history array (if present) + const latestRating = + Array.isArray(data.qualityRating) && data.qualityRating.length + ? data.qualityRating[0] + : undefined; + + return ( +
        + + + {/* Title + municipality */} +
        +
        +

        {name}

        +

        {area}

        +
        + + {/* Quality pill */} +
        + + + {qualityText || "Okänd kvalitet"} + {year ? ` • ${year}` : ""} + +
        +
        + + {/* Quick facts row (expand later maybe) */} +
        +
          + {typeof data.algalText === "string" && ( +
        • + Algblomning:{" "} + {data.algalText} +
        • + )} + {typeof data.euMotive === "string" && ( +
        • + EU-motiv:{" "} + {data.euMotive} +
        • + )} + {latestRating && ( +
        • + Senaste betyg:{" "} + + {latestRating.qualityRatingText} ({latestRating.ratingYear}) + +
        • + )} + {/* contact info */} + {(data.contactMail || data.contactPhone || data.contactUrl) && ( +
        • + Kontakt:{" "} + + {data.contactMail ?? "—"} + {data.contactPhone ? ` • ${data.contactPhone}` : ""} + {data.contactUrl ? ` • ${data.contactUrl}` : ""} + +
        • + )} +
        +
        + + {/* Long description */} + {data.bathInformation && ( +
        +

        Om badplatsen

        +

        + {data.bathInformation} +

        +
        + )} + + {/* History (optional compact list) */} + {Array.isArray(data.qualityRating) && data.qualityRating.length > 0 && ( +
        +

        Historik

        +
          + {data.qualityRating.slice(0, 5).map((r) => ( +
        • + + ● + + {r.ratingYear} + {r.qualityRatingText} +
        • + ))} +
        +
        + )} +
        + ); +} + +function BackBar() { return ( -
        -

        - ← {t("back")} -

        - +
        + + ← Back +
        ); } From 907dd49594473976d185dffbeb42462ce27b7ea9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:07:27 +0200 Subject: [PATCH 088/215] chore(frontend): extend BeachDetail with optional sample date fields (non-breaking) --- frontend/src/types/beaches.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/frontend/src/types/beaches.ts b/frontend/src/types/beaches.ts index 6c6e69400d..e5007a095d 100644 --- a/frontend/src/types/beaches.ts +++ b/frontend/src/types/beaches.ts @@ -74,8 +74,16 @@ export interface BeachDetail { dissuasion?: string[]; euMotive?: string; euType?: boolean; - locationArea?: string; // municipality/area - locationName?: string; // beach name - nutsCode: string; // stable id (same as NUTSKOD) + locationArea?: string; + locationName?: string; + nutsCode: string; qualityRating?: BeachQualityRating[]; + + // ⬇️ Likely sample-date candidates we’ll probe for: + latestSampleDate?: string; + sampleDate?: string; + lastSampleDate?: string; + // sometimes APIs nest: + samples?: Array<{ date?: string; [k: string]: unknown }>; + lastSample?: { date?: string; [k: string]: unknown }; } From a65a000b3bd3a9e737f584680cc993ac8b8d0fcb Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:11:17 +0200 Subject: [PATCH 089/215] feat(frontend): show latest sample date (Provdatum) on Beach detail when available --- frontend/src/components/BeachDetailPage.tsx | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index 29836c25e9..4704401e60 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -32,6 +32,21 @@ function qualityClass(q?: number | string) { return "kpi-unknown"; } +function pickLatestSampleDate(d: BeachDetail): string | undefined { + // check common flat fields first + const direct = d.latestSampleDate || d.sampleDate || d.lastSampleDate; + + if (direct) return direct; + + // try nested shapes if available + if (Array.isArray(d.samples) && d.samples.length) { + return d.samples[0]?.date; + } + if (d.lastSample?.date) return d.lastSample.date; + + return undefined; +} + export default function BeachDetailPage() { const { id } = useParams<{ id: string }>(); @@ -81,6 +96,7 @@ export default function BeachDetailPage() { const qualityNum = data.classification; // 1..4 (current) const qualityText = data.classificationText ?? ""; // e.g. "Bra kvalitet" const year = data.classificationYear; + const latestSampleDate = pickLatestSampleDate(data); // Most recent rating in the history array (if present) const latestRating = @@ -112,6 +128,12 @@ export default function BeachDetailPage() { {/* Quick facts row (expand later maybe) */}
          + {latestSampleDate && ( +
        • + Provdatum:{" "} + {formatDate(latestSampleDate)} +
        • + )} {typeof data.algalText === "string" && (
        • Algblomning:{" "} From 0155e61f6878cc121151aa938b290fb440ea26b4 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 01:13:21 +0200 Subject: [PATCH 090/215] chore(frontend): add formatDate util and use browser locale --- frontend/src/utils/format.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 frontend/src/utils/format.ts diff --git a/frontend/src/utils/format.ts b/frontend/src/utils/format.ts new file mode 100644 index 0000000000..b85be67e60 --- /dev/null +++ b/frontend/src/utils/format.ts @@ -0,0 +1,20 @@ +/** + * Format a date-ish value. If it's already human text, returns as-is. + * If it's an ISO string or timestamp, formats using the current locale. + */ +export function formatDate(input?: string | number | Date): string { + if (!input) return "—"; + const d = new Date(input); + if (Number.isNaN(d.getTime())) { + // Not a real date — just return the original string (e.g., "Ingen provtagning") + return String(input); + } + // Use browser locale; fallback to Swedish style if unavailable. + const locale = + (typeof navigator !== "undefined" && navigator.language) || "sv-SE"; + return d.toLocaleDateString(locale, { + year: "numeric", + month: "short", + day: "numeric", + }); +} From acfcad501faa3a7f3c4e4046593012d940b15f0a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:17:50 +0200 Subject: [PATCH 091/215] feat(frontend): style BeachDetailPage to match mock (quality pill, meta grid, description, contact) --- frontend/src/components/BeachDetailPage.tsx | 282 ++++++++++---------- 1 file changed, 134 insertions(+), 148 deletions(-) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index 4704401e60..7b98596e11 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -1,13 +1,10 @@ -// frontend/src/components/BeachDetailPage.tsx import { useParams, Link } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; import { fetchBeach } from "../api/beaches"; -import type { BeachDetail } from "../types/beaches"; import { formatDate } from "../utils/format"; -// Map quality to CSS class (uses the tokens defined in index.css) -function qualityClass(q?: number | string) { - // Prefer numeric classification 1..4 if present +// Map numeric/class text → color class +function qualityClass(q: number | string | undefined) { if (typeof q === "number") { switch (q) { case 1: @@ -22,35 +19,21 @@ function qualityClass(q?: number | string) { return "kpi-unknown"; } } - // Fallback by text (covers Swedish/English labels from API) - const t = String(q ?? "").toLowerCase(); - if (t.includes("utmärkt") || t.includes("excellent")) return "kpi-excellent"; - if (t.includes("bra") || t.includes("good")) return "kpi-good"; - if (t.includes("tillfreds") || t.includes("sufficient")) - return "kpi-sufficient"; - if (t.includes("dålig") || t.includes("poor")) return "kpi-poor"; - return "kpi-unknown"; -} - -function pickLatestSampleDate(d: BeachDetail): string | undefined { - // check common flat fields first - const direct = d.latestSampleDate || d.sampleDate || d.lastSampleDate; - - if (direct) return direct; - - // try nested shapes if available - if (Array.isArray(d.samples) && d.samples.length) { - return d.samples[0]?.date; + if (typeof q === "string") { + const s = q.toLowerCase(); + if (s.includes("utmärkt")) return "kpi-excellent"; + if (s.includes("bra")) return "kpi-good"; + if (s.includes("tillfreds") || s.includes("sufficient")) + return "kpi-sufficient"; + if (s.includes("dålig") || s.includes("poor")) return "kpi-poor"; } - if (d.lastSample?.date) return d.lastSample.date; - - return undefined; + return "kpi-unknown"; } export default function BeachDetailPage() { const { id } = useParams<{ id: string }>(); - const { data, isLoading, isError, error, refetch } = useQuery({ + const { data, isLoading, isError, error } = useQuery({ queryKey: ["beach", id], enabled: !!id, queryFn: () => fetchBeach(id!), @@ -59,155 +42,158 @@ export default function BeachDetailPage() { if (isLoading) { return ( -
          - +
          +
          +
          -
          -
          -
          +
          +
          -
          +
          ); } if (isError || !data) { return ( -
          - -
          -

          Could not load beach.

          +
          +
          +

          Could not load this beach.

          {(error as Error)?.message ?? "Please try again."}

          - + ← Back to list +
          -
          +
        ); } - // Pull primary fields - const name = data.locationName ?? data.nutsCode; - const area = data.locationArea ?? "—"; - const qualityNum = data.classification; // 1..4 (current) - const qualityText = data.classificationText ?? ""; // e.g. "Bra kvalitet" - const year = data.classificationYear; - const latestSampleDate = pickLatestSampleDate(data); - - // Most recent rating in the history array (if present) - const latestRating = - Array.isArray(data.qualityRating) && data.qualityRating.length - ? data.qualityRating[0] + const title = data.locationName ?? "Beach"; + const muni = data.locationArea ?? ""; + const qualityNum = data.classification; + const qualityText = data.classificationText ?? "Okänd"; + const pillClass = qualityClass(qualityNum ?? qualityText); + + const latestSample = + data.qualityRating && data.qualityRating.length + ? data.qualityRating.reduce((acc, cur) => + cur.ratingYear > acc.ratingYear ? cur : acc + ) : undefined; + // If HaV exposes a specific sample date string elsewhere, prefer that; + // otherwise show the newest rating year as a year-only fallback. + const latestSampleLabel = (data as any).latestSampleDate // if you later add a normalized date, use it here + ? formatDate((data as any).latestSampleDate) + : latestSample + ? String(latestSample.ratingYear) + : "—"; + return ( -
        - +
        + {/* Heading block */} +
        +

        {title}

        +

        {muni || "—"}

        +
        - {/* Title + municipality */} -
        -
        -

        {name}

        -

        {area}

        -
        + {/* KPI row */} +
        + + {qualityText}{" "} + {data.classificationYear ? `• ${data.classificationYear}` : ""} + + {data.euType && EU-bad} +
        - {/* Quality pill */} -
        - - - {qualityText || "Okänd kvalitet"} - {year ? ` • ${year}` : ""} - + {/* Meta card */} +
        +
        +
        +
        Latest sample
        +
        {latestSampleLabel}
        +
        +
        +
        Algal bloom
        +
        {data.algalText ?? "—"}
        +
        +
        +
        EU motive
        +
        {data.euMotive ?? "—"}
        +
        +
        +
        NUTS code
        +
        {data.nutsCode}
        +
        -
        - {/* Quick facts row (expand later maybe) */} -
        -
          - {latestSampleDate && ( -
        • - Provdatum:{" "} - {formatDate(latestSampleDate)} -
        • - )} - {typeof data.algalText === "string" && ( -
        • - Algblomning:{" "} - {data.algalText} -
        • - )} - {typeof data.euMotive === "string" && ( -
        • - EU-motiv:{" "} - {data.euMotive} -
        • - )} - {latestRating && ( -
        • - Senaste betyg:{" "} - - {latestRating.qualityRatingText} ({latestRating.ratingYear}) - -
        • - )} - {/* contact info */} - {(data.contactMail || data.contactPhone || data.contactUrl) && ( -
        • - Kontakt:{" "} - - {data.contactMail ?? "—"} - {data.contactPhone ? ` • ${data.contactPhone}` : ""} - {data.contactUrl ? ` • ${data.contactUrl}` : ""} - -
        • - )} -
        -
        + {/* Actions row */} +
        + + + ← Back + +
        +
        - {/* Long description */} + {/* Description */} {data.bathInformation && ( -
        -

        Om badplatsen

        +
        +

        About this beach

        {data.bathInformation}

        -
        + )} - {/* History (optional compact list) */} - {Array.isArray(data.qualityRating) && data.qualityRating.length > 0 && ( -
        -

        Historik

        -
          - {data.qualityRating.slice(0, 5).map((r) => ( -
        • - - ● - - {r.ratingYear} - {r.qualityRatingText} -
        • - ))} -
        -
        + {/* Contact */} + {(data.contactMail || data.contactPhone || data.contactUrl) && ( +
        +

        Contact

        + {data.contactMail && ( +
        + Mail:{" "} + + {data.contactMail} + +
        + )} + {data.contactPhone &&
        Phone: {data.contactPhone}
        } + {data.contactUrl && ( +
        + Website:{" "} + + {data.contactUrl} + +
        + )} +
        )} -
        - ); -} - -function BackBar() { - return ( -
        - - ← Back - -
        + ); } From 1218f274e9540323ad87cac36499ce2ee7cd1ea7 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:23:17 +0200 Subject: [PATCH 092/215] feat(frontend): add auth token helper (localStorage) --- frontend/src/utils/auth.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 frontend/src/utils/auth.ts diff --git a/frontend/src/utils/auth.ts b/frontend/src/utils/auth.ts new file mode 100644 index 0000000000..55f4047f1a --- /dev/null +++ b/frontend/src/utils/auth.ts @@ -0,0 +1,13 @@ +export const TOKEN_KEY = "BADA_TOKEN"; + +export function getToken(): string { + return localStorage.getItem(TOKEN_KEY) ?? ""; +} + +export function setToken(token: string) { + localStorage.setItem(TOKEN_KEY, token); +} + +export function clearToken() { + localStorage.removeItem(TOKEN_KEY); +} From a71855439cd404c50b307276fd597be48c0a616a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:24:02 +0200 Subject: [PATCH 093/215] feat(frontend): favorites API hooks (list/add/remove) with React Query --- frontend/src/api/favorites.ts | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 frontend/src/api/favorites.ts diff --git a/frontend/src/api/favorites.ts b/frontend/src/api/favorites.ts new file mode 100644 index 0000000000..6c3f5618e8 --- /dev/null +++ b/frontend/src/api/favorites.ts @@ -0,0 +1,73 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getToken } from "../utils/auth"; + +const API_BASE = + import.meta.env.VITE_API_BASE ?? "https://bada-backend.vercel.app/api"; + +export type Favorite = { + _id: string; + userId?: string; + beachId: string; + note?: string; + createdAt?: string; + updatedAt?: string; +}; + +export function useFavorites() { + const token = getToken(); + return useQuery({ + queryKey: ["favorites", token], + enabled: !!token, + queryFn: async (): Promise => { + const res = await fetch(`${API_BASE}/favorites`, { + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) throw new Error("Failed to fetch favorites"); + return res.json(); + }, + staleTime: 60_000, + }); +} + +export function useAddFavorite() { + const qc = useQueryClient(); + const token = getToken(); + return useMutation({ + mutationFn: async (beachId: string) => { + const res = await fetch(`${API_BASE}/favorites`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ beachId }), + }); + if (!res.ok) throw new Error("Failed to add favorite"); + return res.json() as Promise; + }, + onSuccess: () => { + qc.invalidateQueries({ queryKey: ["favorites", token] }); + }, + }); +} + +export function useRemoveFavorite() { + const qc = useQueryClient(); + const token = getToken(); + return useMutation({ + mutationFn: async (vars: { id?: string; beachId?: string }) => { + const url = vars.id + ? `${API_BASE}/favorites/${vars.id}` + : `${API_BASE}/favorites/by-beach/${vars.beachId}`; + const res = await fetch(url, { + method: "DELETE", + headers: { Authorization: `Bearer ${token}` }, + }); + if (!res.ok) throw new Error("Failed to remove favorite"); + return true; + }, + onSuccess: () => { + qc.invalidateQueries({ queryKey: ["favorites", token] }); + }, + }); +} From 081e152c2b8c273844efc716291880901bb03457 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:30:14 +0200 Subject: [PATCH 094/215] feat(frontend): wire favorite toggle on BeachDetailPage (prompt for JWT, add/remove with React Query) --- frontend/src/components/BeachDetailPage.tsx | 53 +++++++++++++++++---- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index 7b98596e11..53e6c3a299 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -1,7 +1,13 @@ import { useParams, Link } from "react-router-dom"; -import { useQuery } from "@tanstack/react-query"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; import { fetchBeach } from "../api/beaches"; import { formatDate } from "../utils/format"; +import { + useFavorites, + useAddFavorite, + useRemoveFavorite, +} from "../api/favorites"; +import { getToken, setToken } from "../utils/auth"; // Map numeric/class text → color class function qualityClass(q: number | string | undefined) { @@ -40,6 +46,15 @@ export default function BeachDetailPage() { staleTime: 5 * 60 * 1000, }); + // --- FAVORITES HOOKS --- + const queryClient = useQueryClient(); + const token = getToken(); + const { data: favorites } = useFavorites(); + const addFav = useAddFavorite(); + const rmFav = useRemoveFavorite(); + const existingFav = favorites?.find((f) => f.beachId === id); + const isFav = !!existingFav; + if (isLoading) { return (
        @@ -85,9 +100,7 @@ export default function BeachDetailPage() { ) : undefined; - // If HaV exposes a specific sample date string elsewhere, prefer that; - // otherwise show the newest rating year as a year-only fallback. - const latestSampleLabel = (data as any).latestSampleDate // if you later add a normalized date, use it here + const latestSampleLabel = (data as any).latestSampleDate ? formatDate((data as any).latestSampleDate) : latestSample ? String(latestSample.ratingYear) @@ -134,12 +147,36 @@ export default function BeachDetailPage() { {/* Actions row */}
        + Date: Mon, 15 Sep 2025 13:44:20 +0200 Subject: [PATCH 095/215] feat(frontend): wire Header search input to global store --- frontend/src/components/Header.tsx | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 528ff5a3a0..9da24be4a2 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; +import { useUI } from "../store/ui"; /** Simple outside-click hook so the menus close on blur */ function useOutsideClose(onClose: () => void) { @@ -40,6 +41,10 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { const userRef = useOutsideClose(() => setUserOpen(false)); const { isDark, setIsDark } = useDarkMode(); + // 🔎 global search state (used by BeachesList to filter) + const search = useUI((s) => s.search); + const setSearch = useUI((s) => s.setSearch); + return (
        @@ -176,15 +181,26 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) {
        - {/* Optional: Search + “Use current location” bar lives just under header */} - {/* I’ll wire these as actual components later */} + {/* Search bar (now actually filters via global store) */}
        -
        +
        setSearch(e.target.value)} /> + {search && ( + + )}
      From d3747a2adde7622965d151beba441e080e412925 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:45:21 +0200 Subject: [PATCH 096/215] feat(frontend): add tiny UI store (zustand) for header search --- frontend/package.json | 3 ++- frontend/src/store/ui.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 frontend/src/store/ui.ts diff --git a/frontend/package.json b/frontend/package.json index 5833ed2a24..39d8d406d2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^15.7.3", - "react-router-dom": "^7.9.1" + "react-router-dom": "^7.9.1", + "zustand": "^5.0.8" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.13", diff --git a/frontend/src/store/ui.ts b/frontend/src/store/ui.ts new file mode 100644 index 0000000000..92d78e933f --- /dev/null +++ b/frontend/src/store/ui.ts @@ -0,0 +1,11 @@ +import { create } from "zustand"; + +type UIState = { + search: string; + setSearch: (value: string) => void; +}; + +export const useUI = create((set) => ({ + search: "", + setSearch: (value) => set({ search: value }), +})); From adb0eeb60b3ce013aeb719c95b7f30986b8902f9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:50:44 +0200 Subject: [PATCH 097/215] feat(frontend): filter BeachesList via Zustand search (Header input) --- frontend/src/components/BeachesList.tsx | 29 +++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 295be45eb9..efe9406f6f 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -7,6 +7,7 @@ import { fetchBeaches } from "../api/beaches"; import { BeachSummary } from "../types/beaches"; import { useGeolocation } from "../hooks/useGeolocation"; import { distanceKm, formatKm } from "../utils/geo"; +import { useUI } from "../store/ui"; // ← ✅ read search text from Zustand export default function BeachesList() { const { data, isLoading, isError, error, refetch } = useQuery({ @@ -38,6 +39,18 @@ export default function BeachesList() { return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); }, [data, coords]); + // 🔎 Filter by search (from Zustand store) + const search = useUI((s) => s.search); + const q = search.trim().toLowerCase(); + + const filtered = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { + if (!q) return items; + return items.filter((b) => { + const hay = `${b.name} ${b.municipality ?? ""}`.toLowerCase(); + return hay.includes(q); + }); + }, [items, q]); + // Refetch once on mount if cache was empty useEffect(() => { if (!data && !isLoading) refetch(); @@ -80,7 +93,19 @@ export default function BeachesList() {

      No beaches found.

      - Try refreshing or adjusting your filters. + Try refreshing or adjusting the filters. +

      +
      + ); + } + + // If we have items but none match the current filter + if (q && filtered.length === 0) { + return ( +
      +

      No matches.

      +

      + Nothing matches “{search}”. Try a different name or municipality.

      ); @@ -111,7 +136,7 @@ export default function BeachesList() { {/* Card list */}
        - {items.slice(0, 50).map((b) => ( + {filtered.slice(0, 50).map((b) => (
      • Date: Mon, 15 Sep 2025 13:57:00 +0200 Subject: [PATCH 098/215] feat(frontend): add UI store (search state) with zustand --- frontend/src/store/ui.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/store/ui.ts b/frontend/src/store/ui.ts index 92d78e933f..a8727b4586 100644 --- a/frontend/src/store/ui.ts +++ b/frontend/src/store/ui.ts @@ -2,10 +2,10 @@ import { create } from "zustand"; type UIState = { search: string; - setSearch: (value: string) => void; + setSearch: (v: string) => void; }; export const useUI = create((set) => ({ search: "", - setSearch: (value) => set({ search: value }), + setSearch: (v) => set({ search: v }), })); From 18dd729a8bc06916677b1c98f472bb3a85c31f6b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:03:20 +0200 Subject: [PATCH 099/215] feat(backend): add havV2Get helper for HaV v2 API calls --- backend/src/lib/hav.ts | 26 +++++++++++++++----------- backend/src/lib/havbackup.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 backend/src/lib/havbackup.ts diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index 801c4045bb..dbb26d179b 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -2,19 +2,16 @@ import { cache } from "./cache.js"; const HAV_BASE_URL = process.env.HAV_BASE_URL!; const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; +const HAV_V2_BASE_URL = process.env.HAV_V2_BASE_URL!; -// Helper to build a cache key function key(path: string) { return `hav:${path}`; } -// Generic fetch wrapper with caching export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { - const k = key(path); + const k = key(`v1:${path}`); const cached = cache.get(k); - if (cached) { - return cached; // ✅ serve from memory if not expired - } + if (cached) return cached; const url = `${HAV_BASE_URL}${path}`; const res = await fetch(url, { @@ -23,12 +20,19 @@ export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { Accept: "application/json", }, }); - - if (!res.ok) { + if (!res.ok) throw new Error(`HaV API error: ${res.status} ${res.statusText}`); - } - const data = await res.json(); - cache.set(k, data, ttlMs); // ✅ cache result + cache.set(k, data, ttlMs); return data; } + +// NEW: v2 fetcher for /bathing-waters endpoints +export async function havV2Get(path: string): Promise { + const base = process.env.HAV_V2_BASE_URL; + if (!base) throw new Error("HAV_V2_BASE_URL not set"); + const url = `${base}${path}`; + const res = await fetch(url); + if (!res.ok) throw new Error(`HaV API error ${res.status}`); + return res.json() as Promise; +} diff --git a/backend/src/lib/havbackup.ts b/backend/src/lib/havbackup.ts new file mode 100644 index 0000000000..801c4045bb --- /dev/null +++ b/backend/src/lib/havbackup.ts @@ -0,0 +1,34 @@ +import { cache } from "./cache.js"; + +const HAV_BASE_URL = process.env.HAV_BASE_URL!; +const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; + +// Helper to build a cache key +function key(path: string) { + return `hav:${path}`; +} + +// Generic fetch wrapper with caching +export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { + const k = key(path); + const cached = cache.get(k); + if (cached) { + return cached; // ✅ serve from memory if not expired + } + + const url = `${HAV_BASE_URL}${path}`; + const res = await fetch(url, { + headers: { + "User-Agent": HAV_USER_AGENT, + Accept: "application/json", + }, + }); + + if (!res.ok) { + throw new Error(`HaV API error: ${res.status} ${res.statusText}`); + } + + const data = await res.json(); + cache.set(k, data, ttlMs); // ✅ cache result + return data; +} From ede02828a9d824daa6cc0d65bb965d9bab8e7c70 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:15:25 +0200 Subject: [PATCH 100/215] feat(backend): add HaV v2 client + getLatestSampleDate helper; unify fetchers and caching --- backend/src/lib/hav.ts | 85 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index dbb26d179b..7390d50aef 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -1,13 +1,21 @@ import { cache } from "./cache.js"; +/** + * Env + * - HAV_BASE_URL: v1 base, e.g. https://badplatsen.havochvatten.se/badplatsen/api + * - HAV_USER_AGENT: something like 'BADA/1.0 (contact: you@example.com)' + * - HAV_V2_BASE_URL: v2 base, e.g. https://api.havochvatten.se/bathingwaters/v2 + */ const HAV_BASE_URL = process.env.HAV_BASE_URL!; const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; const HAV_V2_BASE_URL = process.env.HAV_V2_BASE_URL!; -function key(path: string) { - return `hav:${path}`; +function key(k: string) { + return `hav:${k}`; } +/* ---------------- v1 (Badplatsen) ---------------- */ + export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { const k = key(`v1:${path}`); const cached = cache.get(k); @@ -20,19 +28,72 @@ export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { Accept: "application/json", }, }); - if (!res.ok) - throw new Error(`HaV API error: ${res.status} ${res.statusText}`); + + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(`HaV v1 error ${res.status} ${res.statusText}: ${text}`); + } + const data = await res.json(); cache.set(k, data, ttlMs); return data; } -// NEW: v2 fetcher for /bathing-waters endpoints -export async function havV2Get(path: string): Promise { - const base = process.env.HAV_V2_BASE_URL; - if (!base) throw new Error("HAV_V2_BASE_URL not set"); - const url = `${base}${path}`; - const res = await fetch(url); - if (!res.ok) throw new Error(`HaV API error ${res.status}`); - return res.json() as Promise; +/* ---------------- v2 (Bathing Waters API) ---------------- */ + +/** + * Minimal v2 fetcher. Add simple cache since results don’t change often. + */ +export async function havV2Get( + path: string, + ttlMs = 5 * 60 * 1000, + init?: RequestInit +): Promise { + const k = key(`v2:${path}`); + const cached = cache.get(k); + if (cached) return cached as T; + + const url = `${HAV_V2_BASE_URL}${path}`; + const res = await fetch(url, { + headers: { + Accept: "application/json", + // Add UA if the API requires/helps with diagnostics: + "User-Agent": HAV_USER_AGENT, + }, + ...init, + }); + + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(`HaV v2 error ${res.status} ${res.statusText}: ${text}`); + } + + const data = (await res.json()) as T; + cache.set(k, data, ttlMs); + return data; +} + +/** + * Returns latest water sample date (ISO string) for a bathing water id, + * or null if no dated results exist. + * + * Swagger notes: v2 `/bathing-waters/{id}/results` returns an object with `results: MonitoringResult[]` + * and each item can include `takenAt` (ISO timestamp). + */ +export async function getLatestSampleDate(id: string): Promise { + type MonitoringResult = { takenAt?: string | null }; + type ResultsEnvelope = { results?: MonitoringResult[] }; + + const data = await havV2Get( + `/bathing-waters/${encodeURIComponent(id)}/results` + ); + + const latest = + (data.results ?? []) + .map((r) => r.takenAt) + .filter((d): d is string => !!d) + // Sort desc by ISO date string + .sort((a, b) => (a < b ? 1 : a > b ? -1 : 0))[0] ?? null; + + return latest; } From 51f33f044773e32e826aeb473629f06106cc72af Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:19:21 +0200 Subject: [PATCH 101/215] feat(backend): include latestSampleDate on /api/beaches/:id using HaV v2 results --- backend/src/routes/beaches.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts index cdbbeb736b..1c197f0d90 100644 --- a/backend/src/routes/beaches.ts +++ b/backend/src/routes/beaches.ts @@ -1,16 +1,17 @@ +// backend/src/routes/beaches.ts import { Router } from "express"; -import { havGet } from "../lib/hav.js"; +import { havGet, getLatestSampleDate } from "../lib/hav.js"; export const beachesRouter = Router(); /** * GET /api/beaches * Proxies HaV "feature" endpoint and returns (likely) GeoJSON. - * I keep it as a thin proxy for now; later I can normalize + filter. + * Kept as a thin proxy; normalization can be added later if needed. */ beachesRouter.get("/beaches", async (_req, res, next) => { try { - // Easiest JSON form; if upstream returns XML, will switch to explicit WFS params. + // Easiest JSON form; if upstream returns XML, switch to explicit WFS params. const data = await havGet("/feature/?format=json", 5 * 60 * 1000); // cache 5 min res.json(data); } catch (err) { @@ -20,17 +21,24 @@ beachesRouter.get("/beaches", async (_req, res, next) => { /** * GET /api/beaches/:id - * Proxies HaV detail endpoint for a single bathing site by ID. + * Proxies HaV detail endpoint for a single bathing site by ID + * and augments with latest sample date from the v2 API. * Example ID: SE0441273000000001 */ beachesRouter.get("/beaches/:id", async (req, res, next) => { try { const { id } = req.params; - const data = await havGet( - `/detail/${encodeURIComponent(id)}`, - 5 * 60 * 1000 - ); - res.json(data); + + // Fetch v1 detail and v2 latest sample date in parallel + const [detail, latestSampleDate] = await Promise.all([ + havGet(`/detail/${encodeURIComponent(id)}`, 5 * 60 * 1000), + getLatestSampleDate(id).catch(() => null), // don't fail the whole request if v2 hiccups + ]); + + res.json({ + ...detail, + latestSampleDate, // ISO string or null + }); } catch (err) { next(err); } From ab35d86af6a2531d6a0022e20b87cf14c793c4e1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:37:38 +0200 Subject: [PATCH 102/215] chore(backend): install dotenv for local env loading --- backend/package.json | 5 ++++- backend/src/server.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 backend/src/server.ts diff --git a/backend/package.json b/backend/package.json index 6dfe9244a0..7ed02f8862 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,13 +3,14 @@ "version": "1.0.0", "type": "module", "scripts": { - "dev": "vercel dev", + "dev": "tsx watch src/server.ts", "build": "tsc", "start": "node dist/index.js" }, "dependencies": { "bcryptjs": "^3.0.2", "cors": "^2.8.5", + "dotenv": "^17.2.2", "express": "^5.1.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.18.1", @@ -21,6 +22,8 @@ "@types/express": "^5.0.3", "@types/jsonwebtoken": "^9.0.10", "@types/node": "^20.19.13", + "ts-node-dev": "^2.0.0", + "tsx": "^4.20.5", "typescript": "^5.9.2" }, "engines": { diff --git a/backend/src/server.ts b/backend/src/server.ts new file mode 100644 index 0000000000..06d7986d85 --- /dev/null +++ b/backend/src/server.ts @@ -0,0 +1,8 @@ +import "dotenv/config"; +import app from "./index.js"; + +const port = Number(process.env.PORT ?? 3000); + +app.listen(port, () => { + console.log(`API listening on http://localhost:${port}`); +}); From 923f4044aa524180a97275407d651083a966a3ae Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:40:51 +0200 Subject: [PATCH 103/215] feat(backend): enrich /api/beaches/:id with latestSampleDate from HaV v2 --- backend/src/routes/beaches.ts | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts index 1c197f0d90..3d884054ce 100644 --- a/backend/src/routes/beaches.ts +++ b/backend/src/routes/beaches.ts @@ -1,4 +1,3 @@ -// backend/src/routes/beaches.ts import { Router } from "express"; import { havGet, getLatestSampleDate } from "../lib/hav.js"; @@ -6,12 +5,10 @@ export const beachesRouter = Router(); /** * GET /api/beaches - * Proxies HaV "feature" endpoint and returns (likely) GeoJSON. - * Kept as a thin proxy; normalization can be added later if needed. + * Proxies HaV "feature" endpoint (GeoJSON-like). */ beachesRouter.get("/beaches", async (_req, res, next) => { try { - // Easiest JSON form; if upstream returns XML, switch to explicit WFS params. const data = await havGet("/feature/?format=json", 5 * 60 * 1000); // cache 5 min res.json(data); } catch (err) { @@ -21,24 +18,29 @@ beachesRouter.get("/beaches", async (_req, res, next) => { /** * GET /api/beaches/:id - * Proxies HaV detail endpoint for a single bathing site by ID - * and augments with latest sample date from the v2 API. - * Example ID: SE0441273000000001 + * HaV v1 detail + latest sample date from HaV v2 results. + * Example ID: SE0110180000001869 */ beachesRouter.get("/beaches/:id", async (req, res, next) => { try { const { id } = req.params; - // Fetch v1 detail and v2 latest sample date in parallel - const [detail, latestSampleDate] = await Promise.all([ - havGet(`/detail/${encodeURIComponent(id)}`, 5 * 60 * 1000), - getLatestSampleDate(id).catch(() => null), // don't fail the whole request if v2 hiccups - ]); + // v1 detail + const data = await havGet( + `/detail/${encodeURIComponent(id)}`, + 5 * 60 * 1000 + ); - res.json({ - ...detail, - latestSampleDate, // ISO string or null - }); + // v2 latest sample date + let latestSampleDate: string | null = null; + try { + latestSampleDate = await getLatestSampleDate(id); + } catch (e) { + console.error("[HaV v2 latest sample error]", e); + // keep going; we still return the v1 detail + } + + res.json({ ...data, latestSampleDate }); } catch (err) { next(err); } From e85b6fa8e5885192e235e2d8faa8ea40efabed98 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 22:49:22 +0200 Subject: [PATCH 104/215] fix(backend): read HaV v2 base from HAV_V2_BASE or HAV_V2_BASE_URL; tidy hav.ts --- backend/src/lib/hav.ts | 75 +++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index 7390d50aef..163bc5827f 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -1,21 +1,26 @@ +// backend/src/lib/hav.ts import { cache } from "./cache.js"; -/** - * Env - * - HAV_BASE_URL: v1 base, e.g. https://badplatsen.havochvatten.se/badplatsen/api - * - HAV_USER_AGENT: something like 'BADA/1.0 (contact: you@example.com)' - * - HAV_V2_BASE_URL: v2 base, e.g. https://api.havochvatten.se/bathingwaters/v2 - */ const HAV_BASE_URL = process.env.HAV_BASE_URL!; const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; -const HAV_V2_BASE_URL = process.env.HAV_V2_BASE_URL!; -function key(k: string) { - return `hav:${k}`; -} +// Accept either HAV_V2_BASE or HAV_V2_BASE_URL (backward-compatible) +const HAV_V2_BASE = + process.env.HAV_V2_BASE || process.env.HAV_V2_BASE_URL || ""; + +if (!HAV_BASE_URL) throw new Error("HAV_BASE_URL not set"); +if (!HAV_USER_AGENT) throw new Error("HAV_USER_AGENT not set"); +if (!HAV_V2_BASE) + console.warn( + "[warn] HAV_V2_BASE(_URL) not set — latestSampleDate will be null" + ); -/* ---------------- v1 (Badplatsen) ---------------- */ +// Small key helper for the v1 cache +function key(path: string) { + return `hav:${path}`; +} +// --- v1 fetcher (existing) export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { const k = key(`v1:${path}`); const cached = cache.get(k); @@ -28,71 +33,43 @@ export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { Accept: "application/json", }, }); - - if (!res.ok) { - const text = await res.text().catch(() => ""); - throw new Error(`HaV v1 error ${res.status} ${res.statusText}: ${text}`); - } - + if (!res.ok) + throw new Error(`HaV API error: ${res.status} ${res.statusText}`); const data = await res.json(); cache.set(k, data, ttlMs); return data; } -/* ---------------- v2 (Bathing Waters API) ---------------- */ - -/** - * Minimal v2 fetcher. Add simple cache since results don’t change often. - */ +// --- v2 fetcher export async function havV2Get( path: string, - ttlMs = 5 * 60 * 1000, init?: RequestInit ): Promise { - const k = key(`v2:${path}`); - const cached = cache.get(k); - if (cached) return cached as T; - - const url = `${HAV_V2_BASE_URL}${path}`; + if (!HAV_V2_BASE) throw new Error("HAV_V2_BASE not configured"); + const url = `${HAV_V2_BASE}${path}`; const res = await fetch(url, { - headers: { - Accept: "application/json", - // Add UA if the API requires/helps with diagnostics: - "User-Agent": HAV_USER_AGENT, - }, + headers: { Accept: "application/json" }, ...init, }); - if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`HaV v2 error ${res.status} ${res.statusText}: ${text}`); } - - const data = (await res.json()) as T; - cache.set(k, data, ttlMs); - return data; + return res.json() as Promise; } -/** - * Returns latest water sample date (ISO string) for a bathing water id, - * or null if no dated results exist. - * - * Swagger notes: v2 `/bathing-waters/{id}/results` returns an object with `results: MonitoringResult[]` - * and each item can include `takenAt` (ISO timestamp). - */ +// Pull latest sample date from v2 results export async function getLatestSampleDate(id: string): Promise { type MonitoringResult = { takenAt?: string | null }; - type ResultsEnvelope = { results?: MonitoringResult[] }; + type Results = { results?: MonitoringResult[] }; - const data = await havV2Get( + const data = await havV2Get( `/bathing-waters/${encodeURIComponent(id)}/results` ); - const latest = (data.results ?? []) .map((r) => r.takenAt) .filter((d): d is string => !!d) - // Sort desc by ISO date string .sort((a, b) => (a < b ? 1 : a > b ? -1 : 0))[0] ?? null; return latest; From 521ab563b24ec711cae1bd940acaed08037f9af3 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 15 Sep 2025 23:17:47 +0200 Subject: [PATCH 105/215] chore(backend): add request logger and /api/debug/env to troubleshoot local dev --- backend/src/index.ts | 10 +++++-- backend/src/lib/hav.ts | 55 +++++++++++++++++++++++------------ backend/src/routes/beaches.ts | 36 ++++++++++++++--------- backend/src/server.ts | 22 +++++++++++++- 4 files changed, 87 insertions(+), 36 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index c3e04a4075..eeade12058 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -52,8 +52,10 @@ app.get("/api/health-direct", (_req, res) => { res.json({ ok: true, via: "direct" }); }); -// --- 404 + error handlers --- +// 404 app.use((_req, res) => res.status(404).json({ error: "NotFound" })); + +// Error handler app.use( ( err: unknown, @@ -62,7 +64,11 @@ app.use( _next: express.NextFunction ) => { console.error("[ERROR]", err); - res.status(500).json({ error: "InternalServerError" }); + const message = + process.env.NODE_ENV !== "production" && err instanceof Error + ? err.message + : undefined; + res.status(500).json({ error: "InternalServerError", message }); } ); diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index 163bc5827f..83482a222c 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -2,63 +2,79 @@ import { cache } from "./cache.js"; const HAV_BASE_URL = process.env.HAV_BASE_URL!; -const HAV_USER_AGENT = process.env.HAV_USER_AGENT!; - -// Accept either HAV_V2_BASE or HAV_V2_BASE_URL (backward-compatible) +const HAV_USER_AGENT = process.env.HAV_USER_AGENT || "BADA/1.0"; const HAV_V2_BASE = - process.env.HAV_V2_BASE || process.env.HAV_V2_BASE_URL || ""; - -if (!HAV_BASE_URL) throw new Error("HAV_BASE_URL not set"); -if (!HAV_USER_AGENT) throw new Error("HAV_USER_AGENT not set"); -if (!HAV_V2_BASE) - console.warn( - "[warn] HAV_V2_BASE(_URL) not set — latestSampleDate will be null" - ); + process.env.HAV_V2_BASE ?? "https://api.havochvatten.se/bathingwaters/v2"; -// Small key helper for the v1 cache function key(path: string) { return `hav:${path}`; } -// --- v1 fetcher (existing) +/** v1 fetcher (old endpoints like /feature, /detail) */ export async function havGet(path: string, ttlMs = 5 * 60 * 1000) { const k = key(`v1:${path}`); const cached = cache.get(k); if (cached) return cached; const url = `${HAV_BASE_URL}${path}`; + // TEMP: trace outgoing in dev + if (process.env.NODE_ENV !== "production") { + console.log(`[HaV v1] GET ${url}`); + } + const res = await fetch(url, { headers: { "User-Agent": HAV_USER_AGENT, Accept: "application/json", }, }); - if (!res.ok) - throw new Error(`HaV API error: ${res.status} ${res.statusText}`); + + if (!res.ok) { + const text = await res.text().catch(() => ""); + console.warn( + `[HaV v1] ${res.status} ${res.statusText} for ${url}: ${text.slice( + 0, + 500 + )}` + ); + throw new Error(`HaV v1 error ${res.status} ${res.statusText}`); + } + const data = await res.json(); cache.set(k, data, ttlMs); return data; } -// --- v2 fetcher +/** Minimal v2 fetcher (used for monitoring results, etc.) */ export async function havV2Get( path: string, init?: RequestInit ): Promise { - if (!HAV_V2_BASE) throw new Error("HAV_V2_BASE not configured"); const url = `${HAV_V2_BASE}${path}`; + if (process.env.NODE_ENV !== "production") { + console.log(`[HaV v2] GET ${url}`); + } + const res = await fetch(url, { headers: { Accept: "application/json" }, ...init, }); + if (!res.ok) { const text = await res.text().catch(() => ""); - throw new Error(`HaV v2 error ${res.status} ${res.statusText}: ${text}`); + console.warn( + `[HaV v2] ${res.status} ${res.statusText} for ${url}: ${text.slice( + 0, + 500 + )}` + ); + throw new Error(`HaV v2 error ${res.status} ${res.statusText}`); } + return res.json() as Promise; } -// Pull latest sample date from v2 results +/** Pull latest sample date (ISO string) from /bathing-waters/{id}/results */ export async function getLatestSampleDate(id: string): Promise { type MonitoringResult = { takenAt?: string | null }; type Results = { results?: MonitoringResult[] }; @@ -66,6 +82,7 @@ export async function getLatestSampleDate(id: string): Promise { const data = await havV2Get( `/bathing-waters/${encodeURIComponent(id)}/results` ); + const latest = (data.results ?? []) .map((r) => r.takenAt) diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts index 3d884054ce..464677b2fc 100644 --- a/backend/src/routes/beaches.ts +++ b/backend/src/routes/beaches.ts @@ -1,3 +1,4 @@ +// backend/src/routes/beaches.ts import { Router } from "express"; import { havGet, getLatestSampleDate } from "../lib/hav.js"; @@ -5,7 +6,7 @@ export const beachesRouter = Router(); /** * GET /api/beaches - * Proxies HaV "feature" endpoint (GeoJSON-like). + * Proxies HaV "feature" endpoint and returns (likely) GeoJSON. */ beachesRouter.get("/beaches", async (_req, res, next) => { try { @@ -18,30 +19,37 @@ beachesRouter.get("/beaches", async (_req, res, next) => { /** * GET /api/beaches/:id - * HaV v1 detail + latest sample date from HaV v2 results. - * Example ID: SE0110180000001869 + * Returns HaV v1 detail + (best-effort) latestSampleDate from v2. */ -beachesRouter.get("/beaches/:id", async (req, res, next) => { - try { - const { id } = req.params; +beachesRouter.get("/beaches/:id", async (req, res) => { + const { id } = req.params; - // v1 detail - const data = await havGet( + // 1) fetch v1 detail — if this fails, respond with a 502 and a helpful message + try { + const detail = await havGet( `/detail/${encodeURIComponent(id)}`, 5 * 60 * 1000 ); - // v2 latest sample date + // 2) try to enrich with v2 latest sample date — if that fails, do NOT fail the endpoint let latestSampleDate: string | null = null; try { latestSampleDate = await getLatestSampleDate(id); } catch (e) { - console.error("[HaV v2 latest sample error]", e); - // keep going; we still return the v1 detail + console.warn( + `[beaches/:id] v2 results fetch failed for ${id}:`, + (e as Error)?.message + ); + latestSampleDate = null; } - res.json({ ...data, latestSampleDate }); - } catch (err) { - next(err); + return res.json({ ...detail, latestSampleDate }); + } catch (e) { + // v1 detail failed → surface as a 502 (bad gateway) with a friendly message in dev + const msg = (e as Error)?.message || "Upstream error"; + if (process.env.NODE_ENV !== "production") { + return res.status(502).json({ error: "UpstreamError", message: msg }); + } + return res.status(502).json({ error: "UpstreamError" }); } }); diff --git a/backend/src/server.ts b/backend/src/server.ts index 06d7986d85..809fe6c82d 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,7 +1,27 @@ import "dotenv/config"; import app from "./index.js"; -const port = Number(process.env.PORT ?? 3000); +const port = Number(process.env.PORT) || 3000; + +// 🔎 TEMP: log each request so we see what's being hit +app.use((req, _res, next) => { + console.log(`[req] ${req.method} ${req.url}`); + next(); +}); + +// 🔎 TEMP: quick env presence check (no secrets exposed) +app.get("/api/debug/env", (_req, res) => { + res.json({ + HAV_BASE_URL: !!process.env.HAV_BASE_URL, + HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, + HAV_V2_BASE: !!process.env.HAV_V2_BASE, + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + MONGODB_URI_present: !!process.env.MONGODB_URI, + JWT_SECRET_present: !!process.env.JWT_SECRET, + NODE_ENV: process.env.NODE_ENV ?? "undefined", + PORT: process.env.PORT ?? "undefined", + }); +}); app.listen(port, () => { console.log(`API listening on http://localhost:${port}`); From 15624594a57fa6db6043ee4a3c26064e0174c907 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:12:14 +0200 Subject: [PATCH 106/215] feat(backend): include latestSampleDate on /api/beaches/:id (prefer v1 sampleDate, fallback to v2 results) --- backend/src/lib/hav.ts | 1 - backend/src/routes/beaches.ts | 47 ++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index 83482a222c..2e091ca6a1 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -1,4 +1,3 @@ -// backend/src/lib/hav.ts import { cache } from "./cache.js"; const HAV_BASE_URL = process.env.HAV_BASE_URL!; diff --git a/backend/src/routes/beaches.ts b/backend/src/routes/beaches.ts index 464677b2fc..ebbce0900c 100644 --- a/backend/src/routes/beaches.ts +++ b/backend/src/routes/beaches.ts @@ -1,4 +1,3 @@ -// backend/src/routes/beaches.ts import { Router } from "express"; import { havGet, getLatestSampleDate } from "../lib/hav.js"; @@ -19,37 +18,39 @@ beachesRouter.get("/beaches", async (_req, res, next) => { /** * GET /api/beaches/:id - * Returns HaV v1 detail + (best-effort) latestSampleDate from v2. + * Proxies HaV v1 detail and augments with latestSampleDate. + * Prefers v1.sampleDate (epoch ms); falls back to v2 results when needed. */ -beachesRouter.get("/beaches/:id", async (req, res) => { - const { id } = req.params; - - // 1) fetch v1 detail — if this fails, respond with a 502 and a helpful message +beachesRouter.get("/beaches/:id", async (req, res, next) => { try { + const { id } = req.params; + + // v1 detail const detail = await havGet( `/detail/${encodeURIComponent(id)}`, 5 * 60 * 1000 ); - // 2) try to enrich with v2 latest sample date — if that fails, do NOT fail the endpoint + // prefer v1 sampleDate (epoch ms) let latestSampleDate: string | null = null; - try { - latestSampleDate = await getLatestSampleDate(id); - } catch (e) { - console.warn( - `[beaches/:id] v2 results fetch failed for ${id}:`, - (e as Error)?.message - ); - latestSampleDate = null; + const v1Ms = (detail as any)?.sampleDate; + if (typeof v1Ms === "number" && isFinite(v1Ms)) { + latestSampleDate = new Date(v1Ms).toISOString(); + } else { + // fallback to v2 + try { + latestSampleDate = await getLatestSampleDate(id); + } catch (e) { + // don't fail the whole request if v2 errors; just leave null + console.warn( + "[/beaches/:id] v2 fallback failed:", + (e as Error)?.message + ); + } } - return res.json({ ...detail, latestSampleDate }); - } catch (e) { - // v1 detail failed → surface as a 502 (bad gateway) with a friendly message in dev - const msg = (e as Error)?.message || "Upstream error"; - if (process.env.NODE_ENV !== "production") { - return res.status(502).json({ error: "UpstreamError", message: msg }); - } - return res.status(502).json({ error: "UpstreamError" }); + res.json({ ...detail, latestSampleDate }); + } catch (err) { + next(err); } }); From f04370388a5dbb88c407cfc6be707c0bbc5e5d1c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:19:59 +0200 Subject: [PATCH 107/215] feat(frontend): add short style to formatDate and use for latestSampleDate --- frontend/src/components/BeachDetailPage.tsx | 13 ++-------- frontend/src/utils/format.ts | 28 +++++++++++++++------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/components/BeachDetailPage.tsx index 53e6c3a299..7619e4c158 100644 --- a/frontend/src/components/BeachDetailPage.tsx +++ b/frontend/src/components/BeachDetailPage.tsx @@ -93,17 +93,8 @@ export default function BeachDetailPage() { const qualityText = data.classificationText ?? "Okänd"; const pillClass = qualityClass(qualityNum ?? qualityText); - const latestSample = - data.qualityRating && data.qualityRating.length - ? data.qualityRating.reduce((acc, cur) => - cur.ratingYear > acc.ratingYear ? cur : acc - ) - : undefined; - - const latestSampleLabel = (data as any).latestSampleDate - ? formatDate((data as any).latestSampleDate) - : latestSample - ? String(latestSample.ratingYear) + const latestSampleLabel = data.latestSampleDate + ? formatDate(data.latestSampleDate, "short") : "—"; return ( diff --git a/frontend/src/utils/format.ts b/frontend/src/utils/format.ts index b85be67e60..16ae245146 100644 --- a/frontend/src/utils/format.ts +++ b/frontend/src/utils/format.ts @@ -1,15 +1,29 @@ /** - * Format a date-ish value. If it's already human text, returns as-is. - * If it's an ISO string or timestamp, formats using the current locale. + * Format a date in a human readable way. + * If input is invalid, return as-is. + * If no input, return em dash. + * + * @param input Date input (string, number, Date) + * @param style "long" (default) or "short" (YYYY-MM-DD) + * @returns Formatted date string */ -export function formatDate(input?: string | number | Date): string { + +export function formatDate( + input?: string | number | Date, + style: "long" | "short" = "long" +): string { if (!input) return "—"; const d = new Date(input); - if (Number.isNaN(d.getTime())) { - // Not a real date — just return the original string (e.g., "Ingen provtagning") - return String(input); + if (Number.isNaN(d.getTime())) return String(input); + + if (style === "short") { + return d.toLocaleDateString("sv-SE", { + year: "numeric", + month: "2-digit", + day: "2-digit", + }); } - // Use browser locale; fallback to Swedish style if unavailable. + const locale = (typeof navigator !== "undefined" && navigator.language) || "sv-SE"; return d.toLocaleDateString(locale, { From d2f9556d135d23d298123ebe0a41287b53f59563 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:48:12 +0200 Subject: [PATCH 108/215] chore(frontend): add maplibre-gl (+ types) --- frontend/package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index 39d8d406d2..210767367e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "dependencies": { "@tanstack/react-query": "^5.87.4", "i18next": "^25.5.2", + "maplibre-gl": "^5.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^15.7.3", @@ -20,6 +21,7 @@ }, "devDependencies": { "@tailwindcss/postcss": "^4.1.13", + "@types/maplibre-gl": "^1.13.2", "@types/react": "^18.3.24", "@types/react-dom": "^18.3.7", "@vitejs/plugin-react": "^4.0.3", From ef6f8f04c1bdaed5194062eecd5753a1e8c98a39 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:52:48 +0200 Subject: [PATCH 109/215] style(frontend): import maplibre-gl CSS --- frontend/src/index.css | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/index.css b/frontend/src/index.css index 948b0293ec..2840c6be77 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,4 +1,5 @@ @import "tailwindcss"; +@import "maplibre-gl/dist/maplibre-gl.css"; /* Design tokens (Tailwind v4) */ @theme { From 7c0d3e4b03fc0e007ac3a39da01e6c03857fb6bc Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 00:58:39 +0200 Subject: [PATCH 110/215] feat(frontend): add MapView with MapLibre + MapTiler (compact attribution) --- frontend/src/components/MapView.tsx | 34 +++++++++++++++++++++++++++++ frontend/src/main.tsx | 1 + 2 files changed, 35 insertions(+) create mode 100644 frontend/src/components/MapView.tsx diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx new file mode 100644 index 0000000000..41cd3776ae --- /dev/null +++ b/frontend/src/components/MapView.tsx @@ -0,0 +1,34 @@ +// frontend/src/components/MapView.tsx +import { useEffect, useRef } from "react"; +import maplibregl, { Map, LngLatLike } from "maplibre-gl"; + +const MAPTILER_KEY = import.meta.env.VITE_MAPTILER_KEY; + +export default function MapView() { + const elRef = useRef(null); + const mapRef = useRef(null); + + useEffect(() => { + if (!elRef.current || mapRef.current) return; + + const map = new maplibregl.Map({ + container: elRef.current, + style: `https://api.maptiler.com/maps/outdoor/style.json?key=${MAPTILER_KEY}`, + center: [18.06, 59.33] as LngLatLike, // Stockholm-ish + zoom: 4.5, + minZoom: 2, + cooperativeGestures: true, + attributionControl: { compact: true }, // ✅ type-safe + }); + + mapRef.current = map; + return () => map.remove(); + }, []); + + return ( +
        + ); +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index 46282a8cfe..526803efb3 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter } from "react-router-dom"; import "./i18n"; import "./index.css"; +import "maplibre-gl/dist/maplibre-gl.css"; const queryClient = new QueryClient(); From 85277f5b450fa5be41c9886cbaa379f5bce7c7b1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 01:03:21 +0200 Subject: [PATCH 111/215] feat(frontend): show MapView above list and wire user location marker --- frontend/src/components/BeachesList.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index efe9406f6f..84b2a7f2bc 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -2,12 +2,13 @@ import { useEffect, useMemo } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; +import MapView from "./MapView"; import { fetchBeaches } from "../api/beaches"; import { BeachSummary } from "../types/beaches"; import { useGeolocation } from "../hooks/useGeolocation"; import { distanceKm, formatKm } from "../utils/geo"; -import { useUI } from "../store/ui"; // ← ✅ read search text from Zustand +import { useUI } from "../store/ui"; export default function BeachesList() { const { data, isLoading, isError, error, refetch } = useQuery({ @@ -99,7 +100,6 @@ export default function BeachesList() { ); } - // If we have items but none match the current filter if (q && filtered.length === 0) { return (
        @@ -134,6 +134,11 @@ export default function BeachesList() {
        + {/* Map (mobile-first height; matches card style) */} +
        + +
        + {/* Card list */}
          {filtered.slice(0, 50).map((b) => ( From 2c13f99e83c9042c5372ca0d3814e4923448afaf Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 01:29:08 +0200 Subject: [PATCH 112/215] feat(frontend): square map layout + theme-aware styles and markers --- frontend/src/components/BeachesList.tsx | 14 ++- frontend/src/components/MapView.tsx | 120 ++++++++++++++++++++---- frontend/src/index.css | 7 ++ 3 files changed, 116 insertions(+), 25 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 84b2a7f2bc..3e436f395f 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,4 +1,3 @@ -// frontend/src/components/BeachesList.tsx import { useEffect, useMemo } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; @@ -134,10 +133,15 @@ export default function BeachesList() {
      - {/* Map (mobile-first height; matches card style) */} -
      - -
      + {/* Map (mobile-first) */} + ({ + id: b.id, + name: b.name, + lat: b.lat, + lon: b.lon, + }))} + /> {/* Card list */}
        diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 41cd3776ae..44392d1bb3 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -1,34 +1,114 @@ -// frontend/src/components/MapView.tsx import { useEffect, useRef } from "react"; -import maplibregl, { Map, LngLatLike } from "maplibre-gl"; +import maplibregl, { Map } from "maplibre-gl"; +import "maplibre-gl/dist/maplibre-gl.css"; -const MAPTILER_KEY = import.meta.env.VITE_MAPTILER_KEY; +type Point = { id: string; name: string; lat: number; lon: number }; +type Props = { points?: Point[] }; -export default function MapView() { - const elRef = useRef(null); +function isDark() { + return document.documentElement.classList.contains("dark"); +} +function getAccent() { + // reads your Tailwind token set in :root / .dark + const v = getComputedStyle(document.documentElement) + .getPropertyValue("--color-accent") + .trim(); + // token is "r g b" (from your setup), normalize to rgb() + return v.includes(" ") ? `rgb(${v})` : v || "#0a5a82"; +} +function styleForTheme(key?: string) { + const light = `https://api.maptiler.com/maps/dataviz/style.json?key=${key}`; + const dark = `https://api.maptiler.com/maps/darkmatter/style.json?key=${key}`; + return isDark() ? dark : light; +} + +export default function MapView({ points = [] }: Props) { + const ref = useRef(null); const mapRef = useRef(null); + const markersRef = useRef([]); useEffect(() => { - if (!elRef.current || mapRef.current) return; + if (!ref.current) return; + const key = import.meta.env.VITE_MAPTILER_KEY as string | undefined; const map = new maplibregl.Map({ - container: elRef.current, - style: `https://api.maptiler.com/maps/outdoor/style.json?key=${MAPTILER_KEY}`, - center: [18.06, 59.33] as LngLatLike, // Stockholm-ish - zoom: 4.5, - minZoom: 2, - cooperativeGestures: true, - attributionControl: { compact: true }, // ✅ type-safe + container: ref.current, + style: styleForTheme(key), + center: [15, 62], // Sweden-ish + zoom: 4.3, + attributionControl: { compact: true }, }); - mapRef.current = map; - return () => map.remove(); - }, []); + + map.once("load", () => { + setTimeout(() => map.resize(), 0); + }); + + // helper to (re)draw markers + const drawMarkers = () => { + const accent = getAccent(); + // clear old + markersRef.current.forEach((m) => m.remove()); + markersRef.current = []; + + points.forEach((p) => { + const el = document.createElement("div"); + el.style.width = "12px"; + el.style.height = "12px"; + el.style.borderRadius = "9999px"; + el.style.background = accent; + el.style.boxShadow = isDark() + ? "0 0 0 2px rgba(0,0,0,0.6)" + : "0 0 0 2px rgba(255,255,255,0.85)"; + + const marker = new maplibregl.Marker({ element: el }) + .setLngLat([p.lon, p.lat]) + .setPopup( + new maplibregl.Popup({ closeButton: false }).setText(p.name) + ) + .addTo(map); + + markersRef.current.push(marker); + }); + + // Fit view if we have several points + if (points.length >= 2) { + const b = new maplibregl.LngLatBounds(); + points.forEach((p) => b.extend([p.lon, p.lat])); + map.fitBounds(b, { padding: 24, maxZoom: 8 }); + } + }; + + drawMarkers(); + + // observe changes and swap style + const obs = new MutationObserver(() => { + const url = styleForTheme(key); + map.setStyle(url); + // when style is ready, redraw markers (they survive setStyle, but + // we redraw to refresh marker ring color if accent/border changed) + map.once("styledata", drawMarkers); + }); + obs.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + }); + + return () => { + obs.disconnect(); + markersRef.current.forEach((m) => m.remove()); + map.remove(); + mapRef.current = null; + }; + }, [points]); return ( -
        +
        +
        +
        ); } diff --git a/frontend/src/index.css b/frontend/src/index.css index 2840c6be77..76954940c7 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -87,3 +87,10 @@ color: var(--color-quality-unknown); } } + +@layer utilities { + /* widely supported; falls back to the fixed min-height below */ + .aspect-square { + aspect-ratio: 1 / 1; + } +} From 335eb8dde0c85fcd5bf9203221bdc8dbc6f1e293 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:33:25 +0200 Subject: [PATCH 113/215] feat(map): use published MapTiler Customize styles (light/dark) + dark-mode swap --- frontend/src/components/MapView.tsx | 32 +++++++++++++++++------------ 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 44392d1bb3..59a2e40a22 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -5,21 +5,25 @@ import "maplibre-gl/dist/maplibre-gl.css"; type Point = { id: string; name: string; lat: number; lon: number }; type Props = { points?: Point[] }; +// The published styles from MapTiler Customize (already include ?key=…) +const STYLE_LIGHT = + "https://api.maptiler.com/maps/019951a8-6432-7aea-b555-2ac65a59181f/style.json?key=Dh5hFFvt6R7cmui0rEtJ"; +const STYLE_DARK = + "https://api.maptiler.com/maps/019951b2-24e8-7a45-8534-0731061b7984/style.json?key=Dh5hFFvt6R7cmui0rEtJ"; + function isDark() { return document.documentElement.classList.contains("dark"); } function getAccent() { - // reads your Tailwind token set in :root / .dark + // reads Tailwind token set in :root / .dark const v = getComputedStyle(document.documentElement) .getPropertyValue("--color-accent") .trim(); - // token is "r g b" (from your setup), normalize to rgb() + // token is "r g b" (from the setup), normalize to rgb() return v.includes(" ") ? `rgb(${v})` : v || "#0a5a82"; } -function styleForTheme(key?: string) { - const light = `https://api.maptiler.com/maps/dataviz/style.json?key=${key}`; - const dark = `https://api.maptiler.com/maps/darkmatter/style.json?key=${key}`; - return isDark() ? dark : light; +function styleForTheme() { + return isDark() ? STYLE_DARK : STYLE_LIGHT; } export default function MapView({ points = [] }: Props) { @@ -30,17 +34,21 @@ export default function MapView({ points = [] }: Props) { useEffect(() => { if (!ref.current) return; - const key = import.meta.env.VITE_MAPTILER_KEY as string | undefined; const map = new maplibregl.Map({ container: ref.current, - style: styleForTheme(key), + style: styleForTheme(), center: [15, 62], // Sweden-ish zoom: 4.3, + minZoom: 3, + maxZoom: 14, + dragRotate: false, + pitchWithRotate: false, attributionControl: { compact: true }, }); mapRef.current = map; map.once("load", () => { + // ensure it fills after layout settles setTimeout(() => map.resize(), 0); }); @@ -71,7 +79,7 @@ export default function MapView({ points = [] }: Props) { markersRef.current.push(marker); }); - // Fit view if we have several points + // Fit view if there are several points if (points.length >= 2) { const b = new maplibregl.LngLatBounds(); points.forEach((p) => b.extend([p.lon, p.lat])); @@ -81,12 +89,10 @@ export default function MapView({ points = [] }: Props) { drawMarkers(); - // observe changes and swap style + // Swap style when the page toggles dark mode const obs = new MutationObserver(() => { - const url = styleForTheme(key); + const url = styleForTheme(); map.setStyle(url); - // when style is ready, redraw markers (they survive setStyle, but - // we redraw to refresh marker ring color if accent/border changed) map.once("styledata", drawMarkers); }); obs.observe(document.documentElement, { From 88600d449b729d988ed943695b764b7057e6fe88 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 12:18:04 +0200 Subject: [PATCH 114/215] feat(map): edit map size --- frontend/src/components/MapView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 59a2e40a22..aa60d18680 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -112,7 +112,7 @@ export default function MapView({ points = [] }: Props) {
        From f1fcf3a81671666c6cc0a6f994802aea52e9cb80 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:21:52 +0200 Subject: [PATCH 115/215] feat(map): use Tailwind classes for markers and keep accent color in sync --- frontend/src/components/MapView.tsx | 32 ++++++++++++----------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index aa60d18680..6b089ce8ab 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -1,3 +1,4 @@ +// frontend/src/components/MapView.tsx import { useEffect, useRef } from "react"; import maplibregl, { Map } from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; @@ -5,7 +6,6 @@ import "maplibre-gl/dist/maplibre-gl.css"; type Point = { id: string; name: string; lat: number; lon: number }; type Props = { points?: Point[] }; -// The published styles from MapTiler Customize (already include ?key=…) const STYLE_LIGHT = "https://api.maptiler.com/maps/019951a8-6432-7aea-b555-2ac65a59181f/style.json?key=Dh5hFFvt6R7cmui0rEtJ"; const STYLE_DARK = @@ -15,11 +15,9 @@ function isDark() { return document.documentElement.classList.contains("dark"); } function getAccent() { - // reads Tailwind token set in :root / .dark const v = getComputedStyle(document.documentElement) .getPropertyValue("--color-accent") .trim(); - // token is "r g b" (from the setup), normalize to rgb() return v.includes(" ") ? `rgb(${v})` : v || "#0a5a82"; } function styleForTheme() { @@ -34,10 +32,11 @@ export default function MapView({ points = [] }: Props) { useEffect(() => { if (!ref.current) return; + // Initialize map const map = new maplibregl.Map({ container: ref.current, style: styleForTheme(), - center: [15, 62], // Sweden-ish + center: [15, 62], zoom: 4.3, minZoom: 3, maxZoom: 14, @@ -47,27 +46,23 @@ export default function MapView({ points = [] }: Props) { }); mapRef.current = map; - map.once("load", () => { - // ensure it fills after layout settles - setTimeout(() => map.resize(), 0); - }); + map.once("load", () => setTimeout(() => map.resize(), 0)); - // helper to (re)draw markers + // Draw markers function const drawMarkers = () => { const accent = getAccent(); + // clear old markersRef.current.forEach((m) => m.remove()); markersRef.current = []; points.forEach((p) => { const el = document.createElement("div"); - el.style.width = "12px"; - el.style.height = "12px"; - el.style.borderRadius = "9999px"; + // class for size + border; inline background so it follows the accent token dynamically + el.className = + "w-3 h-3 rounded-full shadow " + + (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); el.style.background = accent; - el.style.boxShadow = isDark() - ? "0 0 0 2px rgba(0,0,0,0.6)" - : "0 0 0 2px rgba(255,255,255,0.85)"; const marker = new maplibregl.Marker({ element: el }) .setLngLat([p.lon, p.lat]) @@ -79,7 +74,6 @@ export default function MapView({ points = [] }: Props) { markersRef.current.push(marker); }); - // Fit view if there are several points if (points.length >= 2) { const b = new maplibregl.LngLatBounds(); points.forEach((p) => b.extend([p.lon, p.lat])); @@ -89,7 +83,7 @@ export default function MapView({ points = [] }: Props) { drawMarkers(); - // Swap style when the page toggles dark mode + // redraw markers when points change const obs = new MutationObserver(() => { const url = styleForTheme(); map.setStyle(url); @@ -100,6 +94,7 @@ export default function MapView({ points = [] }: Props) { attributeFilter: ["class"], }); + // cleanup return () => { obs.disconnect(); markersRef.current.forEach((m) => m.remove()); @@ -112,8 +107,7 @@ export default function MapView({ points = [] }: Props) {
        ); From 36296f783c028d67964d8602abb55a9fee5ec903 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:27:00 +0200 Subject: [PATCH 116/215] feat(map): restore edit map size --- frontend/src/components/MapView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 6b089ce8ab..db728564be 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -107,7 +107,8 @@ export default function MapView({ points = [] }: Props) {
        ); From f8923281fd3e717b3400fe6d4a409deb10d41258 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 18:37:44 +0200 Subject: [PATCH 117/215] =?UTF-8?q?feat(list):=20limit=20=E2=80=9Cnear=20m?= =?UTF-8?q?e=E2=80=9D=20to=2020=20km=20and=20pass=20full=20filtered=20set?= =?UTF-8?q?=20to=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/BeachesList.tsx | 38 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 3e436f395f..c56101d330 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,3 +1,4 @@ +// frontend/src/components/BeachesList.tsx import { useEffect, useMemo } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; @@ -9,6 +10,8 @@ import { useGeolocation } from "../hooks/useGeolocation"; import { distanceKm, formatKm } from "../utils/geo"; import { useUI } from "../store/ui"; +const RADIUS_KM = 10; // ← limit + export default function BeachesList() { const { data, isLoading, isError, error, refetch } = useQuery({ queryKey: ["beaches"], @@ -23,7 +26,7 @@ export default function BeachesList() { request, } = useGeolocation(); - // Derive sorted items (nearest first when we have coords) + // Distances (only used for sorting and the badge) const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { if (!data) return []; if (!coords) return data; @@ -39,11 +42,10 @@ export default function BeachesList() { return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); }, [data, coords]); - // 🔎 Filter by search (from Zustand store) + // Search filter const search = useUI((s) => s.search); const q = search.trim().toLowerCase(); - - const filtered = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { + const filteredBySearch = useMemo(() => { if (!q) return items; return items.filter((b) => { const hay = `${b.name} ${b.municipality ?? ""}`.toLowerCase(); @@ -51,7 +53,14 @@ export default function BeachesList() { }); }, [items, q]); - // Refetch once on mount if cache was empty + // Radius filter (only when we have coords) + const filtered = useMemo(() => { + if (!coords) return filteredBySearch; + return filteredBySearch.filter((b) => + typeof b._distanceKm === "number" ? b._distanceKm <= RADIUS_KM : true + ); + }, [filteredBySearch, coords]); + useEffect(() => { if (!data && !isLoading) refetch(); }, [data, isLoading, refetch]); @@ -99,12 +108,15 @@ export default function BeachesList() { ); } - if (q && filtered.length === 0) { + if ((q || coords) && filtered.length === 0) { return (
        -

        No matches.

        +

        + No matches within {RADIUS_KM} km. +

        - Nothing matches “{search}”. Try a different name or municipality. + Nothing matches “{search}”{" "} + {coords ? `within ${RADIUS_KM} km of you` : ""}.

        ); @@ -126,16 +138,16 @@ export default function BeachesList() {
        {coords && ( - Sorting by proximity to your location. + Showing beaches within {RADIUS_KM} km & sorting by proximity. )} {geoError && {geoError}}
        - {/* Map (mobile-first) */} + {/* Map — pass ALL filtered points so panning doesn’t “run out” of dots */} ({ + points={filtered.map((b) => ({ id: b.id, name: b.name, lat: b.lat, @@ -161,8 +173,7 @@ export default function BeachesList() { {b.lon.toFixed(4)}

        - - {"_distanceKm" in b && b._distanceKm !== undefined && ( + {typeof b._distanceKm === "number" && ( {formatKm(b._distanceKm)} @@ -176,7 +187,6 @@ export default function BeachesList() { ); } -/** small skeleton block for loading state */ function CardSkeleton() { return (
        From 57b58cf9ad53bb5397121cf283d139ae412a6818 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 19:02:54 +0200 Subject: [PATCH 118/215] =?UTF-8?q?feat(map+list):=20default=20Sergels=20t?= =?UTF-8?q?org=20=C2=B120km,=20fit=20to=20radius=20on=20=E2=80=9CUse=20loc?= =?UTF-8?q?ation=E2=80=9D,=20and=20live=20viewport=20filtering=20on=20pan/?= =?UTF-8?q?zoom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/BeachesList.tsx | 149 +++++++++++++++++++----- frontend/src/components/MapView.tsx | 133 +++++++++++++++++---- 2 files changed, 225 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index c56101d330..0516e0d9cb 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -1,5 +1,5 @@ // frontend/src/components/BeachesList.tsx -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Link } from "react-router-dom"; import MapView from "./MapView"; @@ -10,7 +10,12 @@ import { useGeolocation } from "../hooks/useGeolocation"; import { distanceKm, formatKm } from "../utils/geo"; import { useUI } from "../store/ui"; -const RADIUS_KM = 10; // ← limit +// Defaults +const SERGELS_TORG = { lat: 59.3326, lon: 18.0649 }; +const DEFAULT_RADIUS_KM = 20; +const NEARBY_RADIUS_KM = 10; + +type Mode = "default" | "nearby" | "viewport"; export default function BeachesList() { const { data, isLoading, isError, error, refetch } = useQuery({ @@ -19,6 +24,11 @@ export default function BeachesList() { staleTime: 5 * 60 * 1000, }); + // Global search text + const search = useUI((s) => s.search); + const q = search.trim().toLowerCase(); + + // Geo hook const { coords, loading: geoLoading, @@ -26,25 +36,42 @@ export default function BeachesList() { request, } = useGeolocation(); - // Distances (only used for sorting and the badge) + // View state (center/radius or viewport bounds) + const [mode, setMode] = useState("default"); + const [center, setCenter] = useState(SERGELS_TORG); + const [radiusKm, setRadiusKm] = useState(DEFAULT_RADIUS_KM); + const [bounds, setBounds] = useState<{ + west: number; + south: number; + east: number; + north: number; + } | null>(null); + + // Build a distance-annotated array (used for sorting + badge) const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { if (!data) return []; - if (!coords) return data; + // reference point for distance: center in default/nearby; none for viewport + const refPoint = mode === "viewport" ? null : center; + + // Always include _distanceKm (undefined if no ref point) so the type is stable const withDist = data.map((b) => { - const km = distanceKm( - { lat: coords.lat, lon: coords.lon }, - { lat: b.lat, lon: b.lon } - ); + const km = refPoint + ? distanceKm( + { lat: refPoint.lat, lon: refPoint.lon }, + { lat: b.lat, lon: b.lon } + ) + : undefined; return { ...b, _distanceKm: km }; }); - return withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)); - }, [data, coords]); + // Sort only if we have distances + return refPoint + ? withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)) + : withDist; + }, [data, center, mode]); - // Search filter - const search = useUI((s) => s.search); - const q = search.trim().toLowerCase(); + // Text filter const filteredBySearch = useMemo(() => { if (!q) return items; return items.filter((b) => { @@ -53,18 +80,64 @@ export default function BeachesList() { }); }, [items, q]); - // Radius filter (only when we have coords) + // Radius or viewport filter const filtered = useMemo(() => { - if (!coords) return filteredBySearch; - return filteredBySearch.filter((b) => - typeof b._distanceKm === "number" ? b._distanceKm <= RADIUS_KM : true - ); - }, [filteredBySearch, coords]); + if (mode === "viewport" && bounds) { + // show beaches inside current map view + return filteredBySearch.filter( + (b) => + b.lon >= bounds.west && + b.lon <= bounds.east && + b.lat >= bounds.south && + b.lat <= bounds.north + ); + } + + // default/nearby → use radius from current center + return filteredBySearch.filter((b) => { + if (mode === "default" || mode === "nearby") { + const km = distanceKm(center, { lat: b.lat, lon: b.lon }); + return km <= radiusKm; + } + return true; + }); + }, [filteredBySearch, mode, bounds, center, radiusKm]); + // Initial fetch safeguard useEffect(() => { if (!data && !isLoading) refetch(); }, [data, isLoading, refetch]); + // When geolocation coordinates arrive/changes, switch to nearby mode and fit + useEffect(() => { + if (!coords) return; + setCenter({ lat: coords.lat, lon: coords.lon }); + setRadiusKm(NEARBY_RADIUS_KM); + setMode("nearby"); + }, [coords]); + + // “Use current location” — request permission/position (no return value expected) + const handleUseLocation = async () => { + await request(); + // coords effect above will run when the hook sets coordinates + }; + + // Map move -> switch to viewport mode and filter by bounds + const handleMoveEnd = (e: { + bounds: { west: number; south: number; east: number; north: number }; + center: { lon: number; lat: number }; + zoom: number; + }) => { + setBounds(e.bounds); + setMode("viewport"); + }; + + // Focus prop for the map: in default/nearby we guide the map to center+radius + const mapFocus = + mode === "viewport" + ? undefined + : { center: { lon: center.lon, lat: center.lat }, radiusKm }; + /* ---------- STATES ---------- */ if (isLoading) { return ( @@ -108,15 +181,14 @@ export default function BeachesList() { ); } - if ((q || coords) && filtered.length === 0) { + if ((q || mode !== "default") && filtered.length === 0) { return (
        -

        - No matches within {RADIUS_KM} km. -

        +

        No matches here.

        - Nothing matches “{search}”{" "} - {coords ? `within ${RADIUS_KM} km of you` : ""}. + {mode === "viewport" + ? "Pan or zoom to a different area." + : `Try widening the radius around this area.`}

        ); @@ -125,27 +197,38 @@ export default function BeachesList() { /* ---------- UI ---------- */ return (
        - {/* Location action + status */} + {/* Controls + status */}
        - {coords && ( + {mode === "default" && ( + + Default view: Sergels torg ± {DEFAULT_RADIUS_KM} km. + + )} + {mode === "nearby" && ( + + Showing beaches within {NEARBY_RADIUS_KM} km & sorting by + proximity. + + )} + {mode === "viewport" && ( - Showing beaches within {RADIUS_KM} km & sorting by proximity. + Showing beaches in the current map view. )} - {geoError && {geoError}} + {geoError && {geoError}}
        - {/* Map — pass ALL filtered points so panning doesn’t “run out” of dots */} + {/* Map */} ({ id: b.id, @@ -153,9 +236,11 @@ export default function BeachesList() { lat: b.lat, lon: b.lon, }))} + focus={mapFocus} + onMoveEnd={handleMoveEnd} /> - {/* Card list */} + {/* List (keep to 50 for UX) */}
          {filtered.slice(0, 50).map((b) => (
        • diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index db728564be..8970cc3c52 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -1,10 +1,20 @@ // frontend/src/components/MapView.tsx import { useEffect, useRef } from "react"; -import maplibregl, { Map } from "maplibre-gl"; +import maplibregl, { Map, LngLatBoundsLike } from "maplibre-gl"; import "maplibre-gl/dist/maplibre-gl.css"; type Point = { id: string; name: string; lat: number; lon: number }; -type Props = { points?: Point[] }; +type Props = { + points?: Point[]; + /** If provided, the map fits to this center (+ optional radiusKm) */ + focus?: { center: { lon: number; lat: number }; radiusKm?: number }; + /** Notifies parent when user stops moving the map (for viewport-based filtering) */ + onMoveEnd?: (args: { + bounds: { west: number; south: number; east: number; north: number }; + center: { lon: number; lat: number }; + zoom: number; + }) => void; +}; const STYLE_LIGHT = "https://api.maptiler.com/maps/019951a8-6432-7aea-b555-2ac65a59181f/style.json?key=Dh5hFFvt6R7cmui0rEtJ"; @@ -24,22 +34,40 @@ function styleForTheme() { return isDark() ? STYLE_DARK : STYLE_LIGHT; } -export default function MapView({ points = [] }: Props) { +// compute bounds from center + radius (km) +function circleBounds( + center: { lon: number; lat: number }, + radiusKm: number +): LngLatBoundsLike { + const lat = center.lat; + const dLat = radiusKm / 111; // ~111km per deg + const dLon = radiusKm / (111 * Math.cos((lat * Math.PI) / 180)); + const west = center.lon - dLon; + const east = center.lon + dLon; + const south = center.lat - dLat; + const north = center.lat + dLat; + return [ + [west, south], + [east, north], + ]; +} + +export default function MapView({ points = [], focus, onMoveEnd }: Props) { const ref = useRef(null); const mapRef = useRef(null); const markersRef = useRef([]); + // init + theme swap + markers useEffect(() => { if (!ref.current) return; - // Initialize map const map = new maplibregl.Map({ container: ref.current, style: styleForTheme(), - center: [15, 62], - zoom: 4.3, + center: [18.0649, 59.3326], // Stockholm default + zoom: 9, minZoom: 3, - maxZoom: 14, + maxZoom: 16, dragRotate: false, pitchWithRotate: false, attributionControl: { compact: true }, @@ -48,17 +76,13 @@ export default function MapView({ points = [] }: Props) { map.once("load", () => setTimeout(() => map.resize(), 0)); - // Draw markers function const drawMarkers = () => { const accent = getAccent(); - - // clear old markersRef.current.forEach((m) => m.remove()); markersRef.current = []; points.forEach((p) => { const el = document.createElement("div"); - // class for size + border; inline background so it follows the accent token dynamically el.className = "w-3 h-3 rounded-full shadow " + (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); @@ -73,43 +97,102 @@ export default function MapView({ points = [] }: Props) { markersRef.current.push(marker); }); - - if (points.length >= 2) { - const b = new maplibregl.LngLatBounds(); - points.forEach((p) => b.extend([p.lon, p.lat])); - map.fitBounds(b, { padding: 24, maxZoom: 8 }); - } }; drawMarkers(); - // redraw markers when points change + // Theme watcher — swap style, then redraw markers const obs = new MutationObserver(() => { const url = styleForTheme(); map.setStyle(url); - map.once("styledata", drawMarkers); + map.once("styledata", () => { + drawMarkers(); + // re-apply focus after style swap + if (focus?.center && focus.radiusKm) { + const b = circleBounds(focus.center, focus.radiusKm); + map.fitBounds(b, { padding: 24, maxZoom: 12 }); + } else if (focus?.center) { + map.flyTo({ center: [focus.center.lon, focus.center.lat], zoom: 12 }); + } + }); }); obs.observe(document.documentElement, { attributes: true, attributeFilter: ["class"], }); - // cleanup + // Move end callback → parent + if (onMoveEnd) { + map.on("moveend", () => { + const b = map.getBounds(); + const c = map.getCenter(); + onMoveEnd({ + bounds: { + west: b.getWest(), + south: b.getSouth(), + east: b.getEast(), + north: b.getNorth(), + }, + center: { lon: c.lng, lat: c.lat }, + zoom: map.getZoom(), + }); + }); + } + return () => { obs.disconnect(); markersRef.current.forEach((m) => m.remove()); map.remove(); mapRef.current = null; }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // respond to points change (redraw markers + maybe fit) + useEffect(() => { + const map = mapRef.current; + if (!map) return; + + // redraw markers + const accent = getAccent(); + markersRef.current.forEach((m) => m.remove()); + markersRef.current = []; + points.forEach((p) => { + const el = document.createElement("div"); + el.className = + "w-3 h-3 rounded-full shadow " + + (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); + el.style.background = accent; + const marker = new maplibregl.Marker({ element: el }) + .setLngLat([p.lon, p.lat]) + .setPopup(new maplibregl.Popup({ closeButton: false }).setText(p.name)) + .addTo(map); + markersRef.current.push(marker); + }); + + // If points are many, keep view; if 1, zoom closer + if (points.length === 1) { + const p = points[0]; + map.flyTo({ center: [p.lon, p.lat], zoom: 12 }); + } }, [points]); + // respond to focus changes (fit to radius/center) + useEffect(() => { + const map = mapRef.current; + if (!map || !focus?.center) return; + + if (focus.radiusKm && focus.radiusKm > 0) { + const b = circleBounds(focus.center, focus.radiusKm); + map.fitBounds(b, { padding: 24, maxZoom: 12 }); + } else { + map.flyTo({ center: [focus.center.lon, focus.center.lat], zoom: 11 }); + } + }, [focus]); + return (
          -
          +
          ); } From cf89a9e11f252640598054b2238e7cc48137b671 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Tue, 16 Sep 2025 19:16:50 +0200 Subject: [PATCH 119/215] =?UTF-8?q?feat(map+list):=20default=20Sergels=20t?= =?UTF-8?q?org=20=C2=B120km,=20fit=20to=20radius=20on=20=E2=80=9CUse=20loc?= =?UTF-8?q?ation=E2=80=9D,=20and=20live=20viewport=20filtering=20on=20pan/?= =?UTF-8?q?zoom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/BeachesList.tsx | 8 +-- frontend/src/components/MapView.tsx | 72 +++++++++++++------------ 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 0516e0d9cb..3bbed1cee2 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -12,8 +12,8 @@ import { useUI } from "../store/ui"; // Defaults const SERGELS_TORG = { lat: 59.3326, lon: 18.0649 }; -const DEFAULT_RADIUS_KM = 20; -const NEARBY_RADIUS_KM = 10; +const DEFAULT_RADIUS_KM = 10; +const NEARBY_RADIUS_KM = 5; type Mode = "default" | "nearby" | "viewport"; @@ -51,8 +51,8 @@ export default function BeachesList() { const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { if (!data) return []; - // reference point for distance: center in default/nearby; none for viewport - const refPoint = mode === "viewport" ? null : center; + // Prefer user coords when available; otherwise use center in default/nearby; none in viewport + const refPoint = coords ? coords : mode === "viewport" ? null : center; // Always include _distanceKm (undefined if no ref point) so the type is stable const withDist = data.map((b) => { diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 8970cc3c52..655f1dea00 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -64,10 +64,10 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { const map = new maplibregl.Map({ container: ref.current, style: styleForTheme(), - center: [18.0649, 59.3326], // Stockholm default - zoom: 9, + center: [15, 62], + zoom: 4.3, minZoom: 3, - maxZoom: 16, + maxZoom: 14, dragRotate: false, pitchWithRotate: false, attributionControl: { compact: true }, @@ -76,16 +76,18 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { map.once("load", () => setTimeout(() => map.resize(), 0)); + // --- drawMarkers (unchanged) --- const drawMarkers = () => { const accent = getAccent(); markersRef.current.forEach((m) => m.remove()); markersRef.current = []; - points.forEach((p) => { const el = document.createElement("div"); el.className = "w-3 h-3 rounded-full shadow " + - (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); + (document.documentElement.classList.contains("dark") + ? "ring-2 ring-black/60" + : "ring-2 ring-white/85"); el.style.background = accent; const marker = new maplibregl.Marker({ element: el }) @@ -97,47 +99,50 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { markersRef.current.push(marker); }); + + if (points.length >= 2) { + const b = new maplibregl.LngLatBounds(); + points.forEach((p) => b.extend([p.lon, p.lat])); + map.fitBounds(b, { padding: 24, maxZoom: 8 }); + } }; drawMarkers(); - // Theme watcher — swap style, then redraw markers + // Swap style when dark mode toggles const obs = new MutationObserver(() => { const url = styleForTheme(); map.setStyle(url); - map.once("styledata", () => { - drawMarkers(); - // re-apply focus after style swap - if (focus?.center && focus.radiusKm) { - const b = circleBounds(focus.center, focus.radiusKm); - map.fitBounds(b, { padding: 24, maxZoom: 12 }); - } else if (focus?.center) { - map.flyTo({ center: [focus.center.lon, focus.center.lat], zoom: 12 }); - } - }); + map.once("styledata", drawMarkers); }); obs.observe(document.documentElement, { attributes: true, attributeFilter: ["class"], }); - // Move end callback → parent - if (onMoveEnd) { - map.on("moveend", () => { - const b = map.getBounds(); - const c = map.getCenter(); - onMoveEnd({ - bounds: { - west: b.getWest(), - south: b.getSouth(), - east: b.getEast(), - north: b.getNorth(), - }, - center: { lon: c.lng, lat: c.lat }, - zoom: map.getZoom(), - }); + // ✅ Only fire onMoveEnd when the user actually interacted + let userMoving = false; + map.on("movestart", (e) => { + // e.originalEvent exists only for user-initiated interactions + userMoving = !!(e as any).originalEvent; + }); + map.on("moveend", () => { + if (!userMoving) return; + userMoving = false; + if (!onMoveEnd) return; + const b = map.getBounds(); + const c = map.getCenter(); + onMoveEnd({ + bounds: { + west: b.getWest(), + south: b.getSouth(), + east: b.getEast(), + north: b.getNorth(), + }, + center: { lon: c.lng, lat: c.lat }, + zoom: map.getZoom(), }); - } + }); return () => { obs.disconnect(); @@ -145,8 +150,7 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { map.remove(); mapRef.current = null; }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [points, onMoveEnd]); // respond to points change (redraw markers + maybe fit) useEffect(() => { From 9fa11f6a19e1cad236d40e2a393b39470ed3c60e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:52:28 +0200 Subject: [PATCH 120/215] feat(map): adaptive zoom for small radii + no-flicker map init --- frontend/src/components/BeachesList.tsx | 2 +- frontend/src/components/MapView.tsx | 93 ++++++++++--------------- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 3bbed1cee2..df541a525a 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -69,7 +69,7 @@ export default function BeachesList() { return refPoint ? withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)) : withDist; - }, [data, center, mode]); + }, [data, coords, center, mode]); // ← added `coords` here // Text filter const filteredBySearch = useMemo(() => { diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 655f1dea00..e4512fd2cd 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -8,7 +8,7 @@ type Props = { points?: Point[]; /** If provided, the map fits to this center (+ optional radiusKm) */ focus?: { center: { lon: number; lat: number }; radiusKm?: number }; - /** Notifies parent when user stops moving the map (for viewport-based filtering) */ + /** Fires only on *user*-initiated pan/zoom (not programmatic fits) */ onMoveEnd?: (args: { bounds: { west: number; south: number; east: number; north: number }; center: { lon: number; lat: number }; @@ -40,7 +40,7 @@ function circleBounds( radiusKm: number ): LngLatBoundsLike { const lat = center.lat; - const dLat = radiusKm / 111; // ~111km per deg + const dLat = radiusKm / 111; // ~111 km per deg const dLon = radiusKm / (111 * Math.cos((lat * Math.PI) / 180)); const west = center.lon - dLon; const east = center.lon + dLon; @@ -56,8 +56,10 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { const ref = useRef(null); const mapRef = useRef(null); const markersRef = useRef([]); + const onMoveEndRef = useRef(onMoveEnd); + onMoveEndRef.current = onMoveEnd; // keep latest callback without re-binding listeners - // init + theme swap + markers + // Init map ONCE useEffect(() => { if (!ref.current) return; @@ -76,63 +78,32 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { map.once("load", () => setTimeout(() => map.resize(), 0)); - // --- drawMarkers (unchanged) --- - const drawMarkers = () => { - const accent = getAccent(); - markersRef.current.forEach((m) => m.remove()); - markersRef.current = []; - points.forEach((p) => { - const el = document.createElement("div"); - el.className = - "w-3 h-3 rounded-full shadow " + - (document.documentElement.classList.contains("dark") - ? "ring-2 ring-black/60" - : "ring-2 ring-white/85"); - el.style.background = accent; - - const marker = new maplibregl.Marker({ element: el }) - .setLngLat([p.lon, p.lat]) - .setPopup( - new maplibregl.Popup({ closeButton: false }).setText(p.name) - ) - .addTo(map); - - markersRef.current.push(marker); - }); - - if (points.length >= 2) { - const b = new maplibregl.LngLatBounds(); - points.forEach((p) => b.extend([p.lon, p.lat])); - map.fitBounds(b, { padding: 24, maxZoom: 8 }); - } - }; - - drawMarkers(); - - // Swap style when dark mode toggles + // Dark/light swap only when theme class changes const obs = new MutationObserver(() => { const url = styleForTheme(); map.setStyle(url); - map.once("styledata", drawMarkers); + // Markers survive style swaps; if your style clears layers we can redraw, but + // here we just wait for style to settle so tiles don’t appear blank. + map.once("styledata", () => { + // no-op; we keep existing markers (no flicker) + }); }); obs.observe(document.documentElement, { attributes: true, attributeFilter: ["class"], }); - // ✅ Only fire onMoveEnd when the user actually interacted + // Only count *user* moves let userMoving = false; map.on("movestart", (e) => { - // e.originalEvent exists only for user-initiated interactions - userMoving = !!(e as any).originalEvent; + userMoving = !!(e as any).originalEvent; // programmatic fits won't set this }); map.on("moveend", () => { - if (!userMoving) return; + if (!userMoving || !onMoveEndRef.current) return; userMoving = false; - if (!onMoveEnd) return; const b = map.getBounds(); const c = map.getCenter(); - onMoveEnd({ + onMoveEndRef.current({ bounds: { west: b.getWest(), south: b.getSouth(), @@ -150,45 +121,55 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { map.remove(); mapRef.current = null; }; - }, [points, onMoveEnd]); + }, []); // ← no dependencies (don’t recreate the map) - // respond to points change (redraw markers + maybe fit) + // Update markers when points change (NO map re-init, NO auto-fit here) useEffect(() => { const map = mapRef.current; if (!map) return; - // redraw markers const accent = getAccent(); + // clear old markersRef.current.forEach((m) => m.remove()); markersRef.current = []; + points.forEach((p) => { const el = document.createElement("div"); el.className = "w-3 h-3 rounded-full shadow " + (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); el.style.background = accent; + const marker = new maplibregl.Marker({ element: el }) .setLngLat([p.lon, p.lat]) .setPopup(new maplibregl.Popup({ closeButton: false }).setText(p.name)) .addTo(map); + markersRef.current.push(marker); }); - - // If points are many, keep view; if 1, zoom closer - if (points.length === 1) { - const p = points[0]; - map.flyTo({ center: [p.lon, p.lat], zoom: 12 }); - } }, [points]); - // respond to focus changes (fit to radius/center) + // Fit to focus (center + radius) without flashing; allow closer zoom for small radii useEffect(() => { const map = mapRef.current; if (!map || !focus?.center) return; + map.stop(); // cancel any ongoing camera animation + if (focus.radiusKm && focus.radiusKm > 0) { - const b = circleBounds(focus.center, focus.radiusKm); - map.fitBounds(b, { padding: 24, maxZoom: 12 }); + const r = focus.radiusKm; + + // 🔧 TIGHTEN the fit for small radii + // - Shrink the radius a bit so bounds aren’t too wide + // - Reduce padding so more of the view is the actual area of interest + // - Let the map zoom in a bit more + const isSmall = r <= 5; // the 5 km “nearby” case + const effective = isSmall ? r * 0.65 : r; // <— tighten bounds + const padding = isSmall ? 12 : 24; // less padding on small areas + const maxZoom = isSmall ? 16 : 12; // allow closer zoom on small areas + + const b = circleBounds(focus.center, effective); + map.fitBounds(b, { padding, maxZoom }); } else { map.flyTo({ center: [focus.center.lon, focus.center.lat], zoom: 11 }); } From 7e9a5fe913725531ef7c1ee62a63198237a2bff2 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 17 Sep 2025 18:15:16 +0200 Subject: [PATCH 121/215] chore(frontend): remove duplicate LanguageSwitcher under search bar --- frontend/src/App.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d69193de37..177360324c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -9,7 +9,6 @@ export default function App() {
          - } /> } /> From ec4154967ed74aca98de8bf637dab13ac1e9e686 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 17 Sep 2025 19:05:56 +0200 Subject: [PATCH 122/215] =?UTF-8?q?ui(frontend):=20move=20=E2=80=9CUse=20c?= =?UTF-8?q?urrent=20location=E2=80=9D=20button=20below=20map=20for=20bette?= =?UTF-8?q?r=20flow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/App.tsx | 1 - frontend/src/components/BeachesList.tsx | 68 +++++++------------------ 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 177360324c..f2e2a71d3e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,7 +1,6 @@ import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; import BeachDetailPage from "./components/BeachDetailPage"; -import LanguageSwitcher from "./components/LanguageSwitcher"; import Header from "./components/Header"; export default function App() { diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index df541a525a..8f0eaebd6c 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -47,14 +47,10 @@ export default function BeachesList() { north: number; } | null>(null); - // Build a distance-annotated array (used for sorting + badge) + // Distance-annotated list (stable type) const items = useMemo<(BeachSummary & { _distanceKm?: number })[]>(() => { if (!data) return []; - - // Prefer user coords when available; otherwise use center in default/nearby; none in viewport const refPoint = coords ? coords : mode === "viewport" ? null : center; - - // Always include _distanceKm (undefined if no ref point) so the type is stable const withDist = data.map((b) => { const km = refPoint ? distanceKm( @@ -64,12 +60,10 @@ export default function BeachesList() { : undefined; return { ...b, _distanceKm: km }; }); - - // Sort only if we have distances return refPoint ? withDist.sort((a, b) => (a._distanceKm ?? 0) - (b._distanceKm ?? 0)) : withDist; - }, [data, coords, center, mode]); // ← added `coords` here + }, [data, coords, center, mode]); // Text filter const filteredBySearch = useMemo(() => { @@ -83,7 +77,6 @@ export default function BeachesList() { // Radius or viewport filter const filtered = useMemo(() => { if (mode === "viewport" && bounds) { - // show beaches inside current map view return filteredBySearch.filter( (b) => b.lon >= bounds.west && @@ -92,8 +85,6 @@ export default function BeachesList() { b.lat <= bounds.north ); } - - // default/nearby → use radius from current center return filteredBySearch.filter((b) => { if (mode === "default" || mode === "nearby") { const km = distanceKm(center, { lat: b.lat, lon: b.lon }); @@ -108,7 +99,7 @@ export default function BeachesList() { if (!data && !isLoading) refetch(); }, [data, isLoading, refetch]); - // When geolocation coordinates arrive/changes, switch to nearby mode and fit + // Geolocation → nearby mode useEffect(() => { if (!coords) return; setCenter({ lat: coords.lat, lon: coords.lon }); @@ -116,13 +107,11 @@ export default function BeachesList() { setMode("nearby"); }, [coords]); - // “Use current location” — request permission/position (no return value expected) const handleUseLocation = async () => { - await request(); - // coords effect above will run when the hook sets coordinates + await request(); // coords effect handles the rest }; - // Map move -> switch to viewport mode and filter by bounds + // Map move → viewport mode const handleMoveEnd = (e: { bounds: { west: number; south: number; east: number; north: number }; center: { lon: number; lat: number }; @@ -132,7 +121,6 @@ export default function BeachesList() { setMode("viewport"); }; - // Focus prop for the map: in default/nearby we guide the map to center+radius const mapFocus = mode === "viewport" ? undefined @@ -162,7 +150,7 @@ export default function BeachesList() {

          @@ -188,7 +176,7 @@ export default function BeachesList() {

          {mode === "viewport" ? "Pan or zoom to a different area." - : `Try widening the radius around this area.`} + : `Try widening the radius.`}

          ); @@ -197,37 +185,6 @@ export default function BeachesList() { /* ---------- UI ---------- */ return (
          - {/* Controls + status */} -
          - - -
          - {mode === "default" && ( - - Default view: Sergels torg ± {DEFAULT_RADIUS_KM} km. - - )} - {mode === "nearby" && ( - - Showing beaches within {NEARBY_RADIUS_KM} km & sorting by - proximity. - - )} - {mode === "viewport" && ( - - Showing beaches in the current map view. - - )} - {geoError && {geoError}} -
          -
          - {/* Map */} ({ @@ -240,6 +197,17 @@ export default function BeachesList() { onMoveEnd={handleMoveEnd} /> + {/* Use current location — full width, now under the map */} +
          + +
          + {/* List (keep to 50 for UX) */}
            {filtered.slice(0, 50).map((b) => ( From b50d78e129e9e98308f873bd8ed68d2a7a85dfed Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 17 Sep 2025 19:13:10 +0200 Subject: [PATCH 123/215] ui(frontend): edit BADA title size and weight --- frontend/src/components/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 9da24be4a2..710baa2f86 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -109,7 +109,7 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { {/* Title BADA (Spectral, tight tracking) */} BADA From da8499a935f4010046188e8cb1d2ed0500644cfa Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:44:50 +0200 Subject: [PATCH 124/215] ui(frontend): tweak flex alignment of the header text titles --- frontend/src/components/Header.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 710baa2f86..fb3ecaade6 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -105,17 +105,17 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { {/* Center: Brand block */}
            -
            - {/* Title BADA (Spectral, tight tracking) */} +
            + {/* Title BADA */} BADA - {/* Subtitle (Inter, two-line, leading to match height visually) */} -
            + {/* Subtitle stacked next to it */} +
            EU Beaches
            in Sweden From b699d3a71bed7204bbfc23939bef25d4828d1b2f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:08:13 +0200 Subject: [PATCH 125/215] ui(frontend): replace dummy icons with svg icons for the men u and user --- frontend/src/assets/menu_icon.svg | 7 +++ frontend/src/assets/user_icon.svg | 89 ++++++++++++++++++++++++++++++ frontend/src/components/Header.tsx | 49 +++++----------- 3 files changed, 110 insertions(+), 35 deletions(-) create mode 100644 frontend/src/assets/menu_icon.svg create mode 100644 frontend/src/assets/user_icon.svg diff --git a/frontend/src/assets/menu_icon.svg b/frontend/src/assets/menu_icon.svg new file mode 100644 index 0000000000..aac3024411 --- /dev/null +++ b/frontend/src/assets/menu_icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/user_icon.svg b/frontend/src/assets/user_icon.svg new file mode 100644 index 0000000000..919a767f83 --- /dev/null +++ b/frontend/src/assets/user_icon.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index fb3ecaade6..874b4f8f48 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -55,30 +55,13 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { className="p-2 rounded-full hover:bg-surface-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" onClick={() => setMenuOpen((v) => !v)} > - {/* three wavy lines (replace later with /src/assets/menu_icon.svg) */} - + {/* Flyout menu */} @@ -130,17 +113,13 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { className="p-2 rounded-full hover:bg-surface-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50" onClick={() => setUserOpen((v) => !v)} > - {/* swimmer icon (replace with /src/assets/user_icon.svg later */} - + {userOpen && ( From 54c26d2cd757503d3a7272c0aa8b02935d643b25 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 19 Sep 2025 16:26:13 +0200 Subject: [PATCH 126/215] ui(frontend): install svgr plugin so icons inherit theme colors when toggling dark or light theme --- frontend/dist/assets/index-B_a48z93.css | 1 + frontend/dist/assets/index-C4A8iG7V.js | 849 ++++++++++++++++++++++++ frontend/dist/index.html | 20 + frontend/dist/vite.svg | 1 + frontend/package.json | 3 +- frontend/src/assets/menu_icon.svg | 6 +- frontend/src/assets/user_icon.svg | 16 +- frontend/src/components/Header.tsx | 37 +- frontend/src/vite-env.d.ts | 7 + frontend/vite.config.js | 9 +- 10 files changed, 902 insertions(+), 47 deletions(-) create mode 100644 frontend/dist/assets/index-B_a48z93.css create mode 100644 frontend/dist/assets/index-C4A8iG7V.js create mode 100644 frontend/dist/index.html create mode 100644 frontend/dist/vite.svg diff --git a/frontend/dist/assets/index-B_a48z93.css b/frontend/dist/assets/index-B_a48z93.css new file mode 100644 index 0000000000..bc77d441b4 --- /dev/null +++ b/frontend/dist/assets/index-B_a48z93.css @@ -0,0 +1 @@ +.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-scale{background-color:#ffffffbf;border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px #0000001a;padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px #00000059;box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:#0006;color:#fff;display:flex;font-size:1.4em;top:0;right:0;bottom:0;left:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-lg:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#efebe4;--color-surface-muted:#f6f2ec;--color-ink:#1a1a1a;--color-ink-muted:#64686c;--color-border:#cdc8c0;--color-accent:#0a5a82;--color-badge:#e6e4dc;--color-quality-excellent:#1f6fb2;--color-quality-good:#2b8a3e;--color-quality-sufficient:#b08900;--color-quality-poor:#c0392b;--color-quality-unknown:#6b7280;--font-spectral:"Spectral",serif;--font-inter:"Inter",system-ui,sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body,#root{height:100%}body{background-color:var(--color-surface);font-family:var(--font-inter);color:var(--color-ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4{font-family:var(--font-spectral);color:var(--color-ink)}a{border-radius:var(--radius-sm);color:var(--color-accent);text-underline-offset:2px}@media (hover:hover){a:hover{text-decoration-line:underline}}a:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){a:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}a:focus-visible{--tw-outline-style:none;outline-style:none}}@layer components{.card{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface-muted);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.badge{align-items:center;gap:calc(var(--spacing)*1);background-color:var(--color-badge);padding-inline:calc(var(--spacing)*2.5);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-ink);border-radius:3.40282e38px;display:inline-flex}.kpi-excellent{color:var(--color-quality-excellent)}.kpi-good{color:var(--color-quality-good)}.kpi-sufficient{color:var(--color-quality-sufficient)}.kpi-poor{color:var(--color-quality-poor)}.kpi-unknown{color:var(--color-quality-unknown)}}@layer utilities{.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-7{height:calc(var(--spacing)*7)}.h-9{height:calc(var(--spacing)*9)}.h-\[260px\]{height:260px}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-40{width:calc(var(--spacing)*40)}.w-56{width:calc(var(--spacing)*56)}.w-60{width:calc(var(--spacing)*60)}.w-full{width:100%}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.bg-border{background-color:var(--color-border)}.bg-surface{background-color:var(--color-surface)}.bg-surface-muted{background-color:var(--color-surface-muted)}.p-0{padding:calc(var(--spacing)*0)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.pt-2{padding-top:calc(var(--spacing)*2)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.text-left{text-align:left}.font-spectral{font-family:var(--font-spectral)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[-0\.06em\]{--tw-tracking:-.06em;letter-spacing:-.06em}.whitespace-pre-line{white-space:pre-line}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/60{--tw-ring-color:#0009}@supports (color:color-mix(in lab,red,red)){.ring-black\/60{--tw-ring-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.ring-white\/85{--tw-ring-color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.ring-white\/85{--tw-ring-color:color-mix(in oklab,var(--color-white)85%,transparent)}}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:bg-surface-muted:hover{background-color:var(--color-surface-muted)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent\/40:focus{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus\:ring-accent\/40:focus{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:opacity-60:disabled{opacity:.6}.aspect-square{aspect-ratio:1}}.maplibregl-map{-webkit-tap-highlight-color:#0000;font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;overflow:hidden}.maplibregl-canvas{position:absolute;top:0;left:0}.maplibregl-map:fullscreen{width:100%;height:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;z-index:2;position:absolute}.maplibregl-ctrl-top-left{top:0;left:0}.maplibregl-ctrl-top-right{top:0;right:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px #0000001a}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px buttontext}}.maplibregl-ctrl-group button{box-sizing:border-box;cursor:pointer;background-color:#0000;border:0;outline:none;width:29px;height:29px;padding:0;display:block}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;width:100%;height:100%;display:block}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:#0000}.maplibregl-ctrl-group button+button{border-top:1px solid buttontext}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:#0000000d}}.maplibregl-ctrl button:not(:disabled):active{background-color:#0000000d}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:2s linear infinite maplibregl-spin}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:88px;height:23px;margin:0 0 -4px -4px;display:block;overflow:hidden}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:#0000;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:#ffffff80;margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{box-sizing:content-box;color:#000;background-color:#fff;border-radius:12px;min-height:20px;margin:10px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{visibility:visible;padding:2px 28px 2px 8px}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{box-sizing:border-box;cursor:pointer;background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;outline:none;width:24px;height:24px;display:none;position:absolute;top:0;right:0}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;right:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;left:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:#000000bf;text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{box-sizing:border-box;color:#333;background-color:#ffffffbf;border:2px solid #333;border-top:#333;padding:0 5px;font-size:10px}.maplibregl-popup{pointer-events:none;will-change:transform;display:flex;position:absolute;top:0;left:0}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{z-index:1;border:10px solid #0000;width:0;height:0}.maplibregl-popup-anchor-top .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;align-self:center}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;align-self:center}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:#fff;align-self:center}.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:#fff;border-right:none;align-self:center}.maplibregl-popup-close-button{cursor:pointer;background-color:#0000;border:0;border-radius:0 3px 0 0;position:absolute;top:0;right:0}.maplibregl-popup-close-button:hover{background-color:#0000000d}.maplibregl-popup-content{pointer-events:auto;background:#fff;border-radius:3px;padding:15px 10px;position:relative;box-shadow:0 1px 2px #0000001a}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{will-change:transform;transition:opacity .2s;position:absolute;top:0;left:0}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;width:15px;height:15px}.maplibregl-user-location-dot:before{content:"";animation:2s infinite maplibregl-user-location-dot-pulse;position:absolute}.maplibregl-user-location-dot:after{box-sizing:border-box;content:"";border:2px solid #fff;border-radius:50%;width:19px;height:19px;position:absolute;top:-2px;left:-2px;box-shadow:0 0 3px #00000059}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;width:1px;height:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{opacity:.5;background:#fff;border:2px dotted #202020;width:0;height:0;position:absolute;top:0;left:0}.maplibregl-cooperative-gesture-screen{color:#fff;opacity:0;pointer-events:none;z-index:99999;background:#0006;justify-content:center;align-items:center;padding:1rem;font-size:1.4em;line-height:1.2;transition:opacity 1s 1s;display:flex;position:absolute;top:0;right:0;bottom:0;left:0}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity 50ms}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{z-index:99999;width:100%!important;height:100%!important;position:fixed!important;top:0!important;left:0!important}.dark{--color-surface:#161011;--color-surface-muted:#1e1819;--color-ink:#f0ede6;--color-ink-muted:#b8b2aa;--color-border:#40383a;--color-accent:#5ea5d2;--color-badge:#302a2c}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend/dist/assets/index-C4A8iG7V.js b/frontend/dist/assets/index-C4A8iG7V.js new file mode 100644 index 0000000000..a10c17baa0 --- /dev/null +++ b/frontend/dist/assets/index-C4A8iG7V.js @@ -0,0 +1,849 @@ +var Ng=b=>{throw TypeError(b)};var mm=(b,_,x)=>_.has(b)||Ng("Cannot "+x);var ge=(b,_,x)=>(mm(b,_,"read from private field"),x?x.call(b):_.get(b)),Ut=(b,_,x)=>_.has(b)?Ng("Cannot add the same private member more than once"):_ instanceof WeakSet?_.add(b):_.set(b,x),wt=(b,_,x,E)=>(mm(b,_,"write to private field"),E?E.call(b,x):_.set(b,x),x),_r=(b,_,x)=>(mm(b,_,"access private method"),x);var If=(b,_,x,E)=>({set _(z){wt(b,_,z,x)},get _(){return ge(b,_,E)}});(function(){const _=document.createElement("link").relList;if(_&&_.supports&&_.supports("modulepreload"))return;for(const z of document.querySelectorAll('link[rel="modulepreload"]'))E(z);new MutationObserver(z=>{for(const j of z)if(j.type==="childList")for(const C of j.addedNodes)C.tagName==="LINK"&&C.rel==="modulepreload"&&E(C)}).observe(document,{childList:!0,subtree:!0});function x(z){const j={};return z.integrity&&(j.integrity=z.integrity),z.referrerPolicy&&(j.referrerPolicy=z.referrerPolicy),z.crossOrigin==="use-credentials"?j.credentials="include":z.crossOrigin==="anonymous"?j.credentials="omit":j.credentials="same-origin",j}function E(z){if(z.ep)return;z.ep=!0;const j=x(z);fetch(z.href,j)}})();function Hm(b){return b&&b.__esModule&&Object.prototype.hasOwnProperty.call(b,"default")?b.default:b}var gm={exports:{}},Cp={},_m={exports:{}},br={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Ug;function Jy(){if(Ug)return br;Ug=1;var b=Symbol.for("react.element"),_=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),E=Symbol.for("react.strict_mode"),z=Symbol.for("react.profiler"),j=Symbol.for("react.provider"),C=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),G=Symbol.for("react.suspense"),ee=Symbol.for("react.memo"),ae=Symbol.for("react.lazy"),ce=Symbol.iterator;function _e(Te){return Te===null||typeof Te!="object"?null:(Te=ce&&Te[ce]||Te["@@iterator"],typeof Te=="function"?Te:null)}var te={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Oe=Object.assign,Ue={};function Fe(Te,He,Dt){this.props=Te,this.context=He,this.refs=Ue,this.updater=Dt||te}Fe.prototype.isReactComponent={},Fe.prototype.setState=function(Te,He){if(typeof Te!="object"&&typeof Te!="function"&&Te!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Te,He,"setState")},Fe.prototype.forceUpdate=function(Te){this.updater.enqueueForceUpdate(this,Te,"forceUpdate")};function et(){}et.prototype=Fe.prototype;function nt(Te,He,Dt){this.props=Te,this.context=He,this.refs=Ue,this.updater=Dt||te}var qe=nt.prototype=new et;qe.constructor=nt,Oe(qe,Fe.prototype),qe.isPureReactComponent=!0;var We=Array.isArray,Rt=Object.prototype.hasOwnProperty,Vt={current:null},Kt={key:!0,ref:!0,__self:!0,__source:!0};function mt(Te,He,Dt){var qt,or={},Qt=null,Er=null;if(He!=null)for(qt in He.ref!==void 0&&(Er=He.ref),He.key!==void 0&&(Qt=""+He.key),He)Rt.call(He,qt)&&!Kt.hasOwnProperty(qt)&&(or[qt]=He[qt]);var cr=arguments.length-2;if(cr===1)or.children=Dt;else if(1>>1,He=ft[Te];if(0>>1;Tez(or,gt))Qtz(Er,or)?(ft[Te]=Er,ft[Qt]=gt,Te=Qt):(ft[Te]=or,ft[qt]=gt,Te=qt);else if(Qtz(Er,gt))ft[Te]=Er,ft[Qt]=gt,Te=Qt;else break e}}return pt}function z(ft,pt){var gt=ft.sortIndex-pt.sortIndex;return gt!==0?gt:ft.id-pt.id}if(typeof performance=="object"&&typeof performance.now=="function"){var j=performance;b.unstable_now=function(){return j.now()}}else{var C=Date,u=C.now();b.unstable_now=function(){return C.now()-u}}var G=[],ee=[],ae=1,ce=null,_e=3,te=!1,Oe=!1,Ue=!1,Fe=typeof setTimeout=="function"?setTimeout:null,et=typeof clearTimeout=="function"?clearTimeout:null,nt=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function qe(ft){for(var pt=x(ee);pt!==null;){if(pt.callback===null)E(ee);else if(pt.startTime<=ft)E(ee),pt.sortIndex=pt.expirationTime,_(G,pt);else break;pt=x(ee)}}function We(ft){if(Ue=!1,qe(ft),!Oe)if(x(G)!==null)Oe=!0,Mr(Rt);else{var pt=x(ee);pt!==null&&pr(We,pt.startTime-ft)}}function Rt(ft,pt){Oe=!1,Ue&&(Ue=!1,et(mt),mt=-1),te=!0;var gt=_e;try{for(qe(pt),ce=x(G);ce!==null&&(!(ce.expirationTime>pt)||ft&&!Cr());){var Te=ce.callback;if(typeof Te=="function"){ce.callback=null,_e=ce.priorityLevel;var He=Te(ce.expirationTime<=pt);pt=b.unstable_now(),typeof He=="function"?ce.callback=He:ce===x(G)&&E(G),qe(pt)}else E(G);ce=x(G)}if(ce!==null)var Dt=!0;else{var qt=x(ee);qt!==null&&pr(We,qt.startTime-pt),Dt=!1}return Dt}finally{ce=null,_e=gt,te=!1}}var Vt=!1,Kt=null,mt=-1,Tt=5,xt=-1;function Cr(){return!(b.unstable_now()-xtft||125Te?(ft.sortIndex=gt,_(ee,ft),x(G)===null&&ft===x(ee)&&(Ue?(et(mt),mt=-1):Ue=!0,pr(We,gt-Te))):(ft.sortIndex=He,_(G,ft),Oe||te||(Oe=!0,Mr(Rt))),ft},b.unstable_shouldYield=Cr,b.unstable_wrapCallback=function(ft){var pt=_e;return function(){var gt=_e;_e=pt;try{return ft.apply(this,arguments)}finally{_e=gt}}}})(xm)),xm}var qg;function nv(){return qg||(qg=1,vm.exports=rv()),vm.exports}/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Hg;function iv(){if(Hg)return Ks;Hg=1;var b=Wm(),_=nv();function x(s){for(var l="https://reactjs.org/docs/error-decoder.html?invariant="+s,g=1;g"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),G=Object.prototype.hasOwnProperty,ee=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ae={},ce={};function _e(s){return G.call(ce,s)?!0:G.call(ae,s)?!1:ee.test(s)?ce[s]=!0:(ae[s]=!0,!1)}function te(s,l,g,w){if(g!==null&&g.type===0)return!1;switch(typeof l){case"function":case"symbol":return!0;case"boolean":return w?!1:g!==null?!g.acceptsBooleans:(s=s.toLowerCase().slice(0,5),s!=="data-"&&s!=="aria-");default:return!1}}function Oe(s,l,g,w){if(l===null||typeof l>"u"||te(s,l,g,w))return!0;if(w)return!1;if(g!==null)switch(g.type){case 3:return!l;case 4:return l===!1;case 5:return isNaN(l);case 6:return isNaN(l)||1>l}return!1}function Ue(s,l,g,w,k,D,W){this.acceptsBooleans=l===2||l===3||l===4,this.attributeName=w,this.attributeNamespace=k,this.mustUseProperty=g,this.propertyName=s,this.type=l,this.sanitizeURL=D,this.removeEmptyString=W}var Fe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(s){Fe[s]=new Ue(s,0,!1,s,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(s){var l=s[0];Fe[l]=new Ue(l,1,!1,s[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(s){Fe[s]=new Ue(s,2,!1,s.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(s){Fe[s]=new Ue(s,2,!1,s,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(s){Fe[s]=new Ue(s,3,!1,s.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(s){Fe[s]=new Ue(s,3,!0,s,null,!1,!1)}),["capture","download"].forEach(function(s){Fe[s]=new Ue(s,4,!1,s,null,!1,!1)}),["cols","rows","size","span"].forEach(function(s){Fe[s]=new Ue(s,6,!1,s,null,!1,!1)}),["rowSpan","start"].forEach(function(s){Fe[s]=new Ue(s,5,!1,s.toLowerCase(),null,!1,!1)});var et=/[\-:]([a-z])/g;function nt(s){return s[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(s){Fe[s]=new Ue(s,1,!1,s.toLowerCase(),null,!1,!1)}),Fe.xlinkHref=new Ue("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(s){Fe[s]=new Ue(s,1,!1,s.toLowerCase(),null,!0,!0)});function qe(s,l,g,w){var k=Fe.hasOwnProperty(l)?Fe[l]:null;(k!==null?k.type!==0:w||!(2he||k[W]!==D[he]){var ve=` +`+k[W].replace(" at new "," at ");return s.displayName&&ve.includes("")&&(ve=ve.replace("",s.displayName)),ve}while(1<=W&&0<=he);break}}}finally{Dt=!1,Error.prepareStackTrace=g}return(s=s?s.displayName||s.name:"")?He(s):""}function or(s){switch(s.tag){case 5:return He(s.type);case 16:return He("Lazy");case 13:return He("Suspense");case 19:return He("SuspenseList");case 0:case 2:case 15:return s=qt(s.type,!1),s;case 11:return s=qt(s.type.render,!1),s;case 1:return s=qt(s.type,!0),s;default:return""}}function Qt(s){if(s==null)return null;if(typeof s=="function")return s.displayName||s.name||null;if(typeof s=="string")return s;switch(s){case Kt:return"Fragment";case Vt:return"Portal";case Tt:return"Profiler";case mt:return"StrictMode";case mr:return"Suspense";case Vr:return"SuspenseList"}if(typeof s=="object")switch(s.$$typeof){case Cr:return(s.displayName||"Context")+".Consumer";case xt:return(s._context.displayName||"Context")+".Provider";case Br:var l=s.render;return s=s.displayName,s||(s=l.displayName||l.name||"",s=s!==""?"ForwardRef("+s+")":"ForwardRef"),s;case xn:return l=s.displayName||null,l!==null?l:Qt(s.type)||"Memo";case Mr:l=s._payload,s=s._init;try{return Qt(s(l))}catch{}}return null}function Er(s){var l=s.type;switch(s.tag){case 24:return"Cache";case 9:return(l.displayName||"Context")+".Consumer";case 10:return(l._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return s=l.render,s=s.displayName||s.name||"",l.displayName||(s!==""?"ForwardRef("+s+")":"ForwardRef");case 7:return"Fragment";case 5:return l;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Qt(l);case 8:return l===mt?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof l=="function")return l.displayName||l.name||null;if(typeof l=="string")return l}return null}function cr(s){switch(typeof s){case"boolean":case"number":case"string":case"undefined":return s;case"object":return s;default:return""}}function vr(s){var l=s.type;return(s=s.nodeName)&&s.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function zn(s){var l=vr(s)?"checked":"value",g=Object.getOwnPropertyDescriptor(s.constructor.prototype,l),w=""+s[l];if(!s.hasOwnProperty(l)&&typeof g<"u"&&typeof g.get=="function"&&typeof g.set=="function"){var k=g.get,D=g.set;return Object.defineProperty(s,l,{configurable:!0,get:function(){return k.call(this)},set:function(W){w=""+W,D.call(this,W)}}),Object.defineProperty(s,l,{enumerable:g.enumerable}),{getValue:function(){return w},setValue:function(W){w=""+W},stopTracking:function(){s._valueTracker=null,delete s[l]}}}}function Qs(s){s._valueTracker||(s._valueTracker=zn(s))}function yi(s){if(!s)return!1;var l=s._valueTracker;if(!l)return!0;var g=l.getValue(),w="";return s&&(w=vr(s)?s.checked?"true":"false":s.value),s=w,s!==g?(l.setValue(s),!0):!1}function Wr(s){if(s=s||(typeof document<"u"?document:void 0),typeof s>"u")return null;try{return s.activeElement||s.body}catch{return s.body}}function Jr(s,l){var g=l.checked;return gt({},l,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:g??s._wrapperState.initialChecked})}function Gi(s,l){var g=l.defaultValue==null?"":l.defaultValue,w=l.checked!=null?l.checked:l.defaultChecked;g=cr(l.value!=null?l.value:g),s._wrapperState={initialChecked:w,initialValue:g,controlled:l.type==="checkbox"||l.type==="radio"?l.checked!=null:l.value!=null}}function ss(s,l){l=l.checked,l!=null&&qe(s,"checked",l,!1)}function Js(s,l){ss(s,l);var g=cr(l.value),w=l.type;if(g!=null)w==="number"?(g===0&&s.value===""||s.value!=g)&&(s.value=""+g):s.value!==""+g&&(s.value=""+g);else if(w==="submit"||w==="reset"){s.removeAttribute("value");return}l.hasOwnProperty("value")?qi(s,l.type,g):l.hasOwnProperty("defaultValue")&&qi(s,l.type,cr(l.defaultValue)),l.checked==null&&l.defaultChecked!=null&&(s.defaultChecked=!!l.defaultChecked)}function as(s,l,g){if(l.hasOwnProperty("value")||l.hasOwnProperty("defaultValue")){var w=l.type;if(!(w!=="submit"&&w!=="reset"||l.value!==void 0&&l.value!==null))return;l=""+s._wrapperState.initialValue,g||l===s.value||(s.value=l),s.defaultValue=l}g=s.name,g!==""&&(s.name=""),s.defaultChecked=!!s._wrapperState.initialChecked,g!==""&&(s.name=g)}function qi(s,l,g){(l!=="number"||Wr(s.ownerDocument)!==s)&&(g==null?s.defaultValue=""+s._wrapperState.initialValue:s.defaultValue!==""+g&&(s.defaultValue=""+g))}var Is=Array.isArray;function ki(s,l,g,w){if(s=s.options,l){l={};for(var k=0;k"+l.valueOf().toString()+"",l=na.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;l.firstChild;)s.appendChild(l.firstChild)}});function Z(s,l){if(l){var g=s.firstChild;if(g&&g===s.lastChild&&g.nodeType===3){g.nodeValue=l;return}}s.textContent=l}var H={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},X=["Webkit","ms","Moz","O"];Object.keys(H).forEach(function(s){X.forEach(function(l){l=l+s.charAt(0).toUpperCase()+s.substring(1),H[l]=H[s]})});function se(s,l,g){return l==null||typeof l=="boolean"||l===""?"":g||typeof l!="number"||l===0||H.hasOwnProperty(s)&&H[s]?(""+l).trim():l+"px"}function pe(s,l){s=s.style;for(var g in l)if(l.hasOwnProperty(g)){var w=g.indexOf("--")===0,k=se(g,l[g],w);g==="float"&&(g="cssFloat"),w?s.setProperty(g,k):s[g]=k}}var Pe=gt({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ke(s,l){if(l){if(Pe[s]&&(l.children!=null||l.dangerouslySetInnerHTML!=null))throw Error(x(137,s));if(l.dangerouslySetInnerHTML!=null){if(l.children!=null)throw Error(x(60));if(typeof l.dangerouslySetInnerHTML!="object"||!("__html"in l.dangerouslySetInnerHTML))throw Error(x(61))}if(l.style!=null&&typeof l.style!="object")throw Error(x(62))}}function be(s,l){if(s.indexOf("-")===-1)return typeof l.is=="string";switch(s){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Ne=null;function Xe(s){return s=s.target||s.srcElement||window,s.correspondingUseElement&&(s=s.correspondingUseElement),s.nodeType===3?s.parentNode:s}var $e=null,_t=null,we=null;function Ot(s){if(s=ol(s)){if(typeof $e!="function")throw Error(x(280));var l=s.stateNode;l&&(l=Jl(l),$e(s.stateNode,s.type,l))}}function er(s){_t?we?we.push(s):we=[s]:_t=s}function Pt(){if(_t){var s=_t,l=we;if(we=_t=null,Ot(s),l)for(s=0;s>>=0,s===0?32:31-(Vh(s)/Bo|0)|0}var Cu=64,Mu=4194304;function Al(s){switch(s&-s){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return s&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return s}}function jo(s,l){var g=s.pendingLanes;if(g===0)return 0;var w=0,k=s.suspendedLanes,D=s.pingedLanes,W=g&268435455;if(W!==0){var he=W&~k;he!==0?w=Al(he):(D&=W,D!==0&&(w=Al(D)))}else W=g&~k,W!==0?w=Al(W):D!==0&&(w=Al(D));if(w===0)return 0;if(l!==0&&l!==w&&(l&k)===0&&(k=w&-w,D=l&-l,k>=D||k===16&&(D&4194240)!==0))return l;if((w&4)!==0&&(w|=g&16),l=s.entangledLanes,l!==0)for(s=s.entanglements,l&=w;0g;g++)l.push(s);return l}function ao(s,l,g){s.pendingLanes|=l,l!==536870912&&(s.suspendedLanes=0,s.pingedLanes=0),s=s.eventTimes,l=31-Hi(l),s[l]=g}function Od(s,l){var g=s.pendingLanes&~l;s.pendingLanes=l,s.suspendedLanes=0,s.pingedLanes=0,s.expiredLanes&=l,s.mutableReadLanes&=l,s.entangledLanes&=l,l=s.entanglements;var w=s.eventTimes;for(s=s.expirationTimes;0=Wo),Bl=" ",Lc=!1;function Dc(s,l){switch(s){case"keyup":return kc.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ua(s){return s=s.detail,typeof s=="object"&&"data"in s?s.data:null}var Va=!1;function Fc(s,l){switch(s){case"compositionend":return Ua(l);case"keypress":return l.which!==32?null:(Lc=!0,Bl);case"textInput":return s=l.data,s===Bl&&Lc?null:s;default:return null}}function jl(s,l){if(Va)return s==="compositionend"||!Ac&&Dc(s,l)?(s=Ki(),oo=Zo=cs=null,Va=!1,s):null;switch(s){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1=l)return{node:g,offset:l-s};s=w}e:{for(;g;){if(g.nextSibling){g=g.nextSibling;break e}g=g.parentNode}g=void 0}g=Kh(g)}}function Xh(s,l){return s&&l?s===l?!0:s&&s.nodeType===3?!1:l&&l.nodeType===3?Xh(s,l.parentNode):"contains"in s?s.contains(l):s.compareDocumentPosition?!!(s.compareDocumentPosition(l)&16):!1:!1}function $c(){for(var s=window,l=Wr();l instanceof s.HTMLIFrameElement;){try{var g=typeof l.contentWindow.location.href=="string"}catch{g=!1}if(g)s=l.contentWindow;else break;l=Wr(s.document)}return l}function Jo(s){var l=s&&s.nodeName&&s.nodeName.toLowerCase();return l&&(l==="input"&&(s.type==="text"||s.type==="search"||s.type==="tel"||s.type==="url"||s.type==="password")||l==="textarea"||s.contentEditable==="true")}function Du(s){var l=$c(),g=s.focusedElem,w=s.selectionRange;if(l!==g&&g&&g.ownerDocument&&Xh(g.ownerDocument.documentElement,g)){if(w!==null&&Jo(g)){if(l=w.start,s=w.end,s===void 0&&(s=l),"selectionStart"in g)g.selectionStart=l,g.selectionEnd=Math.min(s,g.value.length);else if(s=(l=g.ownerDocument||document)&&l.defaultView||window,s.getSelection){s=s.getSelection();var k=g.textContent.length,D=Math.min(w.start,k);w=w.end===void 0?D:Math.min(w.end,k),!s.extend&&D>w&&(k=w,w=D,D=k),k=Vc(g,D);var W=Vc(g,w);k&&W&&(s.rangeCount!==1||s.anchorNode!==k.node||s.anchorOffset!==k.offset||s.focusNode!==W.node||s.focusOffset!==W.offset)&&(l=l.createRange(),l.setStart(k.node,k.offset),s.removeAllRanges(),D>w?(s.addRange(l),s.extend(W.node,W.offset)):(l.setEnd(W.node,W.offset),s.addRange(l)))}}for(l=[],s=g;s=s.parentNode;)s.nodeType===1&&l.push({element:s,left:s.scrollLeft,top:s.scrollTop});for(typeof g.focus=="function"&&g.focus(),g=0;g=document.documentMode,$a=null,ua=null,ca=null,Za=!1;function Ri(s,l,g){var w=g.window===g?g.document:g.nodeType===9?g:g.ownerDocument;Za||$a==null||$a!==Wr(w)||(w=$a,"selectionStart"in w&&Jo(w)?w={start:w.selectionStart,end:w.selectionEnd}:(w=(w.ownerDocument&&w.ownerDocument.defaultView||window).getSelection(),w={anchorNode:w.anchorNode,anchorOffset:w.anchorOffset,focusNode:w.focusNode,focusOffset:w.focusOffset}),ca&&Ul(ca,w)||(ca=w,w=Wl(ua,"onSelect"),0yo||(s.current=Uu[yo],Uu[yo]=null,yo--)}function dr(s,l){yo++,Uu[yo]=s.current,s.current=l}var Di={},Fn=Dn(Di),ni=Dn(!1),Wa=Di;function gs(s,l){var g=s.type.contextTypes;if(!g)return Di;var w=s.stateNode;if(w&&w.__reactInternalMemoizedUnmaskedChildContext===l)return w.__reactInternalMemoizedMaskedChildContext;var k={},D;for(D in g)k[D]=l[D];return w&&(s=s.stateNode,s.__reactInternalMemoizedUnmaskedChildContext=l,s.__reactInternalMemoizedMaskedChildContext=k),k}function Xn(s){return s=s.childContextTypes,s!=null}function ya(){Rr(ni),Rr(Fn)}function Wc(s,l,g){if(Fn.current!==Di)throw Error(x(168));dr(Fn,l),dr(ni,g)}function ii(s,l,g){var w=s.stateNode;if(l=l.childContextTypes,typeof w.getChildContext!="function")return g;w=w.getChildContext();for(var k in w)if(!(k in l))throw Error(x(108,Er(s)||"Unknown",k));return gt({},g,w)}function vi(s){return s=(s=s.stateNode)&&s.__reactInternalMemoizedMergedChildContext||Di,Wa=Fn.current,dr(Fn,s),dr(ni,ni.current),!0}function vo(s,l,g){var w=s.stateNode;if(!w)throw Error(x(169));g?(s=ii(s,l,Wa),w.__reactInternalMemoizedMergedChildContext=s,Rr(ni),Rr(Fn),dr(Fn,s)):Rr(ni),dr(ni,g)}var Pn=null,va=!1,Kc=!1;function ll(s){Pn===null?Pn=[s]:Pn.push(s)}function Hd(s){va=!0,ll(s)}function Ka(){if(!Kc&&Pn!==null){Kc=!0;var s=0,l=wr;try{var g=Pn;for(wr=1;s>=W,k-=W,wi=1<<32-Hi(l)+k|g<Wt?(Gn=Nt,Nt=null):Gn=Nt.sibling;var Tr=Ye(Re,Nt,De[Wt],ot);if(Tr===null){Nt===null&&(Nt=Gn);break}s&&Nt&&Tr.alternate===null&&l(Re,Nt),Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr,Nt=Gn}if(Wt===De.length)return g(Re,Nt),Xr&&$s(Re,Wt),Et;if(Nt===null){for(;WtWt?(Gn=Nt,Nt=null):Gn=Nt.sibling;var Io=Ye(Re,Nt,Tr.value,ot);if(Io===null){Nt===null&&(Nt=Gn);break}s&&Nt&&Io.alternate===null&&l(Re,Nt),Ce=D(Io,Ce,Wt),jt===null?Et=Io:jt.sibling=Io,jt=Io,Nt=Gn}if(Tr.done)return g(Re,Nt),Xr&&$s(Re,Wt),Et;if(Nt===null){for(;!Tr.done;Wt++,Tr=De.next())Tr=Je(Re,Tr.value,ot),Tr!==null&&(Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr);return Xr&&$s(Re,Wt),Et}for(Nt=w(Re,Nt);!Tr.done;Wt++,Tr=De.next())Tr=bt(Nt,Re,Wt,Tr.value,ot),Tr!==null&&(s&&Tr.alternate!==null&&Nt.delete(Tr.key===null?Wt:Tr.key),Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr);return s&&Nt.forEach(function(em){return l(Re,em)}),Xr&&$s(Re,Wt),Et}function an(Re,Ce,De,ot){if(typeof De=="object"&&De!==null&&De.type===Kt&&De.key===null&&(De=De.props.children),typeof De=="object"&&De!==null){switch(De.$$typeof){case Rt:e:{for(var Et=De.key,jt=Ce;jt!==null;){if(jt.key===Et){if(Et=De.type,Et===Kt){if(jt.tag===7){g(Re,jt.sibling),Ce=k(jt,De.props.children),Ce.return=Re,Re=Ce;break e}}else if(jt.elementType===Et||typeof Et=="object"&&Et!==null&&Et.$$typeof===Mr&&Zu(Et)===jt.type){g(Re,jt.sibling),Ce=k(jt,De.props),Ce.ref=cl(Re,jt,De),Ce.return=Re,Re=Ce;break e}g(Re,jt);break}else l(Re,jt);jt=jt.sibling}De.type===Kt?(Ce=pu(De.props.children,Re.mode,ot,De.key),Ce.return=Re,Re=Ce):(ot=cd(De.type,De.key,De.props,null,Re.mode,ot),ot.ref=cl(Re,Ce,De),ot.return=Re,Re=ot)}return W(Re);case Vt:e:{for(jt=De.key;Ce!==null;){if(Ce.key===jt)if(Ce.tag===4&&Ce.stateNode.containerInfo===De.containerInfo&&Ce.stateNode.implementation===De.implementation){g(Re,Ce.sibling),Ce=k(Ce,De.children||[]),Ce.return=Re,Re=Ce;break e}else{g(Re,Ce);break}else l(Re,Ce);Ce=Ce.sibling}Ce=vp(De,Re.mode,ot),Ce.return=Re,Re=Ce}return W(Re);case Mr:return jt=De._init,an(Re,Ce,jt(De._payload),ot)}if(Is(De))return Mt(Re,Ce,De,ot);if(pt(De))return At(Re,Ce,De,ot);hl(Re,De)}return typeof De=="string"&&De!==""||typeof De=="number"?(De=""+De,Ce!==null&&Ce.tag===6?(g(Re,Ce.sibling),Ce=k(Ce,De),Ce.return=Re,Re=Ce):(g(Re,Ce),Ce=yp(De,Re.mode,ot),Ce.return=Re,Re=Ce),W(Re)):g(Re,Ce)}return an}var oi=eh(!0),ru=eh(!1),yt=Dn(null),kt=null,ba=null,Zs=null;function dl(){Zs=ba=kt=null}function Vn(s){var l=yt.current;Rr(yt),s._currentValue=l}function Gu(s,l,g){for(;s!==null;){var w=s.alternate;if((s.childLanes&l)!==l?(s.childLanes|=l,w!==null&&(w.childLanes|=l)):w!==null&&(w.childLanes&l)!==l&&(w.childLanes|=l),s===g)break;s=s.return}}function $n(s,l){kt=s,Zs=ba=null,s=s.dependencies,s!==null&&s.firstContext!==null&&((s.lanes&l)!==0&&(es=!0),s.firstContext=null)}function Yi(s){var l=s._currentValue;if(Zs!==s)if(s={context:s,memoizedValue:l,next:null},ba===null){if(kt===null)throw Error(x(308));ba=s,kt.dependencies={lanes:0,firstContext:s}}else ba=ba.next=s;return l}var _s=null;function Ir(s){_s===null?_s=[s]:_s.push(s)}function en(s,l,g,w){var k=l.interleaved;return k===null?(g.next=g,Ir(l)):(g.next=k.next,k.next=g),l.interleaved=g,Qi(s,w)}function Qi(s,l){s.lanes|=l;var g=s.alternate;for(g!==null&&(g.lanes|=l),g=s,s=s.return;s!==null;)s.childLanes|=l,g=s.alternate,g!==null&&(g.childLanes|=l),g=s,s=s.return;return g.tag===3?g.stateNode:null}var Bi=!1;function Ya(s){s.updateQueue={baseState:s.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function qu(s,l){s=s.updateQueue,l.updateQueue===s&&(l.updateQueue={baseState:s.baseState,firstBaseUpdate:s.firstBaseUpdate,lastBaseUpdate:s.lastBaseUpdate,shared:s.shared,effects:s.effects})}function Si(s,l){return{eventTime:s,lane:l,tag:0,payload:null,callback:null,next:null}}function Ti(s,l,g){var w=s.updateQueue;if(w===null)return null;if(w=w.shared,(gr&2)!==0){var k=w.pending;return k===null?l.next=l:(l.next=k.next,k.next=l),w.pending=l,Qi(s,g)}return k=w.interleaved,k===null?(l.next=l,Ir(w)):(l.next=k.next,k.next=l),w.interleaved=l,Qi(s,g)}function Qa(s,l,g){if(l=l.updateQueue,l!==null&&(l=l.shared,(g&4194240)!==0)){var w=l.lanes;w&=s.pendingLanes,g|=w,l.lanes=g,Da(s,g)}}function nu(s,l){var g=s.updateQueue,w=s.alternate;if(w!==null&&(w=w.updateQueue,g===w)){var k=null,D=null;if(g=g.firstBaseUpdate,g!==null){do{var W={eventTime:g.eventTime,lane:g.lane,tag:g.tag,payload:g.payload,callback:g.callback,next:null};D===null?k=D=W:D=D.next=W,g=g.next}while(g!==null);D===null?k=D=l:D=D.next=l}else k=D=l;g={baseState:w.baseState,firstBaseUpdate:k,lastBaseUpdate:D,shared:w.shared,effects:w.effects},s.updateQueue=g;return}s=g.lastBaseUpdate,s===null?g.firstBaseUpdate=l:s.next=l,g.lastBaseUpdate=l}function Gs(s,l,g,w){var k=s.updateQueue;Bi=!1;var D=k.firstBaseUpdate,W=k.lastBaseUpdate,he=k.shared.pending;if(he!==null){k.shared.pending=null;var ve=he,je=ve.next;ve.next=null,W===null?D=je:W.next=je,W=ve;var rt=s.alternate;rt!==null&&(rt=rt.updateQueue,he=rt.lastBaseUpdate,he!==W&&(he===null?rt.firstBaseUpdate=je:he.next=je,rt.lastBaseUpdate=ve))}if(D!==null){var Je=k.baseState;W=0,rt=je=ve=null,he=D;do{var Ye=he.lane,bt=he.eventTime;if((w&Ye)===Ye){rt!==null&&(rt=rt.next={eventTime:bt,lane:0,tag:he.tag,payload:he.payload,callback:he.callback,next:null});e:{var Mt=s,At=he;switch(Ye=l,bt=g,At.tag){case 1:if(Mt=At.payload,typeof Mt=="function"){Je=Mt.call(bt,Je,Ye);break e}Je=Mt;break e;case 3:Mt.flags=Mt.flags&-65537|128;case 0:if(Mt=At.payload,Ye=typeof Mt=="function"?Mt.call(bt,Je,Ye):Mt,Ye==null)break e;Je=gt({},Je,Ye);break e;case 2:Bi=!0}}he.callback!==null&&he.lane!==0&&(s.flags|=64,Ye=k.effects,Ye===null?k.effects=[he]:Ye.push(he))}else bt={eventTime:bt,lane:Ye,tag:he.tag,payload:he.payload,callback:he.callback,next:null},rt===null?(je=rt=bt,ve=Je):rt=rt.next=bt,W|=Ye;if(he=he.next,he===null){if(he=k.shared.pending,he===null)break;Ye=he,he=Ye.next,Ye.next=null,k.lastBaseUpdate=Ye,k.shared.pending=null}}while(!0);if(rt===null&&(ve=Je),k.baseState=ve,k.firstBaseUpdate=je,k.lastBaseUpdate=rt,l=k.shared.interleaved,l!==null){k=l;do W|=k.lane,k=k.next;while(k!==l)}else D===null&&(k.shared.lanes=0);ml|=W,s.lanes=W,s.memoizedState=Je}}function Hu(s,l,g){if(s=l.effects,l.effects=null,s!==null)for(l=0;lg?g:4,s(!0);var w=O.transition;O.transition={};try{s(!1),l()}finally{wr=g,O.transition=w}}function li(){return Ae().memoizedState}function ys(s,l,g){var w=ro(s);if(g={lane:w,action:g,hasEagerState:!1,eagerState:null,next:null},Sa(s))pn(l,g);else if(g=en(s,l,g,w),g!==null){var k=Zn();Ma(g,s,w,k),_n(g,l,w)}}function ui(s,l,g){var w=ro(s),k={lane:w,action:g,hasEagerState:!1,eagerState:null,next:null};if(Sa(s))pn(l,k);else{var D=s.alternate;if(s.lanes===0&&(D===null||D.lanes===0)&&(D=l.lastRenderedReducer,D!==null))try{var W=l.lastRenderedState,he=D(W,g);if(k.hasEagerState=!0,k.eagerState=he,fs(he,W)){var ve=l.interleaved;ve===null?(k.next=k,Ir(l)):(k.next=ve.next,ve.next=k),l.interleaved=k;return}}catch{}finally{}g=en(s,l,k,w),g!==null&&(k=Zn(),Ma(g,s,w,k),_n(g,l,w))}}function Sa(s){var l=s.alternate;return s===B||l!==null&&l===B}function pn(s,l){J=Q=!0;var g=s.pending;g===null?l.next=l:(l.next=g.next,g.next=l),s.pending=l}function _n(s,l,g){if((g&4194240)!==0){var w=l.lanes;w&=s.pendingLanes,g|=w,l.lanes=g,Da(s,g)}}var ci={readContext:Yi,useCallback:ne,useContext:ne,useEffect:ne,useImperativeHandle:ne,useInsertionEffect:ne,useLayoutEffect:ne,useMemo:ne,useReducer:ne,useRef:ne,useState:ne,useDebugValue:ne,useDeferredValue:ne,useTransition:ne,useMutableSource:ne,useSyncExternalStore:ne,useId:ne,unstable_isNewReconciler:!1},yn={readContext:Yi,useCallback:function(s,l){return ye().memoizedState=[s,l===void 0?null:l],s},useContext:Yi,useEffect:hn,useImperativeHandle:function(s,l,g){return g=g!=null?g.concat([s]):null,Zt(4194308,4,Ji.bind(null,l,s),g)},useLayoutEffect:function(s,l){return Zt(4194308,4,s,l)},useInsertionEffect:function(s,l){return Zt(4,2,s,l)},useMemo:function(s,l){var g=ye();return l=l===void 0?null:l,s=s(),g.memoizedState=[s,l],s},useReducer:function(s,l,g){var w=ye();return l=g!==void 0?g(l):l,w.memoizedState=w.baseState=l,s={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:s,lastRenderedState:l},w.queue=s,s=s.dispatch=ys.bind(null,B,s),[w.memoizedState,s]},useRef:function(s){var l=ye();return s={current:s},l.memoizedState=s},useState:kr,useDebugValue:qs,useDeferredValue:function(s){return ye().memoizedState=s},useTransition:function(){var s=kr(!1),l=s[0];return s=To.bind(null,s[1]),ye().memoizedState=s,[l,s]},useMutableSource:function(){},useSyncExternalStore:function(s,l,g){var w=B,k=ye();if(Xr){if(g===void 0)throw Error(x(407));g=g()}else{if(g=l(),En===null)throw Error(x(349));(F&30)!==0||Ge(w,l,g)}k.memoizedState=g;var D={value:g,getSnapshot:l};return k.queue=D,hn(Yt.bind(null,w,D,s),[s]),w.flags|=2048,Ur(9,vt.bind(null,w,D,g,l),void 0,null),g},useId:function(){var s=ye(),l=En.identifierPrefix;if(Xr){var g=ai,w=wi;g=(w&~(1<<32-Hi(w)-1)).toString(32)+g,l=":"+l+"R"+g,g=re++,0<\/script>",s=s.removeChild(s.firstChild)):typeof w.is=="string"?s=W.createElement(g,{is:w.is}):(s=W.createElement(g),g==="select"&&(W=s,w.multiple?W.multiple=!0:w.size&&(W.size=w.size))):s=W.createElementNS(s,g),s[Li]=l,s[fa]=w,rp(s,l,!1,!1),l.stateNode=s;e:{switch(W=be(g,w),g){case"dialog":qr("cancel",s),qr("close",s),k=w;break;case"iframe":case"object":case"embed":qr("load",s),k=w;break;case"video":case"audio":for(k=0;knc&&(l.flags|=128,w=!0,oh(D,!1),l.lanes=4194304)}else{if(!w)if(s=T(W),s!==null){if(l.flags|=128,w=!0,g=s.updateQueue,g!==null&&(l.updateQueue=g,l.flags|=4),oh(D,!0),D.tail===null&&D.tailMode==="hidden"&&!W.alternate&&!Xr)return Ci(l),null}else 2*rr()-D.renderingStartTime>nc&&g!==1073741824&&(l.flags|=128,w=!0,oh(D,!1),l.lanes=4194304);D.isBackwards?(W.sibling=l.child,l.child=W):(g=D.last,g!==null?g.sibling=W:l.child=W,D.last=W)}return D.tail!==null?(l=D.tail,D.rendering=l,D.tail=l.sibling,D.renderingStartTime=rr(),l.sibling=null,g=y.current,dr(y,w?g&1|2:g&1),l):(Ci(l),null);case 22:case 23:return gp(),w=l.memoizedState!==null,s!==null&&s.memoizedState!==null!==w&&(l.flags|=8192),w&&(l.mode&1)!==0?(bs&1073741824)!==0&&(Ci(l),l.subtreeFlags&6&&(l.flags|=8192)):Ci(l),null;case 24:return null;case 25:return null}throw Error(x(156,l.tag))}function Wf(s,l){switch(Xc(l),l.tag){case 1:return Xn(l.type)&&ya(),s=l.flags,s&65536?(l.flags=s&-65537|128,l):null;case 3:return o(),Rr(ni),Rr(Fn),A(),s=l.flags,(s&65536)!==0&&(s&128)===0?(l.flags=s&-65537|128,l):null;case 5:return p(l),null;case 13:if(Rr(y),s=l.memoizedState,s!==null&&s.dehydrated!==null){if(l.alternate===null)throw Error(x(340));Sr()}return s=l.flags,s&65536?(l.flags=s&-65537|128,l):null;case 19:return Rr(y),null;case 4:return o(),null;case 10:return Vn(l.type._context),null;case 22:case 23:return gp(),null;case 24:return null;default:return null}}var Yu=!1,di=!1,ef=typeof WeakSet=="function"?WeakSet:Set,Ct=null;function Qu(s,l){var g=s.ref;if(g!==null)if(typeof g=="function")try{g(null)}catch(w){vn(s,l,w)}else g.current=null}function ip(s,l,g){try{g()}catch(w){vn(s,l,w)}}var nd=!1;function sp(s,l){if(al=Kn,s=$c(),Jo(s)){if("selectionStart"in s)var g={start:s.selectionStart,end:s.selectionEnd};else e:{g=(g=s.ownerDocument)&&g.defaultView||window;var w=g.getSelection&&g.getSelection();if(w&&w.rangeCount!==0){g=w.anchorNode;var k=w.anchorOffset,D=w.focusNode;w=w.focusOffset;try{g.nodeType,D.nodeType}catch{g=null;break e}var W=0,he=-1,ve=-1,je=0,rt=0,Je=s,Ye=null;t:for(;;){for(var bt;Je!==g||k!==0&&Je.nodeType!==3||(he=W+k),Je!==D||w!==0&&Je.nodeType!==3||(ve=W+w),Je.nodeType===3&&(W+=Je.nodeValue.length),(bt=Je.firstChild)!==null;)Ye=Je,Je=bt;for(;;){if(Je===s)break t;if(Ye===g&&++je===k&&(he=W),Ye===D&&++rt===w&&(ve=W),(bt=Je.nextSibling)!==null)break;Je=Ye,Ye=Je.parentNode}Je=bt}g=he===-1||ve===-1?null:{start:he,end:ve}}else g=null}g=g||{start:0,end:0}}else g=null;for(pa={focusedElem:s,selectionRange:g},Kn=!1,Ct=l;Ct!==null;)if(l=Ct,s=l.child,(l.subtreeFlags&1028)!==0&&s!==null)s.return=l,Ct=s;else for(;Ct!==null;){l=Ct;try{var Mt=l.alternate;if((l.flags&1024)!==0)switch(l.tag){case 0:case 11:case 15:break;case 1:if(Mt!==null){var At=Mt.memoizedProps,an=Mt.memoizedState,Re=l.stateNode,Ce=Re.getSnapshotBeforeUpdate(l.elementType===l.type?At:Mn(l.type,At),an);Re.__reactInternalSnapshotBeforeUpdate=Ce}break;case 3:var De=l.stateNode.containerInfo;De.nodeType===1?De.textContent="":De.nodeType===9&&De.documentElement&&De.removeChild(De.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(x(163))}}catch(ot){vn(l,l.return,ot)}if(s=l.sibling,s!==null){s.return=l.return,Ct=s;break}Ct=l.return}return Mt=nd,nd=!1,Mt}function Ju(s,l,g){var w=l.updateQueue;if(w=w!==null?w.lastEffect:null,w!==null){var k=w=w.next;do{if((k.tag&s)===s){var D=k.destroy;k.destroy=void 0,D!==void 0&&ip(l,g,D)}k=k.next}while(k!==w)}}function eo(s,l){if(l=l.updateQueue,l=l!==null?l.lastEffect:null,l!==null){var g=l=l.next;do{if((g.tag&s)===s){var w=g.create;g.destroy=w()}g=g.next}while(g!==l)}}function ou(s){var l=s.ref;if(l!==null){var g=s.stateNode;switch(s.tag){case 5:s=g;break;default:s=g}typeof l=="function"?l(s):l.current=s}}function tf(s){var l=s.alternate;l!==null&&(s.alternate=null,tf(l)),s.child=null,s.deletions=null,s.sibling=null,s.tag===5&&(l=s.stateNode,l!==null&&(delete l[Li],delete l[fa],delete l[ma],delete l[Nu],delete l[ga])),s.stateNode=null,s.return=null,s.dependencies=null,s.memoizedProps=null,s.memoizedState=null,s.pendingProps=null,s.stateNode=null,s.updateQueue=null}function rf(s){return s.tag===5||s.tag===3||s.tag===4}function nf(s){e:for(;;){for(;s.sibling===null;){if(s.return===null||rf(s.return))return null;s=s.return}for(s.sibling.return=s.return,s=s.sibling;s.tag!==5&&s.tag!==6&&s.tag!==18;){if(s.flags&2||s.child===null||s.tag===4)continue e;s.child.return=s,s=s.child}if(!(s.flags&2))return s.stateNode}}function ap(s,l,g){var w=s.tag;if(w===5||w===6)s=s.stateNode,l?g.nodeType===8?g.parentNode.insertBefore(s,l):g.insertBefore(s,l):(g.nodeType===8?(l=g.parentNode,l.insertBefore(s,g)):(l=g,l.appendChild(s)),g=g._reactRootContainer,g!=null||l.onclick!==null||(l.onclick=sl));else if(w!==4&&(s=s.child,s!==null))for(ap(s,l,g),s=s.sibling;s!==null;)ap(s,l,g),s=s.sibling}function op(s,l,g){var w=s.tag;if(w===5||w===6)s=s.stateNode,l?g.insertBefore(s,l):g.appendChild(s);else if(w!==4&&(s=s.child,s!==null))for(op(s,l,g),s=s.sibling;s!==null;)op(s,l,g),s=s.sibling}var pi=null,Ca=!1;function to(s,l,g){for(g=g.child;g!==null;)sf(s,l,g),g=g.sibling}function sf(s,l,g){if(jn&&typeof jn.onCommitFiberUnmount=="function")try{jn.onCommitFiberUnmount(os,g)}catch{}switch(g.tag){case 5:di||Qu(g,l);case 6:var w=pi,k=Ca;pi=null,to(s,l,g),pi=w,Ca=k,pi!==null&&(Ca?(s=pi,g=g.stateNode,s.nodeType===8?s.parentNode.removeChild(g):s.removeChild(g)):pi.removeChild(g.stateNode));break;case 18:pi!==null&&(Ca?(s=pi,g=g.stateNode,s.nodeType===8?Ql(s.parentNode,g):s.nodeType===1&&Ql(s,g),zi(s)):Ql(pi,g.stateNode));break;case 4:w=pi,k=Ca,pi=g.stateNode.containerInfo,Ca=!0,to(s,l,g),pi=w,Ca=k;break;case 0:case 11:case 14:case 15:if(!di&&(w=g.updateQueue,w!==null&&(w=w.lastEffect,w!==null))){k=w=w.next;do{var D=k,W=D.destroy;D=D.tag,W!==void 0&&((D&2)!==0||(D&4)!==0)&&ip(g,l,W),k=k.next}while(k!==w)}to(s,l,g);break;case 1:if(!di&&(Qu(g,l),w=g.stateNode,typeof w.componentWillUnmount=="function"))try{w.props=g.memoizedProps,w.state=g.memoizedState,w.componentWillUnmount()}catch(he){vn(g,l,he)}to(s,l,g);break;case 21:to(s,l,g);break;case 22:g.mode&1?(di=(w=di)||g.memoizedState!==null,to(s,l,g),di=w):to(s,l,g);break;default:to(s,l,g)}}function lp(s){var l=s.updateQueue;if(l!==null){s.updateQueue=null;var g=s.stateNode;g===null&&(g=s.stateNode=new ef),l.forEach(function(w){var k=yh.bind(null,s,w);g.has(w)||(g.add(w),w.then(k,k))})}}function Vi(s,l){var g=l.deletions;if(g!==null)for(var w=0;wk&&(k=W),w&=~D}if(w=k,w=rr()-w,w=(120>w?120:480>w?480:1080>w?1080:1920>w?1920:3e3>w?3e3:4320>w?4320:1960*of(w/1960))-w,10s?16:s,_l===null)var w=!1;else{if(s=_l,_l=null,ph=0,(gr&6)!==0)throw Error(x(331));var k=gr;for(gr|=4,Ct=s.current;Ct!==null;){var D=Ct,W=D.child;if((Ct.flags&16)!==0){var he=D.deletions;if(he!==null){for(var ve=0;verr()-cp?hu(s,0):dh|=g),rs(s,l)}function mf(s,l){l===0&&((s.mode&1)===0?l=1:(l=Mu,Mu<<=1,(Mu&130023424)===0&&(Mu=4194304)));var g=Zn();s=Qi(s,l),s!==null&&(ao(s,l,g),rs(s,g))}function Xf(s){var l=s.memoizedState,g=0;l!==null&&(g=l.retryLane),mf(s,g)}function yh(s,l){var g=0;switch(s.tag){case 13:var w=s.stateNode,k=s.memoizedState;k!==null&&(g=k.retryLane);break;case 19:w=s.stateNode;break;default:throw Error(x(314))}w!==null&&w.delete(l),mf(s,g)}var kn;kn=function(s,l,g){if(s!==null)if(s.memoizedProps!==l.pendingProps||ni.current)es=!0;else{if((s.lanes&g)===0&&(l.flags&128)===0)return es=!1,Ui(s,l,g);es=(s.flags&131072)!==0}else es=!1,Xr&&(l.flags&1048576)!==0&&hr(l,dt,l.index);switch(l.lanes=0,l.tag){case 2:var w=l.type;Xu(s,l),s=l.pendingProps;var k=gs(l,Fn.current);$n(l,g),k=me(null,l,w,s,k,g);var D=oe();return l.flags|=1,typeof k=="object"&&k!==null&&typeof k.render=="function"&&k.$$typeof===void 0?(l.tag=1,l.memoizedState=null,l.updateQueue=null,Xn(w)?(D=!0,vi(l)):D=!1,l.memoizedState=k.state!==null&&k.state!==void 0?k.state:null,Ya(l),k.updater=vs,l.stateNode=k,k._reactInternals=l,su(l,w,s,g),l=Ku(null,l,w,!0,D,g)):(l.tag=0,Xr&&D&&ul(l),hi(null,l,k,g),l=l.child),l;case 16:w=l.elementType;e:{switch(Xu(s,l),s=l.pendingProps,k=w._init,w=k(w._payload),l.type=w,k=l.tag=ac(w),s=Mn(w,s),k){case 0:l=Qd(null,l,w,s,g);break e;case 1:l=Wu(null,l,w,s,g);break e;case 11:l=Wp(null,l,w,s,g);break e;case 14:l=ed(null,l,w,Mn(w.type,s),g);break e}throw Error(x(306,w,""))}return l;case 0:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Qd(s,l,w,k,g);case 1:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Wu(s,l,w,k,g);case 3:e:{if(nh(l),s===null)throw Error(x(387));w=l.pendingProps,D=l.memoizedState,k=D.element,qu(s,l),Gs(l,w,null,g);var W=l.memoizedState;if(w=W.element,D.isDehydrated)if(D={element:w,isDehydrated:!1,cache:W.cache,pendingSuspenseBoundaries:W.pendingSuspenseBoundaries,transitions:W.transitions},l.updateQueue.baseState=D,l.memoizedState=D,l.flags&256){k=Ta(Error(x(423)),l),l=Jd(s,l,w,g,k);break e}else if(w!==k){k=Ta(Error(x(424)),l),l=Jd(s,l,w,g,k);break e}else for(Fi=zr(l.stateNode.containerInfo.firstChild),Yn=l,Xr=!0,Oi=null,g=ru(l,null,w,g),l.child=g;g;)g.flags=g.flags&-3|4096,g=g.sibling;else{if(Sr(),w===k){l=xs(s,l,g);break e}hi(s,l,w,g)}l=l.child}return l;case 5:return h(l),s===null&&eu(l),w=l.type,k=l.pendingProps,D=s!==null?s.memoizedProps:null,W=k.children,Xl(w,k)?W=null:D!==null&&Xl(w,D)&&(l.flags|=32),au(s,l),hi(s,l,W,g),l.child;case 6:return s===null&&eu(l),null;case 13:return Kp(s,l,g);case 4:return i(l,l.stateNode.containerInfo),w=l.pendingProps,s===null?l.child=oi(l,null,w,g):hi(s,l,w,g),l.child;case 11:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Wp(s,l,w,k,g);case 7:return hi(s,l,l.pendingProps,g),l.child;case 8:return hi(s,l,l.pendingProps.children,g),l.child;case 12:return hi(s,l,l.pendingProps.children,g),l.child;case 10:e:{if(w=l.type._context,k=l.pendingProps,D=l.memoizedProps,W=k.value,dr(yt,w._currentValue),w._currentValue=W,D!==null)if(fs(D.value,W)){if(D.children===k.children&&!ni.current){l=xs(s,l,g);break e}}else for(D=l.child,D!==null&&(D.return=l);D!==null;){var he=D.dependencies;if(he!==null){W=D.child;for(var ve=he.firstContext;ve!==null;){if(ve.context===w){if(D.tag===1){ve=Si(-1,g&-g),ve.tag=2;var je=D.updateQueue;if(je!==null){je=je.shared;var rt=je.pending;rt===null?ve.next=ve:(ve.next=rt.next,rt.next=ve),je.pending=ve}}D.lanes|=g,ve=D.alternate,ve!==null&&(ve.lanes|=g),Gu(D.return,g,l),he.lanes|=g;break}ve=ve.next}}else if(D.tag===10)W=D.type===l.type?null:D.child;else if(D.tag===18){if(W=D.return,W===null)throw Error(x(341));W.lanes|=g,he=W.alternate,he!==null&&(he.lanes|=g),Gu(W,g,l),W=D.sibling}else W=D.child;if(W!==null)W.return=D;else for(W=D;W!==null;){if(W===l){W=null;break}if(D=W.sibling,D!==null){D.return=W.return,W=D;break}W=W.return}D=W}hi(s,l,k.children,g),l=l.child}return l;case 9:return k=l.type,w=l.pendingProps.children,$n(l,g),k=Yi(k),w=w(k),l.flags|=1,hi(s,l,w,g),l.child;case 14:return w=l.type,k=Mn(w,l.pendingProps),k=Mn(w.type,k),ed(s,l,w,k,g);case 15:return Xd(s,l,l.type,l.pendingProps,g);case 17:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Xu(s,l),l.tag=1,Xn(w)?(s=!0,vi(l)):s=!1,$n(l,g),Pi(l,w,k),su(l,w,k,g),Ku(null,l,w,!0,s,g);case 19:return tp(s,l,g);case 22:return Yd(s,l,g)}throw Error(x(156,l.tag))};function ud(s,l){return ia(s,l)}function gf(s,l,g,w){this.tag=s,this.key=g,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=l,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=w,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ss(s,l,g,w){return new gf(s,l,g,w)}function vh(s){return s=s.prototype,!(!s||!s.isReactComponent)}function ac(s){if(typeof s=="function")return vh(s)?1:0;if(s!=null){if(s=s.$$typeof,s===Br)return 11;if(s===xn)return 14}return 2}function Co(s,l){var g=s.alternate;return g===null?(g=Ss(s.tag,l,s.key,s.mode),g.elementType=s.elementType,g.type=s.type,g.stateNode=s.stateNode,g.alternate=s,s.alternate=g):(g.pendingProps=l,g.type=s.type,g.flags=0,g.subtreeFlags=0,g.deletions=null),g.flags=s.flags&14680064,g.childLanes=s.childLanes,g.lanes=s.lanes,g.child=s.child,g.memoizedProps=s.memoizedProps,g.memoizedState=s.memoizedState,g.updateQueue=s.updateQueue,l=s.dependencies,g.dependencies=l===null?null:{lanes:l.lanes,firstContext:l.firstContext},g.sibling=s.sibling,g.index=s.index,g.ref=s.ref,g}function cd(s,l,g,w,k,D){var W=2;if(w=s,typeof s=="function")vh(s)&&(W=1);else if(typeof s=="string")W=5;else e:switch(s){case Kt:return pu(g.children,k,D,l);case mt:W=8,k|=8;break;case Tt:return s=Ss(12,g,l,k|2),s.elementType=Tt,s.lanes=D,s;case mr:return s=Ss(13,g,l,k),s.elementType=mr,s.lanes=D,s;case Vr:return s=Ss(19,g,l,k),s.elementType=Vr,s.lanes=D,s;case pr:return hd(g,k,D,l);default:if(typeof s=="object"&&s!==null)switch(s.$$typeof){case xt:W=10;break e;case Cr:W=9;break e;case Br:W=11;break e;case xn:W=14;break e;case Mr:W=16,w=null;break e}throw Error(x(130,s==null?s:typeof s,""))}return l=Ss(W,g,l,k),l.elementType=s,l.type=w,l.lanes=D,l}function pu(s,l,g,w){return s=Ss(7,s,w,l),s.lanes=g,s}function hd(s,l,g,w){return s=Ss(22,s,w,l),s.elementType=pr,s.lanes=g,s.stateNode={isHidden:!1},s}function yp(s,l,g){return s=Ss(6,s,null,l),s.lanes=g,s}function vp(s,l,g){return l=Ss(4,s.children!==null?s.children:[],s.key,l),l.lanes=g,l.stateNode={containerInfo:s.containerInfo,pendingChildren:null,implementation:s.implementation},l}function Yf(s,l,g,w,k){this.tag=l,this.containerInfo=s,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Rl(0),this.expirationTimes=Rl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Rl(0),this.identifierPrefix=w,this.onRecoverableError=k,this.mutableSourceEagerHydrationData=null}function xp(s,l,g,w,k,D,W,he,ve){return s=new Yf(s,l,g,he,ve),l===1?(l=1,D===!0&&(l|=8)):l=0,D=Ss(3,null,null,l),s.current=D,D.stateNode=s,D.memoizedState={element:w,isDehydrated:g,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ya(D),s}function Qf(s,l,g){var w=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(b)}catch(_){console.error(_)}}return b(),ym.exports=iv(),ym.exports}var Kg;function av(){if(Kg)return kf;Kg=1;var b=sv();return kf.createRoot=b.createRoot,kf.hydrateRoot=b.hydrateRoot,kf}var ov=av();const lv=Hm(ov);/** + * react-router v7.9.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var Xg="popstate";function uv(b={}){function _(E,z){let{pathname:j,search:C,hash:u}=E.location;return Cm("",{pathname:j,search:C,hash:u},z.state&&z.state.usr||null,z.state&&z.state.key||"default")}function x(E,z){return typeof z=="string"?z:zp(z)}return hv(_,x,null,b)}function An(b,_){if(b===!1||b===null||typeof b>"u")throw new Error(_)}function Tl(b,_){if(!b){typeof console<"u"&&console.warn(_);try{throw new Error(_)}catch{}}}function cv(){return Math.random().toString(36).substring(2,10)}function Yg(b,_){return{usr:b.state,key:b.key,idx:_}}function Cm(b,_,x=null,E){return{pathname:typeof b=="string"?b:b.pathname,search:"",hash:"",...typeof _=="string"?Ld(_):_,state:x,key:_&&_.key||E||cv()}}function zp({pathname:b="/",search:_="",hash:x=""}){return _&&_!=="?"&&(b+=_.charAt(0)==="?"?_:"?"+_),x&&x!=="#"&&(b+=x.charAt(0)==="#"?x:"#"+x),b}function Ld(b){let _={};if(b){let x=b.indexOf("#");x>=0&&(_.hash=b.substring(x),b=b.substring(0,x));let E=b.indexOf("?");E>=0&&(_.search=b.substring(E),b=b.substring(0,E)),b&&(_.pathname=b)}return _}function hv(b,_,x,E={}){let{window:z=document.defaultView,v5Compat:j=!1}=E,C=z.history,u="POP",G=null,ee=ae();ee==null&&(ee=0,C.replaceState({...C.state,idx:ee},""));function ae(){return(C.state||{idx:null}).idx}function ce(){u="POP";let Fe=ae(),et=Fe==null?null:Fe-ee;ee=Fe,G&&G({action:u,location:Ue.location,delta:et})}function _e(Fe,et){u="PUSH";let nt=Cm(Ue.location,Fe,et);ee=ae()+1;let qe=Yg(nt,ee),We=Ue.createHref(nt);try{C.pushState(qe,"",We)}catch(Rt){if(Rt instanceof DOMException&&Rt.name==="DataCloneError")throw Rt;z.location.assign(We)}j&&G&&G({action:u,location:Ue.location,delta:1})}function te(Fe,et){u="REPLACE";let nt=Cm(Ue.location,Fe,et);ee=ae();let qe=Yg(nt,ee),We=Ue.createHref(nt);C.replaceState(qe,"",We),j&&G&&G({action:u,location:Ue.location,delta:0})}function Oe(Fe){return dv(Fe)}let Ue={get action(){return u},get location(){return b(z,C)},listen(Fe){if(G)throw new Error("A history only accepts one active listener");return z.addEventListener(Xg,ce),G=Fe,()=>{z.removeEventListener(Xg,ce),G=null}},createHref(Fe){return _(z,Fe)},createURL:Oe,encodeLocation(Fe){let et=Oe(Fe);return{pathname:et.pathname,search:et.search,hash:et.hash}},push:_e,replace:te,go(Fe){return C.go(Fe)}};return Ue}function dv(b,_=!1){let x="http://localhost";typeof window<"u"&&(x=window.location.origin!=="null"?window.location.origin:window.location.href),An(x,"No window.location.(origin|href) available to create URL");let E=typeof b=="string"?b:zp(b);return E=E.replace(/ $/,"%20"),!_&&E.startsWith("//")&&(E=x+E),new URL(E,x)}function $_(b,_,x="/"){return pv(b,_,x,!1)}function pv(b,_,x,E){let z=typeof _=="string"?Ld(_):_,j=Su(z.pathname||"/",x);if(j==null)return null;let C=Z_(b);fv(C);let u=null;for(let G=0;u==null&&G{let ae={relativePath:ee===void 0?C.path||"":ee,caseSensitive:C.caseSensitive===!0,childrenIndex:u,route:C};if(ae.relativePath.startsWith("/")){if(!ae.relativePath.startsWith(E)&&G)return;An(ae.relativePath.startsWith(E),`Absolute route path "${ae.relativePath}" nested under path "${E}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),ae.relativePath=ae.relativePath.slice(E.length)}let ce=bu([E,ae.relativePath]),_e=x.concat(ae);C.children&&C.children.length>0&&(An(C.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${ce}".`),Z_(C.children,_,_e,ce,G)),!(C.path==null&&!C.index)&&_.push({path:ce,score:bv(ce,C.index),routesMeta:_e})};return b.forEach((C,u)=>{var G;if(C.path===""||!((G=C.path)!=null&&G.includes("?")))j(C,u);else for(let ee of G_(C.path))j(C,u,!0,ee)}),_}function G_(b){let _=b.split("/");if(_.length===0)return[];let[x,...E]=_,z=x.endsWith("?"),j=x.replace(/\?$/,"");if(E.length===0)return z?[j,""]:[j];let C=G_(E.join("/")),u=[];return u.push(...C.map(G=>G===""?j:[j,G].join("/"))),z&&u.push(...C),u.map(G=>b.startsWith("/")&&G===""?"/":G)}function fv(b){b.sort((_,x)=>_.score!==x.score?x.score-_.score:wv(_.routesMeta.map(E=>E.childrenIndex),x.routesMeta.map(E=>E.childrenIndex)))}var mv=/^:[\w-]+$/,gv=3,_v=2,yv=1,vv=10,xv=-2,Qg=b=>b==="*";function bv(b,_){let x=b.split("/"),E=x.length;return x.some(Qg)&&(E+=xv),_&&(E+=_v),x.filter(z=>!Qg(z)).reduce((z,j)=>z+(mv.test(j)?gv:j===""?yv:vv),E)}function wv(b,_){return b.length===_.length&&b.slice(0,-1).every((E,z)=>E===_[z])?b[b.length-1]-_[_.length-1]:0}function Sv(b,_,x=!1){let{routesMeta:E}=b,z={},j="/",C=[];for(let u=0;u{if(ae==="*"){let Oe=u[_e]||"";C=j.slice(0,j.length-Oe.length).replace(/(.)\/+$/,"$1")}const te=u[_e];return ce&&!te?ee[ae]=void 0:ee[ae]=(te||"").replace(/%2F/g,"/"),ee},{}),pathname:j,pathnameBase:C,pattern:b}}function Tv(b,_=!1,x=!0){Tl(b==="*"||!b.endsWith("*")||b.endsWith("/*"),`Route path "${b}" will be treated as if it were "${b.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${b.replace(/\*$/,"/*")}".`);let E=[],z="^"+b.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(C,u,G)=>(E.push({paramName:u,isOptional:G!=null}),G?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return b.endsWith("*")?(E.push({paramName:"*"}),z+=b==="*"||b==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):x?z+="\\/*$":b!==""&&b!=="/"&&(z+="(?:(?=\\/|$))"),[new RegExp(z,_?void 0:"i"),E]}function Pv(b){try{return b.split("/").map(_=>decodeURIComponent(_).replace(/\//g,"%2F")).join("/")}catch(_){return Tl(!1,`The URL path "${b}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${_}).`),b}}function Su(b,_){if(_==="/")return b;if(!b.toLowerCase().startsWith(_.toLowerCase()))return null;let x=_.endsWith("/")?_.length-1:_.length,E=b.charAt(x);return E&&E!=="/"?null:b.slice(x)||"/"}function Cv(b,_="/"){let{pathname:x,search:E="",hash:z=""}=typeof b=="string"?Ld(b):b;return{pathname:x?x.startsWith("/")?x:Mv(x,_):_,search:kv(E),hash:Av(z)}}function Mv(b,_){let x=_.replace(/\/+$/,"").split("/");return b.split("/").forEach(z=>{z===".."?x.length>1&&x.pop():z!=="."&&x.push(z)}),x.length>1?x.join("/"):"/"}function bm(b,_,x,E){return`Cannot include a '${b}' character in a manually specified \`to.${_}\` field [${JSON.stringify(E)}]. Please separate it out to the \`to.${x}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Ev(b){return b.filter((_,x)=>x===0||_.route.path&&_.route.path.length>0)}function q_(b){let _=Ev(b);return _.map((x,E)=>E===_.length-1?x.pathname:x.pathnameBase)}function H_(b,_,x,E=!1){let z;typeof b=="string"?z=Ld(b):(z={...b},An(!z.pathname||!z.pathname.includes("?"),bm("?","pathname","search",z)),An(!z.pathname||!z.pathname.includes("#"),bm("#","pathname","hash",z)),An(!z.search||!z.search.includes("#"),bm("#","search","hash",z)));let j=b===""||z.pathname==="",C=j?"/":z.pathname,u;if(C==null)u=x;else{let ce=_.length-1;if(!E&&C.startsWith("..")){let _e=C.split("/");for(;_e[0]==="..";)_e.shift(),ce-=1;z.pathname=_e.join("/")}u=ce>=0?_[ce]:"/"}let G=Cv(z,u),ee=C&&C!=="/"&&C.endsWith("/"),ae=(j||C===".")&&x.endsWith("/");return!G.pathname.endsWith("/")&&(ee||ae)&&(G.pathname+="/"),G}var bu=b=>b.join("/").replace(/\/\/+/g,"/"),Iv=b=>b.replace(/\/+$/,"").replace(/^\/*/,"/"),kv=b=>!b||b==="?"?"":b.startsWith("?")?b:"?"+b,Av=b=>!b||b==="#"?"":b.startsWith("#")?b:"#"+b;function zv(b){return b!=null&&typeof b.status=="number"&&typeof b.statusText=="string"&&typeof b.internal=="boolean"&&"data"in b}var W_=["POST","PUT","PATCH","DELETE"];new Set(W_);var Rv=["GET",...W_];new Set(Rv);var Dd=Ee.createContext(null);Dd.displayName="DataRouter";var Gf=Ee.createContext(null);Gf.displayName="DataRouterState";Ee.createContext(!1);var K_=Ee.createContext({isTransitioning:!1});K_.displayName="ViewTransition";var Lv=Ee.createContext(new Map);Lv.displayName="Fetchers";var Dv=Ee.createContext(null);Dv.displayName="Await";var Pl=Ee.createContext(null);Pl.displayName="Navigation";var Up=Ee.createContext(null);Up.displayName="Location";var Cl=Ee.createContext({outlet:null,matches:[],isDataRoute:!1});Cl.displayName="Route";var Km=Ee.createContext(null);Km.displayName="RouteError";function Fv(b,{relative:_}={}){An(Vp(),"useHref() may be used only in the context of a component.");let{basename:x,navigator:E}=Ee.useContext(Pl),{hash:z,pathname:j,search:C}=$p(b,{relative:_}),u=j;return x!=="/"&&(u=j==="/"?x:bu([x,j])),E.createHref({pathname:u,search:C,hash:z})}function Vp(){return Ee.useContext(Up)!=null}function Uh(){return An(Vp(),"useLocation() may be used only in the context of a component."),Ee.useContext(Up).location}var X_="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Y_(b){Ee.useContext(Pl).static||Ee.useLayoutEffect(b)}function Ov(){let{isDataRoute:b}=Ee.useContext(Cl);return b?Yv():Bv()}function Bv(){An(Vp(),"useNavigate() may be used only in the context of a component.");let b=Ee.useContext(Dd),{basename:_,navigator:x}=Ee.useContext(Pl),{matches:E}=Ee.useContext(Cl),{pathname:z}=Uh(),j=JSON.stringify(q_(E)),C=Ee.useRef(!1);return Y_(()=>{C.current=!0}),Ee.useCallback((G,ee={})=>{if(Tl(C.current,X_),!C.current)return;if(typeof G=="number"){x.go(G);return}let ae=H_(G,JSON.parse(j),z,ee.relative==="path");b==null&&_!=="/"&&(ae.pathname=ae.pathname==="/"?_:bu([_,ae.pathname])),(ee.replace?x.replace:x.push)(ae,ee.state,ee)},[_,x,j,z,b])}Ee.createContext(null);function jv(){let{matches:b}=Ee.useContext(Cl),_=b[b.length-1];return _?_.params:{}}function $p(b,{relative:_}={}){let{matches:x}=Ee.useContext(Cl),{pathname:E}=Uh(),z=JSON.stringify(q_(x));return Ee.useMemo(()=>H_(b,JSON.parse(z),E,_==="path"),[b,z,E,_])}function Nv(b,_){return Q_(b,_)}function Q_(b,_,x,E,z){var nt;An(Vp(),"useRoutes() may be used only in the context of a component.");let{navigator:j}=Ee.useContext(Pl),{matches:C}=Ee.useContext(Cl),u=C[C.length-1],G=u?u.params:{},ee=u?u.pathname:"/",ae=u?u.pathnameBase:"/",ce=u&&u.route;{let qe=ce&&ce.path||"";J_(ee,!ce||qe.endsWith("*")||qe.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${ee}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let _e=Uh(),te;if(_){let qe=typeof _=="string"?Ld(_):_;An(ae==="/"||((nt=qe.pathname)==null?void 0:nt.startsWith(ae)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${ae}" but pathname "${qe.pathname}" was given in the \`location\` prop.`),te=qe}else te=_e;let Oe=te.pathname||"/",Ue=Oe;if(ae!=="/"){let qe=ae.replace(/^\//,"").split("/");Ue="/"+Oe.replace(/^\//,"").split("/").slice(qe.length).join("/")}let Fe=$_(b,{pathname:Ue});Tl(ce||Fe!=null,`No routes matched location "${te.pathname}${te.search}${te.hash}" `),Tl(Fe==null||Fe[Fe.length-1].route.element!==void 0||Fe[Fe.length-1].route.Component!==void 0||Fe[Fe.length-1].route.lazy!==void 0,`Matched leaf route at location "${te.pathname}${te.search}${te.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let et=Gv(Fe&&Fe.map(qe=>Object.assign({},qe,{params:Object.assign({},G,qe.params),pathname:bu([ae,j.encodeLocation?j.encodeLocation(qe.pathname).pathname:qe.pathname]),pathnameBase:qe.pathnameBase==="/"?ae:bu([ae,j.encodeLocation?j.encodeLocation(qe.pathnameBase).pathname:qe.pathnameBase])})),C,x,E,z);return _&&et?Ee.createElement(Up.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...te},navigationType:"POP"}},et):et}function Uv(){let b=Xv(),_=zv(b)?`${b.status} ${b.statusText}`:b instanceof Error?b.message:JSON.stringify(b),x=b instanceof Error?b.stack:null,E="rgba(200,200,200, 0.5)",z={padding:"0.5rem",backgroundColor:E},j={padding:"2px 4px",backgroundColor:E},C=null;return console.error("Error handled by React Router default ErrorBoundary:",b),C=Ee.createElement(Ee.Fragment,null,Ee.createElement("p",null,"💿 Hey developer 👋"),Ee.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",Ee.createElement("code",{style:j},"ErrorBoundary")," or"," ",Ee.createElement("code",{style:j},"errorElement")," prop on your route.")),Ee.createElement(Ee.Fragment,null,Ee.createElement("h2",null,"Unexpected Application Error!"),Ee.createElement("h3",{style:{fontStyle:"italic"}},_),x?Ee.createElement("pre",{style:z},x):null,C)}var Vv=Ee.createElement(Uv,null),$v=class extends Ee.Component{constructor(b){super(b),this.state={location:b.location,revalidation:b.revalidation,error:b.error}}static getDerivedStateFromError(b){return{error:b}}static getDerivedStateFromProps(b,_){return _.location!==b.location||_.revalidation!=="idle"&&b.revalidation==="idle"?{error:b.error,location:b.location,revalidation:b.revalidation}:{error:b.error!==void 0?b.error:_.error,location:_.location,revalidation:b.revalidation||_.revalidation}}componentDidCatch(b,_){this.props.unstable_onError?this.props.unstable_onError(b,_):console.error("React Router caught the following error during render",b)}render(){return this.state.error!==void 0?Ee.createElement(Cl.Provider,{value:this.props.routeContext},Ee.createElement(Km.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function Zv({routeContext:b,match:_,children:x}){let E=Ee.useContext(Dd);return E&&E.static&&E.staticContext&&(_.route.errorElement||_.route.ErrorBoundary)&&(E.staticContext._deepestRenderedBoundaryId=_.route.id),Ee.createElement(Cl.Provider,{value:b},x)}function Gv(b,_=[],x=null,E=null,z=null){if(b==null){if(!x)return null;if(x.errors)b=x.matches;else if(_.length===0&&!x.initialized&&x.matches.length>0)b=x.matches;else return null}let j=b,C=x==null?void 0:x.errors;if(C!=null){let ee=j.findIndex(ae=>ae.route.id&&(C==null?void 0:C[ae.route.id])!==void 0);An(ee>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(C).join(",")}`),j=j.slice(0,Math.min(j.length,ee+1))}let u=!1,G=-1;if(x)for(let ee=0;ee=0?j=j.slice(0,G+1):j=[j[0]];break}}}return j.reduceRight((ee,ae,ce)=>{let _e,te=!1,Oe=null,Ue=null;x&&(_e=C&&ae.route.id?C[ae.route.id]:void 0,Oe=ae.route.errorElement||Vv,u&&(G<0&&ce===0?(J_("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),te=!0,Ue=null):G===ce&&(te=!0,Ue=ae.route.hydrateFallbackElement||null)));let Fe=_.concat(j.slice(0,ce+1)),et=()=>{let nt;return _e?nt=Oe:te?nt=Ue:ae.route.Component?nt=Ee.createElement(ae.route.Component,null):ae.route.element?nt=ae.route.element:nt=ee,Ee.createElement(Zv,{match:ae,routeContext:{outlet:ee,matches:Fe,isDataRoute:x!=null},children:nt})};return x&&(ae.route.ErrorBoundary||ae.route.errorElement||ce===0)?Ee.createElement($v,{location:x.location,revalidation:x.revalidation,component:Oe,error:_e,children:et(),routeContext:{outlet:null,matches:Fe,isDataRoute:!0},unstable_onError:E}):et()},null)}function Xm(b){return`${b} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qv(b){let _=Ee.useContext(Dd);return An(_,Xm(b)),_}function Hv(b){let _=Ee.useContext(Gf);return An(_,Xm(b)),_}function Wv(b){let _=Ee.useContext(Cl);return An(_,Xm(b)),_}function Ym(b){let _=Wv(b),x=_.matches[_.matches.length-1];return An(x.route.id,`${b} can only be used on routes that contain a unique "id"`),x.route.id}function Kv(){return Ym("useRouteId")}function Xv(){var E;let b=Ee.useContext(Km),_=Hv("useRouteError"),x=Ym("useRouteError");return b!==void 0?b:(E=_.errors)==null?void 0:E[x]}function Yv(){let{router:b}=qv("useNavigate"),_=Ym("useNavigate"),x=Ee.useRef(!1);return Y_(()=>{x.current=!0}),Ee.useCallback(async(z,j={})=>{Tl(x.current,X_),x.current&&(typeof z=="number"?b.navigate(z):await b.navigate(z,{fromRouteId:_,...j}))},[b,_])}var Jg={};function J_(b,_,x){!_&&!Jg[b]&&(Jg[b]=!0,Tl(!1,x))}Ee.memo(Qv);function Qv({routes:b,future:_,state:x,unstable_onError:E}){return Q_(b,void 0,x,E,_)}function Mm(b){An(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Jv({basename:b="/",children:_=null,location:x,navigationType:E="POP",navigator:z,static:j=!1}){An(!Vp(),"You cannot render a inside another . You should never have more than one in your app.");let C=b.replace(/^\/*/,"/"),u=Ee.useMemo(()=>({basename:C,navigator:z,static:j,future:{}}),[C,z,j]);typeof x=="string"&&(x=Ld(x));let{pathname:G="/",search:ee="",hash:ae="",state:ce=null,key:_e="default"}=x,te=Ee.useMemo(()=>{let Oe=Su(G,C);return Oe==null?null:{location:{pathname:Oe,search:ee,hash:ae,state:ce,key:_e},navigationType:E}},[C,G,ee,ae,ce,_e,E]);return Tl(te!=null,` is not able to match the URL "${G}${ee}${ae}" because it does not start with the basename, so the won't render anything.`),te==null?null:Ee.createElement(Pl.Provider,{value:u},Ee.createElement(Up.Provider,{children:_,value:te}))}function ex({children:b,location:_}){return Nv(Em(b),_)}function Em(b,_=[]){let x=[];return Ee.Children.forEach(b,(E,z)=>{if(!Ee.isValidElement(E))return;let j=[..._,z];if(E.type===Ee.Fragment){x.push.apply(x,Em(E.props.children,j));return}An(E.type===Mm,`[${typeof E.type=="string"?E.type:E.type.name}] is not a component. All component children of must be a or `),An(!E.props.index||!E.props.children,"An index route cannot have child routes.");let C={id:E.props.id||j.join("-"),caseSensitive:E.props.caseSensitive,element:E.props.element,Component:E.props.Component,index:E.props.index,path:E.props.path,loader:E.props.loader,action:E.props.action,hydrateFallbackElement:E.props.hydrateFallbackElement,HydrateFallback:E.props.HydrateFallback,errorElement:E.props.errorElement,ErrorBoundary:E.props.ErrorBoundary,hasErrorBoundary:E.props.hasErrorBoundary===!0||E.props.ErrorBoundary!=null||E.props.errorElement!=null,shouldRevalidate:E.props.shouldRevalidate,handle:E.props.handle,lazy:E.props.lazy};E.props.children&&(C.children=Em(E.props.children,j)),x.push(C)}),x}var Lf="get",Df="application/x-www-form-urlencoded";function qf(b){return b!=null&&typeof b.tagName=="string"}function tx(b){return qf(b)&&b.tagName.toLowerCase()==="button"}function rx(b){return qf(b)&&b.tagName.toLowerCase()==="form"}function nx(b){return qf(b)&&b.tagName.toLowerCase()==="input"}function ix(b){return!!(b.metaKey||b.altKey||b.ctrlKey||b.shiftKey)}function sx(b,_){return b.button===0&&(!_||_==="_self")&&!ix(b)}var Af=null;function ax(){if(Af===null)try{new FormData(document.createElement("form"),0),Af=!1}catch{Af=!0}return Af}var ox=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function wm(b){return b!=null&&!ox.has(b)?(Tl(!1,`"${b}" is not a valid \`encType\` for \`
            \`/\`\` and will default to "${Df}"`),null):b}function lx(b,_){let x,E,z,j,C;if(rx(b)){let u=b.getAttribute("action");E=u?Su(u,_):null,x=b.getAttribute("method")||Lf,z=wm(b.getAttribute("enctype"))||Df,j=new FormData(b)}else if(tx(b)||nx(b)&&(b.type==="submit"||b.type==="image")){let u=b.form;if(u==null)throw new Error('Cannot submit a - {/* Flyout menu */} {menuOpen && (
            - {/* Title BADA */} BADA - {/* Subtitle stacked next to it */}
            EU Beaches
            @@ -110,16 +99,10 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) {
            {userOpen && ( @@ -160,7 +143,7 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) {
            - {/* Search bar (now actually filters via global store) */} + {/* Search bar */}
            >; + export default ReactComponent; +} diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 5a33944a9b..ba3d0846cb 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,7 +1,8 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import svgr from "vite-plugin-svgr"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [react(), svgr()], +}); From 8b3afe6a18b8681235b6025fe9207cca25f946e0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:33:21 +0200 Subject: [PATCH 127/215] chore: add .env.example files for backend and frontend with safe placeholders --- backend/.env.example | 17 +++++++++++++++++ frontend/.env.example | 12 ++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 backend/.env.example create mode 100644 frontend/.env.example diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000000..f34be0d1b4 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,17 @@ +# Server +PORT=3000 +NODE_ENV=development + +# Database +MONGODB_URI=mongodb+srv://:@/?retryWrites=true&w=majority + +# Auth +JWT_SECRET=replace-with-a-random-secret + +# CORS +ALLOWED_ORIGIN=http://localhost:5173 + +# HaV API +HAV_BASE_URL=https://badplatsen.havochvatten.se/badplatsen/api +HAV_V2_BASE=https://api.havochvatten.se/bathingwaters/v2 +HAV_USER_AGENT=BADA-App/1.0 (contact: my@email) \ No newline at end of file diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000000..3c8276379c --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,12 @@ +# Backend API +VITE_API_BASE=http://localhost:3000/api + +# Maptiler (optional: needed for MapLibre styles) +VITE_MAPTILER_KEY=your-maptiler-key + +# Map styles +VITE_MAP_STYLE_LIGHT=https://api.maptiler.com/maps/basic-v2/style.json?key=${VITE_MAPTILER_KEY} +VITE_MAP_STYLE_DARK=https://api.maptiler.com/maps/darkmatter/style.json?key=${VITE_MAPTILER_KEY} + +# Default language +VITE_DEFAULT_LANG=sv \ No newline at end of file From d626463cd2c4668490ef2a8c97d02558f797d414 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:56:20 +0200 Subject: [PATCH 128/215] docs: update README --- frontend/README.md | 147 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 5 deletions(-) diff --git a/frontend/README.md b/frontend/README.md index 5cdb1d9cf3..bbf803e0c9 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,8 +1,145 @@ -# Frontend part of Final Project +# 🏖️ BADA – Find Safe Beaches in Sweden -This boilerplate is designed to give you a head start in your React projects, with a focus on understanding the structure and components. As a student of Technigo, you'll find this guide helpful in navigating and utilizing the repository. +**BADA** helps beachgoers and families in Sweden find safe, EU-classified bathing waters with real-time quality updates. +It replaces outdated or clunky websites with a **clean, mobile-friendly experience** where you can browse nearby beaches on a map, check water quality, and save your favourites. -## Getting Started +--- -1. Install the required dependencies using `npm install`. -2. Start the development server using `npm run dev`. \ No newline at end of file +## ✨ Features + +- 🗺 **Map of all EU-classified beaches in Sweden** (MapLibre + OpenStreetMap) +- 📍 **Find the nearest beach** using your device’s location +- 🔬 **View water quality, classification, and recent test results** (data from HaV) +- ❤️ **Create an account and save favourite beaches** to your profile +- 🌗 **Dark mode** and responsive design (mobile → desktop) +- 🌐 **Multi-language support** (Swedish / English) + +--- + +## 🚀 Tech Stack + +**Frontend** + +- React 18 + Vite + TypeScript +- React Router +- Zustand (global state) +- TanStack Query (server state & caching) +- Tailwind CSS +- i18next (translations) +- MapLibre GL (maps) +- Custom React Hooks (geolocation, dark mode, outside click) + +**Backend** + +- Node.js + Express +- MongoDB + Mongoose +- JWT Authentication +- Zod (validation) +- In-memory caching for HaV API responses + +**External APIs** + +- [HaV Bathing Waters API](https://badplatsen.havochvatten.se/) (official Swedish Agency for Marine and Water Management) +- [MapTiler](https://www.maptiler.com/) (map styles) +- _(Planned)_ OpenWeatherMap for weather and water temperature + +--- + +## 📸 Screenshots + +_(Add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ + +--- + +## 🛠 Installation & Setup + +Clone the repo and install dependencies: + +```bash +git clone https://github.com/govargas/bada.git +cd bada + +``` + +**Backend** +cd backend +cp .env.example .env.local # then fill in your values +npm install +npm run dev + +Backend runs on http://localhost:3000 + +**Frontend** +cd frontend +cp .env.example .env.local # then fill in your values +npm install +npm run dev + +Frontend runs on http://localhost:5173 + +--- + +## 🔑 Environment Variables + +See .env.example in both backend/ and frontend/. +Fill in with your own values (MongoDB Atlas, JWT secret, MapTiler key). + +--- + +## 👤 Test User Credentials + +Use these to try the app without registering: +Email: test@bada.app +Password: Test1234 + +This account already has some favourite beaches saved. + +--- + +## 🌍 Deployment + + • Frontend: Deployed on Vercel + • Backend: Deployed on Vercel + +(Will replace with actual links when deployed) + +--- + +## ✅ Requirements Checklist + + • React frontend + • Node.js + Express backend + • MongoDB database + • Authentication (JWT) + • React Router navigation + • Global state management (Zustand) + • ≥2 external libraries (TanStack Query, MapLibre, react-hook-form, i18next) + • Custom React hooks + • Responsive (320px → 1600px+) + • Accessibility & Lighthouse 100% (AA compliant) + • Clean Code practices + +--- + +## 🧭 Roadmap + + • Add drag-and-drop sorting for favourites + • Allow notes/tips per beach (e.g. “good for kids”) + • Integrate OpenWeatherMap for weather & water temperature + • Accessibility extras (reduced motion, ARIA live regions) + • Polish with animations and micro-interactions + +--- + +## 💡 Inspiration & Credits + + • Data from the Swedish Agency for Marine and Water Management (HaV) + • Maps powered by OpenStreetMap + MapTiler + • Built during the Technigo Fullstack JavaScript Bootcamp (2025) + +--- + +## 👨‍💻 Author + +Created by Talo Vargas +2025 From cf91ea4680fe18e4317427ef0a334542c619cadc Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:56:53 +0200 Subject: [PATCH 129/215] docs: add tech stack and deployment badges to README header --- frontend/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/README.md b/frontend/README.md index bbf803e0c9..c287bc7b4f 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,5 +1,15 @@ # 🏖️ BADA – Find Safe Beaches in Sweden +![React](https://img.shields.io/badge/Frontend-React-61DAFB?logo=react&logoColor=white) +![Express](https://img.shields.io/badge/Backend-Express-000000?logo=express&logoColor=white) +![MongoDB](https://img.shields.io/badge/Database-MongoDB-47A248?logo=mongodb&logoColor=white) +![TypeScript](https://img.shields.io/badge/Code-TypeScript-3178C6?logo=typescript&logoColor=white) +![TailwindCSS](https://img.shields.io/badge/UI-Tailwind_CSS-38B2AC?logo=tailwind-css&logoColor=white) +![MapLibre](https://img.shields.io/badge/Maps-MapLibre-4264FB?logo=openstreetmap&logoColor=white) +![i18next](https://img.shields.io/badge/Translations-i18next-26A69A?logo=i18next&logoColor=white) +![TanStack Query](https://img.shields.io/badge/State-TanStack_Query-FF4154?logo=reactquery&logoColor=white) +![Deployed](https://img.shields.io/badge/Deployed-Vercel-000000?logo=vercel&logoColor=white) + **BADA** helps beachgoers and families in Sweden find safe, EU-classified bathing waters with real-time quality updates. It replaces outdated or clunky websites with a **clean, mobile-friendly experience** where you can browse nearby beaches on a map, check water quality, and save your favourites. From 1f89c66cc6015df20054244f4907ed8dbefbd130 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:58:26 +0200 Subject: [PATCH 130/215] docs: move README to root --- README.md | 156 +++++++++++++++++++++++++++++++++++++++++++-- frontend/README.md | 155 -------------------------------------------- 2 files changed, 149 insertions(+), 162 deletions(-) delete mode 100644 frontend/README.md diff --git a/README.md b/README.md index 31466b54c2..c287bc7b4f 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,155 @@ -# Final Project +# 🏖️ BADA – Find Safe Beaches in Sweden -Replace this readme with your own information about your project. +![React](https://img.shields.io/badge/Frontend-React-61DAFB?logo=react&logoColor=white) +![Express](https://img.shields.io/badge/Backend-Express-000000?logo=express&logoColor=white) +![MongoDB](https://img.shields.io/badge/Database-MongoDB-47A248?logo=mongodb&logoColor=white) +![TypeScript](https://img.shields.io/badge/Code-TypeScript-3178C6?logo=typescript&logoColor=white) +![TailwindCSS](https://img.shields.io/badge/UI-Tailwind_CSS-38B2AC?logo=tailwind-css&logoColor=white) +![MapLibre](https://img.shields.io/badge/Maps-MapLibre-4264FB?logo=openstreetmap&logoColor=white) +![i18next](https://img.shields.io/badge/Translations-i18next-26A69A?logo=i18next&logoColor=white) +![TanStack Query](https://img.shields.io/badge/State-TanStack_Query-FF4154?logo=reactquery&logoColor=white) +![Deployed](https://img.shields.io/badge/Deployed-Vercel-000000?logo=vercel&logoColor=white) -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. +**BADA** helps beachgoers and families in Sweden find safe, EU-classified bathing waters with real-time quality updates. +It replaces outdated or clunky websites with a **clean, mobile-friendly experience** where you can browse nearby beaches on a map, check water quality, and save your favourites. -## The problem +--- -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? +## ✨ Features -## View it live +- 🗺 **Map of all EU-classified beaches in Sweden** (MapLibre + OpenStreetMap) +- 📍 **Find the nearest beach** using your device’s location +- 🔬 **View water quality, classification, and recent test results** (data from HaV) +- ❤️ **Create an account and save favourite beaches** to your profile +- 🌗 **Dark mode** and responsive design (mobile → desktop) +- 🌐 **Multi-language support** (Swedish / English) -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. \ No newline at end of file +--- + +## 🚀 Tech Stack + +**Frontend** + +- React 18 + Vite + TypeScript +- React Router +- Zustand (global state) +- TanStack Query (server state & caching) +- Tailwind CSS +- i18next (translations) +- MapLibre GL (maps) +- Custom React Hooks (geolocation, dark mode, outside click) + +**Backend** + +- Node.js + Express +- MongoDB + Mongoose +- JWT Authentication +- Zod (validation) +- In-memory caching for HaV API responses + +**External APIs** + +- [HaV Bathing Waters API](https://badplatsen.havochvatten.se/) (official Swedish Agency for Marine and Water Management) +- [MapTiler](https://www.maptiler.com/) (map styles) +- _(Planned)_ OpenWeatherMap for weather and water temperature + +--- + +## 📸 Screenshots + +_(Add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ + +--- + +## 🛠 Installation & Setup + +Clone the repo and install dependencies: + +```bash +git clone https://github.com/govargas/bada.git +cd bada + +``` + +**Backend** +cd backend +cp .env.example .env.local # then fill in your values +npm install +npm run dev + +Backend runs on http://localhost:3000 + +**Frontend** +cd frontend +cp .env.example .env.local # then fill in your values +npm install +npm run dev + +Frontend runs on http://localhost:5173 + +--- + +## 🔑 Environment Variables + +See .env.example in both backend/ and frontend/. +Fill in with your own values (MongoDB Atlas, JWT secret, MapTiler key). + +--- + +## 👤 Test User Credentials + +Use these to try the app without registering: +Email: test@bada.app +Password: Test1234 + +This account already has some favourite beaches saved. + +--- + +## 🌍 Deployment + + • Frontend: Deployed on Vercel + • Backend: Deployed on Vercel + +(Will replace with actual links when deployed) + +--- + +## ✅ Requirements Checklist + + • React frontend + • Node.js + Express backend + • MongoDB database + • Authentication (JWT) + • React Router navigation + • Global state management (Zustand) + • ≥2 external libraries (TanStack Query, MapLibre, react-hook-form, i18next) + • Custom React hooks + • Responsive (320px → 1600px+) + • Accessibility & Lighthouse 100% (AA compliant) + • Clean Code practices + +--- + +## 🧭 Roadmap + + • Add drag-and-drop sorting for favourites + • Allow notes/tips per beach (e.g. “good for kids”) + • Integrate OpenWeatherMap for weather & water temperature + • Accessibility extras (reduced motion, ARIA live regions) + • Polish with animations and micro-interactions + +--- + +## 💡 Inspiration & Credits + + • Data from the Swedish Agency for Marine and Water Management (HaV) + • Maps powered by OpenStreetMap + MapTiler + • Built during the Technigo Fullstack JavaScript Bootcamp (2025) + +--- + +## 👨‍💻 Author + +Created by Talo Vargas +2025 diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index c287bc7b4f..0000000000 --- a/frontend/README.md +++ /dev/null @@ -1,155 +0,0 @@ -# 🏖️ BADA – Find Safe Beaches in Sweden - -![React](https://img.shields.io/badge/Frontend-React-61DAFB?logo=react&logoColor=white) -![Express](https://img.shields.io/badge/Backend-Express-000000?logo=express&logoColor=white) -![MongoDB](https://img.shields.io/badge/Database-MongoDB-47A248?logo=mongodb&logoColor=white) -![TypeScript](https://img.shields.io/badge/Code-TypeScript-3178C6?logo=typescript&logoColor=white) -![TailwindCSS](https://img.shields.io/badge/UI-Tailwind_CSS-38B2AC?logo=tailwind-css&logoColor=white) -![MapLibre](https://img.shields.io/badge/Maps-MapLibre-4264FB?logo=openstreetmap&logoColor=white) -![i18next](https://img.shields.io/badge/Translations-i18next-26A69A?logo=i18next&logoColor=white) -![TanStack Query](https://img.shields.io/badge/State-TanStack_Query-FF4154?logo=reactquery&logoColor=white) -![Deployed](https://img.shields.io/badge/Deployed-Vercel-000000?logo=vercel&logoColor=white) - -**BADA** helps beachgoers and families in Sweden find safe, EU-classified bathing waters with real-time quality updates. -It replaces outdated or clunky websites with a **clean, mobile-friendly experience** where you can browse nearby beaches on a map, check water quality, and save your favourites. - ---- - -## ✨ Features - -- 🗺 **Map of all EU-classified beaches in Sweden** (MapLibre + OpenStreetMap) -- 📍 **Find the nearest beach** using your device’s location -- 🔬 **View water quality, classification, and recent test results** (data from HaV) -- ❤️ **Create an account and save favourite beaches** to your profile -- 🌗 **Dark mode** and responsive design (mobile → desktop) -- 🌐 **Multi-language support** (Swedish / English) - ---- - -## 🚀 Tech Stack - -**Frontend** - -- React 18 + Vite + TypeScript -- React Router -- Zustand (global state) -- TanStack Query (server state & caching) -- Tailwind CSS -- i18next (translations) -- MapLibre GL (maps) -- Custom React Hooks (geolocation, dark mode, outside click) - -**Backend** - -- Node.js + Express -- MongoDB + Mongoose -- JWT Authentication -- Zod (validation) -- In-memory caching for HaV API responses - -**External APIs** - -- [HaV Bathing Waters API](https://badplatsen.havochvatten.se/) (official Swedish Agency for Marine and Water Management) -- [MapTiler](https://www.maptiler.com/) (map styles) -- _(Planned)_ OpenWeatherMap for weather and water temperature - ---- - -## 📸 Screenshots - -_(Add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ - ---- - -## 🛠 Installation & Setup - -Clone the repo and install dependencies: - -```bash -git clone https://github.com/govargas/bada.git -cd bada - -``` - -**Backend** -cd backend -cp .env.example .env.local # then fill in your values -npm install -npm run dev - -Backend runs on http://localhost:3000 - -**Frontend** -cd frontend -cp .env.example .env.local # then fill in your values -npm install -npm run dev - -Frontend runs on http://localhost:5173 - ---- - -## 🔑 Environment Variables - -See .env.example in both backend/ and frontend/. -Fill in with your own values (MongoDB Atlas, JWT secret, MapTiler key). - ---- - -## 👤 Test User Credentials - -Use these to try the app without registering: -Email: test@bada.app -Password: Test1234 - -This account already has some favourite beaches saved. - ---- - -## 🌍 Deployment - - • Frontend: Deployed on Vercel - • Backend: Deployed on Vercel - -(Will replace with actual links when deployed) - ---- - -## ✅ Requirements Checklist - - • React frontend - • Node.js + Express backend - • MongoDB database - • Authentication (JWT) - • React Router navigation - • Global state management (Zustand) - • ≥2 external libraries (TanStack Query, MapLibre, react-hook-form, i18next) - • Custom React hooks - • Responsive (320px → 1600px+) - • Accessibility & Lighthouse 100% (AA compliant) - • Clean Code practices - ---- - -## 🧭 Roadmap - - • Add drag-and-drop sorting for favourites - • Allow notes/tips per beach (e.g. “good for kids”) - • Integrate OpenWeatherMap for weather & water temperature - • Accessibility extras (reduced motion, ARIA live regions) - • Polish with animations and micro-interactions - ---- - -## 💡 Inspiration & Credits - - • Data from the Swedish Agency for Marine and Water Management (HaV) - • Maps powered by OpenStreetMap + MapTiler - • Built during the Technigo Fullstack JavaScript Bootcamp (2025) - ---- - -## 👨‍💻 Author - -Created by Talo Vargas -2025 From 5e35c12bc4a04e20bce1b34ab2210a3d63f811ff Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:05:14 +0200 Subject: [PATCH 131/215] docs: update README --- README.md | 64 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index c287bc7b4f..3643ef570e 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ It replaces outdated or clunky websites with a **clean, mobile-friendly experien ## 📸 Screenshots -_(Add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ +_(Will add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ --- @@ -72,34 +72,43 @@ cd bada ``` **Backend** + +```bash cd backend cp .env.example .env.local # then fill in your values npm install npm run dev +``` + Backend runs on http://localhost:3000 **Frontend** + +```bash cd frontend cp .env.example .env.local # then fill in your values npm install npm run dev +``` + Frontend runs on http://localhost:5173 --- ## 🔑 Environment Variables -See .env.example in both backend/ and frontend/. -Fill in with your own values (MongoDB Atlas, JWT secret, MapTiler key). +- See .env.example in both backend/ and frontend/. +- Fill in with your own values (MongoDB Atlas, JWT secret, MapTiler key). --- ## 👤 Test User Credentials -Use these to try the app without registering: -Email: test@bada.app +- Use these to try the app without registering: + Email: test@bada.app + Password: Test1234 This account already has some favourite beaches saved. @@ -108,8 +117,8 @@ This account already has some favourite beaches saved. ## 🌍 Deployment - • Frontend: Deployed on Vercel - • Backend: Deployed on Vercel +- Frontend: Deployed on Vercel +- Backend: Deployed on Vercel (Will replace with actual links when deployed) @@ -117,39 +126,38 @@ This account already has some favourite beaches saved. ## ✅ Requirements Checklist - • React frontend - • Node.js + Express backend - • MongoDB database - • Authentication (JWT) - • React Router navigation - • Global state management (Zustand) - • ≥2 external libraries (TanStack Query, MapLibre, react-hook-form, i18next) - • Custom React hooks - • Responsive (320px → 1600px+) - • Accessibility & Lighthouse 100% (AA compliant) - • Clean Code practices +- React frontend +- Node.js + Express backend +- MongoDB database +- Authentication (JWT) +- React Router navigation +- Global state management (Zustand) +- ≥2 external libraries (TanStack Query, MapLibre, react-hook-form, i18next) +- Custom React hooks +- Responsive (320px → 1600px+) +- Accessibility & Lighthouse 100% (AA compliant) +- Clean Code practices --- ## 🧭 Roadmap - • Add drag-and-drop sorting for favourites - • Allow notes/tips per beach (e.g. “good for kids”) - • Integrate OpenWeatherMap for weather & water temperature - • Accessibility extras (reduced motion, ARIA live regions) - • Polish with animations and micro-interactions +- Add drag-and-drop sorting for favourites +- Allow notes/tips per beach (e.g. “good for kids”) +- Integrate OpenWeatherMap for weather & water temperature +- Accessibility extras (reduced motion, ARIA live regions) +- Polish with animations and micro-interactions --- ## 💡 Inspiration & Credits - • Data from the Swedish Agency for Marine and Water Management (HaV) - • Maps powered by OpenStreetMap + MapTiler - • Built during the Technigo Fullstack JavaScript Bootcamp (2025) +- Data from the Swedish Agency for Marine and Water Management (HaV) +- Maps powered by OpenStreetMap + MapTiler +- Built during the Technigo Fullstack JavaScript Bootcamp (2025) --- ## 👨‍💻 Author -Created by Talo Vargas -2025 +Created by Talo Vargas, 2025 From 015c1fa1387b6ff7dabc1ac21887ff0dc8626d8e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:06:45 +0200 Subject: [PATCH 132/215] update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3643ef570e..8ef8dc8816 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,8 @@ Frontend runs on http://localhost:5173 ## 👤 Test User Credentials - Use these to try the app without registering: - Email: test@bada.app + +Email: test@bada.app Password: Test1234 From 5d088ab0756e9c9469e4fe9c419327b10ad883ca Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:23:43 +0200 Subject: [PATCH 133/215] feat(auth): add zustand auth store with localStorage hydration --- frontend/src/store/auth.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frontend/src/store/auth.ts diff --git a/frontend/src/store/auth.ts b/frontend/src/store/auth.ts new file mode 100644 index 0000000000..ae65c08935 --- /dev/null +++ b/frontend/src/store/auth.ts @@ -0,0 +1,36 @@ +import { create } from "zustand"; + +type AuthState = { + token: string | null; + setToken: (t: string | null) => void; + clearToken: () => void; +}; + +const TOKEN_KEY = "bada_token"; + +export const useAuth = create((set) => { + // hydrate from localStorage on first run + const initialToken = + typeof window !== "undefined" + ? window.localStorage.getItem(TOKEN_KEY) + : null; + + return { + token: initialToken, + setToken: (t) => { + if (t) { + window.localStorage.setItem(TOKEN_KEY, t); + } else { + window.localStorage.removeItem(TOKEN_KEY); + } + set({ token: t }); + }, + clearToken: () => { + window.localStorage.removeItem(TOKEN_KEY); + set({ token: null }); + }, + }; +}); + +export const isAuthenticated = () => !!useAuth.getState().token; +export const TOKEN_STORAGE_KEY = TOKEN_KEY; From d3aa85cd5a44f67476da44a21c1fce0918899cab Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 17:58:52 +0200 Subject: [PATCH 134/215] chore: configure @ alias for src in Vite and TypeScript --- frontend/src/routes/ProtectedRoute.tsx | 10 ++++++++++ frontend/tsconfig.json | 6 +++++- frontend/vite.config.js | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 frontend/src/routes/ProtectedRoute.tsx diff --git a/frontend/src/routes/ProtectedRoute.tsx b/frontend/src/routes/ProtectedRoute.tsx new file mode 100644 index 0000000000..cb4bd26406 --- /dev/null +++ b/frontend/src/routes/ProtectedRoute.tsx @@ -0,0 +1,10 @@ +import { Navigate, Outlet, useLocation } from "react-router-dom"; +import { isAuthenticated } from "@/store/auth"; + +export default function ProtectedRoute() { + const location = useLocation(); + if (!isAuthenticated()) { + return ; + } + return ; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 9c029057f5..8c716a1077 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -10,7 +10,11 @@ "noEmit": true, "skipLibCheck": true, "lib": ["ES2022", "DOM"], - "types": ["vite/client"] + "types": ["vite/client"], + "baseUrl": "./src", + "paths": { + "@/*": ["*"] + } }, "include": ["src"] } diff --git a/frontend/vite.config.js b/frontend/vite.config.js index ba3d0846cb..fd9a196187 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,8 +1,14 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; +import path from "path"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), svgr()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, }); From bec6c058eab55d5a2b5e1756aca6f3e409bad8e6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 21:46:42 +0200 Subject: [PATCH 135/215] refactor(pages): move BeachDetailPage to /src/pages and fix imports --- frontend/src/App.tsx | 2 +- frontend/src/{components => pages}/BeachDetailPage.tsx | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename frontend/src/{components => pages}/BeachDetailPage.tsx (100%) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f2e2a71d3e..23b9dd5e20 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,6 +1,6 @@ import { Routes, Route } from "react-router-dom"; import BeachesList from "./components/BeachesList"; -import BeachDetailPage from "./components/BeachDetailPage"; +import BeachDetailPage from "./pages/BeachDetailPage"; import Header from "./components/Header"; export default function App() { diff --git a/frontend/src/components/BeachDetailPage.tsx b/frontend/src/pages/BeachDetailPage.tsx similarity index 100% rename from frontend/src/components/BeachDetailPage.tsx rename to frontend/src/pages/BeachDetailPage.tsx From 92fbdb1fc7dd51b09d0dffedbf8e41b556068213 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 21:49:17 +0200 Subject: [PATCH 136/215] feat(pages): add HomePage, LoginPage, RegisterPage, and FavoritesPage stubs --- frontend/src/pages/FavoritesPage.tsx | 23 +++++++++++++++++++++++ frontend/src/pages/HomePage.tsx | 4 ++++ frontend/src/pages/LoginPage.tsx | 21 +++++++++++++++++++++ frontend/src/pages/RegisterPage.tsx | 18 ++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 frontend/src/pages/FavoritesPage.tsx create mode 100644 frontend/src/pages/HomePage.tsx create mode 100644 frontend/src/pages/LoginPage.tsx create mode 100644 frontend/src/pages/RegisterPage.tsx diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx new file mode 100644 index 0000000000..f8beeb7c9d --- /dev/null +++ b/frontend/src/pages/FavoritesPage.tsx @@ -0,0 +1,23 @@ +import { useFavorites } from "@/api/favorites"; + +export default function FavoritesPage() { + const { data, isLoading, isError } = useFavorites(); + return ( +
            +

            Your favourites

            + {isLoading &&

            Loading…

            } + {isError &&

            Could not load favourites.

            } + {data && data.length === 0 &&

            No favourites yet.

            } +
              + {data?.map((fav) => ( +
            • +
              {fav.beachId}
              + {fav.note && ( +
              {fav.note}
              + )} +
            • + ))} +
            +
            + ); +} diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx new file mode 100644 index 0000000000..d4cbe35199 --- /dev/null +++ b/frontend/src/pages/HomePage.tsx @@ -0,0 +1,4 @@ +import BeachesList from "../components/BeachesList"; +export default function HomePage() { + return ; +} diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx new file mode 100644 index 0000000000..ab4986c4c1 --- /dev/null +++ b/frontend/src/pages/LoginPage.tsx @@ -0,0 +1,21 @@ +import { Link, useLocation } from "react-router-dom"; + +export default function LoginPage() { + const location = useLocation(); + const from = (location.state as any)?.from?.pathname || "/"; + return ( +
            +

            Login

            +

            + (Form coming next. You will be redirected back to {from}{" "} + after login.) +

            +

            + No account?{" "} + + Register here + +

            +
            + ); +} diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx new file mode 100644 index 0000000000..d4855a1951 --- /dev/null +++ b/frontend/src/pages/RegisterPage.tsx @@ -0,0 +1,18 @@ +import { Link } from "react-router-dom"; + +export default function RegisterPage() { + return ( +
            +

            Create account

            +

            + (Form coming next. After registering you’ll be logged in automatically.) +

            +

            + Already have an account?{" "} + + Log in + +

            +
            + ); +} From 704d87c7471ff7e272ecb4c505880c85ba186ebf Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:01:34 +0200 Subject: [PATCH 137/215] fix(router): remove nested BrowserRouter from App and keep single router in main.tsx --- frontend/src/App.tsx | 23 ++++++++++++++++--- frontend/src/api/client.ts | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 frontend/src/api/client.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 23b9dd5e20..3959615a2c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,7 +1,12 @@ import { Routes, Route } from "react-router-dom"; -import BeachesList from "./components/BeachesList"; -import BeachDetailPage from "./pages/BeachDetailPage"; import Header from "./components/Header"; +import ProtectedRoute from "./routes/ProtectedRoute"; + +import HomePage from "./pages/HomePage"; +import BeachDetailPage from "./pages/BeachDetailPage"; +import LoginPage from "./pages/LoginPage"; +import RegisterPage from "./pages/RegisterPage"; +import FavoritesPage from "./pages/FavoritesPage"; export default function App() { return ( @@ -9,8 +14,20 @@ export default function App() {
            - } /> + } /> } /> + + {/* Public */} + } /> + } /> + + {/* Private */} + }> + } /> + + + {/* Optional 404 */} + {/* } /> */}
            diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts new file mode 100644 index 0000000000..afdf6af438 --- /dev/null +++ b/frontend/src/api/client.ts @@ -0,0 +1,45 @@ +import { useAuth } from "@/store/auth"; + +const API_BASE = import.meta.env.VITE_API_BASE; + +type FetchOptions = Omit & { + headers?: Record; +}; + +export async function apiFetch( + path: string, + options: FetchOptions = {} +): Promise { + const token = useAuth.getState().token; // read current token without subscribing + const url = path.startsWith("http") ? path : `${API_BASE}${path}`; + + const headers: Record = { + "Content-Type": "application/json", + ...(options.headers || {}), + }; + + if (token) { + headers.Authorization = `Bearer ${token}`; + } + + const res = await fetch(url, { ...options, headers }); + + // Optional: normalize common error shapes from backend + if (!res.ok) { + let body: any = null; + try { + body = await res.json(); + } catch { + // ignore parse error + } + const error = new Error(body?.error || res.statusText); + (error as any).status = res.status; + (error as any).details = body; + throw error; + } + + // For 204 No Content + if (res.status === 204) return undefined as T; + + return (await res.json()) as T; +} From 4a5bad934f1268010d2c06645115b5c46105a92a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:06:56 +0200 Subject: [PATCH 138/215] feat(api): add apiFetch client with automatic JWT header support and migrate favorites API to apiFetch while preserving token-keyed cache and by-beach delete --- frontend/src/api/client.ts | 2 - frontend/src/api/favorites.ts | 71 +++++++++++++++++------------------ 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index afdf6af438..f907f53e06 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -24,7 +24,6 @@ export async function apiFetch( const res = await fetch(url, { ...options, headers }); - // Optional: normalize common error shapes from backend if (!res.ok) { let body: any = null; try { @@ -38,7 +37,6 @@ export async function apiFetch( throw error; } - // For 204 No Content if (res.status === 204) return undefined as T; return (await res.json()) as T; diff --git a/frontend/src/api/favorites.ts b/frontend/src/api/favorites.ts index 6c3f5618e8..1d42112245 100644 --- a/frontend/src/api/favorites.ts +++ b/frontend/src/api/favorites.ts @@ -1,50 +1,37 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { getToken } from "../utils/auth"; - -const API_BASE = - import.meta.env.VITE_API_BASE ?? "https://bada-backend.vercel.app/api"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { apiFetch } from "./client"; +import { useAuth } from "@/store/auth"; export type Favorite = { _id: string; userId?: string; beachId: string; note?: string; + order?: number; createdAt?: string; updatedAt?: string; }; export function useFavorites() { - const token = getToken(); - return useQuery({ + const token = useAuth.getState().token; + return useQuery({ queryKey: ["favorites", token], enabled: !!token, - queryFn: async (): Promise => { - const res = await fetch(`${API_BASE}/favorites`, { - headers: { Authorization: `Bearer ${token}` }, - }); - if (!res.ok) throw new Error("Failed to fetch favorites"); - return res.json(); - }, + queryFn: () => apiFetch("/favorites"), staleTime: 60_000, }); } export function useAddFavorite() { const qc = useQueryClient(); - const token = getToken(); + const token = useAuth.getState().token; + return useMutation({ - mutationFn: async (beachId: string) => { - const res = await fetch(`${API_BASE}/favorites`, { + mutationFn: (beachId: string) => + apiFetch("/favorites", { method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, body: JSON.stringify({ beachId }), - }); - if (!res.ok) throw new Error("Failed to add favorite"); - return res.json() as Promise; - }, + }), onSuccess: () => { qc.invalidateQueries({ queryKey: ["favorites", token] }); }, @@ -53,20 +40,32 @@ export function useAddFavorite() { export function useRemoveFavorite() { const qc = useQueryClient(); - const token = getToken(); + const token = useAuth.getState().token; + return useMutation({ - mutationFn: async (vars: { id?: string; beachId?: string }) => { + mutationFn: (vars: { id?: string; beachId?: string }) => { const url = vars.id - ? `${API_BASE}/favorites/${vars.id}` - : `${API_BASE}/favorites/by-beach/${vars.beachId}`; - const res = await fetch(url, { - method: "DELETE", - headers: { Authorization: `Bearer ${token}` }, - }); - if (!res.ok) throw new Error("Failed to remove favorite"); - return true; + ? `/favorites/${vars.id}` + : `/favorites/by-beach/${vars.beachId}`; + return apiFetch(url, { method: "DELETE" }); }, - onSuccess: () => { + // small optimistic update (works for either id or beachId) + onMutate: async (vars) => { + await qc.cancelQueries({ queryKey: ["favorites", token] }); + const prev = qc.getQueryData(["favorites", token]); + + if (prev) { + const next = prev.filter((f) => + vars.id ? f._id !== vars.id : f.beachId !== vars.beachId + ); + qc.setQueryData(["favorites", token], next); + } + return { prev }; + }, + onError: (_err, _vars, ctx) => { + if (ctx?.prev) qc.setQueryData(["favorites", token], ctx.prev); + }, + onSettled: () => { qc.invalidateQueries({ queryKey: ["favorites", token] }); }, }); From 6b64dca9391bfda62f5ef0a817fcf7c8af95e3d1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:09:26 +0200 Subject: [PATCH 139/215] refactor(api): migrate beaches API to apiFetch for consistency with favorites --- frontend/src/api/beaches.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/frontend/src/api/beaches.ts b/frontend/src/api/beaches.ts index d7ab4c3a67..02798ca6ff 100644 --- a/frontend/src/api/beaches.ts +++ b/frontend/src/api/beaches.ts @@ -1,21 +1,14 @@ +import { apiFetch } from "./client"; import { BeachSummary, BeachDetail, BeachFeatureCollection, featureToSummary, -} from "../types/beaches"; - -// Helper: fetch JSON with error handling -async function apiGet(path: string): Promise { - const base = import.meta.env.VITE_API_BASE; // e.g. https://bada-backend.vercel.app/api - const res = await fetch(`${base}${path}`); - if (!res.ok) throw new Error(`API error ${res.status}`); - return res.json() as Promise; -} +} from "@/types/beaches"; // List all beaches → BeachSummary[] export async function fetchBeaches(): Promise { - const data = await apiGet("/beaches"); + const data = await apiFetch("/beaches"); return data.features .map(featureToSummary) .filter((x): x is BeachSummary => x !== null); @@ -23,8 +16,5 @@ export async function fetchBeaches(): Promise { // One beach detail export async function fetchBeach(id: string): Promise { - return apiGet(`/beaches/${id}`); + return apiFetch(`/beaches/${id}`); } - -console.log("API_BASE =", import.meta.env.VITE_API_BASE); -// For debugging; remove later From 63999717e2f22ac41c2a6d7d9c515be379aa141c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:11:57 +0200 Subject: [PATCH 140/215] chore(api): add optional API request logging controlled by VITE_DEBUG_API --- frontend/src/api/client.ts | 11 +++++++---- frontend/src/utils/logger.ts | 7 +++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 frontend/src/utils/logger.ts diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index f907f53e06..5fbd5604a4 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1,4 +1,5 @@ import { useAuth } from "@/store/auth"; +import { logApi } from "@/utils/logger"; const API_BASE = import.meta.env.VITE_API_BASE; @@ -10,7 +11,7 @@ export async function apiFetch( path: string, options: FetchOptions = {} ): Promise { - const token = useAuth.getState().token; // read current token without subscribing + const token = useAuth.getState().token; const url = path.startsWith("http") ? path : `${API_BASE}${path}`; const headers: Record = { @@ -22,15 +23,17 @@ export async function apiFetch( headers.Authorization = `Bearer ${token}`; } + logApi("→", options.method ?? "GET", url, { headers, body: options.body }); + const res = await fetch(url, { ...options, headers }); + logApi("←", res.status, res.statusText, url); + if (!res.ok) { let body: any = null; try { body = await res.json(); - } catch { - // ignore parse error - } + } catch {} const error = new Error(body?.error || res.statusText); (error as any).status = res.status; (error as any).details = body; diff --git a/frontend/src/utils/logger.ts b/frontend/src/utils/logger.ts new file mode 100644 index 0000000000..c2800ae698 --- /dev/null +++ b/frontend/src/utils/logger.ts @@ -0,0 +1,7 @@ +const DEBUG = import.meta.env.VITE_DEBUG_API === "true"; + +export function logApi(...args: any[]) { + if (DEBUG) { + console.log("[API]", ...args); + } +} From 9e552e90cf9f10a603353700fe41aec2e25bd43f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Wed, 24 Sep 2025 22:14:49 +0200 Subject: [PATCH 141/215] feat(header): integrate auth store and clear React Query cache on logout --- frontend/src/components/Header.tsx | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index b37b79ee47..990f4751a9 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,6 +1,8 @@ import { useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; import { useUI } from "../store/ui"; +import { useAuth } from "@/store/auth"; +import { useQueryClient } from "@tanstack/react-query"; import MenuIcon from "../assets/menu_icon.svg?react"; import UserIcon from "../assets/user_icon.svg?react"; @@ -30,10 +32,9 @@ function useDarkMode() { type HeaderProps = { languageSwitcher?: React.ReactNode; - authed?: boolean; }; -export default function Header({ languageSwitcher, authed }: HeaderProps) { +export default function Header({ languageSwitcher }: HeaderProps) { const [menuOpen, setMenuOpen] = useState(false); const [userOpen, setUserOpen] = useState(false); const menuRef = useOutsideClose(() => setMenuOpen(false)); @@ -43,6 +44,16 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { const search = useUI((s) => s.search); const setSearch = useUI((s) => s.setSearch); + const { token, clearToken } = useAuth(); + const authed = !!token; + const qc = useQueryClient(); + + function handleLogout() { + clearToken(); + qc.clear(); // clear cached queries (favorites, etc.) + setUserOpen(false); + } + return (
            @@ -124,6 +135,13 @@ export default function Header({ languageSwitcher, authed }: HeaderProps) { Favourite beaches Profile Settings +
            + )} - {authed && ( - <> -
            - Log out - - )}
            )}
            From 2fd838275235aab1a2fd504535b5470332b445f1 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:18:03 +0200 Subject: [PATCH 142/215] feat(auth): update LoginPage with form validation, error handling, and redirect --- frontend/src/pages/LoginPage.tsx | 98 +++++++++++++++++++++++++++++--- package.json | 5 +- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index ab4986c4c1..bd2fcc76ef 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -1,18 +1,98 @@ -import { Link, useLocation } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useNavigate, useLocation, Link } from "react-router-dom"; +import { useAuth } from "@/store/auth"; + +const schema = z.object({ + email: z.string().email("Invalid email address"), + password: z.string().min(8, "Password must be at least 8 characters"), +}); + +type FormData = z.infer; export default function LoginPage() { + const { setToken } = useAuth(); + const navigate = useNavigate(); const location = useLocation(); const from = (location.state as any)?.from?.pathname || "/"; + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + setError, + } = useForm({ + resolver: zodResolver(schema), + }); + + async function onSubmit(values: FormData) { + try { + const res = await fetch(`${import.meta.env.VITE_API_BASE}/auth/login`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(values), + }); + + if (!res.ok) { + const body = await res.json().catch(() => null); + const msg = body?.error ?? res.statusText; + setError("root", { message: msg }); + return; + } + + const data = await res.json(); + setToken(data.token); // Save token in Zustand store + navigate(from, { replace: true }); + } catch (err: any) { + setError("root", { message: err.message ?? "Login failed" }); + } + } + return ( -
            -

            Login

            -

            - (Form coming next. You will be redirected back to {from}{" "} - after login.) -

            -

            +

            +

            Sign in

            + +
            + + + {errors.email && ( +

            {errors.email.message}

            + )} +
            + +
            + + + {errors.password && ( +

            {errors.password.message}

            + )} +
            + + {errors.root && ( +

            {errors.root.message}

            + )} + + + + +

            No account?{" "} - + Register here

            diff --git a/package.json b/package.json index a3a70fcad1..6d57fb4096 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,10 @@ "postinstall": "npm install --prefix backend" }, "dependencies": { - "@tanstack/react-query": "^5.87.4" + "@hookform/resolvers": "^5.2.2", + "@tanstack/react-query": "^5.87.4", + "react-hook-form": "^7.63.0", + "zod": "^4.1.11" }, "devDependencies": { "autoprefixer": "^10.4.21", From 0e5805bfb23cd1e81fe1f7d8f1dc78c2e2a6e343 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:21:35 +0200 Subject: [PATCH 143/215] feat(auth): update RegisterPage with validation and redirect to login --- frontend/src/pages/RegisterPage.tsx | 122 ++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx index d4855a1951..5f9d88dda4 100644 --- a/frontend/src/pages/RegisterPage.tsx +++ b/frontend/src/pages/RegisterPage.tsx @@ -1,16 +1,120 @@ -import { Link } from "react-router-dom"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useNavigate, Link } from "react-router-dom"; + +const schema = z + .object({ + email: z.string().email("Invalid email address"), + password: z.string().min(8, "Password must be at least 8 characters"), + confirmPassword: z.string(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "Passwords must match", + path: ["confirmPassword"], + }); + +type FormData = z.infer; export default function RegisterPage() { + const navigate = useNavigate(); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + setError, + } = useForm({ + resolver: zodResolver(schema), + }); + + async function onSubmit(values: FormData) { + try { + const res = await fetch( + `${import.meta.env.VITE_API_BASE}/auth/register`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + email: values.email, + password: values.password, + }), + } + ); + + if (!res.ok) { + const body = await res.json().catch(() => null); + const msg = body?.error ?? res.statusText; + setError("root", { message: msg }); + return; + } + + // Success → redirect to login + navigate("/login", { replace: true }); + } catch (err: any) { + setError("root", { message: err.message ?? "Registration failed" }); + } + } + return ( -
            -

            Create account

            -

            - (Form coming next. After registering you’ll be logged in automatically.) -

            -

            +

            +

            Register

            +
            +
            + + + {errors.email && ( +

            {errors.email.message}

            + )} +
            + +
            + + + {errors.password && ( +

            {errors.password.message}

            + )} +
            + +
            + + + {errors.confirmPassword && ( +

            + {errors.confirmPassword.message} +

            + )} +
            + + {errors.root && ( +

            {errors.root.message}

            + )} + + +
            + +

            Already have an account?{" "} - - Log in + + Sign in here

            From ddb8cb4da79fb9d13ae844c2dd8ccee4e47feb44 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:24:16 +0200 Subject: [PATCH 144/215] feat(favorites): update protected FavoritesPage with list and remove support --- frontend/src/pages/FavoritesPage.tsx | 71 +++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index f8beeb7c9d..d81c7cd0cd 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -1,20 +1,65 @@ -import { useFavorites } from "@/api/favorites"; +import { useFavorites, useRemoveFavorite } from "@/api/favorites"; +import { Link } from "react-router-dom"; export default function FavoritesPage() { - const { data, isLoading, isError } = useFavorites(); + const { data: favorites, isLoading, isError, error } = useFavorites(); + const rmFav = useRemoveFavorite(); + + if (isLoading) { + return ( +
            +

            Your favorite beaches

            +

            Loading…

            +
            + ); + } + + if (isError) { + return ( +
            +

            Your favorite beaches

            +

            + {(error as Error).message ?? "Could not load favorites"} +

            +
            + ); + } + return ( -
            -

            Your favourites

            - {isLoading &&

            Loading…

            } - {isError &&

            Could not load favourites.

            } - {data && data.length === 0 &&

            No favourites yet.

            } +
            +

            Your favorite beaches

            + + {(!favorites || favorites.length === 0) && ( +

            + You don’t have any favorites yet. Browse{" "} + + all beaches + {" "} + and save your favorites! +

            + )} +
              - {data?.map((fav) => ( -
            • -
              {fav.beachId}
              - {fav.note && ( -
              {fav.note}
              - )} + {favorites?.map((fav) => ( +
            • + + {fav.beachId} + +
            • ))}
            From d023d8c71448de8f7511f321aa4462857f6f4521 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:28:38 +0200 Subject: [PATCH 145/215] fix(deps): add react-hook-form, zod, and resolvers to frontend dependencies --- frontend/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index 52853f7661..fee9e2002c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,13 +10,16 @@ "preview": "vite preview" }, "dependencies": { + "@hookform/resolvers": "^5.2.2", "@tanstack/react-query": "^5.87.4", "i18next": "^25.5.2", "maplibre-gl": "^5.7.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.63.0", "react-i18next": "^15.7.3", "react-router-dom": "^7.9.1", + "zod": "^4.1.11", "zustand": "^5.0.8" }, "devDependencies": { From 976aabf8f081f39e19e2aa94e1f7240a34c9fac6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:34:48 +0200 Subject: [PATCH 146/215] fix(backend): align start script with server.ts entrypoint --- backend/dist/index.d.ts | 5 +- backend/dist/index.d.ts.map | 2 +- backend/dist/index.js | 45 +++++++++++---- backend/dist/index.js.map | 2 +- backend/dist/lib/cache.d.ts | 9 +++ backend/dist/lib/cache.d.ts.map | 1 + backend/dist/lib/cache.js | 22 ++++++++ backend/dist/lib/cache.js.map | 1 + backend/dist/lib/db.d.ts | 3 + backend/dist/lib/db.d.ts.map | 1 + backend/dist/lib/db.js | 33 +++++++++++ backend/dist/lib/db.js.map | 1 + backend/dist/lib/hav.d.ts | 7 +++ backend/dist/lib/hav.d.ts.map | 1 + backend/dist/lib/hav.js | 60 ++++++++++++++++++++ backend/dist/lib/hav.js.map | 1 + backend/dist/lib/havbackup.d.ts | 2 + backend/dist/lib/havbackup.d.ts.map | 1 + backend/dist/lib/havbackup.js | 29 ++++++++++ backend/dist/lib/havbackup.js.map | 1 + backend/dist/middleware/auth.d.ts | 9 +++ backend/dist/middleware/auth.d.ts.map | 1 + backend/dist/middleware/auth.js | 18 ++++++ backend/dist/middleware/auth.js.map | 1 + backend/dist/models/Favorite.d.ts | 38 +++++++++++++ backend/dist/models/Favorite.d.ts.map | 1 + backend/dist/models/Favorite.js | 17 ++++++ backend/dist/models/Favorite.js.map | 1 + backend/dist/models/User.d.ts | 32 +++++++++++ backend/dist/models/User.d.ts.map | 1 + backend/dist/models/User.js | 9 +++ backend/dist/models/User.js.map | 1 + backend/dist/routes/auth.d.ts | 2 + backend/dist/routes/auth.d.ts.map | 1 + backend/dist/routes/auth.js | 59 +++++++++++++++++++ backend/dist/routes/auth.js.map | 1 + backend/dist/routes/beaches.d.ts | 2 + backend/dist/routes/beaches.d.ts.map | 1 + backend/dist/routes/beaches.js | 49 ++++++++++++++++ backend/dist/routes/beaches.js.map | 1 + backend/dist/routes/dbCheck.d.ts | 2 + backend/dist/routes/dbCheck.d.ts.map | 1 + backend/dist/routes/dbCheck.js | 27 +++++++++ backend/dist/routes/dbCheck.js.map | 1 + backend/dist/routes/favorites.d.ts | 2 + backend/dist/routes/favorites.d.ts.map | 1 + backend/dist/routes/favorites.js | 78 ++++++++++++++++++++++++++ backend/dist/routes/favorites.js.map | 1 + backend/dist/routes/health.d.ts | 3 +- backend/dist/routes/health.d.ts.map | 2 +- backend/dist/routes/health.js | 6 +- backend/dist/routes/health.js.map | 2 +- backend/dist/server.d.ts | 2 + backend/dist/server.d.ts.map | 1 + backend/dist/server.js | 25 +++++++++ backend/dist/server.js.map | 1 + backend/package.json | 2 +- 57 files changed, 606 insertions(+), 23 deletions(-) create mode 100644 backend/dist/lib/cache.d.ts create mode 100644 backend/dist/lib/cache.d.ts.map create mode 100644 backend/dist/lib/cache.js create mode 100644 backend/dist/lib/cache.js.map create mode 100644 backend/dist/lib/db.d.ts create mode 100644 backend/dist/lib/db.d.ts.map create mode 100644 backend/dist/lib/db.js create mode 100644 backend/dist/lib/db.js.map create mode 100644 backend/dist/lib/hav.d.ts create mode 100644 backend/dist/lib/hav.d.ts.map create mode 100644 backend/dist/lib/hav.js create mode 100644 backend/dist/lib/hav.js.map create mode 100644 backend/dist/lib/havbackup.d.ts create mode 100644 backend/dist/lib/havbackup.d.ts.map create mode 100644 backend/dist/lib/havbackup.js create mode 100644 backend/dist/lib/havbackup.js.map create mode 100644 backend/dist/middleware/auth.d.ts create mode 100644 backend/dist/middleware/auth.d.ts.map create mode 100644 backend/dist/middleware/auth.js create mode 100644 backend/dist/middleware/auth.js.map create mode 100644 backend/dist/models/Favorite.d.ts create mode 100644 backend/dist/models/Favorite.d.ts.map create mode 100644 backend/dist/models/Favorite.js create mode 100644 backend/dist/models/Favorite.js.map create mode 100644 backend/dist/models/User.d.ts create mode 100644 backend/dist/models/User.d.ts.map create mode 100644 backend/dist/models/User.js create mode 100644 backend/dist/models/User.js.map create mode 100644 backend/dist/routes/auth.d.ts create mode 100644 backend/dist/routes/auth.d.ts.map create mode 100644 backend/dist/routes/auth.js create mode 100644 backend/dist/routes/auth.js.map create mode 100644 backend/dist/routes/beaches.d.ts create mode 100644 backend/dist/routes/beaches.d.ts.map create mode 100644 backend/dist/routes/beaches.js create mode 100644 backend/dist/routes/beaches.js.map create mode 100644 backend/dist/routes/dbCheck.d.ts create mode 100644 backend/dist/routes/dbCheck.d.ts.map create mode 100644 backend/dist/routes/dbCheck.js create mode 100644 backend/dist/routes/dbCheck.js.map create mode 100644 backend/dist/routes/favorites.d.ts create mode 100644 backend/dist/routes/favorites.d.ts.map create mode 100644 backend/dist/routes/favorites.js create mode 100644 backend/dist/routes/favorites.js.map create mode 100644 backend/dist/server.d.ts create mode 100644 backend/dist/server.d.ts.map create mode 100644 backend/dist/server.js create mode 100644 backend/dist/server.js.map diff --git a/backend/dist/index.d.ts b/backend/dist/index.d.ts index 41a92ac86c..72752437ba 100644 --- a/backend/dist/index.d.ts +++ b/backend/dist/index.d.ts @@ -1,4 +1,3 @@ -import serverless from "serverless-http"; -declare const _default: serverless.Handler; -export default _default; +declare const app: import("express-serve-static-core").Express; +export default app; //# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/index.d.ts.map b/backend/dist/index.d.ts.map index f2747608d5..64ac39b267 100644 --- a/backend/dist/index.d.ts.map +++ b/backend/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,UAAU,MAAM,iBAAiB,CAAC;;AA4CzC,wBAA+B"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,GAAG,6CAAY,CAAC;AA8DtB,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/index.js b/backend/dist/index.js index 47e8b865d2..afff69a109 100644 --- a/backend/dist/index.js +++ b/backend/dist/index.js @@ -1,10 +1,14 @@ +console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; -import serverless from "serverless-http"; -// Routers import { healthRouter } from "./routes/health.js"; +import { dbCheckRouter } from "./routes/dbCheck.js"; +import { authRouter } from "./routes/auth.js"; +import { requireAuth } from "./middleware/auth.js"; +import { favoritesRouter } from "./routes/favorites.js"; +import { beachesRouter } from "./routes/beaches.js"; const app = express(); -// CORS — allow only your deployed frontend (you can comma-separate multiple origins) +// --- Global middleware --- const allowed = (process.env.ALLOWED_ORIGIN ?? "") .split(",") .map((s) => s.trim()) @@ -13,19 +17,36 @@ app.use(cors({ origin: allowed.length ? allowed : true, // during local dev if empty, allow all credentials: true, })); -// Body parsing app.use(express.json()); -// Mount routes under /api +// --- Public routes (mounted under /api) --- app.use("/api", healthRouter); -// Simple not-found handler -app.use((_req, res) => { - res.status(404).json({ error: "NotFound" }); +app.use("/api", dbCheckRouter); +app.use("/api", favoritesRouter); +app.use("/api", beachesRouter); +// 🔐 Auth routes (public endpoints: /register, /login) +app.use("/api/auth", authRouter); +// 🔒 Example protected endpoints (require a Bearer token) +app.get("/api/protected/ping", requireAuth, (req, res) => { + res.json({ ok: true, user: req.user }); }); -// Centralized error handler (Express 5 catches async errors) +// To protect /api/auth/me this can also be used +app.get("/api/auth/me", requireAuth, (req, res) => { + res.json({ user: req.user }); +}); +// Temporary direct test +app.get("/api/health-direct", (_req, res) => { + console.log("Hit /api/health-direct"); + res.json({ ok: true, via: "direct" }); +}); +// 404 +app.use((_req, res) => res.status(404).json({ error: "NotFound" })); +// Error handler app.use((err, _req, res, _next) => { console.error("[ERROR]", err); - res.status(500).json({ error: "InternalServerError" }); + const message = process.env.NODE_ENV !== "production" && err instanceof Error + ? err.message + : undefined; + res.status(500).json({ error: "InternalServerError", message }); }); -// Export serverless handler for Vercel -export default serverless(app); +export default app; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/index.js.map b/backend/dist/index.js.map index 71aa48d684..8239b93640 100644 --- a/backend/dist/index.js.map +++ b/backend/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC,UAAU;AACV,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,qFAAqF;AACrF,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;KAC/C,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACnB,GAAG,CAAC,GAAG,CACL,IAAI,CAAC;IACH,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,uCAAuC;IAChF,WAAW,EAAE,IAAI;CAClB,CAAC,CACH,CAAC;AAEF,eAAe;AACf,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,0BAA0B;AAC1B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAE9B,2BAA2B;AAC3B,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACpB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC;AAEH,6DAA6D;AAC7D,GAAG,CAAC,GAAG,CACL,CACE,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B,EAC3B,EAAE;IACF,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC9B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;AACzD,CAAC,CACF,CAAC;AAEF,uCAAuC;AACvC,eAAe,UAAU,CAAC,GAAG,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAE1D,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,4BAA4B;AAC5B,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;KAC/C,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,GAAG,CAAC,GAAG,CACL,IAAI,CAAC;IACH,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,uCAAuC;IAChF,WAAW,EAAE,IAAI;CAClB,CAAC,CACH,CAAC;AAEF,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,6CAA6C;AAC7C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAC9B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC/B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE/B,uDAAuD;AACvD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAEjC,0DAA0D;AAC1D,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACvD,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,gDAAgD;AAChD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAChD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC1C,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,MAAM;AACN,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AAEpE,gBAAgB;AAChB,GAAG,CAAC,GAAG,CACL,CACE,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B,EAC3B,EAAE;IACF,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,YAAY,KAAK;QAC3D,CAAC,CAAC,GAAG,CAAC,OAAO;QACb,CAAC,CAAC,SAAS,CAAC;IAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;AAClE,CAAC,CACF,CAAC;AAEF,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/lib/cache.d.ts b/backend/dist/lib/cache.d.ts new file mode 100644 index 0000000000..f99b188259 --- /dev/null +++ b/backend/dist/lib/cache.d.ts @@ -0,0 +1,9 @@ +export declare class SimpleCache { + private defaultTtlMs; + private store; + constructor(defaultTtlMs?: number); + get(key: string): T | undefined; + set(key: string, value: T, ttlMs?: number): void; +} +export declare const cache: SimpleCache; +//# sourceMappingURL=cache.d.ts.map \ No newline at end of file diff --git a/backend/dist/lib/cache.d.ts.map b/backend/dist/lib/cache.d.ts.map new file mode 100644 index 0000000000..9446086722 --- /dev/null +++ b/backend/dist/lib/cache.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAEA,qBAAa,WAAW;IAGV,OAAO,CAAC,YAAY;IAFhC,OAAO,CAAC,KAAK,CAAiC;gBAE1B,YAAY,SAAS;IAEzC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAUlC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,SAAoB;CAGxD;AAED,eAAO,MAAM,KAAK,aAA0B,CAAC"} \ No newline at end of file diff --git a/backend/dist/lib/cache.js b/backend/dist/lib/cache.js new file mode 100644 index 0000000000..960ed27092 --- /dev/null +++ b/backend/dist/lib/cache.js @@ -0,0 +1,22 @@ +export class SimpleCache { + defaultTtlMs; + store = new Map(); + constructor(defaultTtlMs = 60_000) { + this.defaultTtlMs = defaultTtlMs; + } // default 60s + get(key) { + const hit = this.store.get(key); + if (!hit) + return; + if (Date.now() > hit.expiry) { + this.store.delete(key); + return; + } + return hit.value; + } + set(key, value, ttlMs = this.defaultTtlMs) { + this.store.set(key, { value, expiry: Date.now() + ttlMs }); + } +} +export const cache = new SimpleCache(60_000); // 60s default +//# sourceMappingURL=cache.js.map \ No newline at end of file diff --git a/backend/dist/lib/cache.js.map b/backend/dist/lib/cache.js.map new file mode 100644 index 0000000000..d242e47440 --- /dev/null +++ b/backend/dist/lib/cache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,WAAW;IAGF;IAFZ,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAE9C,YAAoB,eAAe,MAAM;QAArB,iBAAY,GAAZ,YAAY,CAAS;IAAG,CAAC,CAAC,cAAc;IAE5D,GAAG,CAAI,GAAW;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,OAAO,GAAG,CAAC,KAAU,CAAC;IACxB,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAAK,GAAG,IAAI,CAAC,YAAY;QACrD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc"} \ No newline at end of file diff --git a/backend/dist/lib/db.d.ts b/backend/dist/lib/db.d.ts new file mode 100644 index 0000000000..a34a926416 --- /dev/null +++ b/backend/dist/lib/db.d.ts @@ -0,0 +1,3 @@ +import mongoose from "mongoose"; +export declare function connectDB(): Promise; +//# sourceMappingURL=db.d.ts.map \ No newline at end of file diff --git a/backend/dist/lib/db.d.ts.map b/backend/dist/lib/db.d.ts.map new file mode 100644 index 0000000000..cc99ffa183 --- /dev/null +++ b/backend/dist/lib/db.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAahC,wBAAsB,SAAS,6BAmC9B"} \ No newline at end of file diff --git a/backend/dist/lib/db.js b/backend/dist/lib/db.js new file mode 100644 index 0000000000..b92ac881a8 --- /dev/null +++ b/backend/dist/lib/db.js @@ -0,0 +1,33 @@ +import mongoose from "mongoose"; +let cached = global._mongooseCached ?? { + conn: null, + promise: null, +}; +global._mongooseCached = cached; +export async function connectDB() { + if (cached.conn) + return cached.conn; + if (!cached.promise) { + const uri = process.env.MONGODB_URI; + if (!uri) + throw new Error("MONGODB_URI missing"); + mongoose.set("strictQuery", true); + mongoose.set("bufferCommands", false); + cached.promise = mongoose + .connect(uri, { serverSelectionTimeoutMS: 5000 }) + .then((m) => { + cached.conn = m; + // 🔴 Optional debug hooks (remove later) + mongoose.connection.on("connected", () => console.log("[mongo] connected")); + mongoose.connection.on("error", (e) => console.error("[mongo] error", e)); + mongoose.connection.on("disconnected", () => console.warn("[mongo] disconnected")); + return m; + }) + .catch((err) => { + cached.promise = null; + throw err; + }); + } + return cached.promise; +} +//# sourceMappingURL=db.js.map \ No newline at end of file diff --git a/backend/dist/lib/db.js.map b/backend/dist/lib/db.js.map new file mode 100644 index 0000000000..a619785608 --- /dev/null +++ b/backend/dist/lib/db.js.map @@ -0,0 +1 @@ +{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAOhC,IAAI,MAAM,GAAY,MAAc,CAAC,eAAe,IAAI;IACtD,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,IAAI;CACd,CAAC;AACD,MAAc,CAAC,eAAe,GAAG,MAAM,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,MAAM,CAAC,IAAI;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IAEpC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QAEjD,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAClC,QAAQ,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QAEtC,MAAM,CAAC,OAAO,GAAG,QAAQ;aACtB,OAAO,CAAC,GAAG,EAAE,EAAE,wBAAwB,EAAE,IAAI,EAAE,CAAC;aAChD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;YACV,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;YAEhB,yCAAyC;YACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE,CACvC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CACjC,CAAC;YACF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CACpC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAClC,CAAC;YACF,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE,CAC1C,OAAO,CAAC,IAAI,CAAC,sBAAsB,CAAC,CACrC,CAAC;YAEF,OAAO,CAAC,CAAC;QACX,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;YACtB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,MAAM,CAAC,OAAO,CAAC;AACxB,CAAC"} \ No newline at end of file diff --git a/backend/dist/lib/hav.d.ts b/backend/dist/lib/hav.d.ts new file mode 100644 index 0000000000..d5db8757ed --- /dev/null +++ b/backend/dist/lib/hav.d.ts @@ -0,0 +1,7 @@ +/** v1 fetcher (old endpoints like /feature, /detail) */ +export declare function havGet(path: string, ttlMs?: number): Promise; +/** Minimal v2 fetcher (used for monitoring results, etc.) */ +export declare function havV2Get(path: string, init?: RequestInit): Promise; +/** Pull latest sample date (ISO string) from /bathing-waters/{id}/results */ +export declare function getLatestSampleDate(id: string): Promise; +//# sourceMappingURL=hav.d.ts.map \ No newline at end of file diff --git a/backend/dist/lib/hav.d.ts.map b/backend/dist/lib/hav.d.ts.map new file mode 100644 index 0000000000..2b8094856c --- /dev/null +++ b/backend/dist/lib/hav.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"hav.d.ts","sourceRoot":"","sources":["../../src/lib/hav.ts"],"names":[],"mappings":"AAWA,wDAAwD;AACxD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAgB,gBAgC/D;AAED,6DAA6D;AAC7D,wBAAsB,QAAQ,CAAC,CAAC,EAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,6EAA6E;AAC7E,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAe5E"} \ No newline at end of file diff --git a/backend/dist/lib/hav.js b/backend/dist/lib/hav.js new file mode 100644 index 0000000000..7d05a66ed9 --- /dev/null +++ b/backend/dist/lib/hav.js @@ -0,0 +1,60 @@ +import { cache } from "./cache.js"; +const HAV_BASE_URL = process.env.HAV_BASE_URL; +const HAV_USER_AGENT = process.env.HAV_USER_AGENT || "BADA/1.0"; +const HAV_V2_BASE = process.env.HAV_V2_BASE ?? "https://api.havochvatten.se/bathingwaters/v2"; +function key(path) { + return `hav:${path}`; +} +/** v1 fetcher (old endpoints like /feature, /detail) */ +export async function havGet(path, ttlMs = 5 * 60 * 1000) { + const k = key(`v1:${path}`); + const cached = cache.get(k); + if (cached) + return cached; + const url = `${HAV_BASE_URL}${path}`; + // TEMP: trace outgoing in dev + if (process.env.NODE_ENV !== "production") { + console.log(`[HaV v1] GET ${url}`); + } + const res = await fetch(url, { + headers: { + "User-Agent": HAV_USER_AGENT, + Accept: "application/json", + }, + }); + if (!res.ok) { + const text = await res.text().catch(() => ""); + console.warn(`[HaV v1] ${res.status} ${res.statusText} for ${url}: ${text.slice(0, 500)}`); + throw new Error(`HaV v1 error ${res.status} ${res.statusText}`); + } + const data = await res.json(); + cache.set(k, data, ttlMs); + return data; +} +/** Minimal v2 fetcher (used for monitoring results, etc.) */ +export async function havV2Get(path, init) { + const url = `${HAV_V2_BASE}${path}`; + if (process.env.NODE_ENV !== "production") { + console.log(`[HaV v2] GET ${url}`); + } + const res = await fetch(url, { + headers: { Accept: "application/json" }, + ...init, + }); + if (!res.ok) { + const text = await res.text().catch(() => ""); + console.warn(`[HaV v2] ${res.status} ${res.statusText} for ${url}: ${text.slice(0, 500)}`); + throw new Error(`HaV v2 error ${res.status} ${res.statusText}`); + } + return res.json(); +} +/** Pull latest sample date (ISO string) from /bathing-waters/{id}/results */ +export async function getLatestSampleDate(id) { + const data = await havV2Get(`/bathing-waters/${encodeURIComponent(id)}/results`); + const latest = (data.results ?? []) + .map((r) => r.takenAt) + .filter((d) => !!d) + .sort((a, b) => (a < b ? 1 : a > b ? -1 : 0))[0] ?? null; + return latest; +} +//# sourceMappingURL=hav.js.map \ No newline at end of file diff --git a/backend/dist/lib/hav.js.map b/backend/dist/lib/hav.js.map new file mode 100644 index 0000000000..4333e992b7 --- /dev/null +++ b/backend/dist/lib/hav.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hav.js","sourceRoot":"","sources":["../../src/lib/hav.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAa,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,UAAU,CAAC;AAChE,MAAM,WAAW,GACf,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,8CAA8C,CAAC;AAE5E,SAAS,GAAG,CAAC,IAAY;IACvB,OAAO,OAAO,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;IAC9D,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,IAAI,EAAE,CAAC;IACrC,8BAA8B;IAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,OAAO,EAAE;YACP,YAAY,EAAE,cAAc;YAC5B,MAAM,EAAE,kBAAkB;SAC3B;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CACV,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,QAAQ,GAAG,KAAK,IAAI,CAAC,KAAK,CAChE,CAAC,EACD,GAAG,CACJ,EAAE,CACJ,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAY,EACZ,IAAkB;IAElB,MAAM,GAAG,GAAG,GAAG,WAAW,GAAG,IAAI,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,OAAO,EAAE,EAAE,MAAM,EAAE,kBAAkB,EAAE;QACvC,GAAG,IAAI;KACR,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,IAAI,CACV,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,QAAQ,GAAG,KAAK,IAAI,CAAC,KAAK,CAChE,CAAC,EACD,GAAG,CACJ,EAAE,CACJ,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,gBAAgB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAC;AAClC,CAAC;AAED,6EAA6E;AAC7E,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,EAAU;IAIlD,MAAM,IAAI,GAAG,MAAM,QAAQ,CACzB,mBAAmB,kBAAkB,CAAC,EAAE,CAAC,UAAU,CACpD,CAAC;IAEF,MAAM,MAAM,GACV,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;SACrB,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;SAC/B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAE7D,OAAO,MAAM,CAAC;AAChB,CAAC"} \ No newline at end of file diff --git a/backend/dist/lib/havbackup.d.ts b/backend/dist/lib/havbackup.d.ts new file mode 100644 index 0000000000..bf54359ed7 --- /dev/null +++ b/backend/dist/lib/havbackup.d.ts @@ -0,0 +1,2 @@ +export declare function havGet(path: string, ttlMs?: number): Promise; +//# sourceMappingURL=havbackup.d.ts.map \ No newline at end of file diff --git a/backend/dist/lib/havbackup.d.ts.map b/backend/dist/lib/havbackup.d.ts.map new file mode 100644 index 0000000000..f4f2df0fc6 --- /dev/null +++ b/backend/dist/lib/havbackup.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"havbackup.d.ts","sourceRoot":"","sources":["../../src/lib/havbackup.ts"],"names":[],"mappings":"AAWA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,SAAgB,gBAsB/D"} \ No newline at end of file diff --git a/backend/dist/lib/havbackup.js b/backend/dist/lib/havbackup.js new file mode 100644 index 0000000000..bd71ec8a2b --- /dev/null +++ b/backend/dist/lib/havbackup.js @@ -0,0 +1,29 @@ +import { cache } from "./cache.js"; +const HAV_BASE_URL = process.env.HAV_BASE_URL; +const HAV_USER_AGENT = process.env.HAV_USER_AGENT; +// Helper to build a cache key +function key(path) { + return `hav:${path}`; +} +// Generic fetch wrapper with caching +export async function havGet(path, ttlMs = 5 * 60 * 1000) { + const k = key(path); + const cached = cache.get(k); + if (cached) { + return cached; // ✅ serve from memory if not expired + } + const url = `${HAV_BASE_URL}${path}`; + const res = await fetch(url, { + headers: { + "User-Agent": HAV_USER_AGENT, + Accept: "application/json", + }, + }); + if (!res.ok) { + throw new Error(`HaV API error: ${res.status} ${res.statusText}`); + } + const data = await res.json(); + cache.set(k, data, ttlMs); // ✅ cache result + return data; +} +//# sourceMappingURL=havbackup.js.map \ No newline at end of file diff --git a/backend/dist/lib/havbackup.js.map b/backend/dist/lib/havbackup.js.map new file mode 100644 index 0000000000..d785be71e6 --- /dev/null +++ b/backend/dist/lib/havbackup.js.map @@ -0,0 +1 @@ +{"version":3,"file":"havbackup.js","sourceRoot":"","sources":["../../src/lib/havbackup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAa,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,cAAe,CAAC;AAEnD,8BAA8B;AAC9B,SAAS,GAAG,CAAC,IAAY;IACvB,OAAO,OAAO,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,KAAK,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI;IAC9D,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5B,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,MAAM,CAAC,CAAC,qCAAqC;IACtD,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,YAAY,GAAG,IAAI,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,OAAO,EAAE;YACP,YAAY,EAAE,cAAc;YAC5B,MAAM,EAAE,kBAAkB;SAC3B;KACF,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,iBAAiB;IAC5C,OAAO,IAAI,CAAC;AACd,CAAC"} \ No newline at end of file diff --git a/backend/dist/middleware/auth.d.ts b/backend/dist/middleware/auth.d.ts new file mode 100644 index 0000000000..d5a01c36b8 --- /dev/null +++ b/backend/dist/middleware/auth.d.ts @@ -0,0 +1,9 @@ +import { Request, Response, NextFunction } from "express"; +export interface AuthedRequest extends Request { + user: { + sub: string; + email: string; + }; +} +export declare function requireAuth(req: Request, res: Response, next: NextFunction): Response> | undefined; +//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/backend/dist/middleware/auth.d.ts.map b/backend/dist/middleware/auth.d.ts.map new file mode 100644 index 0000000000..203ad8f02f --- /dev/null +++ b/backend/dist/middleware/auth.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG1D,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,kDAkB1E"} \ No newline at end of file diff --git a/backend/dist/middleware/auth.js b/backend/dist/middleware/auth.js new file mode 100644 index 0000000000..42ded0341d --- /dev/null +++ b/backend/dist/middleware/auth.js @@ -0,0 +1,18 @@ +import jwt from "jsonwebtoken"; +export function requireAuth(req, res, next) { + try { + const header = req.headers.authorization; // "Bearer " + if (!header) + return res.status(401).json({ error: "NoAuthHeader" }); + const [type, token] = header.split(" "); + if (type !== "Bearer" || !token) + return res.status(401).json({ error: "InvalidAuthHeader" }); + const payload = jwt.verify(token, process.env.JWT_SECRET); + req.user = payload; // set strongly-typed user + next(); + } + catch { + return res.status(401).json({ error: "InvalidToken" }); + } +} +//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/backend/dist/middleware/auth.js.map b/backend/dist/middleware/auth.js.map new file mode 100644 index 0000000000..7ff30636dc --- /dev/null +++ b/backend/dist/middleware/auth.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/middleware/auth.ts"],"names":[],"mappings":"AACA,OAAO,GAAG,MAAM,cAAc,CAAC;AAM/B,MAAM,UAAU,WAAW,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACzE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,mBAAmB;QAC7D,IAAI,CAAC,MAAM;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;QAEpE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK;YAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,UAAW,CAGxD,CAAC;QACD,GAAqB,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,0BAA0B;QACjE,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/Favorite.d.ts b/backend/dist/models/Favorite.d.ts new file mode 100644 index 0000000000..a784f902d4 --- /dev/null +++ b/backend/dist/models/Favorite.d.ts @@ -0,0 +1,38 @@ +import mongoose, { InferSchemaType, Model } from "mongoose"; +declare const favoriteSchema: mongoose.Schema, {}, {}, {}, {}, { + timestamps: true; +}, { + createdAt: NativeDate; + updatedAt: NativeDate; +} & { + userId: mongoose.Types.ObjectId; + beachId: string; + note: string; + order: number; +}, mongoose.Document, {}, mongoose.ResolveSchemaOptions<{ + timestamps: true; +}>> & mongoose.FlatRecord<{ + createdAt: NativeDate; + updatedAt: NativeDate; +} & { + userId: mongoose.Types.ObjectId; + beachId: string; + note: string; + order: number; +}> & { + _id: mongoose.Types.ObjectId; +} & { + __v: number; +}>; +export type FavoriteDoc = InferSchemaType; +export declare const Favorite: Model; +export {}; +//# sourceMappingURL=Favorite.d.ts.map \ No newline at end of file diff --git a/backend/dist/models/Favorite.d.ts.map b/backend/dist/models/Favorite.d.ts.map new file mode 100644 index 0000000000..9676749a48 --- /dev/null +++ b/backend/dist/models/Favorite.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Favorite.d.ts","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiB,eAAe,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE3E,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAanB,CAAC;AAKF,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,cAAc,CAAC,CAAC;AAEjE,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,WAAW,CAEQ,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/Favorite.js b/backend/dist/models/Favorite.js new file mode 100644 index 0000000000..754aa14d4b --- /dev/null +++ b/backend/dist/models/Favorite.js @@ -0,0 +1,17 @@ +import mongoose, { Schema, model } from "mongoose"; +const favoriteSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + index: true, + }, + beachId: { type: String, required: true }, // HaV beach identifier (string) + note: { type: String, default: "" }, // optional user note + order: { type: Number, default: 0 }, // for drag & drop later +}, { timestamps: true }); +// Prevent duplicates: one user can favorite each beach only once +favoriteSchema.index({ userId: 1, beachId: 1 }, { unique: true }); +export const Favorite = mongoose.models.Favorite ?? + model("Favorite", favoriteSchema); +//# sourceMappingURL=Favorite.js.map \ No newline at end of file diff --git a/backend/dist/models/Favorite.js.map b/backend/dist/models/Favorite.js.map new file mode 100644 index 0000000000..56e03c7e10 --- /dev/null +++ b/backend/dist/models/Favorite.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Favorite.js","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAA0B,MAAM,UAAU,CAAC;AAE3E,MAAM,cAAc,GAAG,IAAI,MAAM,CAC/B;IACE,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC3B,GAAG,EAAE,MAAM;QACX,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;KACZ;IACD,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,gCAAgC;IAC3E,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,qBAAqB;IAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,wBAAwB;CAC9D,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;AAEF,iEAAiE;AACjE,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAIlE,MAAM,CAAC,MAAM,QAAQ,GAClB,QAAQ,CAAC,MAAM,CAAC,QAA+B;IAChD,KAAK,CAAc,UAAU,EAAE,cAAc,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/User.d.ts b/backend/dist/models/User.d.ts new file mode 100644 index 0000000000..17f211810f --- /dev/null +++ b/backend/dist/models/User.d.ts @@ -0,0 +1,32 @@ +import mongoose, { InferSchemaType, Model } from "mongoose"; +declare const userSchema: mongoose.Schema, {}, {}, {}, {}, { + timestamps: true; +}, { + createdAt: NativeDate; + updatedAt: NativeDate; +} & { + email: string; + passwordHash: string; +}, mongoose.Document, {}, mongoose.ResolveSchemaOptions<{ + timestamps: true; +}>> & mongoose.FlatRecord<{ + createdAt: NativeDate; + updatedAt: NativeDate; +} & { + email: string; + passwordHash: string; +}> & { + _id: mongoose.Types.ObjectId; +} & { + __v: number; +}>; +export type UserDoc = InferSchemaType; +export declare const User: Model; +export {}; +//# sourceMappingURL=User.d.ts.map \ No newline at end of file diff --git a/backend/dist/models/User.d.ts.map b/backend/dist/models/User.d.ts.map new file mode 100644 index 0000000000..a1dab3db69 --- /dev/null +++ b/backend/dist/models/User.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiB,eAAe,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE3E,QAAA,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;EAMf,CAAC;AAGF,MAAM,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,UAAU,CAAC,CAAC;AAGzD,eAAO,MAAM,IAAI,EAAE,KAAK,CAAC,OAAO,CAEI,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/User.js b/backend/dist/models/User.js new file mode 100644 index 0000000000..62327434e7 --- /dev/null +++ b/backend/dist/models/User.js @@ -0,0 +1,9 @@ +import mongoose, { Schema, model } from "mongoose"; +const userSchema = new Schema({ + email: { type: String, unique: true, required: true, index: true }, + passwordHash: { type: String, required: true }, +}, { timestamps: true }); +// Always return a single, strongly-typed Model (no union) +export const User = mongoose.models.User ?? + model("User", userSchema); +//# sourceMappingURL=User.js.map \ No newline at end of file diff --git a/backend/dist/models/User.js.map b/backend/dist/models/User.js.map new file mode 100644 index 0000000000..fdef33291e --- /dev/null +++ b/backend/dist/models/User.js.map @@ -0,0 +1 @@ +{"version":3,"file":"User.js","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAA0B,MAAM,UAAU,CAAC;AAE3E,MAAM,UAAU,GAAG,IAAI,MAAM,CAC3B;IACE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IAClE,YAAY,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;CAC/C,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;AAKF,mEAAmE;AACnE,MAAM,CAAC,MAAM,IAAI,GACd,QAAQ,CAAC,MAAM,CAAC,IAAuB;IACxC,KAAK,CAAU,MAAM,EAAE,UAAU,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/auth.d.ts b/backend/dist/routes/auth.d.ts new file mode 100644 index 0000000000..c8cf2855d0 --- /dev/null +++ b/backend/dist/routes/auth.d.ts @@ -0,0 +1,2 @@ +export declare const authRouter: import("express-serve-static-core").Router; +//# sourceMappingURL=auth.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/auth.d.ts.map b/backend/dist/routes/auth.d.ts.map new file mode 100644 index 0000000000..4a2c07e384 --- /dev/null +++ b/backend/dist/routes/auth.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,UAAU,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/auth.js b/backend/dist/routes/auth.js new file mode 100644 index 0000000000..a1aa7c09e9 --- /dev/null +++ b/backend/dist/routes/auth.js @@ -0,0 +1,59 @@ +import { Router } from "express"; +import { z } from "zod"; +import bcrypt from "bcryptjs"; +import jwt from "jsonwebtoken"; +import { User } from "../models/User.js"; +import { connectDB } from "../lib/db.js"; +export const authRouter = Router(); +authRouter.get("/ping", (_req, res) => res.json({ ok: true, from: "auth" })); +const zCreds = z.object({ + email: z.string().email(), + password: z.string().min(8, "Password must be at least 8 characters"), +}); +// POST /api/auth/register +authRouter.post("/register", async (req, res) => { + try { + await connectDB(); + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + const { email, password } = parsed.data; + const exists = await User.findOne({ email }); + if (exists) + return res.status(409).json({ error: "EmailInUse" }); + const passwordHash = await bcrypt.hash(password, 12); + await User.create({ email, passwordHash }); + return res.status(201).json({ ok: true }); + } + catch (err) { + console.error("REGISTER_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } +}); +// POST /api/auth/login +authRouter.post("/login", async (req, res) => { + try { + await connectDB(); + const parsed = zCreds.safeParse(req.body); + if (!parsed.success) + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + const { email, password } = parsed.data; + const user = await User.findOne({ email }); + if (!user) + return res.status(401).json({ error: "InvalidCredentials" }); + const ok = await bcrypt.compare(password, user.passwordHash); + if (!ok) + return res.status(401).json({ error: "InvalidCredentials" }); + const token = jwt.sign({ sub: String(user._id), email }, process.env.JWT_SECRET, { expiresIn: "7d" }); + return res.json({ token }); + } + catch (err) { + console.error("LOGIN_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } +}); +//# sourceMappingURL=auth.js.map \ No newline at end of file diff --git a/backend/dist/routes/auth.js.map b/backend/dist/routes/auth.js.map new file mode 100644 index 0000000000..b0a26bb65c --- /dev/null +++ b/backend/dist/routes/auth.js.map @@ -0,0 +1 @@ +{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,MAAM,MAAM,UAAU,CAAC;AAC9B,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC;AAEnC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAE7E,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;IACzB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,wCAAwC,CAAC;CACtE,CAAC,CAAC;AAEH,0BAA0B;AAC1B,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAElB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO;YACjB,OAAO,GAAG;iBACP,MAAM,CAAC,GAAG,CAAC;iBACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAErE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;QAExC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7C,IAAI,MAAM;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAEjE,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;QAE3C,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACnC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,uBAAuB;AACvB,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC3C,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAElB,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO;YACjB,OAAO,GAAG;iBACP,MAAM,CAAC,GAAG,CAAC;iBACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAErE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;QAExC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAExE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC7D,IAAI,CAAC,EAAE;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAEtE,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CACpB,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,EAChC,OAAO,CAAC,GAAG,CAAC,UAAW,EACvB,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAC;QACF,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;QAChC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/beaches.d.ts b/backend/dist/routes/beaches.d.ts new file mode 100644 index 0000000000..b0f8f18537 --- /dev/null +++ b/backend/dist/routes/beaches.d.ts @@ -0,0 +1,2 @@ +export declare const beachesRouter: import("express-serve-static-core").Router; +//# sourceMappingURL=beaches.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/beaches.d.ts.map b/backend/dist/routes/beaches.d.ts.map new file mode 100644 index 0000000000..9da2bf7acc --- /dev/null +++ b/backend/dist/routes/beaches.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"beaches.d.ts","sourceRoot":"","sources":["../../src/routes/beaches.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/beaches.js b/backend/dist/routes/beaches.js new file mode 100644 index 0000000000..387774b1c7 --- /dev/null +++ b/backend/dist/routes/beaches.js @@ -0,0 +1,49 @@ +import { Router } from "express"; +import { havGet, getLatestSampleDate } from "../lib/hav.js"; +export const beachesRouter = Router(); +/** + * GET /api/beaches + * Proxies HaV "feature" endpoint and returns (likely) GeoJSON. + */ +beachesRouter.get("/beaches", async (_req, res, next) => { + try { + const data = await havGet("/feature/?format=json", 5 * 60 * 1000); // cache 5 min + res.json(data); + } + catch (err) { + next(err); + } +}); +/** + * GET /api/beaches/:id + * Proxies HaV v1 detail and augments with latestSampleDate. + * Prefers v1.sampleDate (epoch ms); falls back to v2 results when needed. + */ +beachesRouter.get("/beaches/:id", async (req, res, next) => { + try { + const { id } = req.params; + // v1 detail + const detail = await havGet(`/detail/${encodeURIComponent(id)}`, 5 * 60 * 1000); + // prefer v1 sampleDate (epoch ms) + let latestSampleDate = null; + const v1Ms = detail?.sampleDate; + if (typeof v1Ms === "number" && isFinite(v1Ms)) { + latestSampleDate = new Date(v1Ms).toISOString(); + } + else { + // fallback to v2 + try { + latestSampleDate = await getLatestSampleDate(id); + } + catch (e) { + // don't fail the whole request if v2 errors; just leave null + console.warn("[/beaches/:id] v2 fallback failed:", e?.message); + } + } + res.json({ ...detail, latestSampleDate }); + } + catch (err) { + next(err); + } +}); +//# sourceMappingURL=beaches.js.map \ No newline at end of file diff --git a/backend/dist/routes/beaches.js.map b/backend/dist/routes/beaches.js.map new file mode 100644 index 0000000000..7631156e61 --- /dev/null +++ b/backend/dist/routes/beaches.js.map @@ -0,0 +1 @@ +{"version":3,"file":"beaches.js","sourceRoot":"","sources":["../../src/routes/beaches.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAE5D,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC;AAEtC;;;GAGG;AACH,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,uBAAuB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,cAAc;QACjF,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;;;GAIG;AACH,aAAa,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAE1B,YAAY;QACZ,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,WAAW,kBAAkB,CAAC,EAAE,CAAC,EAAE,EACnC,CAAC,GAAG,EAAE,GAAG,IAAI,CACd,CAAC;QAEF,kCAAkC;QAClC,IAAI,gBAAgB,GAAkB,IAAI,CAAC;QAC3C,MAAM,IAAI,GAAI,MAAc,EAAE,UAAU,CAAC;QACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,gBAAgB,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;QAClD,CAAC;aAAM,CAAC;YACN,iBAAiB;YACjB,IAAI,CAAC;gBACH,gBAAgB,GAAG,MAAM,mBAAmB,CAAC,EAAE,CAAC,CAAC;YACnD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,6DAA6D;gBAC7D,OAAO,CAAC,IAAI,CACV,oCAAoC,EACnC,CAAW,EAAE,OAAO,CACtB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/dbCheck.d.ts b/backend/dist/routes/dbCheck.d.ts new file mode 100644 index 0000000000..a5ff42650d --- /dev/null +++ b/backend/dist/routes/dbCheck.d.ts @@ -0,0 +1,2 @@ +export declare const dbCheckRouter: import("express-serve-static-core").Router; +//# sourceMappingURL=dbCheck.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/dbCheck.d.ts.map b/backend/dist/routes/dbCheck.d.ts.map new file mode 100644 index 0000000000..5134bd8370 --- /dev/null +++ b/backend/dist/routes/dbCheck.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"dbCheck.d.ts","sourceRoot":"","sources":["../../src/routes/dbCheck.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/dbCheck.js b/backend/dist/routes/dbCheck.js new file mode 100644 index 0000000000..f6c3041af6 --- /dev/null +++ b/backend/dist/routes/dbCheck.js @@ -0,0 +1,27 @@ +import { Router } from "express"; +import mongoose from "mongoose"; +import { connectDB } from "../lib/db.js"; +export const dbCheckRouter = Router(); +dbCheckRouter.get("/db-check", async (_req, res) => { + try { + // Ensure we attempt a connection before reporting + await connectDB(); + const state = mongoose.connection.readyState; // 0=disconnected, 1=connected, 2=connecting, 3=disconnecting + res.json({ + ok: state === 1, + state, + message: state === 1 + ? "MongoDB is connected" + : state === 2 + ? "MongoDB is connecting..." + : state === 3 + ? "MongoDB is disconnecting..." + : "MongoDB is disconnected", + }); + } + catch (err) { + console.error("DB check error:", err); + res.status(500).json({ ok: false, error: "DB check failed" }); + } +}); +//# sourceMappingURL=dbCheck.js.map \ No newline at end of file diff --git a/backend/dist/routes/dbCheck.js.map b/backend/dist/routes/dbCheck.js.map new file mode 100644 index 0000000000..539bdc8393 --- /dev/null +++ b/backend/dist/routes/dbCheck.js.map @@ -0,0 +1 @@ +{"version":3,"file":"dbCheck.js","sourceRoot":"","sources":["../../src/routes/dbCheck.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC;AAEtC,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,EAAE;IACpE,IAAI,CAAC;QACH,kDAAkD;QAClD,MAAM,SAAS,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,6DAA6D;QAE3G,GAAG,CAAC,IAAI,CAAC;YACP,EAAE,EAAE,KAAK,KAAK,CAAC;YACf,KAAK;YACL,OAAO,EACL,KAAK,KAAK,CAAC;gBACT,CAAC,CAAC,sBAAsB;gBACxB,CAAC,CAAC,KAAK,KAAK,CAAC;oBACb,CAAC,CAAC,0BAA0B;oBAC5B,CAAC,CAAC,KAAK,KAAK,CAAC;wBACb,CAAC,CAAC,6BAA6B;wBAC/B,CAAC,CAAC,yBAAyB;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;QACtC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/favorites.d.ts b/backend/dist/routes/favorites.d.ts new file mode 100644 index 0000000000..7a5b8c589d --- /dev/null +++ b/backend/dist/routes/favorites.d.ts @@ -0,0 +1,2 @@ +export declare const favoritesRouter: import("express-serve-static-core").Router; +//# sourceMappingURL=favorites.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/favorites.d.ts.map b/backend/dist/routes/favorites.d.ts.map new file mode 100644 index 0000000000..8f10dbe75e --- /dev/null +++ b/backend/dist/routes/favorites.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"favorites.d.ts","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,eAAe,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/favorites.js b/backend/dist/routes/favorites.js new file mode 100644 index 0000000000..3c5e90a6ef --- /dev/null +++ b/backend/dist/routes/favorites.js @@ -0,0 +1,78 @@ +import { Router } from "express"; +import { z } from "zod"; +import { Favorite } from "../models/Favorite.js"; +import { requireAuth } from "../middleware/auth.js"; +import { connectDB } from "../lib/db.js"; +import mongoose from "mongoose"; +export const favoritesRouter = Router(); +const zCreate = z.object({ + beachId: z.string().min(1), + note: z.string().max(500).optional(), +}); +favoritesRouter.use(async (_req, _res, next) => { + // ensure DB connection for every favorites op + try { + await connectDB(); + next(); + } + catch (e) { + next(e); + } +}); +// GET /api/favorites -> list current user's favorites +favoritesRouter.get("/favorites", requireAuth, async (req, res) => { + const userId = req.user.sub; + const items = await Favorite.find({ userId }) + .sort({ order: 1, createdAt: -1 }) + .lean(); + res.json(items); +}); +// POST /api/favorites +favoritesRouter.post("/favorites", requireAuth, async (req, res) => { + const parsed = zCreate.safeParse(req.body); + if (!parsed.success) { + return res + .status(400) + .json({ error: "InvalidBody", details: z.treeifyError(parsed.error) }); + } + const { beachId, note } = parsed.data; + const userId = req.user.sub; + try { + const created = await Favorite.create({ + userId, + beachId, + note: note ?? "", + }); + return res.status(201).json(created); + } + catch (err) { + if (err?.code === 11000) { + return res.status(409).json({ error: "AlreadyFavorited" }); + } + throw err; + } +}); +// DELETE /api/favorites/:id +favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { + const userId = req.user.sub; + const id = req.params.id; + if (!id) + return res.status(400).json({ error: "MissingId" }); + if (!mongoose.Types.ObjectId.isValid(id)) { + return res.status(400).json({ error: "InvalidId" }); + } + const deleted = await Favorite.findOneAndDelete({ _id: id, userId }); + if (!deleted) + return res.status(404).json({ error: "NotFound" }); + res.json({ ok: true }); +}); +// DELETE /api/favorites/by-beach/:beachId +favoritesRouter.delete("/favorites/by-beach/:beachId", requireAuth, async (req, res) => { + const userId = req.user.sub; + const { beachId } = req.params; + const deleted = await Favorite.findOneAndDelete({ beachId, userId }); + if (!deleted) + return res.status(404).json({ error: "NotFound" }); + res.json({ ok: true }); +}); +//# sourceMappingURL=favorites.js.map \ No newline at end of file diff --git a/backend/dist/routes/favorites.js.map b/backend/dist/routes/favorites.js.map new file mode 100644 index 0000000000..7e1a85bf5a --- /dev/null +++ b/backend/dist/routes/favorites.js.map @@ -0,0 +1 @@ +{"version":3,"file":"favorites.js","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAiB,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC;AAExC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC7C,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAClB,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,uDAAuD;AACvD,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAChE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;SAC1C,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;SACjC,IAAI,EAAE,CAAC;IACV,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,sBAAsB;AACtB,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,GAAG;aACP,MAAM,CAAC,GAAG,CAAC;aACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACtC,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACpC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,IAAI,EAAE;SACjB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;YACxB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,4BAA4B;AAC5B,eAAe,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACvE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAwB,CAAC;IAC/C,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEH,0CAA0C;AAC1C,eAAe,CAAC,MAAM,CACpB,8BAA8B,EAC9B,WAAW,EACX,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAE/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CACF,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/health.d.ts b/backend/dist/routes/health.d.ts index 7358e505fa..1f40d05b31 100644 --- a/backend/dist/routes/health.d.ts +++ b/backend/dist/routes/health.d.ts @@ -1,2 +1,3 @@ -export declare const healthRouter: import("express-serve-static-core").Router; +declare const router: import("express-serve-static-core").Router; +export { router as healthRouter }; //# sourceMappingURL=health.d.ts.map \ No newline at end of file diff --git a/backend/dist/routes/health.d.ts.map b/backend/dist/routes/health.d.ts.map index 62c17ca00b..a085479687 100644 --- a/backend/dist/routes/health.d.ts.map +++ b/backend/dist/routes/health.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY,4CAAW,CAAC"} \ No newline at end of file +{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAWxB,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/health.js b/backend/dist/routes/health.js index 5b4eb11a90..67052ca2a5 100644 --- a/backend/dist/routes/health.js +++ b/backend/dist/routes/health.js @@ -1,10 +1,12 @@ import { Router } from "express"; -export const healthRouter = Router(); -healthRouter.get("/health", async (_req, res) => { +const router = Router(); +router.get("/health", async (_req, res) => { + console.log("Hit /api/health"); res.json({ ok: true, env: process.env.NODE_ENV ?? "development", timestamp: new Date().toISOString(), }); }); +export { router as healthRouter }; //# sourceMappingURL=health.js.map \ No newline at end of file diff --git a/backend/dist/routes/health.js.map b/backend/dist/routes/health.js.map index 4e7138dfa9..06d8444251 100644 --- a/backend/dist/routes/health.js.map +++ b/backend/dist/routes/health.js.map @@ -1 +1 @@ -{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC;AAErC,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE;IAC9C,GAAG,CAAC,IAAI,CAAC;QACP,EAAE,EAAE,IAAI;QACR,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;QAC1C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAEpD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAa,EAAE,GAAa,EAAE,EAAE;IAC3D,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAC/B,GAAG,CAAC,IAAI,CAAC;QACP,EAAE,EAAE,IAAI;QACR,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;QAC1C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/server.d.ts b/backend/dist/server.d.ts new file mode 100644 index 0000000000..140913316c --- /dev/null +++ b/backend/dist/server.d.ts @@ -0,0 +1,2 @@ +import "dotenv/config"; +//# sourceMappingURL=server.d.ts.map \ No newline at end of file diff --git a/backend/dist/server.d.ts.map b/backend/dist/server.d.ts.map new file mode 100644 index 0000000000..ca9c68387a --- /dev/null +++ b/backend/dist/server.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"} \ No newline at end of file diff --git a/backend/dist/server.js b/backend/dist/server.js new file mode 100644 index 0000000000..12aa2fb28a --- /dev/null +++ b/backend/dist/server.js @@ -0,0 +1,25 @@ +import "dotenv/config"; +import app from "./index.js"; +const port = Number(process.env.PORT) || 3000; +// 🔎 TEMP: log each request so we see what's being hit +app.use((req, _res, next) => { + console.log(`[req] ${req.method} ${req.url}`); + next(); +}); +// 🔎 TEMP: quick env presence check (no secrets exposed) +app.get("/api/debug/env", (_req, res) => { + res.json({ + HAV_BASE_URL: !!process.env.HAV_BASE_URL, + HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, + HAV_V2_BASE: !!process.env.HAV_V2_BASE, + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + MONGODB_URI_present: !!process.env.MONGODB_URI, + JWT_SECRET_present: !!process.env.JWT_SECRET, + NODE_ENV: process.env.NODE_ENV ?? "undefined", + PORT: process.env.PORT ?? "undefined", + }); +}); +app.listen(port, () => { + console.log(`API listening on http://localhost:${port}`); +}); +//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/backend/dist/server.js.map b/backend/dist/server.js.map new file mode 100644 index 0000000000..accbee5697 --- /dev/null +++ b/backend/dist/server.js.map @@ -0,0 +1 @@ +{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,GAAG,MAAM,YAAY,CAAC;AAE7B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAE9C,uDAAuD;AACvD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,yDAAyD;AACzD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACtC,GAAG,CAAC,IAAI,CAAC;QACP,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;QAC5C,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QACtC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;QAChD,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QAC9C,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU;QAC5C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,WAAW;QAC7C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;KACtC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 7ed02f8862..4423c9a289 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "tsx watch src/server.ts", "build": "tsc", - "start": "node dist/index.js" + "start": "node dist/server.js" }, "dependencies": { "bcryptjs": "^3.0.2", From e7f5000ce5cf3e8614067d715db415fd95b7f276 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:53:39 +0200 Subject: [PATCH 147/215] fix(auth): use auth store in BeachDetailPage and redirect to /login instead of prompting for JWT --- frontend/src/pages/BeachDetailPage.tsx | 53 +++++++++++++------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/frontend/src/pages/BeachDetailPage.tsx b/frontend/src/pages/BeachDetailPage.tsx index 7619e4c158..a7fc993307 100644 --- a/frontend/src/pages/BeachDetailPage.tsx +++ b/frontend/src/pages/BeachDetailPage.tsx @@ -1,4 +1,4 @@ -import { useParams, Link } from "react-router-dom"; +import { useParams, Link, useNavigate, useLocation } from "react-router-dom"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { fetchBeach } from "../api/beaches"; import { formatDate } from "../utils/format"; @@ -7,7 +7,7 @@ import { useAddFavorite, useRemoveFavorite, } from "../api/favorites"; -import { getToken, setToken } from "../utils/auth"; +import { useAuth } from "@/store/auth"; // NEW // Map numeric/class text → color class function qualityClass(q: number | string | undefined) { @@ -38,6 +38,9 @@ function qualityClass(q: number | string | undefined) { export default function BeachDetailPage() { const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const location = useLocation(); + const { token } = useAuth(); // NEW const { data, isLoading, isError, error } = useQuery({ queryKey: ["beach", id], @@ -48,7 +51,6 @@ export default function BeachDetailPage() { // --- FAVORITES HOOKS --- const queryClient = useQueryClient(); - const token = getToken(); const { data: favorites } = useFavorites(); const addFav = useAddFavorite(); const rmFav = useRemoveFavorite(); @@ -97,6 +99,26 @@ export default function BeachDetailPage() { ? formatDate(data.latestSampleDate, "short") : "—"; + async function handleFavoriteClick() { + if (!token) { + // Not logged in → route to login and return here after + navigate("/login", { replace: false, state: { from: location } }); + return; + } + try { + if (isFav) { + await rmFav.mutateAsync({ id: existingFav?._id, beachId: id }); + } else { + await addFav.mutateAsync(id!); // mutation takes beachId string + } + // Optional: refresh favorites/beach queries + queryClient.invalidateQueries({ queryKey: ["favorites", token] }); + queryClient.invalidateQueries({ queryKey: ["beach", id] }); + } catch (e: any) { + alert(e?.message ?? "Favorite action failed"); + } + } + return (
            {/* Heading block */} @@ -139,30 +161,7 @@ export default function BeachDetailPage() {
            - - ))} +
            + + {title} + +
            + {muni || "—"} +
            +
            + {qText} +
            +
            + +
            + + View + + +
            + + ); + })}
          ); From a7304f30e44113f1afe584d9ac90a505782876dd Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:14:55 +0200 Subject: [PATCH 150/215] feat(favorites): add friendly empty state with CTA to map --- frontend/src/pages/FavoritesPage.tsx | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index a9ba6524d2..b7fc41fce5 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -60,14 +60,19 @@ export default function FavoritesPage() {
          - {(!favorites || favorites.length === 0) && ( -

          - You don’t have any favorites yet. Go to the{" "} - - map - {" "} - and save your first one! -

          + {favorites && favorites.length === 0 && ( +
          +

          No favorites yet

          +

          + Browse the map and tap “Save as favorite” on a beach you like. +

          + + Go to map + +
          )}
            From 2aa87183d6a47511267d6d13dfcd96d88f3bec20 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:16:36 +0200 Subject: [PATCH 151/215] =?UTF-8?q?chore(favorites):=20show=20subtle=20'up?= =?UTF-8?q?dating=E2=80=A6'=20hint=20while=20beach=20details=20load?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/FavoritesPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index b7fc41fce5..c031981f9e 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -99,8 +99,11 @@ export default function FavoritesPage() {
            {muni || "—"}
            -
            +
            {qText} + {details.isLoading && ( + updating… + )}
            From 947e417fcdf3523dda78c540692b85ddc65a46f9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:28:42 +0200 Subject: [PATCH 152/215] feat(favorites): add client-side sorting by name or municipality --- frontend/src/pages/FavoritesPage.tsx | 79 +++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index c031981f9e..e4960e4b06 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -1,6 +1,7 @@ import { useFavorites, useRemoveFavorite } from "@/api/favorites"; import { useBeachDetails } from "@/api/useBeachDetails"; import { Link } from "react-router-dom"; +import { useState, useMemo, useEffect } from "react"; function qualityClass(q?: number | string) { if (typeof q === "number") { @@ -24,6 +25,42 @@ export default function FavoritesPage() { const details = useBeachDetails(ids); const rmFav = useRemoveFavorite(); + // --- Sorting state with persistence --- + const [sortBy, setSortBy] = useState<"name" | "municipality">(() => { + return ( + (localStorage.getItem("favoritesSort") as "name" | "municipality") ?? + "name" + ); + }); + + useEffect(() => { + localStorage.setItem("favoritesSort", sortBy); + }, [sortBy]); + + // --- Derived, enriched list --- + const items = useMemo(() => { + if (!favorites) return []; + const enriched = favorites.map((f) => { + const info = details.byId.get(f.beachId); + return { + fav: f, + name: info?.locationName ?? f.beachId, + muni: info?.locationArea ?? "", + classification: info?.classification, + classificationText: info?.classificationText ?? "Unknown", + }; + }); + + const collator = new Intl.Collator(undefined, { sensitivity: "base" }); + enriched.sort((a, b) => { + if (sortBy === "name") return collator.compare(a.name, b.name); + return collator.compare(a.muni, b.muni); + }); + + return enriched; + }, [favorites, details.byId, sortBy]); + + // --- Loading state --- if (isLoading) { return (
            @@ -40,6 +77,7 @@ export default function FavoritesPage() { ); } + // --- Error state --- if (isError) { return (
            @@ -51,13 +89,29 @@ export default function FavoritesPage() { ); } + // --- Normal render --- return (

            Your favorite beaches

            - - Browse all beaches - +
            + + + Browse all beaches + +
            {favorites && favorites.length === 0 && ( @@ -76,12 +130,9 @@ export default function FavoritesPage() { )}
              - {favorites?.map((fav) => { - const info = details.byId.get(fav.beachId); - const title = info?.locationName ?? fav.beachId; - const muni = info?.locationArea ?? ""; - const qText = info?.classificationText ?? "Unknown"; - const qClass = qualityClass(info?.classification ?? qText); + {items.map((item) => { + const { fav, name, muni, classification, classificationText } = item; + const qClass = qualityClass(classification ?? classificationText); return (
            • - {title} + {name}
              {muni || "—"}
              - {qText} + + {classificationText} + {details.isLoading && ( updating… )} @@ -120,7 +173,7 @@ export default function FavoritesPage() { onClick={() => rmFav.mutateAsync({ id: fav._id, beachId: fav.beachId }) } - aria-label={`Remove ${title} from favorites`} + aria-label={`Remove ${name} from favorites`} > Remove From 56aa68eed53c3f1479cb878c0977625562e7ff1c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:47:49 +0200 Subject: [PATCH 153/215] chore(deps): add dnd-kit for drag and drop --- frontend/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/package.json b/frontend/package.json index fee9e2002c..da10d2f391 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.2.2", "@tanstack/react-query": "^5.87.4", "i18next": "^25.5.2", From 472b9b3c0e3e3381878dc7b211ddddc8ac975ed9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:08:48 +0200 Subject: [PATCH 154/215] feat(favorites): add SortableFavorite component (dnd-kit useSortable) --- frontend/src/components/SortableFavorite.tsx | 103 +++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 frontend/src/components/SortableFavorite.tsx diff --git a/frontend/src/components/SortableFavorite.tsx b/frontend/src/components/SortableFavorite.tsx new file mode 100644 index 0000000000..41f294ec86 --- /dev/null +++ b/frontend/src/components/SortableFavorite.tsx @@ -0,0 +1,103 @@ +import { Link } from "react-router-dom"; +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from "@dnd-kit/utilities"; + +type Props = { + id: string; // beachId + name: string; + muni: string; + classificationText: string; + classificationClass: string; // Tailwind badge class + onRemove: () => void; + disabled?: boolean; +}; + +export default function SortableFavorite({ + id, + name, + muni, + classificationText, + classificationClass, + onRemove, + disabled, +}: Props) { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id, disabled }); + + const style: React.CSSProperties = { + transform: CSS.Transform.toString(transform), + transition, + opacity: isDragging ? 0.8 : 1, + }; + + return ( +
            • +
              +
              + {/* Drag handle (hidden when disabled) */} + {!disabled && ( + + )} + + {name} + +
              +
              {muni || "—"}
              +
              + + {classificationText} + +
              +
              + +
              + + View + + +
              +
            • + ); +} From f2820bf4d12d65127cf1c12fc3c299e3d09e9793 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:13:17 +0200 Subject: [PATCH 155/215] feat(favorites): add custom drag-and-drop reordering with dnd-kit and persist order locally; avoid indexing into possibly-undefined favorites; use explicit Favorite type in enriched map --- frontend/src/pages/FavoritesPage.tsx | 245 ++++++++++++++++++--------- 1 file changed, 164 insertions(+), 81 deletions(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index e4960e4b06..a79f5cc5e9 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -1,7 +1,25 @@ -import { useFavorites, useRemoveFavorite } from "@/api/favorites"; +import { + useFavorites, + useRemoveFavorite, + type Favorite, +} from "@/api/favorites"; import { useBeachDetails } from "@/api/useBeachDetails"; import { Link } from "react-router-dom"; import { useState, useMemo, useEffect } from "react"; +import { + DndContext, + closestCenter, + PointerSensor, + useSensor, + useSensors, + DragEndEvent, +} from "@dnd-kit/core"; +import { + SortableContext, + arrayMove, + rectSortingStrategy, +} from "@dnd-kit/sortable"; +import SortableFavorite from "@/components/SortableFavorite"; function qualityClass(q?: number | string) { if (typeof q === "number") { @@ -19,48 +37,126 @@ function qualityClass(q?: number | string) { return "kpi-unknown"; } +const SORT_KEY = "favoritesSort"; // 'custom' | 'name' | 'municipality' +const ORDER_KEY = "favoritesOrder:v1"; // stores array of beachIds + export default function FavoritesPage() { const { data: favorites, isLoading, isError, error } = useFavorites(); const ids = favorites?.map((f) => f.beachId); const details = useBeachDetails(ids); const rmFav = useRemoveFavorite(); - // --- Sorting state with persistence --- - const [sortBy, setSortBy] = useState<"name" | "municipality">(() => { - return ( - (localStorage.getItem("favoritesSort") as "name" | "municipality") ?? - "name" - ); - }); - + // --- Sort mode with persistence + const [sortBy, setSortBy] = useState<"custom" | "name" | "municipality">( + () => { + return ( + (localStorage.getItem(SORT_KEY) as + | "custom" + | "name" + | "municipality") ?? "name" + ); + } + ); useEffect(() => { - localStorage.setItem("favoritesSort", sortBy); + localStorage.setItem(SORT_KEY, sortBy); }, [sortBy]); - // --- Derived, enriched list --- - const items = useMemo(() => { - if (!favorites) return []; - const enriched = favorites.map((f) => { + // --- Custom order state (array of beachIds), persisted + const [order, setOrder] = useState(() => { + try { + const raw = localStorage.getItem(ORDER_KEY); + return raw ? (JSON.parse(raw) as string[]) : []; + } catch { + return []; + } + }); + // Reconcile order whenever favorites change (add missing, remove gone) + useEffect(() => { + if (!favorites) return; + const favIds = favorites.map((f) => f.beachId); + const known = new Set(order); + // add new + const merged = [...order, ...favIds.filter((id) => !known.has(id))]; + // remove deleted + const filtered = merged.filter((id) => favIds.includes(id)); + if (JSON.stringify(filtered) !== JSON.stringify(order)) { + setOrder(filtered); + } + }, [favorites]); // eslint-disable-line react-hooks/exhaustive-deps + useEffect(() => { + localStorage.setItem(ORDER_KEY, JSON.stringify(order)); + }, [order]); + + // --- Enriched items map + const enriched = useMemo(() => { + const map = new Map< + string, + { + fav: Favorite; + name: string; + muni: string; + classification?: number | string; + classificationText: string; + } + >(); + + if (!favorites) return map; + + for (const f of favorites) { const info = details.byId.get(f.beachId); - return { + map.set(f.beachId, { fav: f, name: info?.locationName ?? f.beachId, muni: info?.locationArea ?? "", - classification: info?.classification, + classification: info?.classification ?? info?.classificationText, classificationText: info?.classificationText ?? "Unknown", - }; - }); + }); + } + return map; + }, [favorites, details.byId]); + + // --- Items to render (IDs in display order) + const displayIds = useMemo(() => { + if (!favorites) return []; + const favIds = favorites.map((f) => f.beachId); + + if (sortBy === "custom" && order.length) { + // follow custom order, but only those that still exist + return order.filter((id) => favIds.includes(id)); + } + // Name / Municipality sorting + const list = favIds.slice(); const collator = new Intl.Collator(undefined, { sensitivity: "base" }); - enriched.sort((a, b) => { - if (sortBy === "name") return collator.compare(a.name, b.name); - return collator.compare(a.muni, b.muni); + list.sort((a, b) => { + const A = enriched.get(a); + const B = enriched.get(b); + if (!A || !B) return 0; + if (sortBy === "name") return collator.compare(A.name, B.name); + return collator.compare(A.muni, B.muni); }); + return list; + }, [favorites, sortBy, order, enriched]); - return enriched; - }, [favorites, details.byId, sortBy]); + // --- DnD sensors + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 5 } }) + ); - // --- Loading state --- + function onDragEnd(event: DragEndEvent) { + if (sortBy !== "custom") return; // only draggable in custom mode + const { active, over } = event; + if (!over || active.id === over.id) return; + + const oldIndex = order.indexOf(String(active.id)); + const newIndex = order.indexOf(String(over.id)); + if (oldIndex === -1 || newIndex === -1) return; + + const next = arrayMove(order, oldIndex, newIndex); + setOrder(next); + } + + // --- Loading state if (isLoading) { return (
              @@ -77,7 +173,7 @@ export default function FavoritesPage() { ); } - // --- Error state --- + // --- Error state if (isError) { return (
              @@ -89,10 +185,10 @@ export default function FavoritesPage() { ); } - // --- Normal render --- + // --- Normal render return (
              -
              +

              Your favorite beaches

              @@ -129,59 +227,44 @@ export default function FavoritesPage() {
              )} -
                - {items.map((item) => { - const { fav, name, muni, classification, classificationText } = item; - const qClass = qualityClass(classification ?? classificationText); + {/* Responsive grid: sm≥768:2col, lg≥1024:3col, 2xl≥1536:4col */} + + +
                  + {displayIds.map((id) => { + const item = enriched.get(id); + if (!item) return null; + const { fav, name, muni, classification, classificationText } = + item; + const klass = qualityClass(classification ?? classificationText); - return ( -
                • -
                  - - {name} - -
                  - {muni || "—"} -
                  -
                  - - {classificationText} - - {details.isLoading && ( - updating… - )} -
                  -
                  - -
                  - - View - - -
                  -
                • - ); - })} -
                + /> + ); + })} +
              + +
              ); } From eb44ad7be60739f75260f11e3beb8f9111276ec0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:25:28 +0200 Subject: [PATCH 156/215] feat(backend): add field to Favorite model and composite unique index --- backend/src/models/Favorite.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/models/Favorite.ts b/backend/src/models/Favorite.ts index 5815e62f97..40b2162b6f 100644 --- a/backend/src/models/Favorite.ts +++ b/backend/src/models/Favorite.ts @@ -8,9 +8,12 @@ const favoriteSchema = new Schema( required: true, index: true, }, - beachId: { type: String, required: true }, // HaV beach identifier (string) - note: { type: String, default: "" }, // optional user note - order: { type: Number, default: 0 }, // for drag & drop later + // HaV beach identifier (string) + beachId: { type: String, required: true, index: true }, + // optional user note + note: { type: String, default: "" }, + // for drag & drop ordering (lower = earlier) + order: { type: Number, default: 0 }, }, { timestamps: true } ); From 59793ad755f3fd1d61c273fa5a4cfe4e0456e358 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:26:34 +0200 Subject: [PATCH 157/215] feat(backend): add PATCH /api/favorites/reorder to persist custom order --- backend/src/routes/favorites.ts | 85 ++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/backend/src/routes/favorites.ts b/backend/src/routes/favorites.ts index 7a07108ec5..3911dc8b80 100644 --- a/backend/src/routes/favorites.ts +++ b/backend/src/routes/favorites.ts @@ -12,8 +12,8 @@ const zCreate = z.object({ note: z.string().max(500).optional(), }); +// Ensure DB connection for every favorites op favoritesRouter.use(async (_req, _res, next) => { - // ensure DB connection for every favorites op try { await connectDB(); next(); @@ -22,19 +22,26 @@ favoritesRouter.use(async (_req, _res, next) => { } }); -// GET /api/favorites -> list current user's favorites +/** + * GET /api/favorites + * List current user's favorites (stable order) + */ favoritesRouter.get("/favorites", requireAuth, async (req, res) => { const userId = (req as AuthedRequest).user.sub; const items = await Favorite.find({ userId }) - .sort({ order: 1, createdAt: -1 }) + .sort({ order: 1, updatedAt: 1, _id: 1 }) // stable sort .lean(); res.json(items); }); -// POST /api/favorites +/** + * POST /api/favorites + */ favoritesRouter.post("/favorites", requireAuth, async (req, res) => { const parsed = zCreate.safeParse(req.body); if (!parsed.success) { + // keep the existing error style + // @ts-ignore - z.treeifyError is available in the codebase return res .status(400) .json({ error: "InvalidBody", details: z.treeifyError(parsed.error) }); @@ -58,7 +65,9 @@ favoritesRouter.post("/favorites", requireAuth, async (req, res) => { } }); -// DELETE /api/favorites/:id +/** + * DELETE /api/favorites/:id + */ favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { const userId = (req as AuthedRequest).user.sub; @@ -74,7 +83,9 @@ favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { res.json({ ok: true }); }); -// DELETE /api/favorites/by-beach/:beachId +/** + * DELETE /api/favorites/by-beach/:beachId + */ favoritesRouter.delete( "/favorites/by-beach/:beachId", requireAuth, @@ -87,3 +98,65 @@ favoritesRouter.delete( res.json({ ok: true }); } ); + +/** + * PATCH /api/favorites/reorder + * Persist custom order. Accepts: { order: string[] } where each string is a beachId. + * Non-owned or unknown ids are ignored. Any favorites not included are appended after. + */ +favoritesRouter.patch("/favorites/reorder", requireAuth, async (req, res) => { + try { + const userId = (req as AuthedRequest).user.sub; + + const schema = z.object({ + order: z.array(z.string()).min(0), + }); + + const parsed = schema.safeParse(req.body); + if (!parsed.success) { + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + } + + const { order } = parsed.data; + + // Fetch user's current favorites + const favs = await Favorite.find({ userId }, { beachId: 1 }).lean(); + const currentIds = new Set(favs.map((f) => f.beachId)); + + // Keep only valid (owned) ids, preserve payload order + const sanitized = order.filter((id) => currentIds.has(id)); + + // Bulk update for provided ids + const ops: any[] = sanitized.map((beachId, idx) => ({ + updateOne: { + filter: { userId, beachId }, + update: { $set: { order: idx } }, + }, + })); + + // Append any missing favorites not included in payload + const missing = favs + .map((f) => f.beachId) + .filter((id) => !sanitized.includes(id)); + + ops.push( + ...missing.map((beachId, addIdx) => ({ + updateOne: { + filter: { userId, beachId }, + update: { $set: { order: sanitized.length + addIdx } }, + }, + })) + ); + + if (ops.length > 0) { + await Favorite.bulkWrite(ops, { ordered: false }); + } + + return res.status(204).end(); + } catch (err) { + console.error("REORDER_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } +}); From 9ad72690a7ce05c8330339d55881447abe4ea9fa Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:21:04 +0200 Subject: [PATCH 158/215] feat(backend): finalize PATCH /favorites/reorder with id de-duplication and typed bulkWrite ops --- backend/src/routes/favorites.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/src/routes/favorites.ts b/backend/src/routes/favorites.ts index 3911dc8b80..a637587d64 100644 --- a/backend/src/routes/favorites.ts +++ b/backend/src/routes/favorites.ts @@ -4,6 +4,7 @@ import { Favorite } from "../models/Favorite.js"; import { requireAuth, AuthedRequest } from "../middleware/auth.js"; import { connectDB } from "../lib/db.js"; import mongoose from "mongoose"; +import type { AnyBulkWriteOperation } from "mongoose"; export const favoritesRouter = Router(); @@ -119,7 +120,13 @@ favoritesRouter.patch("/favorites/reorder", requireAuth, async (req, res) => { .json({ error: "InvalidBody", details: parsed.error.flatten() }); } - const { order } = parsed.data; + // Deduplicate ids while preserving first occurrence order + const seen = new Set(); + const order = parsed.data.order.filter((id) => { + if (seen.has(id)) return false; + seen.add(id); + return true; + }); // Fetch user's current favorites const favs = await Favorite.find({ userId }, { beachId: 1 }).lean(); @@ -129,7 +136,7 @@ favoritesRouter.patch("/favorites/reorder", requireAuth, async (req, res) => { const sanitized = order.filter((id) => currentIds.has(id)); // Bulk update for provided ids - const ops: any[] = sanitized.map((beachId, idx) => ({ + const ops: AnyBulkWriteOperation[] = sanitized.map((beachId, idx) => ({ updateOne: { filter: { userId, beachId }, update: { $set: { order: idx } }, From 4e187c9bd84e7cdbfbfe82af82d663c018ffbb1f Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:26:05 +0200 Subject: [PATCH 159/215] feat(api): add useReorderFavorites mutation to persist favorites order --- frontend/src/api/favorites.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/frontend/src/api/favorites.ts b/frontend/src/api/favorites.ts index 1d42112245..ffea8f1a17 100644 --- a/frontend/src/api/favorites.ts +++ b/frontend/src/api/favorites.ts @@ -70,3 +70,18 @@ export function useRemoveFavorite() { }, }); } + +export function useReorderFavorites() { + const qc = useQueryClient(); + const token = useAuth.getState().token; + return useMutation({ + mutationFn: (order: string[]) => + apiFetch("/favorites/reorder", { + method: "PATCH", + body: JSON.stringify({ order }), + }), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ["favorites", token] }); + }, + }); +} From 5c23ef998b16f85fb45006b0f1f0bdd94ee0bf04 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:33:22 +0200 Subject: [PATCH 160/215] feat(favorites): persist custom drag-and-drop order to server on drag end and removal --- frontend/src/pages/FavoritesPage.tsx | 29 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index a79f5cc5e9..c252f56a4b 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -2,6 +2,7 @@ import { useFavorites, useRemoveFavorite, type Favorite, + useReorderFavorites, } from "@/api/favorites"; import { useBeachDetails } from "@/api/useBeachDetails"; import { Link } from "react-router-dom"; @@ -45,17 +46,13 @@ export default function FavoritesPage() { const ids = favorites?.map((f) => f.beachId); const details = useBeachDetails(ids); const rmFav = useRemoveFavorite(); + const reorderFavs = useReorderFavorites(); // --- Sort mode with persistence const [sortBy, setSortBy] = useState<"custom" | "name" | "municipality">( - () => { - return ( - (localStorage.getItem(SORT_KEY) as - | "custom" - | "name" - | "municipality") ?? "name" - ); - } + () => + (localStorage.getItem(SORT_KEY) as "custom" | "name" | "municipality") ?? + "name" ); useEffect(() => { localStorage.setItem(SORT_KEY, sortBy); @@ -75,9 +72,7 @@ export default function FavoritesPage() { if (!favorites) return; const favIds = favorites.map((f) => f.beachId); const known = new Set(order); - // add new const merged = [...order, ...favIds.filter((id) => !known.has(id))]; - // remove deleted const filtered = merged.filter((id) => favIds.includes(id)); if (JSON.stringify(filtered) !== JSON.stringify(order)) { setOrder(filtered); @@ -121,11 +116,9 @@ export default function FavoritesPage() { const favIds = favorites.map((f) => f.beachId); if (sortBy === "custom" && order.length) { - // follow custom order, but only those that still exist return order.filter((id) => favIds.includes(id)); } - // Name / Municipality sorting const list = favIds.slice(); const collator = new Intl.Collator(undefined, { sensitivity: "base" }); list.sort((a, b) => { @@ -144,7 +137,7 @@ export default function FavoritesPage() { ); function onDragEnd(event: DragEndEvent) { - if (sortBy !== "custom") return; // only draggable in custom mode + if (sortBy !== "custom") return; const { active, over } = event; if (!over || active.id === over.id) return; @@ -154,6 +147,14 @@ export default function FavoritesPage() { const next = arrayMove(order, oldIndex, newIndex); setOrder(next); + + // Call backend to persist + reorderFavs.mutate(next, { + onError: () => { + // Rollback on error + setOrder(order); + }, + }); } // --- Loading state @@ -227,7 +228,6 @@ export default function FavoritesPage() {
          )} - {/* Responsive grid: sm≥768:2col, lg≥1024:3col, 2xl≥1536:4col */} { - // also update local custom order setOrder((prev) => prev.filter((x) => x !== id)); }) } From 7f0b62d8a71a4d2552e8bf19ca82ef93d0878ba8 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 26 Sep 2025 17:48:10 +0200 Subject: [PATCH 161/215] fix(api): subscribe to token in useFavorites and harden apiFetch base URL handling --- frontend/src/api/client.ts | 10 +++++++++- frontend/src/api/favorites.ts | 20 +++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 5fbd5604a4..db113d6685 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -11,6 +11,12 @@ export async function apiFetch( path: string, options: FetchOptions = {} ): Promise { + if (!API_BASE && !path.startsWith("http")) { + throw new Error( + "VITE_API_BASE is not set. Did you configure frontend/.env(.local)?" + ); + } + const token = useAuth.getState().token; const url = path.startsWith("http") ? path : `${API_BASE}${path}`; @@ -33,7 +39,9 @@ export async function apiFetch( let body: any = null; try { body = await res.json(); - } catch {} + } catch { + // non-JSON error response + } const error = new Error(body?.error || res.statusText); (error as any).status = res.status; (error as any).details = body; diff --git a/frontend/src/api/favorites.ts b/frontend/src/api/favorites.ts index ffea8f1a17..ba14f7da9c 100644 --- a/frontend/src/api/favorites.ts +++ b/frontend/src/api/favorites.ts @@ -12,16 +12,25 @@ export type Favorite = { updatedAt?: string; }; +/** + * List favorites for the current user. + * IMPORTANT: subscribe to token via selector so the query updates on login/logout. + */ export function useFavorites() { - const token = useAuth.getState().token; + const token = useAuth((s) => s.token); return useQuery({ queryKey: ["favorites", token], enabled: !!token, queryFn: () => apiFetch("/favorites"), staleTime: 60_000, + // keepPreviousData: true, // optional for smoother transitions }); } +/** + * Add a new favorite by beachId. + * OK to use getState() in mutations to avoid unnecessary re-renders. + */ export function useAddFavorite() { const qc = useQueryClient(); const token = useAuth.getState().token; @@ -38,6 +47,9 @@ export function useAddFavorite() { }); } +/** + * Remove a favorite (by _id or by beachId) with a small optimistic update. + */ export function useRemoveFavorite() { const qc = useQueryClient(); const token = useAuth.getState().token; @@ -49,11 +61,9 @@ export function useRemoveFavorite() { : `/favorites/by-beach/${vars.beachId}`; return apiFetch(url, { method: "DELETE" }); }, - // small optimistic update (works for either id or beachId) onMutate: async (vars) => { await qc.cancelQueries({ queryKey: ["favorites", token] }); const prev = qc.getQueryData(["favorites", token]); - if (prev) { const next = prev.filter((f) => vars.id ? f._id !== vars.id : f.beachId !== vars.beachId @@ -71,6 +81,10 @@ export function useRemoveFavorite() { }); } +/** + * Persist custom order of favorites on the server. + * Accepts an array of beachIds in the desired order. + */ export function useReorderFavorites() { const qc = useQueryClient(); const token = useAuth.getState().token; From 0d93b7a98efc51cb4e85dd908c62894f3c2cce63 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 18:53:37 +0200 Subject: [PATCH 162/215] style(favorites): stack title/sort/link on mobile with responsive flex utilities, prevent card overflow on 320px and replace drag handle with 2x2 grip; remove list bullets --- frontend/src/components/SortableFavorite.tsx | 38 ++++++++++---------- frontend/src/pages/FavoritesPage.tsx | 8 ++--- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/frontend/src/components/SortableFavorite.tsx b/frontend/src/components/SortableFavorite.tsx index 41f294ec86..66a3218454 100644 --- a/frontend/src/components/SortableFavorite.tsx +++ b/frontend/src/components/SortableFavorite.tsx @@ -33,49 +33,46 @@ export default function SortableFavorite({ const style: React.CSSProperties = { transform: CSS.Transform.toString(transform), transition, - opacity: isDragging ? 0.8 : 1, + opacity: isDragging ? 0.85 : 1, }; return (
        • -
          +
          - {/* Drag handle (hidden when disabled) */} + {/* Drag handle */} {!disabled && ( )} + {name}
          +
          {muni || "—"}
          +
          {classificationText} @@ -83,7 +80,8 @@ export default function SortableFavorite({
          -
          + {/* Actions - allow wrapping on narrow screens */} +
          -
          +

          Your favorite beaches

          -
          +
          - + Browse all beaches
          @@ -234,7 +234,7 @@ export default function FavoritesPage() { onDragEnd={onDragEnd} > -
            +
              {displayIds.map((id) => { const item = enriched.get(id); if (!item) return null; From 257f68a7d67c62b2236a2e5173abdbcd2a985b0a Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:06:39 +0200 Subject: [PATCH 163/215] feat(netlify): add SPA redirects for Vite in frontend/_redirects --- frontend/_redirects | 1 + 1 file changed, 1 insertion(+) create mode 100644 frontend/_redirects diff --git a/frontend/_redirects b/frontend/_redirects new file mode 100644 index 0000000000..9fe800e1b2 --- /dev/null +++ b/frontend/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file From 6e187a12ffd29fa4ba24718feb9bd66ea8c8c23e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:11:14 +0200 Subject: [PATCH 164/215] feat(backend): configure CORS via CORS_ORIGIN env (comma-separated) with credentials support --- backend/server.js | 53 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/backend/server.js b/backend/server.js index 070c875189..d5b7ad4af2 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,22 +1,63 @@ +import dotenv from "dotenv"; +dotenv.config(); + import express from "express"; import cors from "cors"; import mongoose from "mongoose"; -const mongoUrl = process.env.MONGO_URL || "mongodb://localhost/final-project"; +// --- DB connection --- +const mongoUrl = + process.env.MONGODB_URI || + process.env.MONGO_URL || + "mongodb://localhost/final-project"; + +mongoose.set("strictQuery", true); mongoose.connect(mongoUrl); mongoose.Promise = Promise; -const port = process.env.PORT || 8080; -const app = express(); +// --- CORS setup --- +// Example: CORS_ORIGIN="http://localhost:5173,https://netlifylink.app +const allowedOrigins = (process.env.CORS_ORIGIN || "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + +const corsOptions = { + origin(origin, callback) { + // Allow non-browser clients (no Origin header) + if (!origin) return callback(null, true); -app.use(cors()); + // If no env configured, fall back to permissive (useful in early dev) + if (allowedOrigins.length === 0) return callback(null, true); + + if (allowedOrigins.includes(origin)) { + return callback(null, true); + } + return callback(new Error("Not allowed by CORS")); + }, + credentials: true, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", +}; + +// Apply CORS + JSON +const app = express(); +app.use(cors(corsOptions)); +// For preflight requests across all routes +app.options("*", cors(corsOptions)); app.use(express.json()); -app.get("/", (req, res) => { +// --- Routes --- +app.get("/", (_req, res) => { res.send("Hello Technigo!"); }); -// Start the server +// --- Server --- +const port = process.env.PORT || 8080; app.listen(port, () => { console.log(`Server running on http://localhost:${port}`); + if (allowedOrigins.length) { + console.log("CORS allowed origins:", allowedOrigins); + } else { + console.log("CORS: no CORS_ORIGIN set -> permissive (dev mode)."); + } }); From ab833cffa3666fd51c925ecf5dea3d601e678c5c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:15:12 +0200 Subject: [PATCH 165/215] feat(backend): add env-driven CORS in index.ts (CORS_ORIGIN with fallback) and keep debug in server.ts --- backend/src/index.ts | 40 +++++++++++++++++++++++++++++++--------- backend/src/server.ts | 10 ++++++++++ 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index eeade12058..c7357c6a38 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -12,19 +12,42 @@ import { beachesRouter } from "./routes/beaches.js"; const app = express(); -// --- Global middleware --- -const allowed = (process.env.ALLOWED_ORIGIN ?? "") +// --- CORS setup --- +// Prefer CORS_ORIGIN; fallback to ALLOWED_ORIGIN. +// Example env: CORS_ORIGIN="http://localhost:5173,https://your-site.netlify.app" +const allowedOrigins = ( + process.env.CORS_ORIGIN || + process.env.ALLOWED_ORIGIN || + "" +) .split(",") .map((s) => s.trim()) .filter(Boolean); -app.use( - cors({ - origin: allowed.length ? allowed : true, // during local dev if empty, allow all - credentials: true, - }) -); +const corsOptions: cors.CorsOptions = { + origin(origin, callback) { + // Allow requests without Origin header (e.g. Postman, curl) + if (!origin) return callback(null, true); + + if (allowedOrigins.length === 0) { + // no origins configured → dev mode → allow all + return callback(null, true); + } + if (allowedOrigins.includes(origin)) { + return callback(null, true); + } + return callback(new Error("Not allowed by CORS")); + }, + credentials: true, + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", +}; + +app.use(cors(corsOptions)); +// Preflight for all routes +app.options("*", cors(corsOptions)); + +// --- Global middleware --- app.use(express.json()); // --- Public routes (mounted under /api) --- @@ -41,7 +64,6 @@ app.get("/api/protected/ping", requireAuth, (req, res) => { res.json({ ok: true, user: (req as any).user }); }); -// To protect /api/auth/me this can also be used app.get("/api/auth/me", requireAuth, (req, res) => { res.json({ user: (req as any).user }); }); diff --git a/backend/src/server.ts b/backend/src/server.ts index 809fe6c82d..1d4b9c4970 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -16,6 +16,7 @@ app.get("/api/debug/env", (_req, res) => { HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, HAV_V2_BASE: !!process.env.HAV_V2_BASE, ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", MONGODB_URI_present: !!process.env.MONGODB_URI, JWT_SECRET_present: !!process.env.JWT_SECRET, NODE_ENV: process.env.NODE_ENV ?? "undefined", @@ -25,4 +26,13 @@ app.get("/api/debug/env", (_req, res) => { app.listen(port, () => { console.log(`API listening on http://localhost:${port}`); + const allowed = (process.env.CORS_ORIGIN || process.env.ALLOWED_ORIGIN || "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + if (allowed.length) { + console.log("CORS allowed origins:", allowed); + } else { + console.log("CORS: no origins configured → permissive (dev mode)."); + } }); From 8c8be4afbaeee227f40eac56dc57dc780fea826b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:28:46 +0200 Subject: [PATCH 166/215] chore: track frontend/package-lock.json for Netlify build --- .gitignore | 3 +- frontend/package-lock.json | 6722 ++++++++++++++++++++++++++++++++++++ 2 files changed, 6724 insertions(+), 1 deletion(-) create mode 100644 frontend/package-lock.json diff --git a/.gitignore b/.gitignore index 3d70248ba2..1622beaf01 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -package-lock.json \ No newline at end of file +package-lock.json +!frontend/package-lock.json \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000000..b0aa769124 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,6722 @@ +{ + "name": "project-final-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "project-final-backend", + "version": "1.0.0", + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@hookform/resolvers": "^5.2.2", + "@tanstack/react-query": "^5.87.4", + "i18next": "^25.5.2", + "maplibre-gl": "^5.7.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.63.0", + "react-i18next": "^15.7.3", + "react-router-dom": "^7.9.1", + "zod": "^4.1.11", + "zustand": "^5.0.8" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.1.13", + "@types/maplibre-gl": "^1.13.2", + "@types/react": "^18.3.24", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^4.0.3", + "autoprefixer": "^10.4.21", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.13", + "typescript": "^5.9.2", + "vite": "^6.3.6", + "vite-plugin-svgr": "^4.5.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/modifiers": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/modifiers/-/modifiers-9.0.0.tgz", + "integrity": "sha512-ybiLc66qRGuZoC20wdSSG6pDXFikui/dCNGthxv4Ndy8ylErY0N3KVxY2bgo7AWwIbxDmXDg3ylAFmnrjcbVvw==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", + "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "license": "ISC", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, + "node_modules/@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.7.tgz", + "integrity": "sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.4.tgz", + "integrity": "sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^4.0.1" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "23.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-23.3.0.tgz", + "integrity": "sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==", + "license": "ISC", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^3.0.0", + "rw": "^1.3.3", + "tinyqueue": "^3.0.0" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, + "node_modules/@maplibre/vt-pbf": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.0.3.tgz", + "integrity": "sha512-YsW99BwnT+ukJRkseBcLuZHfITB4puJoxnqPVjo72rhW/TaawVYsgQHcqWLzTxqknttYoDpgyERzWSa/XrETdA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/vector-tile": "^2.0.4", + "@types/geojson-vt": "3.2.5", + "@types/supercluster": "^7.1.3", + "geojson-vt": "^4.0.2", + "pbf": "^4.0.1", + "supercluster": "^8.0.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.1.tgz", + "integrity": "sha512-HJXwzoZN4eYTdD8bVV22DN8gsPCAj3V20NHKOs8ezfXanGpmVPR7kalUHd+Y31IJp9stdB87VKPFbsGY3H/2ag==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.1.tgz", + "integrity": "sha512-PZlsJVcjHfcH53mOImyt3bc97Ep3FJDXRpk9sMdGX0qgLmY0EIWxCag6EigerGhLVuL8lDVYNnSo8qnTElO4xw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.1.tgz", + "integrity": "sha512-xc6i2AuWh++oGi4ylOFPmzJOEeAa2lJeGUGb4MudOtgfyyjr4UPNK+eEWTPLvmPJIY/pgw6ssFIox23SyrkkJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.1.tgz", + "integrity": "sha512-2ofU89lEpDYhdLAbRdeyz/kX3Y2lpYc6ShRnDjY35bZhd2ipuDMDi6ZTQ9NIag94K28nFMofdnKeHR7BT0CATw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.1.tgz", + "integrity": "sha512-wOsE6H2u6PxsHY/BeFHA4VGQN3KUJFZp7QJBmDYI983fgxq5Th8FDkVuERb2l9vDMs1D5XhOrhBrnqcEY6l8ZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.1.tgz", + "integrity": "sha512-A/xeqaHTlKbQggxCqispFAcNjycpUEHP52mwMQZUNqDUJFFYtPHCXS1VAG29uMlDzIVr+i00tSFWFLivMcoIBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.1.tgz", + "integrity": "sha512-54v4okehwl5TaSIkpp97rAHGp7t3ghinRd/vyC1iXqXMfjYUTm7TfYmCzXDoHUPTTf36L8pr0E7YsD3CfB3ZDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.1.tgz", + "integrity": "sha512-p/LaFyajPN/0PUHjv8TNyxLiA7RwmDoVY3flXHPSzqrGcIp/c2FjwPPP5++u87DGHtw+5kSH5bCJz0mvXngYxw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.1.tgz", + "integrity": "sha512-2AbMhFFkTo6Ptna1zO7kAXXDLi7H9fGTbVaIq2AAYO7yzcAsuTNWPHhb2aTA6GPiP+JXh85Y8CiS54iZoj4opw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.1.tgz", + "integrity": "sha512-Cgef+5aZwuvesQNw9eX7g19FfKX5/pQRIyhoXLCiBOrWopjo7ycfB292TX9MDcDijiuIJlx1IzJz3IoCPfqs9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.1.tgz", + "integrity": "sha512-RPhTwWMzpYYrHrJAS7CmpdtHNKtt2Ueo+BlLBjfZEhYBhK00OsEqM08/7f+eohiF6poe0YRDDd8nAvwtE/Y62Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.1.tgz", + "integrity": "sha512-eSGMVQw9iekut62O7eBdbiccRguuDgiPMsw++BVUg+1K7WjZXHOg/YOT9SWMzPZA+w98G+Fa1VqJgHZOHHnY0Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.1.tgz", + "integrity": "sha512-S208ojx8a4ciIPrLgazF6AgdcNJzQE4+S9rsmOmDJkusvctii+ZvEuIC4v/xFqzbuP8yDjn73oBlNDgF6YGSXQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.1.tgz", + "integrity": "sha512-3Ag8Ls1ggqkGUvSZWYcdgFwriy2lWo+0QlYgEFra/5JGtAd6C5Hw59oojx1DeqcA2Wds2ayRgvJ4qxVTzCHgzg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.1.tgz", + "integrity": "sha512-t9YrKfaxCYe7l7ldFERE1BRg/4TATxIg+YieHQ966jwvo7ddHJxPj9cNFWLAzhkVsbBvNA4qTbPVNsZKBO4NSg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.1.tgz", + "integrity": "sha512-MCgtFB2+SVNuQmmjHf+wfI4CMxy3Tk8XjA5Z//A0AKD7QXUYFMQcns91K6dEHBvZPCnhJSyDWLApk40Iq/H3tA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.1.tgz", + "integrity": "sha512-nEvqG+0jeRmqaUMuwzlfMKwcIVffy/9KGbAGyoa26iu6eSngAYQ512bMXuqqPrlTyfqdlB9FVINs93j534UJrg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.1.tgz", + "integrity": "sha512-RDsLm+phmT3MJd9SNxA9MNuEAO/J2fhW8GXk62G/B4G7sLVumNFbRwDL6v5NrESb48k+QMqdGbHgEtfU0LCpbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.1.tgz", + "integrity": "sha512-hpZB/TImk2FlAFAIsoElM3tLzq57uxnGYwplg6WDyAxbYczSi8O2eQ+H2Lx74504rwKtZ3N2g4bCUkiamzS6TQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.1.tgz", + "integrity": "sha512-SXjv8JlbzKM0fTJidX4eVsH+Wmnp0/WcD8gJxIZyR6Gay5Qcsmdbi9zVtnbkGPG8v2vMR1AD06lGWy5FLMcG7A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.1.tgz", + "integrity": "sha512-StxAO/8ts62KZVRAm4JZYq9+NqNsV7RvimNK+YM7ry//zebEH6meuugqW/P5OFUCjyQgui+9fUxT6d5NShvMvA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.5.1", + "lightningcss": "1.30.1", + "magic-string": "^0.30.18", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.5", + "@emnapi/runtime": "^1.4.5", + "@emnapi/wasi-threads": "^1.0.4", + "@napi-rs/wasm-runtime": "^0.2.12", + "@tybys/wasm-util": "^0.10.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.13.tgz", + "integrity": "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "postcss": "^8.4.41", + "tailwindcss": "4.1.13" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.87.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.87.4.tgz", + "integrity": "sha512-uNsg6zMxraEPDVO2Bn+F3/ctHi+Zsk+MMpcN8h6P7ozqD088F6mFY5TfGM7zuyIrL7HKpDyu6QHfLWiDxh3cuw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.87.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.87.4.tgz", + "integrity": "sha512-T5GT/1ZaNsUXf5I3RhcYuT17I4CPlbZgyLxc/ZGv7ciS6esytlbjb3DgUFO6c8JWYMDpdjSWInyGZUErgzqhcA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.87.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/maplibre-gl": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@types/maplibre-gl/-/maplibre-gl-1.13.2.tgz", + "integrity": "sha512-IC1RBMhKXpGDpiFsEwt17c/hbff0GCS/VmzqmrY6G+kyy2wfv2e7BoSQRAfqrvhBQPCoO8yc0SNCi5HkmCcVqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", + "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.3.tgz", + "integrity": "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", + "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.2", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001741", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", + "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/earcut": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "license": "ISC" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.218", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", + "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "25.5.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.5.2.tgz", + "integrity": "sha512-lW8Zeh37i/o0zVr+NoCHfNnfvVw+M6FQbRp36ZZ/NyHDJ3NJVpp2HhAUyU9WafL5AssymNoOjMRB48mmx2P6Hw==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/maplibre-gl": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.7.1.tgz", + "integrity": "sha512-iCOQB6W/EGgQx8aU4SyfU5a5/GR2E+ELF92NMsqYfs3x+vnY+8mARmz4gor6XZHCz3tv19mnotVDRlRTMNKyGw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/tiny-sdf": "^2.0.7", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^2.0.4", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^23.3.0", + "@maplibre/vt-pbf": "^4.0.3", + "@types/geojson": "^7946.0.16", + "@types/geojson-vt": "3.2.5", + "@types/supercluster": "^7.1.3", + "earcut": "^3.0.2", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.4", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^4.0.1", + "potpack": "^2.1.0", + "quickselect": "^3.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/potpack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.1.0.tgz", + "integrity": "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==", + "license": "ISC" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.63.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.63.0.tgz", + "integrity": "sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-i18next": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.7.3.tgz", + "integrity": "sha512-AANws4tOE+QSq/IeMF/ncoHlMNZaVLxpa5uUGW1wjike68elVYr0018L9xYoqBr1OFO7G7boDPrbn0HpMCJxTw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 25.4.1", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz", + "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz", + "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==", + "license": "MIT", + "dependencies": { + "react-router": "7.9.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.50.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.1.tgz", + "integrity": "sha512-78E9voJHwnXQMiQdiqswVLZwJIzdBKJ1GdI5Zx6XwoFKUIk09/sSrr+05QFzvYb8q6Y9pPV45zzDuYa3907TZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.50.1", + "@rollup/rollup-android-arm64": "4.50.1", + "@rollup/rollup-darwin-arm64": "4.50.1", + "@rollup/rollup-darwin-x64": "4.50.1", + "@rollup/rollup-freebsd-arm64": "4.50.1", + "@rollup/rollup-freebsd-x64": "4.50.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.1", + "@rollup/rollup-linux-arm-musleabihf": "4.50.1", + "@rollup/rollup-linux-arm64-gnu": "4.50.1", + "@rollup/rollup-linux-arm64-musl": "4.50.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.50.1", + "@rollup/rollup-linux-ppc64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-gnu": "4.50.1", + "@rollup/rollup-linux-riscv64-musl": "4.50.1", + "@rollup/rollup-linux-s390x-gnu": "4.50.1", + "@rollup/rollup-linux-x64-gnu": "4.50.1", + "@rollup/rollup-linux-x64-musl": "4.50.1", + "@rollup/rollup-openharmony-arm64": "4.50.1", + "@rollup/rollup-win32-arm64-msvc": "4.50.1", + "@rollup/rollup-win32-ia32-msvc": "4.50.1", + "@rollup/rollup-win32-x64-msvc": "4.50.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", + "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-svgr": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-4.5.0.tgz", + "integrity": "sha512-W+uoSpmVkSmNOGPSsDCWVW/DDAyv+9fap9AZXBvWiQqrboJ08j2vh0tFxTD/LjwqwAd3yYSVJgm54S/1GhbdnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.2.0", + "@svgr/core": "^8.1.0", + "@svgr/plugin-jsx": "^8.1.0" + }, + "peerDependencies": { + "vite": ">=2.6.0" + } + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.11.tgz", + "integrity": "sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", + "integrity": "sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} From e0eca22977a641f0c3cb42726eeb7f377351fdaf Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:34:46 +0200 Subject: [PATCH 167/215] fix(cors): remove invalid app.options('*') preflight handler causing Express 5 path-to-regexp crash --- backend/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/server.js b/backend/server.js index d5b7ad4af2..eb43198137 100644 --- a/backend/server.js +++ b/backend/server.js @@ -43,7 +43,6 @@ const corsOptions = { const app = express(); app.use(cors(corsOptions)); // For preflight requests across all routes -app.options("*", cors(corsOptions)); app.use(express.json()); // --- Routes --- From cf985e0b27d504aad6b7b3623295ac57c7727c93 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:35:40 +0200 Subject: [PATCH 168/215] chore(server): add /api/health endpoint for easy uptime and Mongo check --- backend/server.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/backend/server.js b/backend/server.js index eb43198137..06459bb351 100644 --- a/backend/server.js +++ b/backend/server.js @@ -16,7 +16,7 @@ mongoose.connect(mongoUrl); mongoose.Promise = Promise; // --- CORS setup --- -// Example: CORS_ORIGIN="http://localhost:5173,https://netlifylink.app +// Example: CORS_ORIGIN="http://localhost:5173,https://netlifylink.app" const allowedOrigins = (process.env.CORS_ORIGIN || "") .split(",") .map((s) => s.trim()) @@ -42,7 +42,6 @@ const corsOptions = { // Apply CORS + JSON const app = express(); app.use(cors(corsOptions)); -// For preflight requests across all routes app.use(express.json()); // --- Routes --- @@ -50,6 +49,16 @@ app.get("/", (_req, res) => { res.send("Hello Technigo!"); }); +// ✅ Health check +app.get("/api/health", (_req, res) => { + res.json({ + ok: true, + uptime: process.uptime(), + timestamp: new Date().toISOString(), + mongoConnected: mongoose.connection.readyState === 1, + }); +}); + // --- Server --- const port = process.env.PORT || 8080; app.listen(port, () => { From 4c062419a3cbcacfc478558d3f5ec9900d657b25 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:38:34 +0200 Subject: [PATCH 169/215] fix(backend): remove invalid app.options('*') in src/index.ts (Express 5 path-to-regexp crash) --- backend/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index c7357c6a38..68d7134a5a 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -44,8 +44,6 @@ const corsOptions: cors.CorsOptions = { }; app.use(cors(corsOptions)); -// Preflight for all routes -app.options("*", cors(corsOptions)); // --- Global middleware --- app.use(express.json()); From da83554845e8ffb219c68f45552c773c8fc77278 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 19:51:52 +0200 Subject: [PATCH 170/215] chore(backend): guard & log app.options registrations; dump mounted routes to locate '*' culprit --- backend/src/index.ts | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/backend/src/index.ts b/backend/src/index.ts index 68d7134a5a..9ef2444456 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -12,6 +12,55 @@ import { beachesRouter } from "./routes/beaches.js"; const app = express(); +/** ── TEMP GUARD: log/neutralize invalid Express-5 star preflights ───────────────── */ +const originalOptions = (app as any).options?.bind(app); +(app as any).options = (path: any, ...handlers: any[]) => { + // Log every options registration, so we see who calls it + console.error("[guard] app.options called with path =", path); + // Also log the stack once (trim to keep logs readable) + const stack = (new Error().stack || "").split("\n").slice(0, 8).join("\n"); + console.error(stack); + + if (path === "*") { + console.warn("[guard] Blocking app.options('*') (invalid on Express 5)"); + // don't register, just return app to avoid crash + return app; + } + return originalOptions(path, ...handlers); +}; +/** ───────────────────────────────────────────────────────────────────────────────── */ + +/** ── TEMP: dump routes after mounting, to spot any '*' entries ─────────────────── */ +function dumpRoutes(label: string) { + try { + // @ts-ignore internal structure + const stack = app._router?.stack || []; + console.log(`[routes dump] ${label}`); + for (const layer of stack) { + // layers with .route have concrete methods/paths + if (layer.route) { + const methods = Object.keys(layer.route.methods) + .filter(Boolean) + .join(","); + console.log(" →", methods, layer.route.path); + } else if (layer.name === "router" && layer.handle?.stack) { + // mounted routers + for (const s of layer.handle.stack) { + if (s.route) { + const methods = Object.keys(s.route.methods) + .filter(Boolean) + .join(","); + console.log(" ↳", methods, s.route.path); + } + } + } + } + } catch (e) { + console.log("[routes dump] failed:", e); + } +} +/** ──────────────────────────────────────────────────────────────────────────────── */ + // --- CORS setup --- // Prefer CORS_ORIGIN; fallback to ALLOWED_ORIGIN. // Example env: CORS_ORIGIN="http://localhost:5173,https://your-site.netlify.app" @@ -72,6 +121,8 @@ app.get("/api/health-direct", (_req, res) => { res.json({ ok: true, via: "direct" }); }); +dumpRoutes("after mount"); + // 404 app.use((_req, res) => res.status(404).json({ error: "NotFound" })); From c0ee32d58a8d735a5c176124deddc0baaabeb139 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:41:03 +0200 Subject: [PATCH 171/215] chore(backend): remove temporary route guard --- backend/src/index.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 9ef2444456..d29602e672 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -12,24 +12,6 @@ import { beachesRouter } from "./routes/beaches.js"; const app = express(); -/** ── TEMP GUARD: log/neutralize invalid Express-5 star preflights ───────────────── */ -const originalOptions = (app as any).options?.bind(app); -(app as any).options = (path: any, ...handlers: any[]) => { - // Log every options registration, so we see who calls it - console.error("[guard] app.options called with path =", path); - // Also log the stack once (trim to keep logs readable) - const stack = (new Error().stack || "").split("\n").slice(0, 8).join("\n"); - console.error(stack); - - if (path === "*") { - console.warn("[guard] Blocking app.options('*') (invalid on Express 5)"); - // don't register, just return app to avoid crash - return app; - } - return originalOptions(path, ...handlers); -}; -/** ───────────────────────────────────────────────────────────────────────────────── */ - /** ── TEMP: dump routes after mounting, to spot any '*' entries ─────────────────── */ function dumpRoutes(label: string) { try { From de30ac4eef1f231fe458f8ad14c5b55157c52463 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:41:48 +0200 Subject: [PATCH 172/215] chore(backend): remove temporary route dump logs --- backend/src/index.ts | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index d29602e672..77bc115a9f 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -12,37 +12,6 @@ import { beachesRouter } from "./routes/beaches.js"; const app = express(); -/** ── TEMP: dump routes after mounting, to spot any '*' entries ─────────────────── */ -function dumpRoutes(label: string) { - try { - // @ts-ignore internal structure - const stack = app._router?.stack || []; - console.log(`[routes dump] ${label}`); - for (const layer of stack) { - // layers with .route have concrete methods/paths - if (layer.route) { - const methods = Object.keys(layer.route.methods) - .filter(Boolean) - .join(","); - console.log(" →", methods, layer.route.path); - } else if (layer.name === "router" && layer.handle?.stack) { - // mounted routers - for (const s of layer.handle.stack) { - if (s.route) { - const methods = Object.keys(s.route.methods) - .filter(Boolean) - .join(","); - console.log(" ↳", methods, s.route.path); - } - } - } - } - } catch (e) { - console.log("[routes dump] failed:", e); - } -} -/** ──────────────────────────────────────────────────────────────────────────────── */ - // --- CORS setup --- // Prefer CORS_ORIGIN; fallback to ALLOWED_ORIGIN. // Example env: CORS_ORIGIN="http://localhost:5173,https://your-site.netlify.app" From 25ae2e75cae829cb57ae64f2e9fa33337f7534b4 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:43:07 +0200 Subject: [PATCH 173/215] chore(backend): remove temporary dumpRoutes callfunction --- backend/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 77bc115a9f..68d7134a5a 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -72,8 +72,6 @@ app.get("/api/health-direct", (_req, res) => { res.json({ ok: true, via: "direct" }); }); -dumpRoutes("after mount"); - // 404 app.use((_req, res) => res.status(404).json({ error: "NotFound" })); From 91be46162dcee050921f381bc5506630fb77d0ce Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 20:53:07 +0200 Subject: [PATCH 174/215] chore(backend): remove legacy server.js entrypoint --- backend/README.md | 8 ------ backend/server.js | 71 ----------------------------------------------- 2 files changed, 79 deletions(-) delete mode 100644 backend/README.md delete mode 100644 backend/server.js diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index d1438c9108..0000000000 --- a/backend/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Backend part of Final Project - -This project includes the packages and babel setup for an express server, and is just meant to make things a little simpler to get up and running with. - -## Getting Started - -1. Install the required dependencies using `npm install`. -2. Start the development server using `npm run dev`. \ No newline at end of file diff --git a/backend/server.js b/backend/server.js deleted file mode 100644 index 06459bb351..0000000000 --- a/backend/server.js +++ /dev/null @@ -1,71 +0,0 @@ -import dotenv from "dotenv"; -dotenv.config(); - -import express from "express"; -import cors from "cors"; -import mongoose from "mongoose"; - -// --- DB connection --- -const mongoUrl = - process.env.MONGODB_URI || - process.env.MONGO_URL || - "mongodb://localhost/final-project"; - -mongoose.set("strictQuery", true); -mongoose.connect(mongoUrl); -mongoose.Promise = Promise; - -// --- CORS setup --- -// Example: CORS_ORIGIN="http://localhost:5173,https://netlifylink.app" -const allowedOrigins = (process.env.CORS_ORIGIN || "") - .split(",") - .map((s) => s.trim()) - .filter(Boolean); - -const corsOptions = { - origin(origin, callback) { - // Allow non-browser clients (no Origin header) - if (!origin) return callback(null, true); - - // If no env configured, fall back to permissive (useful in early dev) - if (allowedOrigins.length === 0) return callback(null, true); - - if (allowedOrigins.includes(origin)) { - return callback(null, true); - } - return callback(new Error("Not allowed by CORS")); - }, - credentials: true, - methods: "GET,HEAD,PUT,PATCH,POST,DELETE", -}; - -// Apply CORS + JSON -const app = express(); -app.use(cors(corsOptions)); -app.use(express.json()); - -// --- Routes --- -app.get("/", (_req, res) => { - res.send("Hello Technigo!"); -}); - -// ✅ Health check -app.get("/api/health", (_req, res) => { - res.json({ - ok: true, - uptime: process.uptime(), - timestamp: new Date().toISOString(), - mongoConnected: mongoose.connection.readyState === 1, - }); -}); - -// --- Server --- -const port = process.env.PORT || 8080; -app.listen(port, () => { - console.log(`Server running on http://localhost:${port}`); - if (allowedOrigins.length) { - console.log("CORS allowed origins:", allowedOrigins); - } else { - console.log("CORS: no CORS_ORIGIN set -> permissive (dev mode)."); - } -}); From 4ad597bde08a2da7ef6cb5bdf2cdd972a4d52860 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:02:51 +0200 Subject: [PATCH 175/215] update readme --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8ef8dc8816..c9ed7fcc6c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # 🏖️ BADA – Find Safe Beaches in Sweden +- [Try it out here](https://badaweb.netlify.app/) (deployed on Netlify) + ![React](https://img.shields.io/badge/Frontend-React-61DAFB?logo=react&logoColor=white) ![Express](https://img.shields.io/badge/Backend-Express-000000?logo=express&logoColor=white) ![MongoDB](https://img.shields.io/badge/Database-MongoDB-47A248?logo=mongodb&logoColor=white) @@ -23,6 +25,7 @@ It replaces outdated or clunky websites with a **clean, mobile-friendly experien - ❤️ **Create an account and save favourite beaches** to your profile - 🌗 **Dark mode** and responsive design (mobile → desktop) - 🌐 **Multi-language support** (Swedish / English) +- 🔀 **Drag-and-drop sorting for favorites** --- @@ -118,10 +121,8 @@ This account already has some favourite beaches saved. ## 🌍 Deployment -- Frontend: Deployed on Vercel -- Backend: Deployed on Vercel - -(Will replace with actual links when deployed) +- Frontend: Deployed on Netlify: https://badaweb.netlify.app/ +- Backend: Deployed on Vercel → https://bada-backend.vercel.app/api/health --- @@ -143,7 +144,6 @@ This account already has some favourite beaches saved. ## 🧭 Roadmap -- Add drag-and-drop sorting for favourites - Allow notes/tips per beach (e.g. “good for kids”) - Integrate OpenWeatherMap for weather & water temperature - Accessibility extras (reduced motion, ARIA live regions) From 0dfc6aa6e1b61014c9038e3567b626595fff7166 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:03:59 +0200 Subject: [PATCH 176/215] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9ed7fcc6c..069daff4e9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # 🏖️ BADA – Find Safe Beaches in Sweden -- [Try it out here](https://badaweb.netlify.app/) (deployed on Netlify) - ![React](https://img.shields.io/badge/Frontend-React-61DAFB?logo=react&logoColor=white) ![Express](https://img.shields.io/badge/Backend-Express-000000?logo=express&logoColor=white) ![MongoDB](https://img.shields.io/badge/Database-MongoDB-47A248?logo=mongodb&logoColor=white) @@ -15,6 +13,8 @@ **BADA** helps beachgoers and families in Sweden find safe, EU-classified bathing waters with real-time quality updates. It replaces outdated or clunky websites with a **clean, mobile-friendly experience** where you can browse nearby beaches on a map, check water quality, and save your favourites. +[Try it out here](https://badaweb.netlify.app/) (deployed on Netlify) + --- ## ✨ Features From 2000b34a95d3d9b06d8cc6cb375bfc08851d9620 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:09:30 +0200 Subject: [PATCH 177/215] chore(backend): expose /api/debug/env before 404 for production diagnostics --- backend/src/index.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/backend/src/index.ts b/backend/src/index.ts index 68d7134a5a..8cbe7f27a8 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -72,6 +72,21 @@ app.get("/api/health-direct", (_req, res) => { res.json({ ok: true, via: "direct" }); }); +// 🔎 TEMP: quick env presence check (no secrets exposed) +app.get("/api/debug/env", (_req, res) => { + res.json({ + HAV_BASE_URL: !!process.env.HAV_BASE_URL, + HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, + HAV_V2_BASE: !!process.env.HAV_V2_BASE, + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", + MONGODB_URI_present: !!process.env.MONGODB_URI, + JWT_SECRET_present: !!process.env.JWT_SECRET, + NODE_ENV: process.env.NODE_ENV ?? "undefined", + PORT: process.env.PORT ?? "undefined", + }); +}); + // 404 app.use((_req, res) => res.status(404).json({ error: "NotFound" })); From 41836d399602174511e4948b3f7af51fb6057cff Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:12:53 +0200 Subject: [PATCH 178/215] chore(backend): register /api/debug/env in index.ts before 404; keep server.ts as bootstrap only --- backend/src/index.ts | 68 +++++++++++++++++-------------------------- backend/src/server.ts | 27 ++++------------- 2 files changed, 32 insertions(+), 63 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index 8cbe7f27a8..b17698c4de 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,3 +1,4 @@ +// backend/src/index.ts console.log("Server booted at", new Date().toISOString()); import express from "express"; @@ -12,9 +13,7 @@ import { beachesRouter } from "./routes/beaches.js"; const app = express(); -// --- CORS setup --- -// Prefer CORS_ORIGIN; fallback to ALLOWED_ORIGIN. -// Example env: CORS_ORIGIN="http://localhost:5173,https://your-site.netlify.app" +/** ── CORS (allow comma-separated origins via CORS_ORIGIN or ALLOWED_ORIGIN) ── */ const allowedOrigins = ( process.env.CORS_ORIGIN || process.env.ALLOWED_ORIGIN || @@ -25,72 +24,57 @@ const allowedOrigins = ( .filter(Boolean); const corsOptions: cors.CorsOptions = { - origin(origin, callback) { - // Allow requests without Origin header (e.g. Postman, curl) - if (!origin) return callback(null, true); - - if (allowedOrigins.length === 0) { - // no origins configured → dev mode → allow all - return callback(null, true); - } - - if (allowedOrigins.includes(origin)) { - return callback(null, true); - } - return callback(new Error("Not allowed by CORS")); + origin(origin, cb) { + if (!origin) return cb(null, true); // non-browser clients + if (allowedOrigins.length === 0) return cb(null, true); // dev fallback + if (allowedOrigins.includes(origin)) return cb(null, true); + return cb(new Error("Not allowed by CORS")); }, credentials: true, methods: "GET,HEAD,PUT,PATCH,POST,DELETE", }; app.use(cors(corsOptions)); - -// --- Global middleware --- app.use(express.json()); -// --- Public routes (mounted under /api) --- +/** ── TEMP DIAG: env presence (put this BEFORE the 404) ───────────────────── */ +app.get("/api/debug/env", (_req, res) => { + res.json({ + HAV_BASE_URL: !!process.env.HAV_BASE_URL, + HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, + HAV_V2_BASE: !!process.env.HAV_V2_BASE, + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", + MONGODB_URI_present: !!process.env.MONGODB_URI, + JWT_SECRET_present: !!process.env.JWT_SECRET, + NODE_ENV: process.env.NODE_ENV ?? "undefined", + PORT: process.env.PORT ?? "undefined", + }); +}); + +/** ── Public routers under /api ───────────────────────────────────────────── */ app.use("/api", healthRouter); app.use("/api", dbCheckRouter); app.use("/api", favoritesRouter); app.use("/api", beachesRouter); -// 🔐 Auth routes (public endpoints: /register, /login) +/** ── Auth ───────────────────────────────────────────────────────────────── */ app.use("/api/auth", authRouter); - -// 🔒 Example protected endpoints (require a Bearer token) app.get("/api/protected/ping", requireAuth, (req, res) => { res.json({ ok: true, user: (req as any).user }); }); - app.get("/api/auth/me", requireAuth, (req, res) => { res.json({ user: (req as any).user }); }); -// Temporary direct test +/** ── Quick direct health ping ────────────────────────────────────────────── */ app.get("/api/health-direct", (_req, res) => { console.log("Hit /api/health-direct"); res.json({ ok: true, via: "direct" }); }); -// 🔎 TEMP: quick env presence check (no secrets exposed) -app.get("/api/debug/env", (_req, res) => { - res.json({ - HAV_BASE_URL: !!process.env.HAV_BASE_URL, - HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, - HAV_V2_BASE: !!process.env.HAV_V2_BASE, - ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", - CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", - MONGODB_URI_present: !!process.env.MONGODB_URI, - JWT_SECRET_present: !!process.env.JWT_SECRET, - NODE_ENV: process.env.NODE_ENV ?? "undefined", - PORT: process.env.PORT ?? "undefined", - }); -}); - -// 404 +/** ── 404 + error handler (MUST be last) ──────────────────────────────────── */ app.use((_req, res) => res.status(404).json({ error: "NotFound" })); - -// Error handler app.use( ( err: unknown, diff --git a/backend/src/server.ts b/backend/src/server.ts index 1d4b9c4970..4b6297f28d 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,38 +1,23 @@ +// backend/src/server.ts import "dotenv/config"; import app from "./index.js"; const port = Number(process.env.PORT) || 3000; -// 🔎 TEMP: log each request so we see what's being hit app.use((req, _res, next) => { console.log(`[req] ${req.method} ${req.url}`); next(); }); -// 🔎 TEMP: quick env presence check (no secrets exposed) -app.get("/api/debug/env", (_req, res) => { - res.json({ - HAV_BASE_URL: !!process.env.HAV_BASE_URL, - HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, - HAV_V2_BASE: !!process.env.HAV_V2_BASE, - ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", - CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", - MONGODB_URI_present: !!process.env.MONGODB_URI, - JWT_SECRET_present: !!process.env.JWT_SECRET, - NODE_ENV: process.env.NODE_ENV ?? "undefined", - PORT: process.env.PORT ?? "undefined", - }); -}); - app.listen(port, () => { console.log(`API listening on http://localhost:${port}`); const allowed = (process.env.CORS_ORIGIN || process.env.ALLOWED_ORIGIN || "") .split(",") .map((s) => s.trim()) .filter(Boolean); - if (allowed.length) { - console.log("CORS allowed origins:", allowed); - } else { - console.log("CORS: no origins configured → permissive (dev mode)."); - } + console.log( + allowed.length + ? `CORS allowed origins: ${allowed.join(", ")}` + : "CORS: no origins configured → permissive (dev)" + ); }); From 290d4e1ffd221c47f3e4d63f75622681ee92280b Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:14:16 +0200 Subject: [PATCH 179/215] fix latest commit --- backend/dist/index.d.ts.map | 2 +- backend/dist/index.js | 48 ++++++++++++---- backend/dist/index.js.map | 2 +- backend/dist/models/Favorite.d.ts.map | 2 +- backend/dist/models/Favorite.js | 9 ++- backend/dist/models/Favorite.js.map | 2 +- backend/dist/routes/favorites.d.ts.map | 2 +- backend/dist/routes/favorites.js | 80 ++++++++++++++++++++++++-- backend/dist/routes/favorites.js.map | 2 +- backend/dist/server.d.ts.map | 2 +- backend/dist/server.js | 22 +++---- backend/dist/server.js.map | 2 +- 12 files changed, 132 insertions(+), 43 deletions(-) diff --git a/backend/dist/index.d.ts.map b/backend/dist/index.d.ts.map index 64ac39b267..80160c130c 100644 --- a/backend/dist/index.d.ts.map +++ b/backend/dist/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,QAAA,MAAM,GAAG,6CAAY,CAAC;AA8DtB,eAAe,GAAG,CAAC"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,QAAA,MAAM,GAAG,6CAAY,CAAC;AAgFtB,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/index.js b/backend/dist/index.js index afff69a109..dfc056e263 100644 --- a/backend/dist/index.js +++ b/backend/dist/index.js @@ -1,3 +1,4 @@ +// backend/src/index.ts console.log("Server booted at", new Date().toISOString()); import express from "express"; import cors from "cors"; @@ -8,39 +9,62 @@ import { requireAuth } from "./middleware/auth.js"; import { favoritesRouter } from "./routes/favorites.js"; import { beachesRouter } from "./routes/beaches.js"; const app = express(); -// --- Global middleware --- -const allowed = (process.env.ALLOWED_ORIGIN ?? "") +/** ── CORS (allow comma-separated origins via CORS_ORIGIN or ALLOWED_ORIGIN) ── */ +const allowedOrigins = (process.env.CORS_ORIGIN || + process.env.ALLOWED_ORIGIN || + "") .split(",") .map((s) => s.trim()) .filter(Boolean); -app.use(cors({ - origin: allowed.length ? allowed : true, // during local dev if empty, allow all +const corsOptions = { + origin(origin, cb) { + if (!origin) + return cb(null, true); // non-browser clients + if (allowedOrigins.length === 0) + return cb(null, true); // dev fallback + if (allowedOrigins.includes(origin)) + return cb(null, true); + return cb(new Error("Not allowed by CORS")); + }, credentials: true, -})); + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", +}; +app.use(cors(corsOptions)); app.use(express.json()); -// --- Public routes (mounted under /api) --- +/** ── TEMP DIAG: env presence (put this BEFORE the 404) ───────────────────── */ +app.get("/api/debug/env", (_req, res) => { + res.json({ + HAV_BASE_URL: !!process.env.HAV_BASE_URL, + HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, + HAV_V2_BASE: !!process.env.HAV_V2_BASE, + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", + CORS_ORIGIN: process.env.CORS_ORIGIN ?? "", + MONGODB_URI_present: !!process.env.MONGODB_URI, + JWT_SECRET_present: !!process.env.JWT_SECRET, + NODE_ENV: process.env.NODE_ENV ?? "undefined", + PORT: process.env.PORT ?? "undefined", + }); +}); +/** ── Public routers under /api ───────────────────────────────────────────── */ app.use("/api", healthRouter); app.use("/api", dbCheckRouter); app.use("/api", favoritesRouter); app.use("/api", beachesRouter); -// 🔐 Auth routes (public endpoints: /register, /login) +/** ── Auth ───────────────────────────────────────────────────────────────── */ app.use("/api/auth", authRouter); -// 🔒 Example protected endpoints (require a Bearer token) app.get("/api/protected/ping", requireAuth, (req, res) => { res.json({ ok: true, user: req.user }); }); -// To protect /api/auth/me this can also be used app.get("/api/auth/me", requireAuth, (req, res) => { res.json({ user: req.user }); }); -// Temporary direct test +/** ── Quick direct health ping ────────────────────────────────────────────── */ app.get("/api/health-direct", (_req, res) => { console.log("Hit /api/health-direct"); res.json({ ok: true, via: "direct" }); }); -// 404 +/** ── 404 + error handler (MUST be last) ──────────────────────────────────── */ app.use((_req, res) => res.status(404).json({ error: "NotFound" })); -// Error handler app.use((err, _req, res, _next) => { console.error("[ERROR]", err); const message = process.env.NODE_ENV !== "production" && err instanceof Error diff --git a/backend/dist/index.js.map b/backend/dist/index.js.map index 8239b93640..850d89773b 100644 --- a/backend/dist/index.js.map +++ b/backend/dist/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAE1D,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,4BAA4B;AAC5B,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;KAC/C,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,GAAG,CAAC,GAAG,CACL,IAAI,CAAC;IACH,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,uCAAuC;IAChF,WAAW,EAAE,IAAI;CAClB,CAAC,CACH,CAAC;AAEF,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,6CAA6C;AAC7C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAC9B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC/B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE/B,uDAAuD;AACvD,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAEjC,0DAA0D;AAC1D,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACvD,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,gDAAgD;AAChD,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAChD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC1C,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,MAAM;AACN,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AAEpE,gBAAgB;AAChB,GAAG,CAAC,GAAG,CACL,CACE,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B,EAC3B,EAAE;IACF,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,YAAY,KAAK;QAC3D,CAAC,CAAC,GAAG,CAAC,OAAO;QACb,CAAC,CAAC,SAAS,CAAC;IAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;AAClE,CAAC,CACF,CAAC;AAEF,eAAe,GAAG,CAAC"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uBAAuB;AACvB,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAE1D,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AAEtB,mFAAmF;AACnF,MAAM,cAAc,GAAG,CACrB,OAAO,CAAC,GAAG,CAAC,WAAW;IACvB,OAAO,CAAC,GAAG,CAAC,cAAc;IAC1B,EAAE,CACH;KACE,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;KACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AAEnB,MAAM,WAAW,GAAqB;IACpC,MAAM,CAAC,MAAM,EAAE,EAAE;QACf,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,sBAAsB;QAC1D,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,eAAe;QACvE,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3D,OAAO,EAAE,CAAC,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,WAAW,EAAE,IAAI;IACjB,OAAO,EAAE,gCAAgC;CAC1C,CAAC;AAEF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAC3B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACtC,GAAG,CAAC,IAAI,CAAC;QACP,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;QAC5C,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QACtC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;QAChD,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE;QAC1C,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QAC9C,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU;QAC5C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,WAAW;QAC7C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;KACtC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAC9B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC/B,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE/B,gFAAgF;AAChF,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,qBAAqB,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IACvD,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AACH,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAChD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAG,GAAW,CAAC,IAAI,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC1C,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;AACpE,GAAG,CAAC,GAAG,CACL,CACE,GAAY,EACZ,IAAqB,EACrB,GAAqB,EACrB,KAA2B,EAC3B,EAAE;IACF,OAAO,CAAC,KAAK,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAC9B,MAAM,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,GAAG,YAAY,KAAK;QAC3D,CAAC,CAAC,GAAG,CAAC,OAAO;QACb,CAAC,CAAC,SAAS,CAAC;IAChB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,CAAC,CAAC;AAClE,CAAC,CACF,CAAC;AAEF,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/Favorite.d.ts.map b/backend/dist/models/Favorite.d.ts.map index 9676749a48..c1a7659a51 100644 --- a/backend/dist/models/Favorite.d.ts.map +++ b/backend/dist/models/Favorite.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Favorite.d.ts","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiB,eAAe,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE3E,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAanB,CAAC;AAKF,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,cAAc,CAAC,CAAC;AAEjE,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,WAAW,CAEQ,CAAC"} \ No newline at end of file +{"version":3,"file":"Favorite.d.ts","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAiB,eAAe,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAE3E,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBnB,CAAC;AAKF,MAAM,MAAM,WAAW,GAAG,eAAe,CAAC,OAAO,cAAc,CAAC,CAAC;AAEjE,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,WAAW,CAEQ,CAAC"} \ No newline at end of file diff --git a/backend/dist/models/Favorite.js b/backend/dist/models/Favorite.js index 754aa14d4b..49447408e0 100644 --- a/backend/dist/models/Favorite.js +++ b/backend/dist/models/Favorite.js @@ -6,9 +6,12 @@ const favoriteSchema = new Schema({ required: true, index: true, }, - beachId: { type: String, required: true }, // HaV beach identifier (string) - note: { type: String, default: "" }, // optional user note - order: { type: Number, default: 0 }, // for drag & drop later + // HaV beach identifier (string) + beachId: { type: String, required: true, index: true }, + // optional user note + note: { type: String, default: "" }, + // for drag & drop ordering (lower = earlier) + order: { type: Number, default: 0 }, }, { timestamps: true }); // Prevent duplicates: one user can favorite each beach only once favoriteSchema.index({ userId: 1, beachId: 1 }, { unique: true }); diff --git a/backend/dist/models/Favorite.js.map b/backend/dist/models/Favorite.js.map index 56e03c7e10..2b3a654f5f 100644 --- a/backend/dist/models/Favorite.js.map +++ b/backend/dist/models/Favorite.js.map @@ -1 +1 @@ -{"version":3,"file":"Favorite.js","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAA0B,MAAM,UAAU,CAAC;AAE3E,MAAM,cAAc,GAAG,IAAI,MAAM,CAC/B;IACE,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC3B,GAAG,EAAE,MAAM;QACX,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;KACZ;IACD,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,gCAAgC;IAC3E,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,qBAAqB;IAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,wBAAwB;CAC9D,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;AAEF,iEAAiE;AACjE,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAIlE,MAAM,CAAC,MAAM,QAAQ,GAClB,QAAQ,CAAC,MAAM,CAAC,QAA+B;IAChD,KAAK,CAAc,UAAU,EAAE,cAAc,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"Favorite.js","sourceRoot":"","sources":["../../src/models/Favorite.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,MAAM,EAAE,KAAK,EAA0B,MAAM,UAAU,CAAC;AAE3E,MAAM,cAAc,GAAG,IAAI,MAAM,CAC/B;IACE,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;QAC3B,GAAG,EAAE,MAAM;QACX,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,IAAI;KACZ;IACD,gCAAgC;IAChC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IACtD,qBAAqB;IACrB,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;IACnC,6CAA6C;IAC7C,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE;CACpC,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,CACrB,CAAC;AAEF,iEAAiE;AACjE,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAIlE,MAAM,CAAC,MAAM,QAAQ,GAClB,QAAQ,CAAC,MAAM,CAAC,QAA+B;IAChD,KAAK,CAAc,UAAU,EAAE,cAAc,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/favorites.d.ts.map b/backend/dist/routes/favorites.d.ts.map index 8f10dbe75e..0cd13007e2 100644 --- a/backend/dist/routes/favorites.d.ts.map +++ b/backend/dist/routes/favorites.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"favorites.d.ts","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,eAAe,4CAAW,CAAC"} \ No newline at end of file +{"version":3,"file":"favorites.d.ts","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,eAAe,4CAAW,CAAC"} \ No newline at end of file diff --git a/backend/dist/routes/favorites.js b/backend/dist/routes/favorites.js index 3c5e90a6ef..0d24b4a957 100644 --- a/backend/dist/routes/favorites.js +++ b/backend/dist/routes/favorites.js @@ -9,8 +9,8 @@ const zCreate = z.object({ beachId: z.string().min(1), note: z.string().max(500).optional(), }); +// Ensure DB connection for every favorites op favoritesRouter.use(async (_req, _res, next) => { - // ensure DB connection for every favorites op try { await connectDB(); next(); @@ -19,18 +19,25 @@ favoritesRouter.use(async (_req, _res, next) => { next(e); } }); -// GET /api/favorites -> list current user's favorites +/** + * GET /api/favorites + * List current user's favorites (stable order) + */ favoritesRouter.get("/favorites", requireAuth, async (req, res) => { const userId = req.user.sub; const items = await Favorite.find({ userId }) - .sort({ order: 1, createdAt: -1 }) + .sort({ order: 1, updatedAt: 1, _id: 1 }) // stable sort .lean(); res.json(items); }); -// POST /api/favorites +/** + * POST /api/favorites + */ favoritesRouter.post("/favorites", requireAuth, async (req, res) => { const parsed = zCreate.safeParse(req.body); if (!parsed.success) { + // keep the existing error style + // @ts-ignore - z.treeifyError is available in the codebase return res .status(400) .json({ error: "InvalidBody", details: z.treeifyError(parsed.error) }); @@ -52,7 +59,9 @@ favoritesRouter.post("/favorites", requireAuth, async (req, res) => { throw err; } }); -// DELETE /api/favorites/:id +/** + * DELETE /api/favorites/:id + */ favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { const userId = req.user.sub; const id = req.params.id; @@ -66,7 +75,9 @@ favoritesRouter.delete("/favorites/:id", requireAuth, async (req, res) => { return res.status(404).json({ error: "NotFound" }); res.json({ ok: true }); }); -// DELETE /api/favorites/by-beach/:beachId +/** + * DELETE /api/favorites/by-beach/:beachId + */ favoritesRouter.delete("/favorites/by-beach/:beachId", requireAuth, async (req, res) => { const userId = req.user.sub; const { beachId } = req.params; @@ -75,4 +86,61 @@ favoritesRouter.delete("/favorites/by-beach/:beachId", requireAuth, async (req, return res.status(404).json({ error: "NotFound" }); res.json({ ok: true }); }); +/** + * PATCH /api/favorites/reorder + * Persist custom order. Accepts: { order: string[] } where each string is a beachId. + * Non-owned or unknown ids are ignored. Any favorites not included are appended after. + */ +favoritesRouter.patch("/favorites/reorder", requireAuth, async (req, res) => { + try { + const userId = req.user.sub; + const schema = z.object({ + order: z.array(z.string()).min(0), + }); + const parsed = schema.safeParse(req.body); + if (!parsed.success) { + return res + .status(400) + .json({ error: "InvalidBody", details: parsed.error.flatten() }); + } + // Deduplicate ids while preserving first occurrence order + const seen = new Set(); + const order = parsed.data.order.filter((id) => { + if (seen.has(id)) + return false; + seen.add(id); + return true; + }); + // Fetch user's current favorites + const favs = await Favorite.find({ userId }, { beachId: 1 }).lean(); + const currentIds = new Set(favs.map((f) => f.beachId)); + // Keep only valid (owned) ids, preserve payload order + const sanitized = order.filter((id) => currentIds.has(id)); + // Bulk update for provided ids + const ops = sanitized.map((beachId, idx) => ({ + updateOne: { + filter: { userId, beachId }, + update: { $set: { order: idx } }, + }, + })); + // Append any missing favorites not included in payload + const missing = favs + .map((f) => f.beachId) + .filter((id) => !sanitized.includes(id)); + ops.push(...missing.map((beachId, addIdx) => ({ + updateOne: { + filter: { userId, beachId }, + update: { $set: { order: sanitized.length + addIdx } }, + }, + }))); + if (ops.length > 0) { + await Favorite.bulkWrite(ops, { ordered: false }); + } + return res.status(204).end(); + } + catch (err) { + console.error("REORDER_ERR", err); + return res.status(500).json({ error: "InternalServerError" }); + } +}); //# sourceMappingURL=favorites.js.map \ No newline at end of file diff --git a/backend/dist/routes/favorites.js.map b/backend/dist/routes/favorites.js.map index 7e1a85bf5a..24bc69e212 100644 --- a/backend/dist/routes/favorites.js.map +++ b/backend/dist/routes/favorites.js.map @@ -1 +1 @@ -{"version":3,"file":"favorites.js","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAiB,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC;AAExC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC7C,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAClB,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,uDAAuD;AACvD,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAChE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;SAC1C,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC;SACjC,IAAI,EAAE,CAAC;IACV,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,sBAAsB;AACtB,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,GAAG;aACP,MAAM,CAAC,GAAG,CAAC;aACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACtC,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACpC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,IAAI,EAAE;SACjB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;YACxB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,4BAA4B;AAC5B,eAAe,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACvE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAwB,CAAC;IAC/C,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEH,0CAA0C;AAC1C,eAAe,CAAC,MAAM,CACpB,8BAA8B,EAC9B,WAAW,EACX,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAE/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CACF,CAAC"} \ No newline at end of file +{"version":3,"file":"favorites.js","sourceRoot":"","sources":["../../src/routes/favorites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAiB,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAGhC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC;AAExC,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,8CAA8C;AAC9C,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC7C,IAAI,CAAC;QACH,MAAM,SAAS,EAAE,CAAC;QAClB,IAAI,EAAE,CAAC;IACT,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,CAAC,CAAC,CAAC;IACV,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,eAAe,CAAC,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAChE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;SAC1C,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,cAAc;SACvD,IAAI,EAAE,CAAC;IACV,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,gCAAgC;QAChC,2DAA2D;QAC3D,OAAO,GAAG;aACP,MAAM,CAAC,GAAG,CAAC;aACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACtC,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC;YACpC,MAAM;YACN,OAAO;YACP,IAAI,EAAE,IAAI,IAAI,EAAE;SACjB,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,IAAI,GAAG,EAAE,IAAI,KAAK,KAAK,EAAE,CAAC;YACxB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,eAAe,CAAC,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACvE,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAE/C,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAwB,CAAC;IAC/C,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CAAC,CAAC;AAEH;;GAEG;AACH,eAAe,CAAC,MAAM,CACpB,8BAA8B,EAC9B,WAAW,EACX,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IACjB,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAE/B,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACzB,CAAC,CACF,CAAC;AAEF;;;;GAIG;AACH,eAAe,CAAC,KAAK,CAAC,oBAAoB,EAAE,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC1E,IAAI,CAAC;QACH,MAAM,MAAM,GAAI,GAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;QAE/C,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;YACtB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;SAClC,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,GAAG;iBACP,MAAM,CAAC,GAAG,CAAC;iBACX,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,0DAA0D;QAC1D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE;YAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,iCAAiC;QACjC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QAEvD,sDAAsD;QACtD,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3D,+BAA+B;QAC/B,MAAM,GAAG,GAA4B,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;YACpE,SAAS,EAAE;gBACT,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;gBAC3B,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;aACjC;SACF,CAAC,CAAC,CAAC;QAEJ,uDAAuD;QACvD,MAAM,OAAO,GAAG,IAAI;aACjB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;aACrB,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAE3C,GAAG,CAAC,IAAI,CACN,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;YACnC,SAAS,EAAE;gBACT,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE;gBAC3B,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,EAAE,EAAE;aACvD;SACF,CAAC,CAAC,CACJ,CAAC;QAEF,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAClC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/server.d.ts.map b/backend/dist/server.d.ts.map index ca9c68387a..46039523e5 100644 --- a/backend/dist/server.d.ts.map +++ b/backend/dist/server.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"} \ No newline at end of file +{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,eAAe,CAAC"} \ No newline at end of file diff --git a/backend/dist/server.js b/backend/dist/server.js index 12aa2fb28a..a15c22b2d3 100644 --- a/backend/dist/server.js +++ b/backend/dist/server.js @@ -1,25 +1,19 @@ +// backend/src/server.ts import "dotenv/config"; import app from "./index.js"; const port = Number(process.env.PORT) || 3000; -// 🔎 TEMP: log each request so we see what's being hit app.use((req, _res, next) => { console.log(`[req] ${req.method} ${req.url}`); next(); }); -// 🔎 TEMP: quick env presence check (no secrets exposed) -app.get("/api/debug/env", (_req, res) => { - res.json({ - HAV_BASE_URL: !!process.env.HAV_BASE_URL, - HAV_USER_AGENT: !!process.env.HAV_USER_AGENT, - HAV_V2_BASE: !!process.env.HAV_V2_BASE, - ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN ?? "", - MONGODB_URI_present: !!process.env.MONGODB_URI, - JWT_SECRET_present: !!process.env.JWT_SECRET, - NODE_ENV: process.env.NODE_ENV ?? "undefined", - PORT: process.env.PORT ?? "undefined", - }); -}); app.listen(port, () => { console.log(`API listening on http://localhost:${port}`); + const allowed = (process.env.CORS_ORIGIN || process.env.ALLOWED_ORIGIN || "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + console.log(allowed.length + ? `CORS allowed origins: ${allowed.join(", ")}` + : "CORS: no origins configured → permissive (dev)"); }); //# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/backend/dist/server.js.map b/backend/dist/server.js.map index accbee5697..a96291bc2b 100644 --- a/backend/dist/server.js.map +++ b/backend/dist/server.js.map @@ -1 +1 @@ -{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,GAAG,MAAM,YAAY,CAAC;AAE7B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAE9C,uDAAuD;AACvD,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,yDAAyD;AACzD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IACtC,GAAG,CAAC,IAAI,CAAC;QACP,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,cAAc,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc;QAC5C,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QACtC,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;QAChD,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;QAC9C,kBAAkB,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU;QAC5C,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,WAAW;QAC7C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,WAAW;KACtC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,wBAAwB;AACxB,OAAO,eAAe,CAAC;AACvB,OAAO,GAAG,MAAM,YAAY,CAAC;AAE7B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAE9C,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC1B,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACpB,OAAO,CAAC,GAAG,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;SAC1E,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO,CAAC,GAAG,CACT,OAAO,CAAC,MAAM;QACZ,CAAC,CAAC,yBAAyB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAC/C,CAAC,CAAC,gDAAgD,CACrD,CAAC;AACJ,CAAC,CAAC,CAAC"} \ No newline at end of file From 29ecb559cfe68fc6f0bfd79973a74ef03589338e Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 21:32:37 +0200 Subject: [PATCH 180/215] docs: update README test account to smoke@test.com (prod-verified) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 069daff4e9..76146a2012 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Frontend runs on http://localhost:5173 - Use these to try the app without registering: -Email: test@bada.app +Email: smoke@test.com Password: Test1234 From 60000dc127213466cebdae644836ca0a62e552b6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Sun, 28 Sep 2025 22:44:37 +0200 Subject: [PATCH 181/215] feat(icons): add light- and dark-mode favicon variants and update index.html --- frontend/index.html | 13 ++++++- frontend/public/icons/bada-icon-dark.svg | 42 +++++++++++++++++++++++ frontend/public/icons/bada-icon-light.svg | 42 +++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 frontend/public/icons/bada-icon-dark.svg create mode 100644 frontend/public/icons/bada-icon-light.svg diff --git a/frontend/index.html b/frontend/index.html index e94d2ce651..bc79442457 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,7 +2,18 @@ - + + BADA diff --git a/frontend/public/icons/bada-icon-dark.svg b/frontend/public/icons/bada-icon-dark.svg new file mode 100644 index 0000000000..aff5bc226b --- /dev/null +++ b/frontend/public/icons/bada-icon-dark.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/public/icons/bada-icon-light.svg b/frontend/public/icons/bada-icon-light.svg new file mode 100644 index 0000000000..42bbb873b7 --- /dev/null +++ b/frontend/public/icons/bada-icon-light.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0da8c38e19e9caf24ba149dccc7a6af57b417a87 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 10 Oct 2025 17:06:13 +0200 Subject: [PATCH 182/215] refactor(router): replace react-router-dom imports with react-router --- frontend/dist/assets/index-B_a48z93.css | 1 - frontend/dist/assets/index-C4A8iG7V.js | 849 ------------------ frontend/dist/assets/index-CznPG4kI.css | 1 + frontend/dist/assets/index-Dq4zsFBs.js | 872 +++++++++++++++++++ frontend/dist/icons/bada-icon-dark.svg | 42 + frontend/dist/icons/bada-icon-light.svg | 42 + frontend/dist/index.html | 17 +- frontend/package-lock.json | 132 ++- frontend/package.json | 3 +- frontend/src/App.tsx | 2 +- frontend/src/components/BeachesList.tsx | 2 +- frontend/src/components/Header.tsx | 2 +- frontend/src/components/SortableFavorite.tsx | 2 +- frontend/src/main.tsx | 2 +- frontend/src/pages/BeachDetailPage.tsx | 2 +- frontend/src/pages/FavoritesPage.tsx | 2 +- frontend/src/pages/LoginPage.tsx | 2 +- frontend/src/pages/RegisterPage.tsx | 2 +- frontend/src/routes/ProtectedRoute.tsx | 2 +- 19 files changed, 1094 insertions(+), 885 deletions(-) delete mode 100644 frontend/dist/assets/index-B_a48z93.css delete mode 100644 frontend/dist/assets/index-C4A8iG7V.js create mode 100644 frontend/dist/assets/index-CznPG4kI.css create mode 100644 frontend/dist/assets/index-Dq4zsFBs.js create mode 100644 frontend/dist/icons/bada-icon-dark.svg create mode 100644 frontend/dist/icons/bada-icon-light.svg diff --git a/frontend/dist/assets/index-B_a48z93.css b/frontend/dist/assets/index-B_a48z93.css deleted file mode 100644 index bc77d441b4..0000000000 --- a/frontend/dist/assets/index-B_a48z93.css +++ /dev/null @@ -1 +0,0 @@ -.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-scale{background-color:#ffffffbf;border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px #0000001a;padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px #00000059;box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:#0006;color:#fff;display:flex;font-size:1.4em;top:0;right:0;bottom:0;left:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-lg:64rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#efebe4;--color-surface-muted:#f6f2ec;--color-ink:#1a1a1a;--color-ink-muted:#64686c;--color-border:#cdc8c0;--color-accent:#0a5a82;--color-badge:#e6e4dc;--color-quality-excellent:#1f6fb2;--color-quality-good:#2b8a3e;--color-quality-sufficient:#b08900;--color-quality-poor:#c0392b;--color-quality-unknown:#6b7280;--font-spectral:"Spectral",serif;--font-inter:"Inter",system-ui,sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body,#root{height:100%}body{background-color:var(--color-surface);font-family:var(--font-inter);color:var(--color-ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4{font-family:var(--font-spectral);color:var(--color-ink)}a{border-radius:var(--radius-sm);color:var(--color-accent);text-underline-offset:2px}@media (hover:hover){a:hover{text-decoration-line:underline}}a:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){a:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}a:focus-visible{--tw-outline-style:none;outline-style:none}}@layer components{.card{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface-muted);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.badge{align-items:center;gap:calc(var(--spacing)*1);background-color:var(--color-badge);padding-inline:calc(var(--spacing)*2.5);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-ink);border-radius:3.40282e38px;display:inline-flex}.kpi-excellent{color:var(--color-quality-excellent)}.kpi-good{color:var(--color-quality-good)}.kpi-sufficient{color:var(--color-quality-sufficient)}.kpi-poor{color:var(--color-quality-poor)}.kpi-unknown{color:var(--color-quality-unknown)}}@layer utilities{.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline-block{display:inline-block}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-7{height:calc(var(--spacing)*7)}.h-9{height:calc(var(--spacing)*9)}.h-\[260px\]{height:260px}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-40{width:calc(var(--spacing)*40)}.w-56{width:calc(var(--spacing)*56)}.w-60{width:calc(var(--spacing)*60)}.w-full{width:100%}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.bg-border{background-color:var(--color-border)}.bg-surface{background-color:var(--color-surface)}.bg-surface-muted{background-color:var(--color-surface-muted)}.p-0{padding:calc(var(--spacing)*0)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.pt-2{padding-top:calc(var(--spacing)*2)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.text-left{text-align:left}.font-spectral{font-family:var(--font-spectral)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[-0\.06em\]{--tw-tracking:-.06em;letter-spacing:-.06em}.whitespace-pre-line{white-space:pre-line}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/60{--tw-ring-color:#0009}@supports (color:color-mix(in lab,red,red)){.ring-black\/60{--tw-ring-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.ring-white\/85{--tw-ring-color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.ring-white\/85{--tw-ring-color:color-mix(in oklab,var(--color-white)85%,transparent)}}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:bg-surface-muted:hover{background-color:var(--color-surface-muted)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent\/40:focus{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus\:ring-accent\/40:focus{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:opacity-60:disabled{opacity:.6}.aspect-square{aspect-ratio:1}}.maplibregl-map{-webkit-tap-highlight-color:#0000;font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;overflow:hidden}.maplibregl-canvas{position:absolute;top:0;left:0}.maplibregl-map:fullscreen{width:100%;height:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;z-index:2;position:absolute}.maplibregl-ctrl-top-left{top:0;left:0}.maplibregl-ctrl-top-right{top:0;right:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px #0000001a}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px buttontext}}.maplibregl-ctrl-group button{box-sizing:border-box;cursor:pointer;background-color:#0000;border:0;outline:none;width:29px;height:29px;padding:0;display:block}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;width:100%;height:100%;display:block}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:#0000}.maplibregl-ctrl-group button+button{border-top:1px solid buttontext}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:#0000000d}}.maplibregl-ctrl button:not(:disabled):active{background-color:#0000000d}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:2s linear infinite maplibregl-spin}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:88px;height:23px;margin:0 0 -4px -4px;display:block;overflow:hidden}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:#0000;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:#ffffff80;margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{box-sizing:content-box;color:#000;background-color:#fff;border-radius:12px;min-height:20px;margin:10px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{visibility:visible;padding:2px 28px 2px 8px}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{box-sizing:border-box;cursor:pointer;background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;outline:none;width:24px;height:24px;display:none;position:absolute;top:0;right:0}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;right:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;left:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:#000000bf;text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{box-sizing:border-box;color:#333;background-color:#ffffffbf;border:2px solid #333;border-top:#333;padding:0 5px;font-size:10px}.maplibregl-popup{pointer-events:none;will-change:transform;display:flex;position:absolute;top:0;left:0}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{z-index:1;border:10px solid #0000;width:0;height:0}.maplibregl-popup-anchor-top .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;align-self:center}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;align-self:center}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:#fff;align-self:center}.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:#fff;border-right:none;align-self:center}.maplibregl-popup-close-button{cursor:pointer;background-color:#0000;border:0;border-radius:0 3px 0 0;position:absolute;top:0;right:0}.maplibregl-popup-close-button:hover{background-color:#0000000d}.maplibregl-popup-content{pointer-events:auto;background:#fff;border-radius:3px;padding:15px 10px;position:relative;box-shadow:0 1px 2px #0000001a}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{will-change:transform;transition:opacity .2s;position:absolute;top:0;left:0}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;width:15px;height:15px}.maplibregl-user-location-dot:before{content:"";animation:2s infinite maplibregl-user-location-dot-pulse;position:absolute}.maplibregl-user-location-dot:after{box-sizing:border-box;content:"";border:2px solid #fff;border-radius:50%;width:19px;height:19px;position:absolute;top:-2px;left:-2px;box-shadow:0 0 3px #00000059}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;width:1px;height:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{opacity:.5;background:#fff;border:2px dotted #202020;width:0;height:0;position:absolute;top:0;left:0}.maplibregl-cooperative-gesture-screen{color:#fff;opacity:0;pointer-events:none;z-index:99999;background:#0006;justify-content:center;align-items:center;padding:1rem;font-size:1.4em;line-height:1.2;transition:opacity 1s 1s;display:flex;position:absolute;top:0;right:0;bottom:0;left:0}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity 50ms}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{z-index:99999;width:100%!important;height:100%!important;position:fixed!important;top:0!important;left:0!important}.dark{--color-surface:#161011;--color-surface-muted:#1e1819;--color-ink:#f0ede6;--color-ink-muted:#b8b2aa;--color-border:#40383a;--color-accent:#5ea5d2;--color-badge:#302a2c}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend/dist/assets/index-C4A8iG7V.js b/frontend/dist/assets/index-C4A8iG7V.js deleted file mode 100644 index a10c17baa0..0000000000 --- a/frontend/dist/assets/index-C4A8iG7V.js +++ /dev/null @@ -1,849 +0,0 @@ -var Ng=b=>{throw TypeError(b)};var mm=(b,_,x)=>_.has(b)||Ng("Cannot "+x);var ge=(b,_,x)=>(mm(b,_,"read from private field"),x?x.call(b):_.get(b)),Ut=(b,_,x)=>_.has(b)?Ng("Cannot add the same private member more than once"):_ instanceof WeakSet?_.add(b):_.set(b,x),wt=(b,_,x,E)=>(mm(b,_,"write to private field"),E?E.call(b,x):_.set(b,x),x),_r=(b,_,x)=>(mm(b,_,"access private method"),x);var If=(b,_,x,E)=>({set _(z){wt(b,_,z,x)},get _(){return ge(b,_,E)}});(function(){const _=document.createElement("link").relList;if(_&&_.supports&&_.supports("modulepreload"))return;for(const z of document.querySelectorAll('link[rel="modulepreload"]'))E(z);new MutationObserver(z=>{for(const j of z)if(j.type==="childList")for(const C of j.addedNodes)C.tagName==="LINK"&&C.rel==="modulepreload"&&E(C)}).observe(document,{childList:!0,subtree:!0});function x(z){const j={};return z.integrity&&(j.integrity=z.integrity),z.referrerPolicy&&(j.referrerPolicy=z.referrerPolicy),z.crossOrigin==="use-credentials"?j.credentials="include":z.crossOrigin==="anonymous"?j.credentials="omit":j.credentials="same-origin",j}function E(z){if(z.ep)return;z.ep=!0;const j=x(z);fetch(z.href,j)}})();function Hm(b){return b&&b.__esModule&&Object.prototype.hasOwnProperty.call(b,"default")?b.default:b}var gm={exports:{}},Cp={},_m={exports:{}},br={};/** - * @license React - * react.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Ug;function Jy(){if(Ug)return br;Ug=1;var b=Symbol.for("react.element"),_=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),E=Symbol.for("react.strict_mode"),z=Symbol.for("react.profiler"),j=Symbol.for("react.provider"),C=Symbol.for("react.context"),u=Symbol.for("react.forward_ref"),G=Symbol.for("react.suspense"),ee=Symbol.for("react.memo"),ae=Symbol.for("react.lazy"),ce=Symbol.iterator;function _e(Te){return Te===null||typeof Te!="object"?null:(Te=ce&&Te[ce]||Te["@@iterator"],typeof Te=="function"?Te:null)}var te={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Oe=Object.assign,Ue={};function Fe(Te,He,Dt){this.props=Te,this.context=He,this.refs=Ue,this.updater=Dt||te}Fe.prototype.isReactComponent={},Fe.prototype.setState=function(Te,He){if(typeof Te!="object"&&typeof Te!="function"&&Te!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Te,He,"setState")},Fe.prototype.forceUpdate=function(Te){this.updater.enqueueForceUpdate(this,Te,"forceUpdate")};function et(){}et.prototype=Fe.prototype;function nt(Te,He,Dt){this.props=Te,this.context=He,this.refs=Ue,this.updater=Dt||te}var qe=nt.prototype=new et;qe.constructor=nt,Oe(qe,Fe.prototype),qe.isPureReactComponent=!0;var We=Array.isArray,Rt=Object.prototype.hasOwnProperty,Vt={current:null},Kt={key:!0,ref:!0,__self:!0,__source:!0};function mt(Te,He,Dt){var qt,or={},Qt=null,Er=null;if(He!=null)for(qt in He.ref!==void 0&&(Er=He.ref),He.key!==void 0&&(Qt=""+He.key),He)Rt.call(He,qt)&&!Kt.hasOwnProperty(qt)&&(or[qt]=He[qt]);var cr=arguments.length-2;if(cr===1)or.children=Dt;else if(1>>1,He=ft[Te];if(0>>1;Tez(or,gt))Qtz(Er,or)?(ft[Te]=Er,ft[Qt]=gt,Te=Qt):(ft[Te]=or,ft[qt]=gt,Te=qt);else if(Qtz(Er,gt))ft[Te]=Er,ft[Qt]=gt,Te=Qt;else break e}}return pt}function z(ft,pt){var gt=ft.sortIndex-pt.sortIndex;return gt!==0?gt:ft.id-pt.id}if(typeof performance=="object"&&typeof performance.now=="function"){var j=performance;b.unstable_now=function(){return j.now()}}else{var C=Date,u=C.now();b.unstable_now=function(){return C.now()-u}}var G=[],ee=[],ae=1,ce=null,_e=3,te=!1,Oe=!1,Ue=!1,Fe=typeof setTimeout=="function"?setTimeout:null,et=typeof clearTimeout=="function"?clearTimeout:null,nt=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function qe(ft){for(var pt=x(ee);pt!==null;){if(pt.callback===null)E(ee);else if(pt.startTime<=ft)E(ee),pt.sortIndex=pt.expirationTime,_(G,pt);else break;pt=x(ee)}}function We(ft){if(Ue=!1,qe(ft),!Oe)if(x(G)!==null)Oe=!0,Mr(Rt);else{var pt=x(ee);pt!==null&&pr(We,pt.startTime-ft)}}function Rt(ft,pt){Oe=!1,Ue&&(Ue=!1,et(mt),mt=-1),te=!0;var gt=_e;try{for(qe(pt),ce=x(G);ce!==null&&(!(ce.expirationTime>pt)||ft&&!Cr());){var Te=ce.callback;if(typeof Te=="function"){ce.callback=null,_e=ce.priorityLevel;var He=Te(ce.expirationTime<=pt);pt=b.unstable_now(),typeof He=="function"?ce.callback=He:ce===x(G)&&E(G),qe(pt)}else E(G);ce=x(G)}if(ce!==null)var Dt=!0;else{var qt=x(ee);qt!==null&&pr(We,qt.startTime-pt),Dt=!1}return Dt}finally{ce=null,_e=gt,te=!1}}var Vt=!1,Kt=null,mt=-1,Tt=5,xt=-1;function Cr(){return!(b.unstable_now()-xtft||125Te?(ft.sortIndex=gt,_(ee,ft),x(G)===null&&ft===x(ee)&&(Ue?(et(mt),mt=-1):Ue=!0,pr(We,gt-Te))):(ft.sortIndex=He,_(G,ft),Oe||te||(Oe=!0,Mr(Rt))),ft},b.unstable_shouldYield=Cr,b.unstable_wrapCallback=function(ft){var pt=_e;return function(){var gt=_e;_e=pt;try{return ft.apply(this,arguments)}finally{_e=gt}}}})(xm)),xm}var qg;function nv(){return qg||(qg=1,vm.exports=rv()),vm.exports}/** - * @license React - * react-dom.production.min.js - * - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */var Hg;function iv(){if(Hg)return Ks;Hg=1;var b=Wm(),_=nv();function x(s){for(var l="https://reactjs.org/docs/error-decoder.html?invariant="+s,g=1;g"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),G=Object.prototype.hasOwnProperty,ee=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,ae={},ce={};function _e(s){return G.call(ce,s)?!0:G.call(ae,s)?!1:ee.test(s)?ce[s]=!0:(ae[s]=!0,!1)}function te(s,l,g,w){if(g!==null&&g.type===0)return!1;switch(typeof l){case"function":case"symbol":return!0;case"boolean":return w?!1:g!==null?!g.acceptsBooleans:(s=s.toLowerCase().slice(0,5),s!=="data-"&&s!=="aria-");default:return!1}}function Oe(s,l,g,w){if(l===null||typeof l>"u"||te(s,l,g,w))return!0;if(w)return!1;if(g!==null)switch(g.type){case 3:return!l;case 4:return l===!1;case 5:return isNaN(l);case 6:return isNaN(l)||1>l}return!1}function Ue(s,l,g,w,k,D,W){this.acceptsBooleans=l===2||l===3||l===4,this.attributeName=w,this.attributeNamespace=k,this.mustUseProperty=g,this.propertyName=s,this.type=l,this.sanitizeURL=D,this.removeEmptyString=W}var Fe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(s){Fe[s]=new Ue(s,0,!1,s,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(s){var l=s[0];Fe[l]=new Ue(l,1,!1,s[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(s){Fe[s]=new Ue(s,2,!1,s.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(s){Fe[s]=new Ue(s,2,!1,s,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(s){Fe[s]=new Ue(s,3,!1,s.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(s){Fe[s]=new Ue(s,3,!0,s,null,!1,!1)}),["capture","download"].forEach(function(s){Fe[s]=new Ue(s,4,!1,s,null,!1,!1)}),["cols","rows","size","span"].forEach(function(s){Fe[s]=new Ue(s,6,!1,s,null,!1,!1)}),["rowSpan","start"].forEach(function(s){Fe[s]=new Ue(s,5,!1,s.toLowerCase(),null,!1,!1)});var et=/[\-:]([a-z])/g;function nt(s){return s[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(s){var l=s.replace(et,nt);Fe[l]=new Ue(l,1,!1,s,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(s){Fe[s]=new Ue(s,1,!1,s.toLowerCase(),null,!1,!1)}),Fe.xlinkHref=new Ue("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(s){Fe[s]=new Ue(s,1,!1,s.toLowerCase(),null,!0,!0)});function qe(s,l,g,w){var k=Fe.hasOwnProperty(l)?Fe[l]:null;(k!==null?k.type!==0:w||!(2he||k[W]!==D[he]){var ve=` -`+k[W].replace(" at new "," at ");return s.displayName&&ve.includes("")&&(ve=ve.replace("",s.displayName)),ve}while(1<=W&&0<=he);break}}}finally{Dt=!1,Error.prepareStackTrace=g}return(s=s?s.displayName||s.name:"")?He(s):""}function or(s){switch(s.tag){case 5:return He(s.type);case 16:return He("Lazy");case 13:return He("Suspense");case 19:return He("SuspenseList");case 0:case 2:case 15:return s=qt(s.type,!1),s;case 11:return s=qt(s.type.render,!1),s;case 1:return s=qt(s.type,!0),s;default:return""}}function Qt(s){if(s==null)return null;if(typeof s=="function")return s.displayName||s.name||null;if(typeof s=="string")return s;switch(s){case Kt:return"Fragment";case Vt:return"Portal";case Tt:return"Profiler";case mt:return"StrictMode";case mr:return"Suspense";case Vr:return"SuspenseList"}if(typeof s=="object")switch(s.$$typeof){case Cr:return(s.displayName||"Context")+".Consumer";case xt:return(s._context.displayName||"Context")+".Provider";case Br:var l=s.render;return s=s.displayName,s||(s=l.displayName||l.name||"",s=s!==""?"ForwardRef("+s+")":"ForwardRef"),s;case xn:return l=s.displayName||null,l!==null?l:Qt(s.type)||"Memo";case Mr:l=s._payload,s=s._init;try{return Qt(s(l))}catch{}}return null}function Er(s){var l=s.type;switch(s.tag){case 24:return"Cache";case 9:return(l.displayName||"Context")+".Consumer";case 10:return(l._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return s=l.render,s=s.displayName||s.name||"",l.displayName||(s!==""?"ForwardRef("+s+")":"ForwardRef");case 7:return"Fragment";case 5:return l;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Qt(l);case 8:return l===mt?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof l=="function")return l.displayName||l.name||null;if(typeof l=="string")return l}return null}function cr(s){switch(typeof s){case"boolean":case"number":case"string":case"undefined":return s;case"object":return s;default:return""}}function vr(s){var l=s.type;return(s=s.nodeName)&&s.toLowerCase()==="input"&&(l==="checkbox"||l==="radio")}function zn(s){var l=vr(s)?"checked":"value",g=Object.getOwnPropertyDescriptor(s.constructor.prototype,l),w=""+s[l];if(!s.hasOwnProperty(l)&&typeof g<"u"&&typeof g.get=="function"&&typeof g.set=="function"){var k=g.get,D=g.set;return Object.defineProperty(s,l,{configurable:!0,get:function(){return k.call(this)},set:function(W){w=""+W,D.call(this,W)}}),Object.defineProperty(s,l,{enumerable:g.enumerable}),{getValue:function(){return w},setValue:function(W){w=""+W},stopTracking:function(){s._valueTracker=null,delete s[l]}}}}function Qs(s){s._valueTracker||(s._valueTracker=zn(s))}function yi(s){if(!s)return!1;var l=s._valueTracker;if(!l)return!0;var g=l.getValue(),w="";return s&&(w=vr(s)?s.checked?"true":"false":s.value),s=w,s!==g?(l.setValue(s),!0):!1}function Wr(s){if(s=s||(typeof document<"u"?document:void 0),typeof s>"u")return null;try{return s.activeElement||s.body}catch{return s.body}}function Jr(s,l){var g=l.checked;return gt({},l,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:g??s._wrapperState.initialChecked})}function Gi(s,l){var g=l.defaultValue==null?"":l.defaultValue,w=l.checked!=null?l.checked:l.defaultChecked;g=cr(l.value!=null?l.value:g),s._wrapperState={initialChecked:w,initialValue:g,controlled:l.type==="checkbox"||l.type==="radio"?l.checked!=null:l.value!=null}}function ss(s,l){l=l.checked,l!=null&&qe(s,"checked",l,!1)}function Js(s,l){ss(s,l);var g=cr(l.value),w=l.type;if(g!=null)w==="number"?(g===0&&s.value===""||s.value!=g)&&(s.value=""+g):s.value!==""+g&&(s.value=""+g);else if(w==="submit"||w==="reset"){s.removeAttribute("value");return}l.hasOwnProperty("value")?qi(s,l.type,g):l.hasOwnProperty("defaultValue")&&qi(s,l.type,cr(l.defaultValue)),l.checked==null&&l.defaultChecked!=null&&(s.defaultChecked=!!l.defaultChecked)}function as(s,l,g){if(l.hasOwnProperty("value")||l.hasOwnProperty("defaultValue")){var w=l.type;if(!(w!=="submit"&&w!=="reset"||l.value!==void 0&&l.value!==null))return;l=""+s._wrapperState.initialValue,g||l===s.value||(s.value=l),s.defaultValue=l}g=s.name,g!==""&&(s.name=""),s.defaultChecked=!!s._wrapperState.initialChecked,g!==""&&(s.name=g)}function qi(s,l,g){(l!=="number"||Wr(s.ownerDocument)!==s)&&(g==null?s.defaultValue=""+s._wrapperState.initialValue:s.defaultValue!==""+g&&(s.defaultValue=""+g))}var Is=Array.isArray;function ki(s,l,g,w){if(s=s.options,l){l={};for(var k=0;k"+l.valueOf().toString()+"",l=na.firstChild;s.firstChild;)s.removeChild(s.firstChild);for(;l.firstChild;)s.appendChild(l.firstChild)}});function Z(s,l){if(l){var g=s.firstChild;if(g&&g===s.lastChild&&g.nodeType===3){g.nodeValue=l;return}}s.textContent=l}var H={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},X=["Webkit","ms","Moz","O"];Object.keys(H).forEach(function(s){X.forEach(function(l){l=l+s.charAt(0).toUpperCase()+s.substring(1),H[l]=H[s]})});function se(s,l,g){return l==null||typeof l=="boolean"||l===""?"":g||typeof l!="number"||l===0||H.hasOwnProperty(s)&&H[s]?(""+l).trim():l+"px"}function pe(s,l){s=s.style;for(var g in l)if(l.hasOwnProperty(g)){var w=g.indexOf("--")===0,k=se(g,l[g],w);g==="float"&&(g="cssFloat"),w?s.setProperty(g,k):s[g]=k}}var Pe=gt({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ke(s,l){if(l){if(Pe[s]&&(l.children!=null||l.dangerouslySetInnerHTML!=null))throw Error(x(137,s));if(l.dangerouslySetInnerHTML!=null){if(l.children!=null)throw Error(x(60));if(typeof l.dangerouslySetInnerHTML!="object"||!("__html"in l.dangerouslySetInnerHTML))throw Error(x(61))}if(l.style!=null&&typeof l.style!="object")throw Error(x(62))}}function be(s,l){if(s.indexOf("-")===-1)return typeof l.is=="string";switch(s){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Ne=null;function Xe(s){return s=s.target||s.srcElement||window,s.correspondingUseElement&&(s=s.correspondingUseElement),s.nodeType===3?s.parentNode:s}var $e=null,_t=null,we=null;function Ot(s){if(s=ol(s)){if(typeof $e!="function")throw Error(x(280));var l=s.stateNode;l&&(l=Jl(l),$e(s.stateNode,s.type,l))}}function er(s){_t?we?we.push(s):we=[s]:_t=s}function Pt(){if(_t){var s=_t,l=we;if(we=_t=null,Ot(s),l)for(s=0;s>>=0,s===0?32:31-(Vh(s)/Bo|0)|0}var Cu=64,Mu=4194304;function Al(s){switch(s&-s){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return s&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return s&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return s}}function jo(s,l){var g=s.pendingLanes;if(g===0)return 0;var w=0,k=s.suspendedLanes,D=s.pingedLanes,W=g&268435455;if(W!==0){var he=W&~k;he!==0?w=Al(he):(D&=W,D!==0&&(w=Al(D)))}else W=g&~k,W!==0?w=Al(W):D!==0&&(w=Al(D));if(w===0)return 0;if(l!==0&&l!==w&&(l&k)===0&&(k=w&-w,D=l&-l,k>=D||k===16&&(D&4194240)!==0))return l;if((w&4)!==0&&(w|=g&16),l=s.entangledLanes,l!==0)for(s=s.entanglements,l&=w;0g;g++)l.push(s);return l}function ao(s,l,g){s.pendingLanes|=l,l!==536870912&&(s.suspendedLanes=0,s.pingedLanes=0),s=s.eventTimes,l=31-Hi(l),s[l]=g}function Od(s,l){var g=s.pendingLanes&~l;s.pendingLanes=l,s.suspendedLanes=0,s.pingedLanes=0,s.expiredLanes&=l,s.mutableReadLanes&=l,s.entangledLanes&=l,l=s.entanglements;var w=s.eventTimes;for(s=s.expirationTimes;0=Wo),Bl=" ",Lc=!1;function Dc(s,l){switch(s){case"keyup":return kc.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Ua(s){return s=s.detail,typeof s=="object"&&"data"in s?s.data:null}var Va=!1;function Fc(s,l){switch(s){case"compositionend":return Ua(l);case"keypress":return l.which!==32?null:(Lc=!0,Bl);case"textInput":return s=l.data,s===Bl&&Lc?null:s;default:return null}}function jl(s,l){if(Va)return s==="compositionend"||!Ac&&Dc(s,l)?(s=Ki(),oo=Zo=cs=null,Va=!1,s):null;switch(s){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1=l)return{node:g,offset:l-s};s=w}e:{for(;g;){if(g.nextSibling){g=g.nextSibling;break e}g=g.parentNode}g=void 0}g=Kh(g)}}function Xh(s,l){return s&&l?s===l?!0:s&&s.nodeType===3?!1:l&&l.nodeType===3?Xh(s,l.parentNode):"contains"in s?s.contains(l):s.compareDocumentPosition?!!(s.compareDocumentPosition(l)&16):!1:!1}function $c(){for(var s=window,l=Wr();l instanceof s.HTMLIFrameElement;){try{var g=typeof l.contentWindow.location.href=="string"}catch{g=!1}if(g)s=l.contentWindow;else break;l=Wr(s.document)}return l}function Jo(s){var l=s&&s.nodeName&&s.nodeName.toLowerCase();return l&&(l==="input"&&(s.type==="text"||s.type==="search"||s.type==="tel"||s.type==="url"||s.type==="password")||l==="textarea"||s.contentEditable==="true")}function Du(s){var l=$c(),g=s.focusedElem,w=s.selectionRange;if(l!==g&&g&&g.ownerDocument&&Xh(g.ownerDocument.documentElement,g)){if(w!==null&&Jo(g)){if(l=w.start,s=w.end,s===void 0&&(s=l),"selectionStart"in g)g.selectionStart=l,g.selectionEnd=Math.min(s,g.value.length);else if(s=(l=g.ownerDocument||document)&&l.defaultView||window,s.getSelection){s=s.getSelection();var k=g.textContent.length,D=Math.min(w.start,k);w=w.end===void 0?D:Math.min(w.end,k),!s.extend&&D>w&&(k=w,w=D,D=k),k=Vc(g,D);var W=Vc(g,w);k&&W&&(s.rangeCount!==1||s.anchorNode!==k.node||s.anchorOffset!==k.offset||s.focusNode!==W.node||s.focusOffset!==W.offset)&&(l=l.createRange(),l.setStart(k.node,k.offset),s.removeAllRanges(),D>w?(s.addRange(l),s.extend(W.node,W.offset)):(l.setEnd(W.node,W.offset),s.addRange(l)))}}for(l=[],s=g;s=s.parentNode;)s.nodeType===1&&l.push({element:s,left:s.scrollLeft,top:s.scrollTop});for(typeof g.focus=="function"&&g.focus(),g=0;g=document.documentMode,$a=null,ua=null,ca=null,Za=!1;function Ri(s,l,g){var w=g.window===g?g.document:g.nodeType===9?g:g.ownerDocument;Za||$a==null||$a!==Wr(w)||(w=$a,"selectionStart"in w&&Jo(w)?w={start:w.selectionStart,end:w.selectionEnd}:(w=(w.ownerDocument&&w.ownerDocument.defaultView||window).getSelection(),w={anchorNode:w.anchorNode,anchorOffset:w.anchorOffset,focusNode:w.focusNode,focusOffset:w.focusOffset}),ca&&Ul(ca,w)||(ca=w,w=Wl(ua,"onSelect"),0yo||(s.current=Uu[yo],Uu[yo]=null,yo--)}function dr(s,l){yo++,Uu[yo]=s.current,s.current=l}var Di={},Fn=Dn(Di),ni=Dn(!1),Wa=Di;function gs(s,l){var g=s.type.contextTypes;if(!g)return Di;var w=s.stateNode;if(w&&w.__reactInternalMemoizedUnmaskedChildContext===l)return w.__reactInternalMemoizedMaskedChildContext;var k={},D;for(D in g)k[D]=l[D];return w&&(s=s.stateNode,s.__reactInternalMemoizedUnmaskedChildContext=l,s.__reactInternalMemoizedMaskedChildContext=k),k}function Xn(s){return s=s.childContextTypes,s!=null}function ya(){Rr(ni),Rr(Fn)}function Wc(s,l,g){if(Fn.current!==Di)throw Error(x(168));dr(Fn,l),dr(ni,g)}function ii(s,l,g){var w=s.stateNode;if(l=l.childContextTypes,typeof w.getChildContext!="function")return g;w=w.getChildContext();for(var k in w)if(!(k in l))throw Error(x(108,Er(s)||"Unknown",k));return gt({},g,w)}function vi(s){return s=(s=s.stateNode)&&s.__reactInternalMemoizedMergedChildContext||Di,Wa=Fn.current,dr(Fn,s),dr(ni,ni.current),!0}function vo(s,l,g){var w=s.stateNode;if(!w)throw Error(x(169));g?(s=ii(s,l,Wa),w.__reactInternalMemoizedMergedChildContext=s,Rr(ni),Rr(Fn),dr(Fn,s)):Rr(ni),dr(ni,g)}var Pn=null,va=!1,Kc=!1;function ll(s){Pn===null?Pn=[s]:Pn.push(s)}function Hd(s){va=!0,ll(s)}function Ka(){if(!Kc&&Pn!==null){Kc=!0;var s=0,l=wr;try{var g=Pn;for(wr=1;s>=W,k-=W,wi=1<<32-Hi(l)+k|g<Wt?(Gn=Nt,Nt=null):Gn=Nt.sibling;var Tr=Ye(Re,Nt,De[Wt],ot);if(Tr===null){Nt===null&&(Nt=Gn);break}s&&Nt&&Tr.alternate===null&&l(Re,Nt),Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr,Nt=Gn}if(Wt===De.length)return g(Re,Nt),Xr&&$s(Re,Wt),Et;if(Nt===null){for(;WtWt?(Gn=Nt,Nt=null):Gn=Nt.sibling;var Io=Ye(Re,Nt,Tr.value,ot);if(Io===null){Nt===null&&(Nt=Gn);break}s&&Nt&&Io.alternate===null&&l(Re,Nt),Ce=D(Io,Ce,Wt),jt===null?Et=Io:jt.sibling=Io,jt=Io,Nt=Gn}if(Tr.done)return g(Re,Nt),Xr&&$s(Re,Wt),Et;if(Nt===null){for(;!Tr.done;Wt++,Tr=De.next())Tr=Je(Re,Tr.value,ot),Tr!==null&&(Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr);return Xr&&$s(Re,Wt),Et}for(Nt=w(Re,Nt);!Tr.done;Wt++,Tr=De.next())Tr=bt(Nt,Re,Wt,Tr.value,ot),Tr!==null&&(s&&Tr.alternate!==null&&Nt.delete(Tr.key===null?Wt:Tr.key),Ce=D(Tr,Ce,Wt),jt===null?Et=Tr:jt.sibling=Tr,jt=Tr);return s&&Nt.forEach(function(em){return l(Re,em)}),Xr&&$s(Re,Wt),Et}function an(Re,Ce,De,ot){if(typeof De=="object"&&De!==null&&De.type===Kt&&De.key===null&&(De=De.props.children),typeof De=="object"&&De!==null){switch(De.$$typeof){case Rt:e:{for(var Et=De.key,jt=Ce;jt!==null;){if(jt.key===Et){if(Et=De.type,Et===Kt){if(jt.tag===7){g(Re,jt.sibling),Ce=k(jt,De.props.children),Ce.return=Re,Re=Ce;break e}}else if(jt.elementType===Et||typeof Et=="object"&&Et!==null&&Et.$$typeof===Mr&&Zu(Et)===jt.type){g(Re,jt.sibling),Ce=k(jt,De.props),Ce.ref=cl(Re,jt,De),Ce.return=Re,Re=Ce;break e}g(Re,jt);break}else l(Re,jt);jt=jt.sibling}De.type===Kt?(Ce=pu(De.props.children,Re.mode,ot,De.key),Ce.return=Re,Re=Ce):(ot=cd(De.type,De.key,De.props,null,Re.mode,ot),ot.ref=cl(Re,Ce,De),ot.return=Re,Re=ot)}return W(Re);case Vt:e:{for(jt=De.key;Ce!==null;){if(Ce.key===jt)if(Ce.tag===4&&Ce.stateNode.containerInfo===De.containerInfo&&Ce.stateNode.implementation===De.implementation){g(Re,Ce.sibling),Ce=k(Ce,De.children||[]),Ce.return=Re,Re=Ce;break e}else{g(Re,Ce);break}else l(Re,Ce);Ce=Ce.sibling}Ce=vp(De,Re.mode,ot),Ce.return=Re,Re=Ce}return W(Re);case Mr:return jt=De._init,an(Re,Ce,jt(De._payload),ot)}if(Is(De))return Mt(Re,Ce,De,ot);if(pt(De))return At(Re,Ce,De,ot);hl(Re,De)}return typeof De=="string"&&De!==""||typeof De=="number"?(De=""+De,Ce!==null&&Ce.tag===6?(g(Re,Ce.sibling),Ce=k(Ce,De),Ce.return=Re,Re=Ce):(g(Re,Ce),Ce=yp(De,Re.mode,ot),Ce.return=Re,Re=Ce),W(Re)):g(Re,Ce)}return an}var oi=eh(!0),ru=eh(!1),yt=Dn(null),kt=null,ba=null,Zs=null;function dl(){Zs=ba=kt=null}function Vn(s){var l=yt.current;Rr(yt),s._currentValue=l}function Gu(s,l,g){for(;s!==null;){var w=s.alternate;if((s.childLanes&l)!==l?(s.childLanes|=l,w!==null&&(w.childLanes|=l)):w!==null&&(w.childLanes&l)!==l&&(w.childLanes|=l),s===g)break;s=s.return}}function $n(s,l){kt=s,Zs=ba=null,s=s.dependencies,s!==null&&s.firstContext!==null&&((s.lanes&l)!==0&&(es=!0),s.firstContext=null)}function Yi(s){var l=s._currentValue;if(Zs!==s)if(s={context:s,memoizedValue:l,next:null},ba===null){if(kt===null)throw Error(x(308));ba=s,kt.dependencies={lanes:0,firstContext:s}}else ba=ba.next=s;return l}var _s=null;function Ir(s){_s===null?_s=[s]:_s.push(s)}function en(s,l,g,w){var k=l.interleaved;return k===null?(g.next=g,Ir(l)):(g.next=k.next,k.next=g),l.interleaved=g,Qi(s,w)}function Qi(s,l){s.lanes|=l;var g=s.alternate;for(g!==null&&(g.lanes|=l),g=s,s=s.return;s!==null;)s.childLanes|=l,g=s.alternate,g!==null&&(g.childLanes|=l),g=s,s=s.return;return g.tag===3?g.stateNode:null}var Bi=!1;function Ya(s){s.updateQueue={baseState:s.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function qu(s,l){s=s.updateQueue,l.updateQueue===s&&(l.updateQueue={baseState:s.baseState,firstBaseUpdate:s.firstBaseUpdate,lastBaseUpdate:s.lastBaseUpdate,shared:s.shared,effects:s.effects})}function Si(s,l){return{eventTime:s,lane:l,tag:0,payload:null,callback:null,next:null}}function Ti(s,l,g){var w=s.updateQueue;if(w===null)return null;if(w=w.shared,(gr&2)!==0){var k=w.pending;return k===null?l.next=l:(l.next=k.next,k.next=l),w.pending=l,Qi(s,g)}return k=w.interleaved,k===null?(l.next=l,Ir(w)):(l.next=k.next,k.next=l),w.interleaved=l,Qi(s,g)}function Qa(s,l,g){if(l=l.updateQueue,l!==null&&(l=l.shared,(g&4194240)!==0)){var w=l.lanes;w&=s.pendingLanes,g|=w,l.lanes=g,Da(s,g)}}function nu(s,l){var g=s.updateQueue,w=s.alternate;if(w!==null&&(w=w.updateQueue,g===w)){var k=null,D=null;if(g=g.firstBaseUpdate,g!==null){do{var W={eventTime:g.eventTime,lane:g.lane,tag:g.tag,payload:g.payload,callback:g.callback,next:null};D===null?k=D=W:D=D.next=W,g=g.next}while(g!==null);D===null?k=D=l:D=D.next=l}else k=D=l;g={baseState:w.baseState,firstBaseUpdate:k,lastBaseUpdate:D,shared:w.shared,effects:w.effects},s.updateQueue=g;return}s=g.lastBaseUpdate,s===null?g.firstBaseUpdate=l:s.next=l,g.lastBaseUpdate=l}function Gs(s,l,g,w){var k=s.updateQueue;Bi=!1;var D=k.firstBaseUpdate,W=k.lastBaseUpdate,he=k.shared.pending;if(he!==null){k.shared.pending=null;var ve=he,je=ve.next;ve.next=null,W===null?D=je:W.next=je,W=ve;var rt=s.alternate;rt!==null&&(rt=rt.updateQueue,he=rt.lastBaseUpdate,he!==W&&(he===null?rt.firstBaseUpdate=je:he.next=je,rt.lastBaseUpdate=ve))}if(D!==null){var Je=k.baseState;W=0,rt=je=ve=null,he=D;do{var Ye=he.lane,bt=he.eventTime;if((w&Ye)===Ye){rt!==null&&(rt=rt.next={eventTime:bt,lane:0,tag:he.tag,payload:he.payload,callback:he.callback,next:null});e:{var Mt=s,At=he;switch(Ye=l,bt=g,At.tag){case 1:if(Mt=At.payload,typeof Mt=="function"){Je=Mt.call(bt,Je,Ye);break e}Je=Mt;break e;case 3:Mt.flags=Mt.flags&-65537|128;case 0:if(Mt=At.payload,Ye=typeof Mt=="function"?Mt.call(bt,Je,Ye):Mt,Ye==null)break e;Je=gt({},Je,Ye);break e;case 2:Bi=!0}}he.callback!==null&&he.lane!==0&&(s.flags|=64,Ye=k.effects,Ye===null?k.effects=[he]:Ye.push(he))}else bt={eventTime:bt,lane:Ye,tag:he.tag,payload:he.payload,callback:he.callback,next:null},rt===null?(je=rt=bt,ve=Je):rt=rt.next=bt,W|=Ye;if(he=he.next,he===null){if(he=k.shared.pending,he===null)break;Ye=he,he=Ye.next,Ye.next=null,k.lastBaseUpdate=Ye,k.shared.pending=null}}while(!0);if(rt===null&&(ve=Je),k.baseState=ve,k.firstBaseUpdate=je,k.lastBaseUpdate=rt,l=k.shared.interleaved,l!==null){k=l;do W|=k.lane,k=k.next;while(k!==l)}else D===null&&(k.shared.lanes=0);ml|=W,s.lanes=W,s.memoizedState=Je}}function Hu(s,l,g){if(s=l.effects,l.effects=null,s!==null)for(l=0;lg?g:4,s(!0);var w=O.transition;O.transition={};try{s(!1),l()}finally{wr=g,O.transition=w}}function li(){return Ae().memoizedState}function ys(s,l,g){var w=ro(s);if(g={lane:w,action:g,hasEagerState:!1,eagerState:null,next:null},Sa(s))pn(l,g);else if(g=en(s,l,g,w),g!==null){var k=Zn();Ma(g,s,w,k),_n(g,l,w)}}function ui(s,l,g){var w=ro(s),k={lane:w,action:g,hasEagerState:!1,eagerState:null,next:null};if(Sa(s))pn(l,k);else{var D=s.alternate;if(s.lanes===0&&(D===null||D.lanes===0)&&(D=l.lastRenderedReducer,D!==null))try{var W=l.lastRenderedState,he=D(W,g);if(k.hasEagerState=!0,k.eagerState=he,fs(he,W)){var ve=l.interleaved;ve===null?(k.next=k,Ir(l)):(k.next=ve.next,ve.next=k),l.interleaved=k;return}}catch{}finally{}g=en(s,l,k,w),g!==null&&(k=Zn(),Ma(g,s,w,k),_n(g,l,w))}}function Sa(s){var l=s.alternate;return s===B||l!==null&&l===B}function pn(s,l){J=Q=!0;var g=s.pending;g===null?l.next=l:(l.next=g.next,g.next=l),s.pending=l}function _n(s,l,g){if((g&4194240)!==0){var w=l.lanes;w&=s.pendingLanes,g|=w,l.lanes=g,Da(s,g)}}var ci={readContext:Yi,useCallback:ne,useContext:ne,useEffect:ne,useImperativeHandle:ne,useInsertionEffect:ne,useLayoutEffect:ne,useMemo:ne,useReducer:ne,useRef:ne,useState:ne,useDebugValue:ne,useDeferredValue:ne,useTransition:ne,useMutableSource:ne,useSyncExternalStore:ne,useId:ne,unstable_isNewReconciler:!1},yn={readContext:Yi,useCallback:function(s,l){return ye().memoizedState=[s,l===void 0?null:l],s},useContext:Yi,useEffect:hn,useImperativeHandle:function(s,l,g){return g=g!=null?g.concat([s]):null,Zt(4194308,4,Ji.bind(null,l,s),g)},useLayoutEffect:function(s,l){return Zt(4194308,4,s,l)},useInsertionEffect:function(s,l){return Zt(4,2,s,l)},useMemo:function(s,l){var g=ye();return l=l===void 0?null:l,s=s(),g.memoizedState=[s,l],s},useReducer:function(s,l,g){var w=ye();return l=g!==void 0?g(l):l,w.memoizedState=w.baseState=l,s={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:s,lastRenderedState:l},w.queue=s,s=s.dispatch=ys.bind(null,B,s),[w.memoizedState,s]},useRef:function(s){var l=ye();return s={current:s},l.memoizedState=s},useState:kr,useDebugValue:qs,useDeferredValue:function(s){return ye().memoizedState=s},useTransition:function(){var s=kr(!1),l=s[0];return s=To.bind(null,s[1]),ye().memoizedState=s,[l,s]},useMutableSource:function(){},useSyncExternalStore:function(s,l,g){var w=B,k=ye();if(Xr){if(g===void 0)throw Error(x(407));g=g()}else{if(g=l(),En===null)throw Error(x(349));(F&30)!==0||Ge(w,l,g)}k.memoizedState=g;var D={value:g,getSnapshot:l};return k.queue=D,hn(Yt.bind(null,w,D,s),[s]),w.flags|=2048,Ur(9,vt.bind(null,w,D,g,l),void 0,null),g},useId:function(){var s=ye(),l=En.identifierPrefix;if(Xr){var g=ai,w=wi;g=(w&~(1<<32-Hi(w)-1)).toString(32)+g,l=":"+l+"R"+g,g=re++,0<\/script>",s=s.removeChild(s.firstChild)):typeof w.is=="string"?s=W.createElement(g,{is:w.is}):(s=W.createElement(g),g==="select"&&(W=s,w.multiple?W.multiple=!0:w.size&&(W.size=w.size))):s=W.createElementNS(s,g),s[Li]=l,s[fa]=w,rp(s,l,!1,!1),l.stateNode=s;e:{switch(W=be(g,w),g){case"dialog":qr("cancel",s),qr("close",s),k=w;break;case"iframe":case"object":case"embed":qr("load",s),k=w;break;case"video":case"audio":for(k=0;knc&&(l.flags|=128,w=!0,oh(D,!1),l.lanes=4194304)}else{if(!w)if(s=T(W),s!==null){if(l.flags|=128,w=!0,g=s.updateQueue,g!==null&&(l.updateQueue=g,l.flags|=4),oh(D,!0),D.tail===null&&D.tailMode==="hidden"&&!W.alternate&&!Xr)return Ci(l),null}else 2*rr()-D.renderingStartTime>nc&&g!==1073741824&&(l.flags|=128,w=!0,oh(D,!1),l.lanes=4194304);D.isBackwards?(W.sibling=l.child,l.child=W):(g=D.last,g!==null?g.sibling=W:l.child=W,D.last=W)}return D.tail!==null?(l=D.tail,D.rendering=l,D.tail=l.sibling,D.renderingStartTime=rr(),l.sibling=null,g=y.current,dr(y,w?g&1|2:g&1),l):(Ci(l),null);case 22:case 23:return gp(),w=l.memoizedState!==null,s!==null&&s.memoizedState!==null!==w&&(l.flags|=8192),w&&(l.mode&1)!==0?(bs&1073741824)!==0&&(Ci(l),l.subtreeFlags&6&&(l.flags|=8192)):Ci(l),null;case 24:return null;case 25:return null}throw Error(x(156,l.tag))}function Wf(s,l){switch(Xc(l),l.tag){case 1:return Xn(l.type)&&ya(),s=l.flags,s&65536?(l.flags=s&-65537|128,l):null;case 3:return o(),Rr(ni),Rr(Fn),A(),s=l.flags,(s&65536)!==0&&(s&128)===0?(l.flags=s&-65537|128,l):null;case 5:return p(l),null;case 13:if(Rr(y),s=l.memoizedState,s!==null&&s.dehydrated!==null){if(l.alternate===null)throw Error(x(340));Sr()}return s=l.flags,s&65536?(l.flags=s&-65537|128,l):null;case 19:return Rr(y),null;case 4:return o(),null;case 10:return Vn(l.type._context),null;case 22:case 23:return gp(),null;case 24:return null;default:return null}}var Yu=!1,di=!1,ef=typeof WeakSet=="function"?WeakSet:Set,Ct=null;function Qu(s,l){var g=s.ref;if(g!==null)if(typeof g=="function")try{g(null)}catch(w){vn(s,l,w)}else g.current=null}function ip(s,l,g){try{g()}catch(w){vn(s,l,w)}}var nd=!1;function sp(s,l){if(al=Kn,s=$c(),Jo(s)){if("selectionStart"in s)var g={start:s.selectionStart,end:s.selectionEnd};else e:{g=(g=s.ownerDocument)&&g.defaultView||window;var w=g.getSelection&&g.getSelection();if(w&&w.rangeCount!==0){g=w.anchorNode;var k=w.anchorOffset,D=w.focusNode;w=w.focusOffset;try{g.nodeType,D.nodeType}catch{g=null;break e}var W=0,he=-1,ve=-1,je=0,rt=0,Je=s,Ye=null;t:for(;;){for(var bt;Je!==g||k!==0&&Je.nodeType!==3||(he=W+k),Je!==D||w!==0&&Je.nodeType!==3||(ve=W+w),Je.nodeType===3&&(W+=Je.nodeValue.length),(bt=Je.firstChild)!==null;)Ye=Je,Je=bt;for(;;){if(Je===s)break t;if(Ye===g&&++je===k&&(he=W),Ye===D&&++rt===w&&(ve=W),(bt=Je.nextSibling)!==null)break;Je=Ye,Ye=Je.parentNode}Je=bt}g=he===-1||ve===-1?null:{start:he,end:ve}}else g=null}g=g||{start:0,end:0}}else g=null;for(pa={focusedElem:s,selectionRange:g},Kn=!1,Ct=l;Ct!==null;)if(l=Ct,s=l.child,(l.subtreeFlags&1028)!==0&&s!==null)s.return=l,Ct=s;else for(;Ct!==null;){l=Ct;try{var Mt=l.alternate;if((l.flags&1024)!==0)switch(l.tag){case 0:case 11:case 15:break;case 1:if(Mt!==null){var At=Mt.memoizedProps,an=Mt.memoizedState,Re=l.stateNode,Ce=Re.getSnapshotBeforeUpdate(l.elementType===l.type?At:Mn(l.type,At),an);Re.__reactInternalSnapshotBeforeUpdate=Ce}break;case 3:var De=l.stateNode.containerInfo;De.nodeType===1?De.textContent="":De.nodeType===9&&De.documentElement&&De.removeChild(De.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(x(163))}}catch(ot){vn(l,l.return,ot)}if(s=l.sibling,s!==null){s.return=l.return,Ct=s;break}Ct=l.return}return Mt=nd,nd=!1,Mt}function Ju(s,l,g){var w=l.updateQueue;if(w=w!==null?w.lastEffect:null,w!==null){var k=w=w.next;do{if((k.tag&s)===s){var D=k.destroy;k.destroy=void 0,D!==void 0&&ip(l,g,D)}k=k.next}while(k!==w)}}function eo(s,l){if(l=l.updateQueue,l=l!==null?l.lastEffect:null,l!==null){var g=l=l.next;do{if((g.tag&s)===s){var w=g.create;g.destroy=w()}g=g.next}while(g!==l)}}function ou(s){var l=s.ref;if(l!==null){var g=s.stateNode;switch(s.tag){case 5:s=g;break;default:s=g}typeof l=="function"?l(s):l.current=s}}function tf(s){var l=s.alternate;l!==null&&(s.alternate=null,tf(l)),s.child=null,s.deletions=null,s.sibling=null,s.tag===5&&(l=s.stateNode,l!==null&&(delete l[Li],delete l[fa],delete l[ma],delete l[Nu],delete l[ga])),s.stateNode=null,s.return=null,s.dependencies=null,s.memoizedProps=null,s.memoizedState=null,s.pendingProps=null,s.stateNode=null,s.updateQueue=null}function rf(s){return s.tag===5||s.tag===3||s.tag===4}function nf(s){e:for(;;){for(;s.sibling===null;){if(s.return===null||rf(s.return))return null;s=s.return}for(s.sibling.return=s.return,s=s.sibling;s.tag!==5&&s.tag!==6&&s.tag!==18;){if(s.flags&2||s.child===null||s.tag===4)continue e;s.child.return=s,s=s.child}if(!(s.flags&2))return s.stateNode}}function ap(s,l,g){var w=s.tag;if(w===5||w===6)s=s.stateNode,l?g.nodeType===8?g.parentNode.insertBefore(s,l):g.insertBefore(s,l):(g.nodeType===8?(l=g.parentNode,l.insertBefore(s,g)):(l=g,l.appendChild(s)),g=g._reactRootContainer,g!=null||l.onclick!==null||(l.onclick=sl));else if(w!==4&&(s=s.child,s!==null))for(ap(s,l,g),s=s.sibling;s!==null;)ap(s,l,g),s=s.sibling}function op(s,l,g){var w=s.tag;if(w===5||w===6)s=s.stateNode,l?g.insertBefore(s,l):g.appendChild(s);else if(w!==4&&(s=s.child,s!==null))for(op(s,l,g),s=s.sibling;s!==null;)op(s,l,g),s=s.sibling}var pi=null,Ca=!1;function to(s,l,g){for(g=g.child;g!==null;)sf(s,l,g),g=g.sibling}function sf(s,l,g){if(jn&&typeof jn.onCommitFiberUnmount=="function")try{jn.onCommitFiberUnmount(os,g)}catch{}switch(g.tag){case 5:di||Qu(g,l);case 6:var w=pi,k=Ca;pi=null,to(s,l,g),pi=w,Ca=k,pi!==null&&(Ca?(s=pi,g=g.stateNode,s.nodeType===8?s.parentNode.removeChild(g):s.removeChild(g)):pi.removeChild(g.stateNode));break;case 18:pi!==null&&(Ca?(s=pi,g=g.stateNode,s.nodeType===8?Ql(s.parentNode,g):s.nodeType===1&&Ql(s,g),zi(s)):Ql(pi,g.stateNode));break;case 4:w=pi,k=Ca,pi=g.stateNode.containerInfo,Ca=!0,to(s,l,g),pi=w,Ca=k;break;case 0:case 11:case 14:case 15:if(!di&&(w=g.updateQueue,w!==null&&(w=w.lastEffect,w!==null))){k=w=w.next;do{var D=k,W=D.destroy;D=D.tag,W!==void 0&&((D&2)!==0||(D&4)!==0)&&ip(g,l,W),k=k.next}while(k!==w)}to(s,l,g);break;case 1:if(!di&&(Qu(g,l),w=g.stateNode,typeof w.componentWillUnmount=="function"))try{w.props=g.memoizedProps,w.state=g.memoizedState,w.componentWillUnmount()}catch(he){vn(g,l,he)}to(s,l,g);break;case 21:to(s,l,g);break;case 22:g.mode&1?(di=(w=di)||g.memoizedState!==null,to(s,l,g),di=w):to(s,l,g);break;default:to(s,l,g)}}function lp(s){var l=s.updateQueue;if(l!==null){s.updateQueue=null;var g=s.stateNode;g===null&&(g=s.stateNode=new ef),l.forEach(function(w){var k=yh.bind(null,s,w);g.has(w)||(g.add(w),w.then(k,k))})}}function Vi(s,l){var g=l.deletions;if(g!==null)for(var w=0;wk&&(k=W),w&=~D}if(w=k,w=rr()-w,w=(120>w?120:480>w?480:1080>w?1080:1920>w?1920:3e3>w?3e3:4320>w?4320:1960*of(w/1960))-w,10s?16:s,_l===null)var w=!1;else{if(s=_l,_l=null,ph=0,(gr&6)!==0)throw Error(x(331));var k=gr;for(gr|=4,Ct=s.current;Ct!==null;){var D=Ct,W=D.child;if((Ct.flags&16)!==0){var he=D.deletions;if(he!==null){for(var ve=0;verr()-cp?hu(s,0):dh|=g),rs(s,l)}function mf(s,l){l===0&&((s.mode&1)===0?l=1:(l=Mu,Mu<<=1,(Mu&130023424)===0&&(Mu=4194304)));var g=Zn();s=Qi(s,l),s!==null&&(ao(s,l,g),rs(s,g))}function Xf(s){var l=s.memoizedState,g=0;l!==null&&(g=l.retryLane),mf(s,g)}function yh(s,l){var g=0;switch(s.tag){case 13:var w=s.stateNode,k=s.memoizedState;k!==null&&(g=k.retryLane);break;case 19:w=s.stateNode;break;default:throw Error(x(314))}w!==null&&w.delete(l),mf(s,g)}var kn;kn=function(s,l,g){if(s!==null)if(s.memoizedProps!==l.pendingProps||ni.current)es=!0;else{if((s.lanes&g)===0&&(l.flags&128)===0)return es=!1,Ui(s,l,g);es=(s.flags&131072)!==0}else es=!1,Xr&&(l.flags&1048576)!==0&&hr(l,dt,l.index);switch(l.lanes=0,l.tag){case 2:var w=l.type;Xu(s,l),s=l.pendingProps;var k=gs(l,Fn.current);$n(l,g),k=me(null,l,w,s,k,g);var D=oe();return l.flags|=1,typeof k=="object"&&k!==null&&typeof k.render=="function"&&k.$$typeof===void 0?(l.tag=1,l.memoizedState=null,l.updateQueue=null,Xn(w)?(D=!0,vi(l)):D=!1,l.memoizedState=k.state!==null&&k.state!==void 0?k.state:null,Ya(l),k.updater=vs,l.stateNode=k,k._reactInternals=l,su(l,w,s,g),l=Ku(null,l,w,!0,D,g)):(l.tag=0,Xr&&D&&ul(l),hi(null,l,k,g),l=l.child),l;case 16:w=l.elementType;e:{switch(Xu(s,l),s=l.pendingProps,k=w._init,w=k(w._payload),l.type=w,k=l.tag=ac(w),s=Mn(w,s),k){case 0:l=Qd(null,l,w,s,g);break e;case 1:l=Wu(null,l,w,s,g);break e;case 11:l=Wp(null,l,w,s,g);break e;case 14:l=ed(null,l,w,Mn(w.type,s),g);break e}throw Error(x(306,w,""))}return l;case 0:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Qd(s,l,w,k,g);case 1:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Wu(s,l,w,k,g);case 3:e:{if(nh(l),s===null)throw Error(x(387));w=l.pendingProps,D=l.memoizedState,k=D.element,qu(s,l),Gs(l,w,null,g);var W=l.memoizedState;if(w=W.element,D.isDehydrated)if(D={element:w,isDehydrated:!1,cache:W.cache,pendingSuspenseBoundaries:W.pendingSuspenseBoundaries,transitions:W.transitions},l.updateQueue.baseState=D,l.memoizedState=D,l.flags&256){k=Ta(Error(x(423)),l),l=Jd(s,l,w,g,k);break e}else if(w!==k){k=Ta(Error(x(424)),l),l=Jd(s,l,w,g,k);break e}else for(Fi=zr(l.stateNode.containerInfo.firstChild),Yn=l,Xr=!0,Oi=null,g=ru(l,null,w,g),l.child=g;g;)g.flags=g.flags&-3|4096,g=g.sibling;else{if(Sr(),w===k){l=xs(s,l,g);break e}hi(s,l,w,g)}l=l.child}return l;case 5:return h(l),s===null&&eu(l),w=l.type,k=l.pendingProps,D=s!==null?s.memoizedProps:null,W=k.children,Xl(w,k)?W=null:D!==null&&Xl(w,D)&&(l.flags|=32),au(s,l),hi(s,l,W,g),l.child;case 6:return s===null&&eu(l),null;case 13:return Kp(s,l,g);case 4:return i(l,l.stateNode.containerInfo),w=l.pendingProps,s===null?l.child=oi(l,null,w,g):hi(s,l,w,g),l.child;case 11:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Wp(s,l,w,k,g);case 7:return hi(s,l,l.pendingProps,g),l.child;case 8:return hi(s,l,l.pendingProps.children,g),l.child;case 12:return hi(s,l,l.pendingProps.children,g),l.child;case 10:e:{if(w=l.type._context,k=l.pendingProps,D=l.memoizedProps,W=k.value,dr(yt,w._currentValue),w._currentValue=W,D!==null)if(fs(D.value,W)){if(D.children===k.children&&!ni.current){l=xs(s,l,g);break e}}else for(D=l.child,D!==null&&(D.return=l);D!==null;){var he=D.dependencies;if(he!==null){W=D.child;for(var ve=he.firstContext;ve!==null;){if(ve.context===w){if(D.tag===1){ve=Si(-1,g&-g),ve.tag=2;var je=D.updateQueue;if(je!==null){je=je.shared;var rt=je.pending;rt===null?ve.next=ve:(ve.next=rt.next,rt.next=ve),je.pending=ve}}D.lanes|=g,ve=D.alternate,ve!==null&&(ve.lanes|=g),Gu(D.return,g,l),he.lanes|=g;break}ve=ve.next}}else if(D.tag===10)W=D.type===l.type?null:D.child;else if(D.tag===18){if(W=D.return,W===null)throw Error(x(341));W.lanes|=g,he=W.alternate,he!==null&&(he.lanes|=g),Gu(W,g,l),W=D.sibling}else W=D.child;if(W!==null)W.return=D;else for(W=D;W!==null;){if(W===l){W=null;break}if(D=W.sibling,D!==null){D.return=W.return,W=D;break}W=W.return}D=W}hi(s,l,k.children,g),l=l.child}return l;case 9:return k=l.type,w=l.pendingProps.children,$n(l,g),k=Yi(k),w=w(k),l.flags|=1,hi(s,l,w,g),l.child;case 14:return w=l.type,k=Mn(w,l.pendingProps),k=Mn(w.type,k),ed(s,l,w,k,g);case 15:return Xd(s,l,l.type,l.pendingProps,g);case 17:return w=l.type,k=l.pendingProps,k=l.elementType===w?k:Mn(w,k),Xu(s,l),l.tag=1,Xn(w)?(s=!0,vi(l)):s=!1,$n(l,g),Pi(l,w,k),su(l,w,k,g),Ku(null,l,w,!0,s,g);case 19:return tp(s,l,g);case 22:return Yd(s,l,g)}throw Error(x(156,l.tag))};function ud(s,l){return ia(s,l)}function gf(s,l,g,w){this.tag=s,this.key=g,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=l,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=w,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ss(s,l,g,w){return new gf(s,l,g,w)}function vh(s){return s=s.prototype,!(!s||!s.isReactComponent)}function ac(s){if(typeof s=="function")return vh(s)?1:0;if(s!=null){if(s=s.$$typeof,s===Br)return 11;if(s===xn)return 14}return 2}function Co(s,l){var g=s.alternate;return g===null?(g=Ss(s.tag,l,s.key,s.mode),g.elementType=s.elementType,g.type=s.type,g.stateNode=s.stateNode,g.alternate=s,s.alternate=g):(g.pendingProps=l,g.type=s.type,g.flags=0,g.subtreeFlags=0,g.deletions=null),g.flags=s.flags&14680064,g.childLanes=s.childLanes,g.lanes=s.lanes,g.child=s.child,g.memoizedProps=s.memoizedProps,g.memoizedState=s.memoizedState,g.updateQueue=s.updateQueue,l=s.dependencies,g.dependencies=l===null?null:{lanes:l.lanes,firstContext:l.firstContext},g.sibling=s.sibling,g.index=s.index,g.ref=s.ref,g}function cd(s,l,g,w,k,D){var W=2;if(w=s,typeof s=="function")vh(s)&&(W=1);else if(typeof s=="string")W=5;else e:switch(s){case Kt:return pu(g.children,k,D,l);case mt:W=8,k|=8;break;case Tt:return s=Ss(12,g,l,k|2),s.elementType=Tt,s.lanes=D,s;case mr:return s=Ss(13,g,l,k),s.elementType=mr,s.lanes=D,s;case Vr:return s=Ss(19,g,l,k),s.elementType=Vr,s.lanes=D,s;case pr:return hd(g,k,D,l);default:if(typeof s=="object"&&s!==null)switch(s.$$typeof){case xt:W=10;break e;case Cr:W=9;break e;case Br:W=11;break e;case xn:W=14;break e;case Mr:W=16,w=null;break e}throw Error(x(130,s==null?s:typeof s,""))}return l=Ss(W,g,l,k),l.elementType=s,l.type=w,l.lanes=D,l}function pu(s,l,g,w){return s=Ss(7,s,w,l),s.lanes=g,s}function hd(s,l,g,w){return s=Ss(22,s,w,l),s.elementType=pr,s.lanes=g,s.stateNode={isHidden:!1},s}function yp(s,l,g){return s=Ss(6,s,null,l),s.lanes=g,s}function vp(s,l,g){return l=Ss(4,s.children!==null?s.children:[],s.key,l),l.lanes=g,l.stateNode={containerInfo:s.containerInfo,pendingChildren:null,implementation:s.implementation},l}function Yf(s,l,g,w,k){this.tag=l,this.containerInfo=s,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Rl(0),this.expirationTimes=Rl(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Rl(0),this.identifierPrefix=w,this.onRecoverableError=k,this.mutableSourceEagerHydrationData=null}function xp(s,l,g,w,k,D,W,he,ve){return s=new Yf(s,l,g,he,ve),l===1?(l=1,D===!0&&(l|=8)):l=0,D=Ss(3,null,null,l),s.current=D,D.stateNode=s,D.memoizedState={element:w,isDehydrated:g,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ya(D),s}function Qf(s,l,g){var w=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(b)}catch(_){console.error(_)}}return b(),ym.exports=iv(),ym.exports}var Kg;function av(){if(Kg)return kf;Kg=1;var b=sv();return kf.createRoot=b.createRoot,kf.hydrateRoot=b.hydrateRoot,kf}var ov=av();const lv=Hm(ov);/** - * react-router v7.9.1 - * - * Copyright (c) Remix Software Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE.md file in the root directory of this source tree. - * - * @license MIT - */var Xg="popstate";function uv(b={}){function _(E,z){let{pathname:j,search:C,hash:u}=E.location;return Cm("",{pathname:j,search:C,hash:u},z.state&&z.state.usr||null,z.state&&z.state.key||"default")}function x(E,z){return typeof z=="string"?z:zp(z)}return hv(_,x,null,b)}function An(b,_){if(b===!1||b===null||typeof b>"u")throw new Error(_)}function Tl(b,_){if(!b){typeof console<"u"&&console.warn(_);try{throw new Error(_)}catch{}}}function cv(){return Math.random().toString(36).substring(2,10)}function Yg(b,_){return{usr:b.state,key:b.key,idx:_}}function Cm(b,_,x=null,E){return{pathname:typeof b=="string"?b:b.pathname,search:"",hash:"",...typeof _=="string"?Ld(_):_,state:x,key:_&&_.key||E||cv()}}function zp({pathname:b="/",search:_="",hash:x=""}){return _&&_!=="?"&&(b+=_.charAt(0)==="?"?_:"?"+_),x&&x!=="#"&&(b+=x.charAt(0)==="#"?x:"#"+x),b}function Ld(b){let _={};if(b){let x=b.indexOf("#");x>=0&&(_.hash=b.substring(x),b=b.substring(0,x));let E=b.indexOf("?");E>=0&&(_.search=b.substring(E),b=b.substring(0,E)),b&&(_.pathname=b)}return _}function hv(b,_,x,E={}){let{window:z=document.defaultView,v5Compat:j=!1}=E,C=z.history,u="POP",G=null,ee=ae();ee==null&&(ee=0,C.replaceState({...C.state,idx:ee},""));function ae(){return(C.state||{idx:null}).idx}function ce(){u="POP";let Fe=ae(),et=Fe==null?null:Fe-ee;ee=Fe,G&&G({action:u,location:Ue.location,delta:et})}function _e(Fe,et){u="PUSH";let nt=Cm(Ue.location,Fe,et);ee=ae()+1;let qe=Yg(nt,ee),We=Ue.createHref(nt);try{C.pushState(qe,"",We)}catch(Rt){if(Rt instanceof DOMException&&Rt.name==="DataCloneError")throw Rt;z.location.assign(We)}j&&G&&G({action:u,location:Ue.location,delta:1})}function te(Fe,et){u="REPLACE";let nt=Cm(Ue.location,Fe,et);ee=ae();let qe=Yg(nt,ee),We=Ue.createHref(nt);C.replaceState(qe,"",We),j&&G&&G({action:u,location:Ue.location,delta:0})}function Oe(Fe){return dv(Fe)}let Ue={get action(){return u},get location(){return b(z,C)},listen(Fe){if(G)throw new Error("A history only accepts one active listener");return z.addEventListener(Xg,ce),G=Fe,()=>{z.removeEventListener(Xg,ce),G=null}},createHref(Fe){return _(z,Fe)},createURL:Oe,encodeLocation(Fe){let et=Oe(Fe);return{pathname:et.pathname,search:et.search,hash:et.hash}},push:_e,replace:te,go(Fe){return C.go(Fe)}};return Ue}function dv(b,_=!1){let x="http://localhost";typeof window<"u"&&(x=window.location.origin!=="null"?window.location.origin:window.location.href),An(x,"No window.location.(origin|href) available to create URL");let E=typeof b=="string"?b:zp(b);return E=E.replace(/ $/,"%20"),!_&&E.startsWith("//")&&(E=x+E),new URL(E,x)}function $_(b,_,x="/"){return pv(b,_,x,!1)}function pv(b,_,x,E){let z=typeof _=="string"?Ld(_):_,j=Su(z.pathname||"/",x);if(j==null)return null;let C=Z_(b);fv(C);let u=null;for(let G=0;u==null&&G{let ae={relativePath:ee===void 0?C.path||"":ee,caseSensitive:C.caseSensitive===!0,childrenIndex:u,route:C};if(ae.relativePath.startsWith("/")){if(!ae.relativePath.startsWith(E)&&G)return;An(ae.relativePath.startsWith(E),`Absolute route path "${ae.relativePath}" nested under path "${E}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),ae.relativePath=ae.relativePath.slice(E.length)}let ce=bu([E,ae.relativePath]),_e=x.concat(ae);C.children&&C.children.length>0&&(An(C.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${ce}".`),Z_(C.children,_,_e,ce,G)),!(C.path==null&&!C.index)&&_.push({path:ce,score:bv(ce,C.index),routesMeta:_e})};return b.forEach((C,u)=>{var G;if(C.path===""||!((G=C.path)!=null&&G.includes("?")))j(C,u);else for(let ee of G_(C.path))j(C,u,!0,ee)}),_}function G_(b){let _=b.split("/");if(_.length===0)return[];let[x,...E]=_,z=x.endsWith("?"),j=x.replace(/\?$/,"");if(E.length===0)return z?[j,""]:[j];let C=G_(E.join("/")),u=[];return u.push(...C.map(G=>G===""?j:[j,G].join("/"))),z&&u.push(...C),u.map(G=>b.startsWith("/")&&G===""?"/":G)}function fv(b){b.sort((_,x)=>_.score!==x.score?x.score-_.score:wv(_.routesMeta.map(E=>E.childrenIndex),x.routesMeta.map(E=>E.childrenIndex)))}var mv=/^:[\w-]+$/,gv=3,_v=2,yv=1,vv=10,xv=-2,Qg=b=>b==="*";function bv(b,_){let x=b.split("/"),E=x.length;return x.some(Qg)&&(E+=xv),_&&(E+=_v),x.filter(z=>!Qg(z)).reduce((z,j)=>z+(mv.test(j)?gv:j===""?yv:vv),E)}function wv(b,_){return b.length===_.length&&b.slice(0,-1).every((E,z)=>E===_[z])?b[b.length-1]-_[_.length-1]:0}function Sv(b,_,x=!1){let{routesMeta:E}=b,z={},j="/",C=[];for(let u=0;u{if(ae==="*"){let Oe=u[_e]||"";C=j.slice(0,j.length-Oe.length).replace(/(.)\/+$/,"$1")}const te=u[_e];return ce&&!te?ee[ae]=void 0:ee[ae]=(te||"").replace(/%2F/g,"/"),ee},{}),pathname:j,pathnameBase:C,pattern:b}}function Tv(b,_=!1,x=!0){Tl(b==="*"||!b.endsWith("*")||b.endsWith("/*"),`Route path "${b}" will be treated as if it were "${b.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${b.replace(/\*$/,"/*")}".`);let E=[],z="^"+b.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(C,u,G)=>(E.push({paramName:u,isOptional:G!=null}),G?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return b.endsWith("*")?(E.push({paramName:"*"}),z+=b==="*"||b==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):x?z+="\\/*$":b!==""&&b!=="/"&&(z+="(?:(?=\\/|$))"),[new RegExp(z,_?void 0:"i"),E]}function Pv(b){try{return b.split("/").map(_=>decodeURIComponent(_).replace(/\//g,"%2F")).join("/")}catch(_){return Tl(!1,`The URL path "${b}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${_}).`),b}}function Su(b,_){if(_==="/")return b;if(!b.toLowerCase().startsWith(_.toLowerCase()))return null;let x=_.endsWith("/")?_.length-1:_.length,E=b.charAt(x);return E&&E!=="/"?null:b.slice(x)||"/"}function Cv(b,_="/"){let{pathname:x,search:E="",hash:z=""}=typeof b=="string"?Ld(b):b;return{pathname:x?x.startsWith("/")?x:Mv(x,_):_,search:kv(E),hash:Av(z)}}function Mv(b,_){let x=_.replace(/\/+$/,"").split("/");return b.split("/").forEach(z=>{z===".."?x.length>1&&x.pop():z!=="."&&x.push(z)}),x.length>1?x.join("/"):"/"}function bm(b,_,x,E){return`Cannot include a '${b}' character in a manually specified \`to.${_}\` field [${JSON.stringify(E)}]. Please separate it out to the \`to.${x}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Ev(b){return b.filter((_,x)=>x===0||_.route.path&&_.route.path.length>0)}function q_(b){let _=Ev(b);return _.map((x,E)=>E===_.length-1?x.pathname:x.pathnameBase)}function H_(b,_,x,E=!1){let z;typeof b=="string"?z=Ld(b):(z={...b},An(!z.pathname||!z.pathname.includes("?"),bm("?","pathname","search",z)),An(!z.pathname||!z.pathname.includes("#"),bm("#","pathname","hash",z)),An(!z.search||!z.search.includes("#"),bm("#","search","hash",z)));let j=b===""||z.pathname==="",C=j?"/":z.pathname,u;if(C==null)u=x;else{let ce=_.length-1;if(!E&&C.startsWith("..")){let _e=C.split("/");for(;_e[0]==="..";)_e.shift(),ce-=1;z.pathname=_e.join("/")}u=ce>=0?_[ce]:"/"}let G=Cv(z,u),ee=C&&C!=="/"&&C.endsWith("/"),ae=(j||C===".")&&x.endsWith("/");return!G.pathname.endsWith("/")&&(ee||ae)&&(G.pathname+="/"),G}var bu=b=>b.join("/").replace(/\/\/+/g,"/"),Iv=b=>b.replace(/\/+$/,"").replace(/^\/*/,"/"),kv=b=>!b||b==="?"?"":b.startsWith("?")?b:"?"+b,Av=b=>!b||b==="#"?"":b.startsWith("#")?b:"#"+b;function zv(b){return b!=null&&typeof b.status=="number"&&typeof b.statusText=="string"&&typeof b.internal=="boolean"&&"data"in b}var W_=["POST","PUT","PATCH","DELETE"];new Set(W_);var Rv=["GET",...W_];new Set(Rv);var Dd=Ee.createContext(null);Dd.displayName="DataRouter";var Gf=Ee.createContext(null);Gf.displayName="DataRouterState";Ee.createContext(!1);var K_=Ee.createContext({isTransitioning:!1});K_.displayName="ViewTransition";var Lv=Ee.createContext(new Map);Lv.displayName="Fetchers";var Dv=Ee.createContext(null);Dv.displayName="Await";var Pl=Ee.createContext(null);Pl.displayName="Navigation";var Up=Ee.createContext(null);Up.displayName="Location";var Cl=Ee.createContext({outlet:null,matches:[],isDataRoute:!1});Cl.displayName="Route";var Km=Ee.createContext(null);Km.displayName="RouteError";function Fv(b,{relative:_}={}){An(Vp(),"useHref() may be used only in the context of a component.");let{basename:x,navigator:E}=Ee.useContext(Pl),{hash:z,pathname:j,search:C}=$p(b,{relative:_}),u=j;return x!=="/"&&(u=j==="/"?x:bu([x,j])),E.createHref({pathname:u,search:C,hash:z})}function Vp(){return Ee.useContext(Up)!=null}function Uh(){return An(Vp(),"useLocation() may be used only in the context of a component."),Ee.useContext(Up).location}var X_="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function Y_(b){Ee.useContext(Pl).static||Ee.useLayoutEffect(b)}function Ov(){let{isDataRoute:b}=Ee.useContext(Cl);return b?Yv():Bv()}function Bv(){An(Vp(),"useNavigate() may be used only in the context of a component.");let b=Ee.useContext(Dd),{basename:_,navigator:x}=Ee.useContext(Pl),{matches:E}=Ee.useContext(Cl),{pathname:z}=Uh(),j=JSON.stringify(q_(E)),C=Ee.useRef(!1);return Y_(()=>{C.current=!0}),Ee.useCallback((G,ee={})=>{if(Tl(C.current,X_),!C.current)return;if(typeof G=="number"){x.go(G);return}let ae=H_(G,JSON.parse(j),z,ee.relative==="path");b==null&&_!=="/"&&(ae.pathname=ae.pathname==="/"?_:bu([_,ae.pathname])),(ee.replace?x.replace:x.push)(ae,ee.state,ee)},[_,x,j,z,b])}Ee.createContext(null);function jv(){let{matches:b}=Ee.useContext(Cl),_=b[b.length-1];return _?_.params:{}}function $p(b,{relative:_}={}){let{matches:x}=Ee.useContext(Cl),{pathname:E}=Uh(),z=JSON.stringify(q_(x));return Ee.useMemo(()=>H_(b,JSON.parse(z),E,_==="path"),[b,z,E,_])}function Nv(b,_){return Q_(b,_)}function Q_(b,_,x,E,z){var nt;An(Vp(),"useRoutes() may be used only in the context of a component.");let{navigator:j}=Ee.useContext(Pl),{matches:C}=Ee.useContext(Cl),u=C[C.length-1],G=u?u.params:{},ee=u?u.pathname:"/",ae=u?u.pathnameBase:"/",ce=u&&u.route;{let qe=ce&&ce.path||"";J_(ee,!ce||qe.endsWith("*")||qe.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${ee}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. - -Please change the parent to .`)}let _e=Uh(),te;if(_){let qe=typeof _=="string"?Ld(_):_;An(ae==="/"||((nt=qe.pathname)==null?void 0:nt.startsWith(ae)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${ae}" but pathname "${qe.pathname}" was given in the \`location\` prop.`),te=qe}else te=_e;let Oe=te.pathname||"/",Ue=Oe;if(ae!=="/"){let qe=ae.replace(/^\//,"").split("/");Ue="/"+Oe.replace(/^\//,"").split("/").slice(qe.length).join("/")}let Fe=$_(b,{pathname:Ue});Tl(ce||Fe!=null,`No routes matched location "${te.pathname}${te.search}${te.hash}" `),Tl(Fe==null||Fe[Fe.length-1].route.element!==void 0||Fe[Fe.length-1].route.Component!==void 0||Fe[Fe.length-1].route.lazy!==void 0,`Matched leaf route at location "${te.pathname}${te.search}${te.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let et=Gv(Fe&&Fe.map(qe=>Object.assign({},qe,{params:Object.assign({},G,qe.params),pathname:bu([ae,j.encodeLocation?j.encodeLocation(qe.pathname).pathname:qe.pathname]),pathnameBase:qe.pathnameBase==="/"?ae:bu([ae,j.encodeLocation?j.encodeLocation(qe.pathnameBase).pathname:qe.pathnameBase])})),C,x,E,z);return _&&et?Ee.createElement(Up.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...te},navigationType:"POP"}},et):et}function Uv(){let b=Xv(),_=zv(b)?`${b.status} ${b.statusText}`:b instanceof Error?b.message:JSON.stringify(b),x=b instanceof Error?b.stack:null,E="rgba(200,200,200, 0.5)",z={padding:"0.5rem",backgroundColor:E},j={padding:"2px 4px",backgroundColor:E},C=null;return console.error("Error handled by React Router default ErrorBoundary:",b),C=Ee.createElement(Ee.Fragment,null,Ee.createElement("p",null,"💿 Hey developer 👋"),Ee.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",Ee.createElement("code",{style:j},"ErrorBoundary")," or"," ",Ee.createElement("code",{style:j},"errorElement")," prop on your route.")),Ee.createElement(Ee.Fragment,null,Ee.createElement("h2",null,"Unexpected Application Error!"),Ee.createElement("h3",{style:{fontStyle:"italic"}},_),x?Ee.createElement("pre",{style:z},x):null,C)}var Vv=Ee.createElement(Uv,null),$v=class extends Ee.Component{constructor(b){super(b),this.state={location:b.location,revalidation:b.revalidation,error:b.error}}static getDerivedStateFromError(b){return{error:b}}static getDerivedStateFromProps(b,_){return _.location!==b.location||_.revalidation!=="idle"&&b.revalidation==="idle"?{error:b.error,location:b.location,revalidation:b.revalidation}:{error:b.error!==void 0?b.error:_.error,location:_.location,revalidation:b.revalidation||_.revalidation}}componentDidCatch(b,_){this.props.unstable_onError?this.props.unstable_onError(b,_):console.error("React Router caught the following error during render",b)}render(){return this.state.error!==void 0?Ee.createElement(Cl.Provider,{value:this.props.routeContext},Ee.createElement(Km.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function Zv({routeContext:b,match:_,children:x}){let E=Ee.useContext(Dd);return E&&E.static&&E.staticContext&&(_.route.errorElement||_.route.ErrorBoundary)&&(E.staticContext._deepestRenderedBoundaryId=_.route.id),Ee.createElement(Cl.Provider,{value:b},x)}function Gv(b,_=[],x=null,E=null,z=null){if(b==null){if(!x)return null;if(x.errors)b=x.matches;else if(_.length===0&&!x.initialized&&x.matches.length>0)b=x.matches;else return null}let j=b,C=x==null?void 0:x.errors;if(C!=null){let ee=j.findIndex(ae=>ae.route.id&&(C==null?void 0:C[ae.route.id])!==void 0);An(ee>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(C).join(",")}`),j=j.slice(0,Math.min(j.length,ee+1))}let u=!1,G=-1;if(x)for(let ee=0;ee=0?j=j.slice(0,G+1):j=[j[0]];break}}}return j.reduceRight((ee,ae,ce)=>{let _e,te=!1,Oe=null,Ue=null;x&&(_e=C&&ae.route.id?C[ae.route.id]:void 0,Oe=ae.route.errorElement||Vv,u&&(G<0&&ce===0?(J_("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),te=!0,Ue=null):G===ce&&(te=!0,Ue=ae.route.hydrateFallbackElement||null)));let Fe=_.concat(j.slice(0,ce+1)),et=()=>{let nt;return _e?nt=Oe:te?nt=Ue:ae.route.Component?nt=Ee.createElement(ae.route.Component,null):ae.route.element?nt=ae.route.element:nt=ee,Ee.createElement(Zv,{match:ae,routeContext:{outlet:ee,matches:Fe,isDataRoute:x!=null},children:nt})};return x&&(ae.route.ErrorBoundary||ae.route.errorElement||ce===0)?Ee.createElement($v,{location:x.location,revalidation:x.revalidation,component:Oe,error:_e,children:et(),routeContext:{outlet:null,matches:Fe,isDataRoute:!0},unstable_onError:E}):et()},null)}function Xm(b){return`${b} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function qv(b){let _=Ee.useContext(Dd);return An(_,Xm(b)),_}function Hv(b){let _=Ee.useContext(Gf);return An(_,Xm(b)),_}function Wv(b){let _=Ee.useContext(Cl);return An(_,Xm(b)),_}function Ym(b){let _=Wv(b),x=_.matches[_.matches.length-1];return An(x.route.id,`${b} can only be used on routes that contain a unique "id"`),x.route.id}function Kv(){return Ym("useRouteId")}function Xv(){var E;let b=Ee.useContext(Km),_=Hv("useRouteError"),x=Ym("useRouteError");return b!==void 0?b:(E=_.errors)==null?void 0:E[x]}function Yv(){let{router:b}=qv("useNavigate"),_=Ym("useNavigate"),x=Ee.useRef(!1);return Y_(()=>{x.current=!0}),Ee.useCallback(async(z,j={})=>{Tl(x.current,X_),x.current&&(typeof z=="number"?b.navigate(z):await b.navigate(z,{fromRouteId:_,...j}))},[b,_])}var Jg={};function J_(b,_,x){!_&&!Jg[b]&&(Jg[b]=!0,Tl(!1,x))}Ee.memo(Qv);function Qv({routes:b,future:_,state:x,unstable_onError:E}){return Q_(b,void 0,x,E,_)}function Mm(b){An(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function Jv({basename:b="/",children:_=null,location:x,navigationType:E="POP",navigator:z,static:j=!1}){An(!Vp(),"You cannot render a inside another . You should never have more than one in your app.");let C=b.replace(/^\/*/,"/"),u=Ee.useMemo(()=>({basename:C,navigator:z,static:j,future:{}}),[C,z,j]);typeof x=="string"&&(x=Ld(x));let{pathname:G="/",search:ee="",hash:ae="",state:ce=null,key:_e="default"}=x,te=Ee.useMemo(()=>{let Oe=Su(G,C);return Oe==null?null:{location:{pathname:Oe,search:ee,hash:ae,state:ce,key:_e},navigationType:E}},[C,G,ee,ae,ce,_e,E]);return Tl(te!=null,` is not able to match the URL "${G}${ee}${ae}" because it does not start with the basename, so the won't render anything.`),te==null?null:Ee.createElement(Pl.Provider,{value:u},Ee.createElement(Up.Provider,{children:_,value:te}))}function ex({children:b,location:_}){return Nv(Em(b),_)}function Em(b,_=[]){let x=[];return Ee.Children.forEach(b,(E,z)=>{if(!Ee.isValidElement(E))return;let j=[..._,z];if(E.type===Ee.Fragment){x.push.apply(x,Em(E.props.children,j));return}An(E.type===Mm,`[${typeof E.type=="string"?E.type:E.type.name}] is not a component. All component children of must be a or `),An(!E.props.index||!E.props.children,"An index route cannot have child routes.");let C={id:E.props.id||j.join("-"),caseSensitive:E.props.caseSensitive,element:E.props.element,Component:E.props.Component,index:E.props.index,path:E.props.path,loader:E.props.loader,action:E.props.action,hydrateFallbackElement:E.props.hydrateFallbackElement,HydrateFallback:E.props.HydrateFallback,errorElement:E.props.errorElement,ErrorBoundary:E.props.ErrorBoundary,hasErrorBoundary:E.props.hasErrorBoundary===!0||E.props.ErrorBoundary!=null||E.props.errorElement!=null,shouldRevalidate:E.props.shouldRevalidate,handle:E.props.handle,lazy:E.props.lazy};E.props.children&&(C.children=Em(E.props.children,j)),x.push(C)}),x}var Lf="get",Df="application/x-www-form-urlencoded";function qf(b){return b!=null&&typeof b.tagName=="string"}function tx(b){return qf(b)&&b.tagName.toLowerCase()==="button"}function rx(b){return qf(b)&&b.tagName.toLowerCase()==="form"}function nx(b){return qf(b)&&b.tagName.toLowerCase()==="input"}function ix(b){return!!(b.metaKey||b.altKey||b.ctrlKey||b.shiftKey)}function sx(b,_){return b.button===0&&(!_||_==="_self")&&!ix(b)}var Af=null;function ax(){if(Af===null)try{new FormData(document.createElement("form"),0),Af=!1}catch{Af=!0}return Af}var ox=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function wm(b){return b!=null&&!ox.has(b)?(Tl(!1,`"${b}" is not a valid \`encType\` for \`
              \`/\`\` and will default to "${Df}"`),null):b}function lx(b,_){let x,E,z,j,C;if(rx(b)){let u=b.getAttribute("action");E=u?Su(u,_):null,x=b.getAttribute("method")||Lf,z=wm(b.getAttribute("enctype"))||Df,j=new FormData(b)}else if(tx(b)||nx(b)&&(b.type==="submit"||b.type==="image")){let u=b.form;if(u==null)throw new Error('Cannot submit a
          ); @@ -161,9 +163,9 @@ export default function BeachesList() { if (!items.length) { return (
          -

          No beaches found.

          +

          {t("beachesList.noBeachesFound")}

          - Try refreshing or adjusting the filters. + {t("beachesList.emptyState")}

          ); @@ -172,11 +174,11 @@ export default function BeachesList() { if ((q || mode !== "default") && filtered.length === 0) { return (
          -

          No matches here.

          +

          {t("beachesList.noMatches")}

          {mode === "viewport" - ? "Pan or zoom to a different area." - : `Try widening the radius.`} + ? t("beachesList.panOrZoom") + : t("beachesList.widenRadius")}

          ); @@ -204,7 +206,7 @@ export default function BeachesList() { onClick={handleUseLocation} disabled={geoLoading} > - {geoLoading ? "Getting location…" : "Use current location"} + {geoLoading ? t("beachesList.gettingLocation") : t("beachesList.requestLocation")}
          diff --git a/frontend/src/components/ErrorBoundary.tsx b/frontend/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000000..41adacdd29 --- /dev/null +++ b/frontend/src/components/ErrorBoundary.tsx @@ -0,0 +1,74 @@ +import React, { Component, ErrorInfo, ReactNode } from "react"; +import { Link } from "react-router"; +import i18n from "../i18n"; + +interface Props { + children: ReactNode; +} + +interface State { + hasError: boolean; + error: Error | null; +} + +export default class ErrorBoundary extends Component { + public state: State = { + hasError: false, + error: null, + }; + + public static getDerivedStateFromError(error: Error): State { + return { hasError: true, error }; + } + + public componentDidCatch(error: Error, errorInfo: ErrorInfo) { + console.error("ErrorBoundary caught an error:", error, errorInfo); + } + + public render() { + if (this.state.hasError) { + const t = (key: string) => i18n.t(key); + return ( +
          +
          +
          +

          + {t("errorBoundary.title")} +

          +

          + {t("errorBoundary.message")} +

          + {this.state.error && ( +
          + + {t("errorBoundary.technicalDetails")} + +
          +                    {this.state.error.toString()}
          +                  
          +
          + )} +
          + + + {t("errorBoundary.goHome")} + +
          +
          +
          +
          + ); + } + + return this.props.children; + } +} + diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 26fb0098c9..3e2fd93dc7 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,5 +1,6 @@ import { useEffect, useRef, useState } from "react"; import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; import { useUI } from "../store/ui"; import { useAuth } from "@/store/auth"; import { useQueryClient } from "@tanstack/react-query"; @@ -35,6 +36,7 @@ type HeaderProps = { }; export default function Header({ languageSwitcher }: HeaderProps) { + const { t } = useTranslation(); const [menuOpen, setMenuOpen] = useState(false); const [userOpen, setUserOpen] = useState(false); const menuRef = useOutsideClose(() => setMenuOpen(false)); @@ -55,12 +57,20 @@ export default function Header({ languageSwitcher }: HeaderProps) { } return ( -
          -
          - {/* Left: Hamburger */} -
          + <> + {/* Skip navigation link for accessibility */} + + {t("header.skipToMain")} + +
          +
          + {/* Left: Hamburger */} +
          @@ -99,9 +109,9 @@ export default function Header({ languageSwitcher }: HeaderProps) { BADA
          - EU Beaches + {t("header.euBeaches")}
          - in Sweden + {t("header.inSweden")}
          @@ -109,7 +119,7 @@ export default function Header({ languageSwitcher }: HeaderProps) { {/* Right: User menu */}
          )} @@ -148,7 +158,7 @@ export default function Header({ languageSwitcher }: HeaderProps) { className="w-full text-left px-2 py-1.5 rounded-lg hover:bg-surface-muted text-sm" onClick={() => setIsDark(!isDark)} > - {isDark ? "Dark theme: on" : "Dark theme: off"} + {isDark ? t("theme.darkOn") : t("theme.darkOff")}
          )} @@ -160,7 +170,8 @@ export default function Header({ languageSwitcher }: HeaderProps) {
          setSearch(e.target.value)} @@ -169,15 +180,16 @@ export default function Header({ languageSwitcher }: HeaderProps) { )}
          + ); } diff --git a/frontend/src/components/LanguageSwitcher.tsx b/frontend/src/components/LanguageSwitcher.tsx index 285d8d916c..dd4284ce98 100644 --- a/frontend/src/components/LanguageSwitcher.tsx +++ b/frontend/src/components/LanguageSwitcher.tsx @@ -10,18 +10,26 @@ export default function LanguageSwitcher() { } return ( -
          +
          diff --git a/frontend/src/components/SortableFavorite.tsx b/frontend/src/components/SortableFavorite.tsx index 494533854e..e3dfb5066f 100644 --- a/frontend/src/components/SortableFavorite.tsx +++ b/frontend/src/components/SortableFavorite.tsx @@ -1,4 +1,5 @@ import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; @@ -21,6 +22,7 @@ export default function SortableFavorite({ onRemove, disabled, }: Props) { + const { t } = useTranslation(); const { attributes, listeners, @@ -47,7 +49,7 @@ export default function SortableFavorite({ {/* Drag handle */} {!disabled && (
        • diff --git a/frontend/src/index.css b/frontend/src/index.css index 76954940c7..25282de35c 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -93,4 +93,40 @@ .aspect-square { aspect-ratio: 1 / 1; } + + /* Screen reader only */ + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; + } + + /* Show on focus */ + .sr-only:focus { + position: static; + width: auto; + height: auto; + padding: inherit; + margin: inherit; + overflow: visible; + clip: auto; + white-space: normal; + } +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } } diff --git a/frontend/src/locales/en/common.json b/frontend/src/locales/en/common.json index b309d87b64..dd970ff70f 100644 --- a/frontend/src/locales/en/common.json +++ b/frontend/src/locales/en/common.json @@ -15,5 +15,139 @@ "poor": "Poor", "unknown": "Unknown" }, - "sourceTextNote": "Source text in Swedish" + "sourceTextNote": "Source text in Swedish", + "header": { + "menu": "Menu", + "language": "Language", + "searchPlaceholder": "Search beaches…", + "clear": "Clear", + "openMenu": "Open menu", + "userMenu": "User menu", + "skipToMain": "Skip to main content", + "euBeaches": "EU Beaches", + "inSweden": "in Sweden" + }, + "nav": { + "whatIsEUBeach": "What is an EU Beach?", + "about": "About BADA", + "terms": "Terms of Use", + "contact": "Contact", + "favoriteBeaches": "Favourite beaches", + "profile": "Profile", + "settings": "Settings", + "logOut": "Log out", + "signIn": "Sign in", + "register": "Register", + "account": "ACCOUNT", + "user": "USER" + }, + "auth": { + "signInTitle": "Sign in", + "registerTitle": "Register", + "email": "Email", + "password": "Password", + "confirmPassword": "Confirm Password", + "signIn": "Sign in", + "signInProgress": "Signing in…", + "register": "Register", + "registerProgress": "Registering…", + "noAccount": "No account?", + "registerHere": "Register here", + "alreadyHaveAccount": "Already have an account?", + "signInHere": "Sign in here", + "signInSuccess": "Successfully signed in!", + "registerSuccess": "Registration successful! Please sign in.", + "signInFailed": "Login failed", + "registerFailed": "Registration failed", + "emailRequired": "Email is required", + "passwordRequired": "Password is required", + "passwordTooShort": "Password must be at least 8 characters", + "confirmPasswordRequired": "Confirm password is required", + "passwordsMustMatch": "Passwords must match", + "invalidEmail": "Invalid email address" + }, + "favorites": { + "title": "Your favorite beaches", + "sortBy": "Sort by:", + "sortOptions": { + "custom": "Custom (drag & drop)", + "name": "Name (A–Z)", + "municipality": "Municipality (A–Z)" + }, + "browseAll": "Browse all beaches", + "empty": "You have no favorite beaches yet", + "addFavorites": "Add favorites from the beach map", + "view": "View", + "remove": "Remove", + "dragToReorder": "Drag to reorder", + "added": "Added to favorites", + "removed": "Removed from favorites" + }, + "beachDetail": { + "addFavorite": "Add favorite", + "removeFavorite": "Remove favorite", + "added": "Added to favorites", + "removed": "Removed from favorites", + "bathInformation": "About this beach", + "latestSampleDate": "Latest sample", + "beach": "Beach", + "algalBloom": "Algal bloom", + "euMotive": "EU motive", + "nutsCode": "NUTS code", + "mail": "Mail:", + "phone": "Phone:", + "website": "Website:", + "pleaseTryAgain": "Please try again.", + "euBad": "EU Beach" + }, + "beachesList": { + "modeDefault": "Show all", + "modeNearby": "Near me", + "modeViewport": "Show viewport", + "gettingLocation": "Getting location…", + "locationError": "Could not get your location", + "requestLocation": "Use current location", + "noBeachesFound": "No beaches found", + "emptyState": "Try adjusting your search filters", + "loadError": "Could not load beaches.", + "pleaseRetry": "Please try again.", + "retry": "Retry", + "noMatches": "No matches here.", + "panOrZoom": "Pan or zoom to a different area.", + "widenRadius": "Try widening the radius." + }, + "theme": { + "darkOn": "Dark theme: on", + "darkOff": "Dark theme: off" + }, + "pages": { + "about": { + "title": "About BADA" + }, + "whatIsEUBeach": { + "title": "What is an EU Beach?" + }, + "terms": { + "title": "Terms of Use" + }, + "contact": { + "title": "Contact" + }, + "profile": { + "title": "Your Profile" + }, + "settings": { + "title": "Settings", + "language": "Language", + "languageDescription": "Select your language" + }, + "backToMap": "← Back to map" + }, + "errorBoundary": { + "title": "Oops! Something went wrong", + "message": "We encountered an unexpected error. Please try refreshing the page or return to the homepage.", + "technicalDetails": "Technical details", + "reloadPage": "Reload Page", + "goHome": "Go Home" + } } diff --git a/frontend/src/locales/sv/common.json b/frontend/src/locales/sv/common.json index 05aabe9130..cb0c3017ba 100644 --- a/frontend/src/locales/sv/common.json +++ b/frontend/src/locales/sv/common.json @@ -15,5 +15,139 @@ "poor": "Dålig kvalitet", "unknown": "Okänd" }, - "sourceTextNote": "Källtext på svenska" + "sourceTextNote": "Källtext på svenska", + "header": { + "menu": "Meny", + "language": "Språk", + "searchPlaceholder": "Sök badplatser…", + "clear": "Rensa", + "openMenu": "Öppna meny", + "userMenu": "Användarmenyn", + "skipToMain": "Hoppa till huvudinnehållet", + "euBeaches": "EU-bad", + "inSweden": "i Sverige" + }, + "nav": { + "whatIsEUBeach": "Vad är en EU-badplats?", + "about": "Om BADA", + "terms": "Användarvillkor", + "contact": "Kontakt", + "favoriteBeaches": "Favoritbadplatser", + "profile": "Profil", + "settings": "Inställningar", + "logOut": "Logga ut", + "signIn": "Logga in", + "register": "Registrera", + "account": "KONTO", + "user": "ANVÄNDARE" + }, + "auth": { + "signInTitle": "Logga in", + "registerTitle": "Registrera", + "email": "E-post", + "password": "Lösenord", + "confirmPassword": "Bekräfta lösenord", + "signIn": "Logga in", + "signInProgress": "Loggar in…", + "register": "Registrera", + "registerProgress": "Registrerar…", + "noAccount": "Inget konto?", + "registerHere": "Registrera här", + "alreadyHaveAccount": "Har du redan ett konto?", + "signInHere": "Logga in här", + "signInSuccess": "Inloggad!", + "registerSuccess": "Registrering lyckades! Logga in.", + "signInFailed": "Inloggning misslyckades", + "registerFailed": "Registrering misslyckades", + "emailRequired": "E-post krävs", + "passwordRequired": "Lösenord krävs", + "passwordTooShort": "Lösenordet måste vara minst 8 tecken", + "confirmPasswordRequired": "Bekräftelse av lösenord krävs", + "passwordsMustMatch": "Lösenorden måste matcha", + "invalidEmail": "Ogiltig e-postadress" + }, + "favorites": { + "title": "Dina favoritbadplatser", + "sortBy": "Sortera efter:", + "sortOptions": { + "custom": "Egen ordning (dra & släpp)", + "name": "Namn (A–Ö)", + "municipality": "Kommun (A–Ö)" + }, + "browseAll": "Bläddra bland alla badplatser", + "empty": "Du har inga favoritbadplatser ännu", + "addFavorites": "Lägg till favoriter från badplatskartan", + "view": "Visa", + "remove": "Ta bort", + "dragToReorder": "Dra för att ändra ordning", + "added": "Tillagd som favorit", + "removed": "Borttagen från favoriter" + }, + "beachDetail": { + "addFavorite": "Lägg till favorit", + "removeFavorite": "Ta bort favorit", + "added": "Tillagd som favorit", + "removed": "Borttagen från favoriter", + "bathInformation": "Om badplatsen", + "latestSampleDate": "Senaste provtagning", + "beach": "Badplats", + "algalBloom": "Algblomning", + "euMotive": "EU-motivering", + "nutsCode": "NUTS-kod", + "mail": "E-post:", + "phone": "Telefon:", + "website": "Webbplats:", + "pleaseTryAgain": "Vänligen försök igen.", + "euBad": "EU-bad" + }, + "beachesList": { + "modeDefault": "Visa alla", + "modeNearby": "Nära mig", + "modeViewport": "Visa i fönster", + "gettingLocation": "Hämtar position…", + "locationError": "Kunde inte hitta din position", + "requestLocation": "Använd min position", + "noBeachesFound": "Inga badplatser hittades", + "emptyState": "Prova att ändra sökfilter", + "loadError": "Kunde inte ladda badplatser.", + "pleaseRetry": "Vänligen försök igen.", + "retry": "Försök igen", + "noMatches": "Inga träffar här.", + "panOrZoom": "Panorera eller zooma till ett annat område.", + "widenRadius": "Prova att öka sökradien." + }, + "theme": { + "darkOn": "Mörkt tema: på", + "darkOff": "Mörkt tema: av" + }, + "pages": { + "about": { + "title": "Om BADA" + }, + "whatIsEUBeach": { + "title": "Vad är en EU-badplats?" + }, + "terms": { + "title": "Användarvillkor" + }, + "contact": { + "title": "Kontakt" + }, + "profile": { + "title": "Din profil" + }, + "settings": { + "title": "Inställningar", + "language": "Språk", + "languageDescription": "Välj ditt språk" + }, + "backToMap": "← Tillbaka till kartan" + }, + "errorBoundary": { + "title": "Hoppsan! Något gick fel", + "message": "Vi stötte på ett oväntat fel. Vänligen försök uppdatera sidan eller återvänd till startsidan.", + "technicalDetails": "Tekniska detaljer", + "reloadPage": "Ladda om sidan", + "goHome": "Gå till startsidan" + } } diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index d43275f44a..d73697d231 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,17 +3,50 @@ import ReactDOM from "react-dom/client"; import App from "./App"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter } from "react-router"; -import "./i18n"; +import { Toaster } from "react-hot-toast"; +import i18n from "./i18n"; import "./index.css"; import "maplibre-gl/dist/maplibre-gl.css"; const queryClient = new QueryClient(); +// Set HTML lang attribute based on i18n language +i18n.on("languageChanged", (lng) => { + document.documentElement.lang = lng; +}); + +// Set initial language on mount +document.documentElement.lang = i18n.language; + ReactDOM.createRoot(document.getElementById("root")!).render( + diff --git a/frontend/src/pages/AboutPage.tsx b/frontend/src/pages/AboutPage.tsx new file mode 100644 index 0000000000..5d0bb79dd7 --- /dev/null +++ b/frontend/src/pages/AboutPage.tsx @@ -0,0 +1,77 @@ +import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; + +export default function AboutPage() { + const { t } = useTranslation(); + return ( +
          +

          {t("pages.about.title")}

          + +
          +

          What is BADA?

          +

          + BADA helps beachgoers and families in Sweden find safe, EU-classified bathing waters + with real-time quality updates. We replace outdated or clunky websites with a clean, + mobile-friendly experience. +

          +

          + Our mission is to make water quality information accessible to everyone, helping you + make informed decisions about where to swim and enjoy Sweden's beautiful coastal waters. +

          +
          + +
          +

          Data Source

          +

          + BADA uses official data from the Swedish Agency for Marine and Water Management + (Havs- och Vattenmyndigheten, HaV), which monitors and classifies EU bathing waters + across Sweden. +

          +

          + Water quality classifications are based on the European Bathing Water Directive and + are updated regularly throughout the bathing season. +

          +
          + +
          +

          Features

          +
            +
          • 🗺️ Interactive map of all EU-classified beaches in Sweden
          • +
          • 📍 Find the nearest beach based on your current location
          • +
          • 🔬 View water quality, classification, and recent test results
          • +
          • ❤️ Save your favorite beaches to your profile
          • +
          • 🌗 Dark mode and responsive design
          • +
          • 🌐 Multi-language support (Swedish / English)
          • +
          +
          + +
          +

          Built With

          +

          + BADA is built with React, TypeScript, Node.js, Express, and MongoDB. + Maps are powered by MapLibre and OpenStreetMap. + See our{" "} + + GitHub repository + {" "} + for more details. +

          +
          + +
          + + {t("pages.backToMap")} + +
          +
          + ); +} + diff --git a/frontend/src/pages/BeachDetailPage.tsx b/frontend/src/pages/BeachDetailPage.tsx index 6aa3195d68..264f88e48a 100644 --- a/frontend/src/pages/BeachDetailPage.tsx +++ b/frontend/src/pages/BeachDetailPage.tsx @@ -1,5 +1,7 @@ import { useParams, Link, useNavigate, useLocation } from "react-router"; import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useTranslation } from "react-i18next"; +import toast from "react-hot-toast"; import { fetchBeach } from "../api/beaches"; import { formatDate } from "../utils/format"; import { @@ -7,7 +9,7 @@ import { useAddFavorite, useRemoveFavorite, } from "../api/favorites"; -import { useAuth } from "@/store/auth"; // NEW +import { useAuth } from "@/store/auth"; // Map numeric/class text → color class function qualityClass(q: number | string | undefined) { @@ -37,10 +39,11 @@ function qualityClass(q: number | string | undefined) { } export default function BeachDetailPage() { + const { t } = useTranslation(); const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const location = useLocation(); - const { token } = useAuth(); // NEW + const { token } = useAuth(); const { data, isLoading, isError, error } = useQuery({ queryKey: ["beach", id], @@ -74,25 +77,25 @@ export default function BeachDetailPage() { return (
          -

          Could not load this beach.

          +

          {t("loadError")}

          - {(error as Error)?.message ?? "Please try again."} + {(error as Error)?.message ?? t("beachDetail.pleaseTryAgain")}

          - ← Back to list + ← {t("back")}
          ); } - const title = data.locationName ?? "Beach"; + const title = data.locationName ?? t("beachDetail.beach"); const muni = data.locationArea ?? ""; const qualityNum = data.classification; - const qualityText = data.classificationText ?? "Okänd"; + const qualityText = data.classificationText ?? t("classification.unknown"); const pillClass = qualityClass(qualityNum ?? qualityText); const latestSampleLabel = data.latestSampleDate @@ -101,21 +104,22 @@ export default function BeachDetailPage() { async function handleFavoriteClick() { if (!token) { - // Not logged in → route to login and return here after navigate("/login", { replace: false, state: { from: location } }); return; } try { if (isFav) { await rmFav.mutateAsync({ id: existingFav?._id, beachId: id }); + toast.success(t("favorites.removed")); } else { - await addFav.mutateAsync(id!); // mutation takes beachId string + await addFav.mutateAsync(id!); + toast.success(t("favorites.added")); } - // Optional: refresh favorites/beach queries queryClient.invalidateQueries({ queryKey: ["favorites", token] }); queryClient.invalidateQueries({ queryKey: ["beach", id] }); } catch (e: any) { - alert(e?.message ?? "Favorite action failed"); + const errorMsg = e?.message ?? t("favorites.removed"); + toast.error(errorMsg); } } @@ -133,26 +137,26 @@ export default function BeachDetailPage() { {qualityText}{" "} {data.classificationYear ? `• ${data.classificationYear}` : ""} - {data.euType && EU-bad} + {data.euType && {t("beachDetail.euBad")}}
        {/* Meta card */}
        -
        Latest sample
        +
        {t("beachDetail.latestSampleDate")}
        {latestSampleLabel}
        -
        Algal bloom
        +
        {t("beachDetail.algalBloom")}
        {data.algalText ?? "—"}
        -
        EU motive
        +
        {t("beachDetail.euMotive")}
        {data.euMotive ?? "—"}
        -
        NUTS code
        +
        {t("beachDetail.nutsCode")}
        {data.nutsCode}
        @@ -164,14 +168,14 @@ export default function BeachDetailPage() { onClick={handleFavoriteClick} disabled={addFav.isPending || rmFav.isPending} > - {isFav ? "★ Remove favorite" : "☆ Save as favorite"} + {isFav ? `★ ${t("beachDetail.removeFavorite")}` : `☆ ${t("beachDetail.addFavorite")}`} - ← Back + ← {t("back")}
        @@ -179,7 +183,7 @@ export default function BeachDetailPage() { {/* Description */} {data.bathInformation && (
        -

        About this beach

        +

        {t("beachDetail.bathInformation")}

        {data.bathInformation}

        @@ -189,10 +193,10 @@ export default function BeachDetailPage() { {/* Contact */} {(data.contactMail || data.contactPhone || data.contactUrl) && (
        -

        Contact

        +

        {t("nav.contact")}

        {data.contactMail && (
        - Mail:{" "} + {t("beachDetail.mail")}{" "}
        )} - {data.contactPhone &&
        Phone: {data.contactPhone}
        } + {data.contactPhone &&
        {t("beachDetail.phone")} {data.contactPhone}
        } {data.contactUrl && (
        - Website:{" "} + {t("beachDetail.website")}{" "} +

        {t("pages.contact.title")}

        + +
        +

        Get in Touch

        +

        + We'd love to hear from you! Whether you have feedback, suggestions, questions, or + found a bug, please don't hesitate to reach out. +

        +
        + +
        +

        GitHub

        +

        + For bug reports, feature requests, or code contributions, please visit our{" "} + + GitHub repository + . +

        +
        + +
        +

        Issues & Feedback

        +

        + If you encounter any issues with the application or have suggestions for improvement, + please open an issue on GitHub. Your feedback helps us make BADA better. +

        +
        + +
        +

        Data Accuracy

        +

        + If you notice any inaccuracies in the water quality data or beach information, please + let us know. We rely on official sources, but errors can occur and we'll work to + correct them promptly. +

        +
        + +
        +

        Privacy

        +

        + For questions about privacy and data handling, please see our terms of use or contact + us through GitHub. +

        +
        + +
        + + {t("pages.backToMap")} + +
        + + ); +} + diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index 16c79de3b3..a0d3244c45 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -5,6 +5,7 @@ import { useReorderFavorites, } from "@/api/favorites"; import { useBeachDetails } from "@/api/useBeachDetails"; +import { useTranslation } from "react-i18next"; import { Link } from "react-router"; import { useState, useMemo, useEffect } from "react"; import { @@ -18,7 +19,7 @@ import { import { SortableContext, arrayMove, - rectSortingStrategy, + verticalListSortingStrategy, } from "@dnd-kit/sortable"; import SortableFavorite from "@/components/SortableFavorite"; @@ -42,6 +43,7 @@ const SORT_KEY = "favoritesSort"; // 'custom' | 'name' | 'municipality' const ORDER_KEY = "favoritesOrder:v1"; // stores array of beachIds export default function FavoritesPage() { + const { t } = useTranslation(); const { data: favorites, isLoading, isError, error } = useFavorites(); const ids = favorites?.map((f) => f.beachId); const details = useBeachDetails(ids); @@ -104,11 +106,11 @@ export default function FavoritesPage() { name: info?.locationName ?? f.beachId, muni: info?.locationArea ?? "", classification: info?.classification ?? info?.classificationText, - classificationText: info?.classificationText ?? "Unknown", + classificationText: info?.classificationText ?? t("classification.unknown"), }); } return map; - }, [favorites, details.byId]); + }, [favorites, details.byId, t]); // --- Items to render (IDs in display order) const displayIds = useMemo(() => { @@ -161,7 +163,7 @@ export default function FavoritesPage() { if (isLoading) { return (
        -

        Your favorite beaches

        +

        {t("favorites.title")}

        {[...Array(3)].map((_, i) => (
        @@ -178,9 +180,9 @@ export default function FavoritesPage() { if (isError) { return (
        -

        Your favorite beaches

        +

        {t("favorites.title")}

        - {(error as Error)?.message ?? "Could not load favorites"} + {(error as Error)?.message ?? t("loadError")}

        ); @@ -190,40 +192,40 @@ export default function FavoritesPage() { return (
        -

        Your favorite beaches

        +

        {t("favorites.title")}

        - Browse all beaches + {t("favorites.browseAll")}
        {favorites && favorites.length === 0 && (
        -

        No favorites yet

        +

        {t("favorites.empty")}

        - Browse the map and tap “Save as favorite” on a beach you like. + {t("favorites.addFavorites")}

        - Go to map + {t("beachesList.modeDefault")}
        )} @@ -233,8 +235,8 @@ export default function FavoritesPage() { collisionDetection={closestCenter} onDragEnd={onDragEnd} > - -
          + +
            {displayIds.map((id) => { const item = enriched.get(id); if (!item) return null; diff --git a/frontend/src/pages/LoginPage.tsx b/frontend/src/pages/LoginPage.tsx index 710c23f09c..e818ecff04 100644 --- a/frontend/src/pages/LoginPage.tsx +++ b/frontend/src/pages/LoginPage.tsx @@ -1,22 +1,29 @@ +import { useMemo } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useTranslation } from "react-i18next"; import { useNavigate, useLocation, Link } from "react-router"; +import toast from "react-hot-toast"; import { useAuth } from "@/store/auth"; -const schema = z.object({ - email: z.string().email("Invalid email address"), - password: z.string().min(8, "Password must be at least 8 characters"), -}); - -type FormData = z.infer; +type FormData = { + email: string; + password: string; +}; export default function LoginPage() { + const { t } = useTranslation(); const { setToken } = useAuth(); const navigate = useNavigate(); const location = useLocation(); const from = (location.state as any)?.from?.pathname || "/"; + const schema = useMemo(() => z.object({ + email: z.string().email(t("auth.invalidEmail")), + password: z.string().min(8, t("auth.passwordTooShort")), + }), [t]); + const { register, handleSubmit, @@ -38,24 +45,29 @@ export default function LoginPage() { const body = await res.json().catch(() => null); const msg = body?.error ?? res.statusText; setError("root", { message: msg }); + toast.error(msg); return; } const data = await res.json(); - setToken(data.token); // Save token in Zustand store + setToken(data.token); + toast.success(t("auth.signInSuccess")); navigate(from, { replace: true }); } catch (err: any) { - setError("root", { message: err.message ?? "Login failed" }); + const errorMsg = err.message ?? t("auth.signInFailed"); + setError("root", { message: errorMsg }); + toast.error(errorMsg); } } return (
            -

            Sign in

            +

            {t("auth.signInTitle")}

            - +
            - + - {isSubmitting ? "Signing in…" : "Sign in"} + {isSubmitting ? t("auth.signInProgress") : t("auth.signIn")}

            - No account?{" "} + {t("auth.noAccount")}{" "} - Register here + {t("auth.registerHere")}

            diff --git a/frontend/src/pages/ProfilePage.tsx b/frontend/src/pages/ProfilePage.tsx new file mode 100644 index 0000000000..259345064d --- /dev/null +++ b/frontend/src/pages/ProfilePage.tsx @@ -0,0 +1,67 @@ +import { Link, useNavigate } from "react-router"; +import { useTranslation } from "react-i18next"; +import { useAuth } from "@/store/auth"; +import { useQueryClient } from "@tanstack/react-query"; + +export default function ProfilePage() { + const { t } = useTranslation(); + const { token, clearToken } = useAuth(); + const navigate = useNavigate(); + const queryClient = useQueryClient(); + + const handleLogout = () => { + clearToken(); + queryClient.clear(); + navigate("/"); + }; + + return ( +
            +

            {t("pages.profile.title")}

            + +
            +

            Account Information

            +

            + You are currently signed in. Your account allows you to save favorite beaches and + access them from any device. +

            +
            + +
            +

            Your Favorites

            +

            + Manage your saved beaches and keep track of your favorite swimming spots. +

            + + View My Favorites + +
            + +
            +

            Sign Out

            +

            + Sign out of your account. You'll need to sign in again to access your favorites. +

            + +
            + +
            + + {t("pages.backToMap")} + +
            +
            + ); +} + diff --git a/frontend/src/pages/RegisterPage.tsx b/frontend/src/pages/RegisterPage.tsx index 4d964f0afb..6bc31e7597 100644 --- a/frontend/src/pages/RegisterPage.tsx +++ b/frontend/src/pages/RegisterPage.tsx @@ -1,24 +1,32 @@ +import { useMemo } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; +import { useTranslation } from "react-i18next"; import { useNavigate, Link } from "react-router"; +import toast from "react-hot-toast"; -const schema = z - .object({ - email: z.string().email("Invalid email address"), - password: z.string().min(8, "Password must be at least 8 characters"), - confirmPassword: z.string(), - }) - .refine((data) => data.password === data.confirmPassword, { - message: "Passwords must match", - path: ["confirmPassword"], - }); - -type FormData = z.infer; +type FormData = { + email: string; + password: string; + confirmPassword: string; +}; export default function RegisterPage() { + const { t } = useTranslation(); const navigate = useNavigate(); + const schema = useMemo(() => z + .object({ + email: z.string().email(t("auth.invalidEmail")), + password: z.string().min(8, t("auth.passwordTooShort")), + confirmPassword: z.string(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: t("auth.passwordsMustMatch"), + path: ["confirmPassword"], + }), [t]); + const { register, handleSubmit, @@ -46,23 +54,27 @@ export default function RegisterPage() { const body = await res.json().catch(() => null); const msg = body?.error ?? res.statusText; setError("root", { message: msg }); + toast.error(msg); return; } - // Success → redirect to login + toast.success(t("auth.registerSuccess")); navigate("/login", { replace: true }); } catch (err: any) { - setError("root", { message: err.message ?? "Registration failed" }); + const errorMsg = err.message ?? t("auth.registerFailed"); + setError("root", { message: errorMsg }); + toast.error(errorMsg); } } return (
            -

            Register

            +

            {t("auth.registerTitle")}

            - +
            - +
            - + - {isSubmitting ? "Registering…" : "Register"} + {isSubmitting ? t("auth.registerProgress") : t("auth.register")}

            - Already have an account?{" "} + {t("auth.alreadyHaveAccount")}{" "} - Sign in here + {t("auth.signInHere")}

            diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx new file mode 100644 index 0000000000..d1bcda743c --- /dev/null +++ b/frontend/src/pages/SettingsPage.tsx @@ -0,0 +1,45 @@ +import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; +import LanguageSwitcher from "@/components/LanguageSwitcher"; + +export default function SettingsPage() { + const { t } = useTranslation(); + + return ( +
            +

            Settings

            + +
            +

            Language

            +

            + Choose your preferred language for BADA. +

            + +
            + +
            +

            Dark Mode

            +

            + Toggle dark mode from the user menu in the header. +

            +
            + +
            +

            Notifications

            +

            + Notification preferences will be available in a future update. +

            +
            + +
            + + ← Back to map + +
            +
            + ); +} + diff --git a/frontend/src/pages/TermsPage.tsx b/frontend/src/pages/TermsPage.tsx new file mode 100644 index 0000000000..71e0feea9e --- /dev/null +++ b/frontend/src/pages/TermsPage.tsx @@ -0,0 +1,87 @@ +import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; + +export default function TermsPage() { + const { t } = useTranslation(); + return ( +
            +

            {t("pages.terms.title")}

            + +
            +

            Acceptance of Terms

            +

            + By using BADA, you agree to these terms of use. If you do not agree with any part + of these terms, please do not use our service. +

            +
            + +
            +

            Service Description

            +

            + BADA provides information about EU-classified bathing waters in Sweden. We aggregate + data from official sources and present it in an accessible format. +

            +
            + +
            +

            Disclaimer

            +

            + The water quality information provided by BADA is based on official data from the + Swedish Agency for Marine and Water Management. While we strive for accuracy, we + cannot guarantee the completeness or timeliness of all information. +

            +

            + BADA is provided "as is" without warranty of any kind. Users should exercise their + own judgment and discretion when using this information to make decisions about + swimming or other water activities. +

            +
            + +
            +

            User Accounts

            +

            + When you create an account, you are responsible for maintaining the confidentiality + of your account information and password. You agree to notify us immediately of any + unauthorized use of your account. +

            +
            + +
            +

            Intellectual Property

            +

            + The BADA application and its original content, features, and functionality are owned + by the BADA project and are protected by international copyright, trademark, and + other intellectual property laws. +

            +
            + +
            +

            Changes to Terms

            +

            + We reserve the right to modify these terms at any time. We will notify users of any + significant changes by posting the new terms on this page. +

            +
            + +
            +

            Contact

            +

            + If you have questions about these terms, please{" "} + + contact us + . +

            +
            + +
            + + {t("pages.backToMap")} + +
            +
            + ); +} + diff --git a/frontend/src/pages/WhatIsEUBeachPage.tsx b/frontend/src/pages/WhatIsEUBeachPage.tsx new file mode 100644 index 0000000000..1d81398361 --- /dev/null +++ b/frontend/src/pages/WhatIsEUBeachPage.tsx @@ -0,0 +1,89 @@ +import { Link } from "react-router"; +import { useTranslation } from "react-i18next"; + +export default function WhatIsEUBeachPage() { + const { t } = useTranslation(); + return ( +
            +

            {t("pages.whatIsEUBeach.title")}

            + +
            +

            EU Bathing Water Directive

            +

            + An EU bathing water is a beach or section of a coastline where the European Union's + Bathing Water Directive (2006/7/EC) applies. This directive requires member states + to monitor and regularly test the water quality at designated bathing sites. +

            +

            + In Sweden, the Swedish Agency for Marine and Water Management (HaV) monitors these + beaches and classifies them according to water quality standards. +

            +
            + +
            +

            Water Quality Classifications

            + +
            +
            +
            + Excellent +
            +

            + Best water quality. The water meets the highest standards with excellent microbiological quality. +

            +
            + +
            +
            + Good +
            +

            + Good water quality. The water meets the standards with good microbiological quality. +

            +
            + +
            +
            + Sufficient +
            +

            + Sufficient water quality. The water meets the minimum standards. +

            +
            + +
            +
            + Poor +
            +

            + Poor water quality. Swimming is not recommended. The water does not meet the minimum standards. +

            +
            +
            +
            + +
            +

            Monitoring

            +

            + Water quality is monitored throughout the bathing season (typically May to September). + Samples are taken regularly and tested for bacteria and other contaminants that could + affect human health. +

            +

            + The classification is based on data from the last four bathing seasons to provide a + stable and reliable assessment of water quality. +

            +
            + +
            + + {t("pages.backToMap")} + +
            +
            + ); +} + From f1103705895a6e4b95bf5fcfa51dd971d3730480 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 31 Oct 2025 16:54:33 +0100 Subject: [PATCH 184/215] docs: Update README --- README.md | 164 +----------------------------------------------------- 1 file changed, 1 insertion(+), 163 deletions(-) diff --git a/README.md b/README.md index 0f7afd4a8a..3d8552f084 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ It replaces outdated or clunky websites with a **clean, mobile-friendly experien ## 📸 Screenshots -_(Will add screenshots or GIFs here once deployed – e.g. home page, map view, beach detail page, favourites page)_ +_(Coming soon)_ --- @@ -100,168 +100,6 @@ Frontend runs on http://localhost:5173 --- -## 📚 API Documentation - -### Base URL - -- Production: `https://bada-backend.vercel.app/api` -- Local: `http://localhost:3000/api` - -### Authentication - -Most endpoints require JWT authentication. Include the token in the Authorization header: - -``` -Authorization: Bearer -``` - -### Endpoints - -#### Health Check - -- **GET** `/health` -- **Description**: Check if the backend is running -- **Auth**: Not required -- **Response**: `{ status: "ok", timestamp: "..." }` - -#### Beaches - -**GET** `/beaches` - -- List all EU-classified beaches in Sweden -- **Auth**: Not required -- **Response**: GeoJSON FeatureCollection -- **Example**: - -```json -{ - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": { - "NUTSKOD": "SE110006001", - "NAMN": "Folhem", - "KMN_NAMN": "Stockholm" - }, - "geometry": { - "type": "Point", - "coordinates": [18.0555, 59.3326] - } - } - ] -} -``` - -**GET** `/beaches/:id` - -- Get detailed information about a specific beach -- **Auth**: Not required -- **Response**: BeachDetail object -- **Example**: - -```json -{ - "nutsCode": "SE110006001", - "locationName": "Folhem", - "locationArea": "Stockholm", - "classification": 1, - "classificationText": "Bra kvalitet", - "classificationYear": 2024, - "bathInformation": "Public beach with facilities", - "latestSampleDate": "2024-07-15" -} -``` - -#### Authentication - -**POST** `/auth/register` - -- Register a new user -- **Auth**: Not required -- **Request body**: - -```json -{ - "email": "user@example.com", - "password": "securepassword123" -} -``` - -- **Response**: `{ message: "User created successfully" }` - -**POST** `/auth/login` - -- Login and get JWT token -- **Auth**: Not required -- **Request body**: - -```json -{ - "email": "user@example.com", - "password": "securepassword123" -} -``` - -- **Response**: `{ token: "eyJhbGciOiJIUzI1NiIs..." }` - -#### Favorites - -**GET** `/favorites` - -- Get user's favorite beaches -- **Auth**: Required -- **Response**: Array of favorite objects -- **Example**: - -```json -[ - { - "_id": "65abc123def456", - "userId": "user123", - "beachId": "SE110006001", - "order": 0, - "createdAt": "2024-01-15T10:00:00.000Z" - } -] -``` - -**POST** `/favorites` - -- Add a beach to favorites -- **Auth**: Required -- **Request body**: - -```json -{ - "beachId": "SE110006001" -} -``` - -- **Response**: Created favorite object - -**DELETE** `/favorites/:id` - -- Remove a beach from favorites -- **Auth**: Required -- **Response**: `{ message: "Favorite removed" }` - -**PUT** `/favorites/reorder` - -- Reorder favorite beaches (drag & drop) -- **Auth**: Required -- **Request body**: - -```json -{ - "order": ["SE110006001", "SE220015002", "SE330024003"] -} -``` - -- **Response**: Updated order - ---- - ## 🔑 Environment Variables - See .env.example in both backend/ and frontend/. From 586494aa469add57e3811e7a1abf74f6d888a8ae Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:08:40 +0100 Subject: [PATCH 185/215] feat: Complete Swedish localization and responsive improvements - Add Swedish translations for About, Terms, Contact, and EU Beach pages - Implement responsive grid for beach cards (1/2/4 columns) - Update favorites to full-width layout --- frontend/src/components/BeachesList.tsx | 2 +- frontend/src/locales/en/common.json | 63 ++++++++++++++++++++++-- frontend/src/locales/sv/common.json | 63 ++++++++++++++++++++++-- frontend/src/pages/AboutPage.tsx | 42 +++++++--------- frontend/src/pages/ContactPage.tsx | 27 +++++----- frontend/src/pages/TermsPage.tsx | 43 ++++++---------- frontend/src/pages/WhatIsEUBeachPage.tsx | 36 ++++++-------- 7 files changed, 178 insertions(+), 98 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 4fedeb9e83..be53fe8c20 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -211,7 +211,7 @@ export default function BeachesList() {
        {/* List (keep to 50 for UX) */} -
          +
            {filtered.slice(0, 50).map((b) => (
          • {t("pages.about.title")}
            -

            What is BADA?

            +

            {t("pages.about.whatIsBADA")}

            - BADA helps beachgoers and families in Sweden find safe, EU-classified bathing waters - with real-time quality updates. We replace outdated or clunky websites with a clean, - mobile-friendly experience. + {t("pages.about.whatIsBADADesc")}

            - Our mission is to make water quality information accessible to everyone, helping you - make informed decisions about where to swim and enjoy Sweden's beautiful coastal waters. + {t("pages.about.whatIsBADAMission")}

            -

            Data Source

            +

            {t("pages.about.dataSource")}

            - BADA uses official data from the Swedish Agency for Marine and Water Management - (Havs- och Vattenmyndigheten, HaV), which monitors and classifies EU bathing waters - across Sweden. + {t("pages.about.dataSourceDesc")}

            - Water quality classifications are based on the European Bathing Water Directive and - are updated regularly throughout the bathing season. + {t("pages.about.dataSourceDirective")}

            -

            Features

            +

            {t("pages.about.features")}

              -
            • 🗺️ Interactive map of all EU-classified beaches in Sweden
            • -
            • 📍 Find the nearest beach based on your current location
            • -
            • 🔬 View water quality, classification, and recent test results
            • -
            • ❤️ Save your favorite beaches to your profile
            • -
            • 🌗 Dark mode and responsive design
            • -
            • 🌐 Multi-language support (Swedish / English)
            • +
            • {t("pages.about.featureMap")}
            • +
            • {t("pages.about.featureLocation")}
            • +
            • {t("pages.about.featureQuality")}
            • +
            • {t("pages.about.featureFavorites")}
            • +
            • {t("pages.about.featureDarkMode")}
            • +
            • {t("pages.about.featureLang")}
            -

            Built With

            +

            {t("pages.about.builtWith")}

            - BADA is built with React, TypeScript, Node.js, Express, and MongoDB. - Maps are powered by MapLibre and OpenStreetMap. - See our{" "} + {t("pages.about.builtWithDesc")}{" "} - GitHub repository + {t("pages.about.githubRepo")} {" "} - for more details. + {t("pages.about.builtWithDesc2")}

            diff --git a/frontend/src/pages/ContactPage.tsx b/frontend/src/pages/ContactPage.tsx index f8f8f61e92..e1c94d6b10 100644 --- a/frontend/src/pages/ContactPage.tsx +++ b/frontend/src/pages/ContactPage.tsx @@ -8,50 +8,45 @@ export default function ContactPage() {

            {t("pages.contact.title")}

            -

            Get in Touch

            +

            {t("pages.contact.getInTouch")}

            - We'd love to hear from you! Whether you have feedback, suggestions, questions, or - found a bug, please don't hesitate to reach out. + {t("pages.contact.getInTouchDesc")}

            -

            GitHub

            +

            {t("pages.contact.github")}

            - For bug reports, feature requests, or code contributions, please visit our{" "} + {t("pages.contact.githubDesc")}{" "} - GitHub repository + {t("pages.contact.githubRepo")} .

            -

            Issues & Feedback

            +

            {t("pages.contact.feedback")}

            - If you encounter any issues with the application or have suggestions for improvement, - please open an issue on GitHub. Your feedback helps us make BADA better. + {t("pages.contact.feedbackDesc")}

            -

            Data Accuracy

            +

            {t("pages.contact.accuracy")}

            - If you notice any inaccuracies in the water quality data or beach information, please - let us know. We rely on official sources, but errors can occur and we'll work to - correct them promptly. + {t("pages.contact.accuracyDesc")}

            -

            Privacy

            +

            {t("pages.contact.privacy")}

            - For questions about privacy and data handling, please see our terms of use or contact - us through GitHub. + {t("pages.contact.privacyDesc")}

            diff --git a/frontend/src/pages/TermsPage.tsx b/frontend/src/pages/TermsPage.tsx index 71e0feea9e..1a7c75daa8 100644 --- a/frontend/src/pages/TermsPage.tsx +++ b/frontend/src/pages/TermsPage.tsx @@ -8,67 +8,56 @@ export default function TermsPage() {

            {t("pages.terms.title")}

            -

            Acceptance of Terms

            +

            {t("pages.terms.acceptance")}

            - By using BADA, you agree to these terms of use. If you do not agree with any part - of these terms, please do not use our service. + {t("pages.terms.acceptanceDesc")}

            -

            Service Description

            +

            {t("pages.terms.service")}

            - BADA provides information about EU-classified bathing waters in Sweden. We aggregate - data from official sources and present it in an accessible format. + {t("pages.terms.serviceDesc")}

            -

            Disclaimer

            +

            {t("pages.terms.disclaimer")}

            - The water quality information provided by BADA is based on official data from the - Swedish Agency for Marine and Water Management. While we strive for accuracy, we - cannot guarantee the completeness or timeliness of all information. + {t("pages.terms.disclaimerDesc")}

            - BADA is provided "as is" without warranty of any kind. Users should exercise their - own judgment and discretion when using this information to make decisions about - swimming or other water activities. + {t("pages.terms.disclaimerDesc2")}

            -

            User Accounts

            +

            {t("pages.terms.accounts")}

            - When you create an account, you are responsible for maintaining the confidentiality - of your account information and password. You agree to notify us immediately of any - unauthorized use of your account. + {t("pages.terms.accountsDesc")}

            -

            Intellectual Property

            +

            {t("pages.terms.ip")}

            - The BADA application and its original content, features, and functionality are owned - by the BADA project and are protected by international copyright, trademark, and - other intellectual property laws. + {t("pages.terms.ipDesc")}

            -

            Changes to Terms

            +

            {t("pages.terms.changes")}

            - We reserve the right to modify these terms at any time. We will notify users of any - significant changes by posting the new terms on this page. + {t("pages.terms.changesDesc")}

            -

            Contact

            +

            {t("pages.terms.contact")}

            - If you have questions about these terms, please{" "} + {t("pages.terms.contactDesc")}{" "} - contact us + {t("pages.terms.contactUs")} .

            diff --git a/frontend/src/pages/WhatIsEUBeachPage.tsx b/frontend/src/pages/WhatIsEUBeachPage.tsx index 1d81398361..889b668a2f 100644 --- a/frontend/src/pages/WhatIsEUBeachPage.tsx +++ b/frontend/src/pages/WhatIsEUBeachPage.tsx @@ -8,70 +8,64 @@ export default function WhatIsEUBeachPage() {

            {t("pages.whatIsEUBeach.title")}

            -

            EU Bathing Water Directive

            +

            {t("pages.whatIsEUBeach.directive")}

            - An EU bathing water is a beach or section of a coastline where the European Union's - Bathing Water Directive (2006/7/EC) applies. This directive requires member states - to monitor and regularly test the water quality at designated bathing sites. + {t("pages.whatIsEUBeach.directiveDesc")}

            - In Sweden, the Swedish Agency for Marine and Water Management (HaV) monitors these - beaches and classifies them according to water quality standards. + {t("pages.whatIsEUBeach.directiveDesc2")}

            -

            Water Quality Classifications

            +

            {t("pages.whatIsEUBeach.classifications")}

            - Excellent + {t("classification.excellent")}

            - Best water quality. The water meets the highest standards with excellent microbiological quality. + {t("pages.whatIsEUBeach.excellentDesc")}

            - Good + {t("classification.good")}

            - Good water quality. The water meets the standards with good microbiological quality. + {t("pages.whatIsEUBeach.goodDesc")}

            - Sufficient + {t("classification.sufficient")}

            - Sufficient water quality. The water meets the minimum standards. + {t("pages.whatIsEUBeach.sufficientDesc")}

            - Poor + {t("classification.poor")}

            - Poor water quality. Swimming is not recommended. The water does not meet the minimum standards. + {t("pages.whatIsEUBeach.poorDesc")}

            -

            Monitoring

            +

            {t("pages.whatIsEUBeach.monitoring")}

            - Water quality is monitored throughout the bathing season (typically May to September). - Samples are taken regularly and tested for bacteria and other contaminants that could - affect human health. + {t("pages.whatIsEUBeach.monitoringDesc")}

            - The classification is based on data from the last four bathing seasons to provide a - stable and reliable assessment of water quality. + {t("pages.whatIsEUBeach.monitoringDesc2")}

            From 8c66ca754b706380df740975cd3392578273e742 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:17:19 +0100 Subject: [PATCH 186/215] fix: Make search work across all beaches in Sweden - Bypass map radius/viewport filters when user is searching - Disable map auto-fit when search is active to keep map static - Improve empty state messaging for search vs map filters --- frontend/src/components/BeachesList.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index be53fe8c20..79941b3425 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -76,8 +76,13 @@ export default function BeachesList() { }); }, [items, q]); - // Radius or viewport filter + // Radius or viewport filter (skip if user is searching) const filtered = useMemo(() => { + // If user is searching, don't filter by map bounds + if (q) { + return filteredBySearch; + } + if (mode === "viewport" && bounds) { return filteredBySearch.filter( (b) => @@ -94,7 +99,7 @@ export default function BeachesList() { } return true; }); - }, [filteredBySearch, mode, bounds, center, radiusKm]); + }, [filteredBySearch, mode, bounds, center, radiusKm, q]); // Initial fetch safeguard useEffect(() => { @@ -124,7 +129,7 @@ export default function BeachesList() { }; const mapFocus = - mode === "viewport" + mode === "viewport" || q ? undefined : { center: { lon: center.lon, lat: center.lat }, radiusKm }; @@ -176,7 +181,9 @@ export default function BeachesList() {

            {t("beachesList.noMatches")}

            - {mode === "viewport" + {q + ? t("beachesList.emptyState") + : mode === "viewport" ? t("beachesList.panOrZoom") : t("beachesList.widenRadius")}

            From 3bbdcf0e6ee9dfe1933b5c13b6b77a2affc26ba9 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:29:41 +0100 Subject: [PATCH 187/215] fix: Improve location button behavior and map filtering - Reset to default mode when clicking "Use current location" to ensure map fits - Clear bounds when switching to nearby mode to prevent stale state - Add onFitBounds callback to switch to viewport after programmatic f --- frontend/src/components/BeachesList.tsx | 14 +++++++++++++ frontend/src/components/MapView.tsx | 27 ++++++++++++++++++++----- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/BeachesList.tsx b/frontend/src/components/BeachesList.tsx index 79941b3425..289964dbec 100644 --- a/frontend/src/components/BeachesList.tsx +++ b/frontend/src/components/BeachesList.tsx @@ -111,10 +111,13 @@ export default function BeachesList() { if (!coords) return; setCenter({ lat: coords.lat, lon: coords.lon }); setRadiusKm(NEARBY_RADIUS_KM); + setBounds(null); // Clear bounds when switching to nearby mode setMode("nearby"); }, [coords]); const handleUseLocation = async () => { + // Reset mode to trigger map fit + setMode("default"); await request(); // coords effect handles the rest }; @@ -128,6 +131,16 @@ export default function BeachesList() { setMode("viewport"); }; + // Programmatic fit → viewport mode + const handleFitBounds = (e: { + bounds: { west: number; south: number; east: number; north: number }; + center: { lon: number; lat: number }; + zoom: number; + }) => { + setBounds(e.bounds); + setMode("viewport"); + }; + const mapFocus = mode === "viewport" || q ? undefined @@ -204,6 +217,7 @@ export default function BeachesList() { }))} focus={mapFocus} onMoveEnd={handleMoveEnd} + onFitBounds={handleFitBounds} /> {/* Use current location — full width, now under the map */} diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index e4512fd2cd..7b8d5bb999 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -14,6 +14,12 @@ type Props = { center: { lon: number; lat: number }; zoom: number; }) => void; + /** Fires after programmatic fits */ + onFitBounds?: (args: { + bounds: { west: number; south: number; east: number; north: number }; + center: { lon: number; lat: number }; + zoom: number; + }) => void; }; const STYLE_LIGHT = @@ -52,12 +58,14 @@ function circleBounds( ]; } -export default function MapView({ points = [], focus, onMoveEnd }: Props) { +export default function MapView({ points = [], focus, onMoveEnd, onFitBounds }: Props) { const ref = useRef(null); const mapRef = useRef(null); const markersRef = useRef([]); const onMoveEndRef = useRef(onMoveEnd); onMoveEndRef.current = onMoveEnd; // keep latest callback without re-binding listeners + const onFitBoundsRef = useRef(onFitBounds); + onFitBoundsRef.current = onFitBounds; // Init map ONCE useEffect(() => { @@ -99,11 +107,9 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { userMoving = !!(e as any).originalEvent; // programmatic fits won't set this }); map.on("moveend", () => { - if (!userMoving || !onMoveEndRef.current) return; - userMoving = false; const b = map.getBounds(); const c = map.getCenter(); - onMoveEndRef.current({ + const boundsObj = { bounds: { west: b.getWest(), south: b.getSouth(), @@ -112,7 +118,18 @@ export default function MapView({ points = [], focus, onMoveEnd }: Props) { }, center: { lon: c.lng, lat: c.lat }, zoom: map.getZoom(), - }); + }; + + if (userMoving) { + // User-initiated move + userMoving = false; + if (onMoveEndRef.current) { + onMoveEndRef.current(boundsObj); + } + } else if (onFitBoundsRef.current) { + // Programmatic fit + onFitBoundsRef.current(boundsObj); + } }); return () => { From 03af0e9aaf82e4a41b761edb30fc3679c36c8121 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:34:49 +0100 Subject: [PATCH 188/215] feat: Add clickable links to beach details in map marker popups --- frontend/src/components/MapView.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 7b8d5bb999..aeaf4b8aad 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -159,7 +159,11 @@ export default function MapView({ points = [], focus, onMoveEnd, onFitBounds }: const marker = new maplibregl.Marker({ element: el }) .setLngLat([p.lon, p.lat]) - .setPopup(new maplibregl.Popup({ closeButton: false }).setText(p.name)) + .setPopup( + new maplibregl.Popup({ closeButton: false }).setHTML( + `${p.name}` + ) + ) .addTo(map); markersRef.current.push(marker); From 773d5615f6bc13a7e3415466ff0475d723dd1db4 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:37:58 +0100 Subject: [PATCH 189/215] fix(routing): add SPA redirects and cache external API calls - Copy _redirects file to dist directory for proper SPA routing - Cache HaV v2 latest sample date requests (5min TTL) Resolves beach detail page 404 errors on page reload by ensuring the redirect rules are deployed. Also reduces external API load by caching v2 results endpoint responses. --- backend/src/lib/hav.ts | 5 +++++ frontend/vite.config.js | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/src/lib/hav.ts b/backend/src/lib/hav.ts index 2e091ca6a1..23d51c7489 100644 --- a/backend/src/lib/hav.ts +++ b/backend/src/lib/hav.ts @@ -75,6 +75,10 @@ export async function havV2Get( /** Pull latest sample date (ISO string) from /bathing-waters/{id}/results */ export async function getLatestSampleDate(id: string): Promise { + const k = key(`v2:results:${id}`); + const cached = cache.get(k); + if (cached !== undefined) return cached; + type MonitoringResult = { takenAt?: string | null }; type Results = { results?: MonitoringResult[] }; @@ -88,5 +92,6 @@ export async function getLatestSampleDate(id: string): Promise { .filter((d): d is string => !!d) .sort((a, b) => (a < b ? 1 : a > b ? -1 : 0))[0] ?? null; + cache.set(k, latest, 5 * 60 * 1000); return latest; } diff --git a/frontend/vite.config.js b/frontend/vite.config.js index fd9a196187..40958d8212 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -3,7 +3,6 @@ import react from "@vitejs/plugin-react"; import svgr from "vite-plugin-svgr"; import path from "path"; -// https://vitejs.dev/config/ export default defineConfig({ plugins: [react(), svgr()], resolve: { @@ -11,4 +10,12 @@ export default defineConfig({ "@": path.resolve(__dirname, "./src"), }, }, + publicDir: "public", + build: { + rollupOptions: { + input: { + main: path.resolve(__dirname, "index.html"), + }, + }, + }, }); From dd57d5a12ba31edf2efce5b150be4eac3e43bc5c Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:44:11 +0100 Subject: [PATCH 190/215] fix: move _redirects to public folder for Netlify SPA routing --- frontend/{ => dist}/_redirects | 0 frontend/dist/assets/index-BOXha8QM.css | 1 - frontend/dist/assets/main-p_R7C9Fr.css | 1 + .../{index-Df7HsKlx.js => main-voNoOY85.js} | 204 +++++++++--------- frontend/dist/index.html | 39 +++- frontend/public/_redirects | 1 + 6 files changed, 132 insertions(+), 114 deletions(-) rename frontend/{ => dist}/_redirects (100%) delete mode 100644 frontend/dist/assets/index-BOXha8QM.css create mode 100644 frontend/dist/assets/main-p_R7C9Fr.css rename frontend/dist/assets/{index-Df7HsKlx.js => main-voNoOY85.js} (50%) create mode 100644 frontend/public/_redirects diff --git a/frontend/_redirects b/frontend/dist/_redirects similarity index 100% rename from frontend/_redirects rename to frontend/dist/_redirects diff --git a/frontend/dist/assets/index-BOXha8QM.css b/frontend/dist/assets/index-BOXha8QM.css deleted file mode 100644 index c785ffaf83..0000000000 --- a/frontend/dist/assets/index-BOXha8QM.css +++ /dev/null @@ -1 +0,0 @@ -.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-scale{background-color:#ffffffbf;border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px #0000001a;padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px #00000059;box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:#0006;color:#fff;display:flex;font-size:1.4em;top:0;right:0;bottom:0;left:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-600:oklch(57.7% .245 27.325);--color-red-950:oklch(25.8% .092 26.042);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-lg:64rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-2xl:1rem;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#efebe4;--color-surface-muted:#f6f2ec;--color-ink:#1a1a1a;--color-ink-muted:#64686c;--color-border:#cdc8c0;--color-accent:#0a5a82;--color-badge:#e6e4dc;--color-quality-excellent:#1f6fb2;--color-quality-good:#2b8a3e;--color-quality-sufficient:#b08900;--color-quality-poor:#c0392b;--color-quality-unknown:#6b7280;--font-spectral:"Spectral",serif;--font-inter:"Inter",system-ui,sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body,#root{height:100%}body{background-color:var(--color-surface);font-family:var(--font-inter);color:var(--color-ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4{font-family:var(--font-spectral);color:var(--color-ink)}a{border-radius:var(--radius-sm);color:var(--color-accent);text-underline-offset:2px}@media (hover:hover){a:hover{text-decoration-line:underline}}a:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){a:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}a:focus-visible{--tw-outline-style:none;outline-style:none}}@layer components{.card{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface-muted);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.badge{align-items:center;gap:calc(var(--spacing)*1);background-color:var(--color-badge);padding-inline:calc(var(--spacing)*2.5);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-ink);border-radius:3.40282e38px;display:inline-flex}.kpi-excellent{color:var(--color-quality-excellent)}.kpi-good{color:var(--color-quality-good)}.kpi-sufficient{color:var(--color-quality-sufficient)}.kpi-poor{color:var(--color-quality-poor)}.kpi-unknown{color:var(--color-quality-unknown)}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-9{height:calc(var(--spacing)*9)}.h-\[260px\]{height:260px}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-6{width:calc(var(--spacing)*6)}.w-40{width:calc(var(--spacing)*40)}.w-56{width:calc(var(--spacing)*56)}.w-60{width:calc(var(--spacing)*60)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.border-red-600{border-color:var(--color-red-600)}.bg-accent{background-color:var(--color-accent)}.bg-border{background-color:var(--color-border)}.bg-ink\/60{background-color:#1a1a1a99}@supports (color:color-mix(in lab,red,red)){.bg-ink\/60{background-color:color-mix(in oklab,var(--color-ink)60%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-muted{background-color:var(--color-surface-muted)}.p-0{padding:calc(var(--spacing)*0)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.text-center{text-align:center}.text-justify{text-align:justify}.text-left{text-align:left}.font-spectral{font-family:var(--font-spectral)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[-0\.06em\]{--tw-tracking:-.06em;letter-spacing:-.06em}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.text-red-600{color:var(--color-red-600)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/60{--tw-ring-color:#0009}@supports (color:color-mix(in lab,red,red)){.ring-black\/60{--tw-ring-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.ring-white\/85{--tw-ring-color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.ring-white\/85{--tw-ring-color:color-mix(in oklab,var(--color-white)85%,transparent)}}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition\!{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.hover\:bg-accent\/90:hover{background-color:#0a5a82e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/90:hover{background-color:color-mix(in oklab,var(--color-accent)90%,transparent)}}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:bg-surface-muted:hover{background-color:var(--color-surface-muted)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:not-sr-only:focus{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.focus\:absolute:focus{position:absolute}.focus\:top-4:focus{top:calc(var(--spacing)*4)}.focus\:left-4:focus{left:calc(var(--spacing)*4)}.focus\:z-50:focus{z-index:50}.focus\:rounded:focus{border-radius:.25rem}.focus\:bg-accent:focus{background-color:var(--color-accent)}.focus\:px-4:focus{padding-inline:calc(var(--spacing)*4)}.focus\:py-2:focus{padding-block:calc(var(--spacing)*2)}.focus\:text-white:focus{color:var(--color-white)}.focus\:underline:focus{text-decoration-line:underline}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent\/40:focus{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus\:ring-accent\/40:focus{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width:40rem){.sm\:max-w-none{max-width:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:calc(var(--spacing)*4)}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (min-width:96rem){.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme:dark){@media (hover:hover){.dark\:hover\:bg-red-950:hover{background-color:var(--color-red-950)}}}.aspect-square{aspect-ratio:1}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sr-only:focus{width:auto;height:auto;padding:inherit;margin:inherit;clip:auto;white-space:normal;position:static;overflow:visible}}.maplibregl-map{-webkit-tap-highlight-color:#0000;font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;overflow:hidden}.maplibregl-canvas{position:absolute;top:0;left:0}.maplibregl-map:fullscreen{width:100%;height:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;z-index:2;position:absolute}.maplibregl-ctrl-top-left{top:0;left:0}.maplibregl-ctrl-top-right{top:0;right:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px #0000001a}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px buttontext}}.maplibregl-ctrl-group button{box-sizing:border-box;cursor:pointer;background-color:#0000;border:0;outline:none;width:29px;height:29px;padding:0;display:block}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;width:100%;height:100%;display:block}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:#0000}.maplibregl-ctrl-group button+button{border-top:1px solid buttontext}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:#0000000d}}.maplibregl-ctrl button:not(:disabled):active{background-color:#0000000d}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:2s linear infinite maplibregl-spin}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:88px;height:23px;margin:0 0 -4px -4px;display:block;overflow:hidden}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:#0000;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:#ffffff80;margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{box-sizing:content-box;color:#000;background-color:#fff;border-radius:12px;min-height:20px;margin:10px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{visibility:visible;padding:2px 28px 2px 8px}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{box-sizing:border-box;cursor:pointer;background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;outline:none;width:24px;height:24px;display:none;position:absolute;top:0;right:0}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;right:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;left:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:#000000bf;text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{box-sizing:border-box;color:#333;background-color:#ffffffbf;border:2px solid #333;border-top:#333;padding:0 5px;font-size:10px}.maplibregl-popup{pointer-events:none;will-change:transform;display:flex;position:absolute;top:0;left:0}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{z-index:1;border:10px solid #0000;width:0;height:0}.maplibregl-popup-anchor-top .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;align-self:center}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;align-self:center}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:#fff;align-self:center}.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:#fff;border-right:none;align-self:center}.maplibregl-popup-close-button{cursor:pointer;background-color:#0000;border:0;border-radius:0 3px 0 0;position:absolute;top:0;right:0}.maplibregl-popup-close-button:hover{background-color:#0000000d}.maplibregl-popup-content{pointer-events:auto;background:#fff;border-radius:3px;padding:15px 10px;position:relative;box-shadow:0 1px 2px #0000001a}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{will-change:transform;transition:opacity .2s;position:absolute;top:0;left:0}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;width:15px;height:15px}.maplibregl-user-location-dot:before{content:"";animation:2s infinite maplibregl-user-location-dot-pulse;position:absolute}.maplibregl-user-location-dot:after{box-sizing:border-box;content:"";border:2px solid #fff;border-radius:50%;width:19px;height:19px;position:absolute;top:-2px;left:-2px;box-shadow:0 0 3px #00000059}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;width:1px;height:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{opacity:.5;background:#fff;border:2px dotted #202020;width:0;height:0;position:absolute;top:0;left:0}.maplibregl-cooperative-gesture-screen{color:#fff;opacity:0;pointer-events:none;z-index:99999;background:#0006;justify-content:center;align-items:center;padding:1rem;font-size:1.4em;line-height:1.2;transition:opacity 1s 1s;display:flex;position:absolute;top:0;right:0;bottom:0;left:0}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity 50ms}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{z-index:99999;width:100%!important;height:100%!important;position:fixed!important;top:0!important;left:0!important}.dark{--color-surface:#161011;--color-surface-muted:#1e1819;--color-ink:#f0ede6;--color-ink-muted:#b8b2aa;--color-border:#40383a;--color-accent:#5ea5d2;--color-badge:#302a2c}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend/dist/assets/main-p_R7C9Fr.css b/frontend/dist/assets/main-p_R7C9Fr.css new file mode 100644 index 0000000000..2cf89c19f3 --- /dev/null +++ b/frontend/dist/assets/main-p_R7C9Fr.css @@ -0,0 +1 @@ +.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-scale{background-color:#ffffffbf;border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px #0000001a;padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px #00000059;box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:#0006;color:#fff;display:flex;font-size:1.4em;top:0;right:0;bottom:0;left:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-600:oklch(57.7% .245 27.325);--color-red-950:oklch(25.8% .092 26.042);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-lg:64rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#efebe4;--color-surface-muted:#f6f2ec;--color-ink:#1a1a1a;--color-ink-muted:#64686c;--color-border:#cdc8c0;--color-accent:#0a5a82;--color-badge:#e6e4dc;--color-quality-excellent:#1f6fb2;--color-quality-good:#2b8a3e;--color-quality-sufficient:#b08900;--color-quality-poor:#c0392b;--color-quality-unknown:#6b7280;--font-spectral:"Spectral",serif;--font-inter:"Inter",system-ui,sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body,#root{height:100%}body{background-color:var(--color-surface);font-family:var(--font-inter);color:var(--color-ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4{font-family:var(--font-spectral);color:var(--color-ink)}a{border-radius:var(--radius-sm);color:var(--color-accent);text-underline-offset:2px}@media (hover:hover){a:hover{text-decoration-line:underline}}a:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){a:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}a:focus-visible{--tw-outline-style:none;outline-style:none}}@layer components{.card{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface-muted);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.badge{align-items:center;gap:calc(var(--spacing)*1);background-color:var(--color-badge);padding-inline:calc(var(--spacing)*2.5);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-ink);border-radius:3.40282e38px;display:inline-flex}.kpi-excellent{color:var(--color-quality-excellent)}.kpi-good{color:var(--color-quality-good)}.kpi-sufficient{color:var(--color-quality-sufficient)}.kpi-poor{color:var(--color-quality-poor)}.kpi-unknown{color:var(--color-quality-unknown)}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-9{height:calc(var(--spacing)*9)}.h-\[260px\]{height:260px}.h-px{height:1px}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-6{width:calc(var(--spacing)*6)}.w-40{width:calc(var(--spacing)*40)}.w-56{width:calc(var(--spacing)*56)}.w-60{width:calc(var(--spacing)*60)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.border-red-600{border-color:var(--color-red-600)}.bg-accent{background-color:var(--color-accent)}.bg-border{background-color:var(--color-border)}.bg-ink\/60{background-color:#1a1a1a99}@supports (color:color-mix(in lab,red,red)){.bg-ink\/60{background-color:color-mix(in oklab,var(--color-ink)60%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-muted{background-color:var(--color-surface-muted)}.p-0{padding:calc(var(--spacing)*0)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.text-center{text-align:center}.text-justify{text-align:justify}.text-left{text-align:left}.font-spectral{font-family:var(--font-spectral)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[-0\.06em\]{--tw-tracking:-.06em;letter-spacing:-.06em}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.text-red-600{color:var(--color-red-600)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/60{--tw-ring-color:#0009}@supports (color:color-mix(in lab,red,red)){.ring-black\/60{--tw-ring-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.ring-white\/85{--tw-ring-color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.ring-white\/85{--tw-ring-color:color-mix(in oklab,var(--color-white)85%,transparent)}}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition\!{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media (hover:hover){.hover\:bg-accent\/90:hover{background-color:#0a5a82e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/90:hover{background-color:color-mix(in oklab,var(--color-accent)90%,transparent)}}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:bg-surface-muted:hover{background-color:var(--color-surface-muted)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:not-sr-only:focus{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.focus\:absolute:focus{position:absolute}.focus\:top-4:focus{top:calc(var(--spacing)*4)}.focus\:left-4:focus{left:calc(var(--spacing)*4)}.focus\:z-50:focus{z-index:50}.focus\:rounded:focus{border-radius:.25rem}.focus\:bg-accent:focus{background-color:var(--color-accent)}.focus\:px-4:focus{padding-inline:calc(var(--spacing)*4)}.focus\:py-2:focus{padding-block:calc(var(--spacing)*2)}.focus\:text-white:focus{color:var(--color-white)}.focus\:underline:focus{text-decoration-line:underline}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent\/40:focus{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus\:ring-accent\/40:focus{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width:40rem){.sm\:max-w-none{max-width:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:calc(var(--spacing)*4)}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width:96rem){.\32xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme:dark){@media (hover:hover){.dark\:hover\:bg-red-950:hover{background-color:var(--color-red-950)}}}.aspect-square{aspect-ratio:1}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sr-only:focus{width:auto;height:auto;padding:inherit;margin:inherit;clip:auto;white-space:normal;position:static;overflow:visible}}.maplibregl-map{-webkit-tap-highlight-color:#0000;font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;overflow:hidden}.maplibregl-canvas{position:absolute;top:0;left:0}.maplibregl-map:fullscreen{width:100%;height:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;z-index:2;position:absolute}.maplibregl-ctrl-top-left{top:0;left:0}.maplibregl-ctrl-top-right{top:0;right:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px #0000001a}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px buttontext}}.maplibregl-ctrl-group button{box-sizing:border-box;cursor:pointer;background-color:#0000;border:0;outline:none;width:29px;height:29px;padding:0;display:block}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;width:100%;height:100%;display:block}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:#0000}.maplibregl-ctrl-group button+button{border-top:1px solid buttontext}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:#0000000d}}.maplibregl-ctrl button:not(:disabled):active{background-color:#0000000d}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:2s linear infinite maplibregl-spin}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:88px;height:23px;margin:0 0 -4px -4px;display:block;overflow:hidden}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:#0000;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:#ffffff80;margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{box-sizing:content-box;color:#000;background-color:#fff;border-radius:12px;min-height:20px;margin:10px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{visibility:visible;padding:2px 28px 2px 8px}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{box-sizing:border-box;cursor:pointer;background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;outline:none;width:24px;height:24px;display:none;position:absolute;top:0;right:0}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;right:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;left:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:#000000bf;text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{box-sizing:border-box;color:#333;background-color:#ffffffbf;border:2px solid #333;border-top:#333;padding:0 5px;font-size:10px}.maplibregl-popup{pointer-events:none;will-change:transform;display:flex;position:absolute;top:0;left:0}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{z-index:1;border:10px solid #0000;width:0;height:0}.maplibregl-popup-anchor-top .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;align-self:center}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;align-self:center}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:#fff;align-self:center}.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:#fff;border-right:none;align-self:center}.maplibregl-popup-close-button{cursor:pointer;background-color:#0000;border:0;border-radius:0 3px 0 0;position:absolute;top:0;right:0}.maplibregl-popup-close-button:hover{background-color:#0000000d}.maplibregl-popup-content{pointer-events:auto;background:#fff;border-radius:3px;padding:15px 10px;position:relative;box-shadow:0 1px 2px #0000001a}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{will-change:transform;transition:opacity .2s;position:absolute;top:0;left:0}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;width:15px;height:15px}.maplibregl-user-location-dot:before{content:"";animation:2s infinite maplibregl-user-location-dot-pulse;position:absolute}.maplibregl-user-location-dot:after{box-sizing:border-box;content:"";border:2px solid #fff;border-radius:50%;width:19px;height:19px;position:absolute;top:-2px;left:-2px;box-shadow:0 0 3px #00000059}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;width:1px;height:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{opacity:.5;background:#fff;border:2px dotted #202020;width:0;height:0;position:absolute;top:0;left:0}.maplibregl-cooperative-gesture-screen{color:#fff;opacity:0;pointer-events:none;z-index:99999;background:#0006;justify-content:center;align-items:center;padding:1rem;font-size:1.4em;line-height:1.2;transition:opacity 1s 1s;display:flex;position:absolute;top:0;right:0;bottom:0;left:0}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity 50ms}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{z-index:99999;width:100%!important;height:100%!important;position:fixed!important;top:0!important;left:0!important}.dark{--color-surface:#161011;--color-surface-muted:#1e1819;--color-ink:#f0ede6;--color-ink-muted:#b8b2aa;--color-border:#40383a;--color-accent:#5ea5d2;--color-badge:#302a2c}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend/dist/assets/index-Df7HsKlx.js b/frontend/dist/assets/main-voNoOY85.js similarity index 50% rename from frontend/dist/assets/index-Df7HsKlx.js rename to frontend/dist/assets/main-voNoOY85.js index 011113ac33..037bdc434f 100644 --- a/frontend/dist/assets/index-Df7HsKlx.js +++ b/frontend/dist/assets/main-voNoOY85.js @@ -1,4 +1,4 @@ -var mw=Object.defineProperty;var Kv=n=>{throw TypeError(n)};var gw=(n,o,u)=>o in n?mw(n,o,{enumerable:!0,configurable:!0,writable:!0,value:u}):n[o]=u;var Xv=(n,o,u)=>gw(n,typeof o!="symbol"?o+"":o,u),Ty=(n,o,u)=>o.has(n)||Kv("Cannot "+u);var _e=(n,o,u)=>(Ty(n,o,"read from private field"),u?u.call(n):o.get(n)),Wt=(n,o,u)=>o.has(n)?Kv("Cannot add the same private member more than once"):o instanceof WeakSet?o.add(n):o.set(n,u),Mt=(n,o,u,m)=>(Ty(n,o,"write to private field"),m?m.call(n,u):o.set(n,u),u),wr=(n,o,u)=>(Ty(n,o,"access private method"),u);var ag=(n,o,u,m)=>({set _(b){Mt(n,o,b,u)},get _(){return _e(n,o,m)}});(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const b of document.querySelectorAll('link[rel="modulepreload"]'))m(b);new MutationObserver(b=>{for(const P of b)if(P.type==="childList")for(const w of P.addedNodes)w.tagName==="LINK"&&w.rel==="modulepreload"&&m(w)}).observe(document,{childList:!0,subtree:!0});function u(b){const P={};return b.integrity&&(P.integrity=b.integrity),b.referrerPolicy&&(P.referrerPolicy=b.referrerPolicy),b.crossOrigin==="use-credentials"?P.credentials="include":b.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function m(b){if(b.ep)return;b.ep=!0;const P=u(b);fetch(b.href,P)}})();function k_(n){return n&&n.__esModule&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n}var Py={exports:{}},Bp={},Cy={exports:{}},Gr={};/** +var yw=Object.defineProperty;var Xv=r=>{throw TypeError(r)};var _w=(r,o,u)=>o in r?yw(r,o,{enumerable:!0,configurable:!0,writable:!0,value:u}):r[o]=u;var Yv=(r,o,u)=>_w(r,typeof o!="symbol"?o+"":o,u),Cy=(r,o,u)=>o.has(r)||Xv("Cannot "+u);var ye=(r,o,u)=>(Cy(r,o,"read from private field"),u?u.call(r):o.get(r)),Wt=(r,o,u)=>o.has(r)?Xv("Cannot add the same private member more than once"):o instanceof WeakSet?o.add(r):o.set(r,u),It=(r,o,u,m)=>(Cy(r,o,"write to private field"),m?m.call(r,u):o.set(r,u),u),wr=(r,o,u)=>(Cy(r,o,"access private method"),u);var lg=(r,o,u,m)=>({set _(b){It(r,o,b,u)},get _(){return ye(r,o,m)}});(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const b of document.querySelectorAll('link[rel="modulepreload"]'))m(b);new MutationObserver(b=>{for(const P of b)if(P.type==="childList")for(const w of P.addedNodes)w.tagName==="LINK"&&w.rel==="modulepreload"&&m(w)}).observe(document,{childList:!0,subtree:!0});function u(b){const P={};return b.integrity&&(P.integrity=b.integrity),b.referrerPolicy&&(P.referrerPolicy=b.referrerPolicy),b.crossOrigin==="use-credentials"?P.credentials="include":b.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function m(b){if(b.ep)return;b.ep=!0;const P=u(b);fetch(b.href,P)}})();function M_(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var ky={exports:{}},$p={},Ey={exports:{}},Gr={};/** * @license React * react.production.min.js * @@ -6,7 +6,7 @@ var mw=Object.defineProperty;var Kv=n=>{throw TypeError(n)};var gw=(n,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Yv;function yw(){if(Yv)return Gr;Yv=1;var n=Symbol.for("react.element"),o=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),m=Symbol.for("react.strict_mode"),b=Symbol.for("react.profiler"),P=Symbol.for("react.provider"),w=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),L=Symbol.for("react.suspense"),B=Symbol.for("react.memo"),V=Symbol.for("react.lazy"),G=Symbol.iterator;function te(Me){return Me===null||typeof Me!="object"?null:(Me=G&&Me[G]||Me["@@iterator"],typeof Me=="function"?Me:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},Se=Object.assign,ye={};function fe(Me,Ye,jt){this.props=Me,this.context=Ye,this.refs=ye,this.updater=jt||q}fe.prototype.isReactComponent={},fe.prototype.setState=function(Me,Ye){if(typeof Me!="object"&&typeof Me!="function"&&Me!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Me,Ye,"setState")},fe.prototype.forceUpdate=function(Me){this.updater.enqueueForceUpdate(this,Me,"forceUpdate")};function Fe(){}Fe.prototype=fe.prototype;function Ce(Me,Ye,jt){this.props=Me,this.context=Ye,this.refs=ye,this.updater=jt||q}var Ie=Ce.prototype=new Fe;Ie.constructor=Ce,Se(Ie,fe.prototype),Ie.isPureReactComponent=!0;var Le=Array.isArray,Xe=Object.prototype.hasOwnProperty,mt={current:null},yt={key:!0,ref:!0,__self:!0,__source:!0};function Qe(Me,Ye,jt){var tr,fr={},Kt=null,Lr=null;if(Ye!=null)for(tr in Ye.ref!==void 0&&(Lr=Ye.ref),Ye.key!==void 0&&(Kt=""+Ye.key),Ye)Xe.call(Ye,tr)&&!yt.hasOwnProperty(tr)&&(fr[tr]=Ye[tr]);var sr=arguments.length-2;if(sr===1)fr.children=jt;else if(1{throw TypeError(n)};var gw=(n,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Jv;function _w(){if(Jv)return Bp;Jv=1;var n=M_(),o=Symbol.for("react.element"),u=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,b=n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,P={key:!0,ref:!0,__self:!0,__source:!0};function w(l,L,B){var V,G={},te=null,q=null;B!==void 0&&(te=""+B),L.key!==void 0&&(te=""+L.key),L.ref!==void 0&&(q=L.ref);for(V in L)m.call(L,V)&&!P.hasOwnProperty(V)&&(G[V]=L[V]);if(l&&l.defaultProps)for(V in L=l.defaultProps,L)G[V]===void 0&&(G[V]=L[V]);return{$$typeof:o,type:l,key:te,ref:q,props:G,_owner:b.current}}return Bp.Fragment=u,Bp.jsx=w,Bp.jsxs=w,Bp}var ex;function vw(){return ex||(ex=1,Py.exports=_w()),Py.exports}var J=vw(),ee=M_();const hn=k_(ee);var lg={},Ey={exports:{}},Po={},ky={exports:{}},My={};/** + */var ex;function xw(){if(ex)return $p;ex=1;var r=A_(),o=Symbol.for("react.element"),u=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,b=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,P={key:!0,ref:!0,__self:!0,__source:!0};function w(l,L,N){var Z,V={},ee=null,q=null;N!==void 0&&(ee=""+N),L.key!==void 0&&(ee=""+L.key),L.ref!==void 0&&(q=L.ref);for(Z in L)m.call(L,Z)&&!P.hasOwnProperty(Z)&&(V[Z]=L[Z]);if(l&&l.defaultProps)for(Z in L=l.defaultProps,L)V[Z]===void 0&&(V[Z]=L[Z]);return{$$typeof:o,type:l,key:ee,ref:q,props:V,_owner:b.current}}return $p.Fragment=u,$p.jsx=w,$p.jsxs=w,$p}var tx;function bw(){return tx||(tx=1,ky.exports=xw()),ky.exports}var J=bw(),te=A_();const hn=M_(te);var cg={},Iy={exports:{}},Po={},My={exports:{}},Ay={};/** * @license React * scheduler.production.min.js * @@ -22,7 +22,7 @@ var mw=Object.defineProperty;var Kv=n=>{throw TypeError(n)};var gw=(n,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var tx;function xw(){return tx||(tx=1,(function(n){function o(_t,ht){var Ct=_t.length;_t.push(ht);e:for(;0>>1,Ye=_t[Me];if(0>>1;Meb(fr,Ct))Ktb(Lr,fr)?(_t[Me]=Lr,_t[Kt]=Ct,Me=Kt):(_t[Me]=fr,_t[tr]=Ct,Me=tr);else if(Ktb(Lr,Ct))_t[Me]=Lr,_t[Kt]=Ct,Me=Kt;else break e}}return ht}function b(_t,ht){var Ct=_t.sortIndex-ht.sortIndex;return Ct!==0?Ct:_t.id-ht.id}if(typeof performance=="object"&&typeof performance.now=="function"){var P=performance;n.unstable_now=function(){return P.now()}}else{var w=Date,l=w.now();n.unstable_now=function(){return w.now()-l}}var L=[],B=[],V=1,G=null,te=3,q=!1,Se=!1,ye=!1,fe=typeof setTimeout=="function"?setTimeout:null,Fe=typeof clearTimeout=="function"?clearTimeout:null,Ce=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Ie(_t){for(var ht=u(B);ht!==null;){if(ht.callback===null)m(B);else if(ht.startTime<=_t)m(B),ht.sortIndex=ht.expirationTime,o(L,ht);else break;ht=u(B)}}function Le(_t){if(ye=!1,Ie(_t),!Se)if(u(L)!==null)Se=!0,ar(Xe);else{var ht=u(B);ht!==null&&Zt(Le,ht.startTime-_t)}}function Xe(_t,ht){Se=!1,ye&&(ye=!1,Fe(Qe),Qe=-1),q=!0;var Ct=te;try{for(Ie(ht),G=u(L);G!==null&&(!(G.expirationTime>ht)||_t&&!It());){var Me=G.callback;if(typeof Me=="function"){G.callback=null,te=G.priorityLevel;var Ye=Me(G.expirationTime<=ht);ht=n.unstable_now(),typeof Ye=="function"?G.callback=Ye:G===u(L)&&m(L),Ie(ht)}else m(L);G=u(L)}if(G!==null)var jt=!0;else{var tr=u(B);tr!==null&&Zt(Le,tr.startTime-ht),jt=!1}return jt}finally{G=null,te=Ct,q=!1}}var mt=!1,yt=null,Qe=-1,it=5,at=-1;function It(){return!(n.unstable_now()-at_t||125<_t?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):it=0<_t?Math.floor(1e3/_t):5},n.unstable_getCurrentPriorityLevel=function(){return te},n.unstable_getFirstCallbackNode=function(){return u(L)},n.unstable_next=function(_t){switch(te){case 1:case 2:case 3:var ht=3;break;default:ht=te}var Ct=te;te=ht;try{return _t()}finally{te=Ct}},n.unstable_pauseExecution=function(){},n.unstable_requestPaint=function(){},n.unstable_runWithPriority=function(_t,ht){switch(_t){case 1:case 2:case 3:case 4:case 5:break;default:_t=3}var Ct=te;te=_t;try{return ht()}finally{te=Ct}},n.unstable_scheduleCallback=function(_t,ht,Ct){var Me=n.unstable_now();switch(typeof Ct=="object"&&Ct!==null?(Ct=Ct.delay,Ct=typeof Ct=="number"&&0Me?(_t.sortIndex=Ct,o(B,_t),u(L)===null&&_t===u(B)&&(ye?(Fe(Qe),Qe=-1):ye=!0,Zt(Le,Ct-Me))):(_t.sortIndex=Ye,o(L,_t),Se||q||(Se=!0,ar(Xe))),_t},n.unstable_shouldYield=It,n.unstable_wrapCallback=function(_t){var ht=te;return function(){var Ct=te;te=ht;try{return _t.apply(this,arguments)}finally{te=Ct}}}})(My)),My}var rx;function bw(){return rx||(rx=1,ky.exports=xw()),ky.exports}/** + */var rx;function ww(){return rx||(rx=1,(function(r){function o(_t,ft){var Ct=_t.length;_t.push(ft);e:for(;0>>1,Qe=_t[Ie];if(0>>1;Ieb(fr,Ct))Ktb(Lr,fr)?(_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt):(_t[Ie]=fr,_t[tr]=Ct,Ie=tr);else if(Ktb(Lr,Ct))_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt;else break e}}return ft}function b(_t,ft){var Ct=_t.sortIndex-ft.sortIndex;return Ct!==0?Ct:_t.id-ft.id}if(typeof performance=="object"&&typeof performance.now=="function"){var P=performance;r.unstable_now=function(){return P.now()}}else{var w=Date,l=w.now();r.unstable_now=function(){return w.now()-l}}var L=[],N=[],Z=1,V=null,ee=3,q=!1,be=!1,pe=!1,Pe=typeof setTimeout=="function"?setTimeout:null,ze=typeof clearTimeout=="function"?clearTimeout:null,Re=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Te(_t){for(var ft=u(N);ft!==null;){if(ft.callback===null)m(N);else if(ft.startTime<=_t)m(N),ft.sortIndex=ft.expirationTime,o(L,ft);else break;ft=u(N)}}function Fe(_t){if(pe=!1,Te(_t),!be)if(u(L)!==null)be=!0,ar(Ye);else{var ft=u(N);ft!==null&&Zt(Fe,ft.startTime-_t)}}function Ye(_t,ft){be=!1,pe&&(pe=!1,ze(dt),dt=-1),q=!0;var Ct=ee;try{for(Te(ft),V=u(L);V!==null&&(!(V.expirationTime>ft)||_t&&!Rt());){var Ie=V.callback;if(typeof Ie=="function"){V.callback=null,ee=V.priorityLevel;var Qe=Ie(V.expirationTime<=ft);ft=r.unstable_now(),typeof Qe=="function"?V.callback=Qe:V===u(L)&&m(L),Te(ft)}else m(L);V=u(L)}if(V!==null)var jt=!0;else{var tr=u(N);tr!==null&&Zt(Fe,tr.startTime-ft),jt=!1}return jt}finally{V=null,ee=Ct,q=!1}}var at=!1,ct=null,dt=-1,ot=5,Xe=-1;function Rt(){return!(r.unstable_now()-Xe_t||125<_t?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ot=0<_t?Math.floor(1e3/_t):5},r.unstable_getCurrentPriorityLevel=function(){return ee},r.unstable_getFirstCallbackNode=function(){return u(L)},r.unstable_next=function(_t){switch(ee){case 1:case 2:case 3:var ft=3;break;default:ft=ee}var Ct=ee;ee=ft;try{return _t()}finally{ee=Ct}},r.unstable_pauseExecution=function(){},r.unstable_requestPaint=function(){},r.unstable_runWithPriority=function(_t,ft){switch(_t){case 1:case 2:case 3:case 4:case 5:break;default:_t=3}var Ct=ee;ee=_t;try{return ft()}finally{ee=Ct}},r.unstable_scheduleCallback=function(_t,ft,Ct){var Ie=r.unstable_now();switch(typeof Ct=="object"&&Ct!==null?(Ct=Ct.delay,Ct=typeof Ct=="number"&&0Ie?(_t.sortIndex=Ct,o(N,_t),u(L)===null&&_t===u(N)&&(pe?(ze(dt),dt=-1):pe=!0,Zt(Fe,Ct-Ie))):(_t.sortIndex=Qe,o(L,_t),be||q||(be=!0,ar(Ye))),_t},r.unstable_shouldYield=Rt,r.unstable_wrapCallback=function(_t){var ft=ee;return function(){var Ct=ee;ee=ft;try{return _t.apply(this,arguments)}finally{ee=Ct}}}})(Ay)),Ay}var nx;function Sw(){return nx||(nx=1,My.exports=ww()),My.exports}/** * @license React * react-dom.production.min.js * @@ -30,14 +30,14 @@ var mw=Object.defineProperty;var Kv=n=>{throw TypeError(n)};var gw=(n,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var nx;function ww(){if(nx)return Po;nx=1;var n=M_(),o=bw();function u(a){for(var d="https://reactjs.org/docs/error-decoder.html?invariant="+a,x=1;x"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),L=Object.prototype.hasOwnProperty,B=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,V={},G={};function te(a){return L.call(G,a)?!0:L.call(V,a)?!1:B.test(a)?G[a]=!0:(V[a]=!0,!1)}function q(a,d,x,C){if(x!==null&&x.type===0)return!1;switch(typeof d){case"function":case"symbol":return!0;case"boolean":return C?!1:x!==null?!x.acceptsBooleans:(a=a.toLowerCase().slice(0,5),a!=="data-"&&a!=="aria-");default:return!1}}function Se(a,d,x,C){if(d===null||typeof d>"u"||q(a,d,x,C))return!0;if(C)return!1;if(x!==null)switch(x.type){case 3:return!d;case 4:return d===!1;case 5:return isNaN(d);case 6:return isNaN(d)||1>d}return!1}function ye(a,d,x,C,z,O,Q){this.acceptsBooleans=d===2||d===3||d===4,this.attributeName=C,this.attributeNamespace=z,this.mustUseProperty=x,this.propertyName=a,this.type=d,this.sanitizeURL=O,this.removeEmptyString=Q}var fe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){fe[a]=new ye(a,0,!1,a,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var d=a[0];fe[d]=new ye(d,1,!1,a[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(a){fe[a]=new ye(a,2,!1,a.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){fe[a]=new ye(a,2,!1,a,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){fe[a]=new ye(a,3,!1,a.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(a){fe[a]=new ye(a,3,!0,a,null,!1,!1)}),["capture","download"].forEach(function(a){fe[a]=new ye(a,4,!1,a,null,!1,!1)}),["cols","rows","size","span"].forEach(function(a){fe[a]=new ye(a,6,!1,a,null,!1,!1)}),["rowSpan","start"].forEach(function(a){fe[a]=new ye(a,5,!1,a.toLowerCase(),null,!1,!1)});var Fe=/[\-:]([a-z])/g;function Ce(a){return a[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var d=a.replace(Fe,Ce);fe[d]=new ye(d,1,!1,a,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var d=a.replace(Fe,Ce);fe[d]=new ye(d,1,!1,a,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(a){var d=a.replace(Fe,Ce);fe[d]=new ye(d,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(a){fe[a]=new ye(a,1,!1,a.toLowerCase(),null,!1,!1)}),fe.xlinkHref=new ye("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(a){fe[a]=new ye(a,1,!1,a.toLowerCase(),null,!0,!0)});function Ie(a,d,x,C){var z=fe.hasOwnProperty(d)?fe[d]:null;(z!==null?z.type!==0:C||!(2"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),L=Object.prototype.hasOwnProperty,N=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Z={},V={};function ee(a){return L.call(V,a)?!0:L.call(Z,a)?!1:N.test(a)?V[a]=!0:(Z[a]=!0,!1)}function q(a,d,x,C){if(x!==null&&x.type===0)return!1;switch(typeof d){case"function":case"symbol":return!0;case"boolean":return C?!1:x!==null?!x.acceptsBooleans:(a=a.toLowerCase().slice(0,5),a!=="data-"&&a!=="aria-");default:return!1}}function be(a,d,x,C){if(d===null||typeof d>"u"||q(a,d,x,C))return!0;if(C)return!1;if(x!==null)switch(x.type){case 3:return!d;case 4:return d===!1;case 5:return isNaN(d);case 6:return isNaN(d)||1>d}return!1}function pe(a,d,x,C,R,O,Q){this.acceptsBooleans=d===2||d===3||d===4,this.attributeName=C,this.attributeNamespace=R,this.mustUseProperty=x,this.propertyName=a,this.type=d,this.sanitizeURL=O,this.removeEmptyString=Q}var Pe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){Pe[a]=new pe(a,0,!1,a,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var d=a[0];Pe[d]=new pe(d,1,!1,a[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(a){Pe[a]=new pe(a,2,!1,a.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){Pe[a]=new pe(a,2,!1,a,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){Pe[a]=new pe(a,3,!1,a.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(a){Pe[a]=new pe(a,3,!0,a,null,!1,!1)}),["capture","download"].forEach(function(a){Pe[a]=new pe(a,4,!1,a,null,!1,!1)}),["cols","rows","size","span"].forEach(function(a){Pe[a]=new pe(a,6,!1,a,null,!1,!1)}),["rowSpan","start"].forEach(function(a){Pe[a]=new pe(a,5,!1,a.toLowerCase(),null,!1,!1)});var ze=/[\-:]([a-z])/g;function Re(a){return a[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(a){Pe[a]=new pe(a,1,!1,a.toLowerCase(),null,!1,!1)}),Pe.xlinkHref=new pe("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(a){Pe[a]=new pe(a,1,!1,a.toLowerCase(),null,!0,!0)});function Te(a,d,x,C){var R=Pe.hasOwnProperty(d)?Pe[d]:null;(R!==null?R.type!==0:C||!(2me||z[Q]!==O[me]){var ke=` -`+z[Q].replace(" at new "," at ");return a.displayName&&ke.includes("")&&(ke=ke.replace("",a.displayName)),ke}while(1<=Q&&0<=me);break}}}finally{jt=!1,Error.prepareStackTrace=x}return(a=a?a.displayName||a.name:"")?Ye(a):""}function fr(a){switch(a.tag){case 5:return Ye(a.type);case 16:return Ye("Lazy");case 13:return Ye("Suspense");case 19:return Ye("SuspenseList");case 0:case 2:case 15:return a=tr(a.type,!1),a;case 11:return a=tr(a.type.render,!1),a;case 1:return a=tr(a.type,!0),a;default:return""}}function Kt(a){if(a==null)return null;if(typeof a=="function")return a.displayName||a.name||null;if(typeof a=="string")return a;switch(a){case yt:return"Fragment";case mt:return"Portal";case it:return"Profiler";case Qe:return"StrictMode";case At:return"Suspense";case Ht:return"SuspenseList"}if(typeof a=="object")switch(a.$$typeof){case It:return(a.displayName||"Context")+".Consumer";case at:return(a._context.displayName||"Context")+".Provider";case Rt:var d=a.render;return a=a.displayName,a||(a=d.displayName||d.name||"",a=a!==""?"ForwardRef("+a+")":"ForwardRef"),a;case Rr:return d=a.displayName||null,d!==null?d:Kt(a.type)||"Memo";case ar:d=a._payload,a=a._init;try{return Kt(a(d))}catch{}}return null}function Lr(a){var d=a.type;switch(a.tag){case 24:return"Cache";case 9:return(d.displayName||"Context")+".Consumer";case 10:return(d._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=d.render,a=a.displayName||a.name||"",d.displayName||(a!==""?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return d;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Kt(d);case 8:return d===Qe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof d=="function")return d.displayName||d.name||null;if(typeof d=="string")return d}return null}function sr(a){switch(typeof a){case"boolean":case"number":case"string":case"undefined":return a;case"object":return a;default:return""}}function yr(a){var d=a.type;return(a=a.nodeName)&&a.toLowerCase()==="input"&&(d==="checkbox"||d==="radio")}function lr(a){var d=yr(a)?"checked":"value",x=Object.getOwnPropertyDescriptor(a.constructor.prototype,d),C=""+a[d];if(!a.hasOwnProperty(d)&&typeof x<"u"&&typeof x.get=="function"&&typeof x.set=="function"){var z=x.get,O=x.set;return Object.defineProperty(a,d,{configurable:!0,get:function(){return z.call(this)},set:function(Q){C=""+Q,O.call(this,Q)}}),Object.defineProperty(a,d,{enumerable:x.enumerable}),{getValue:function(){return C},setValue:function(Q){C=""+Q},stopTracking:function(){a._valueTracker=null,delete a[d]}}}}function ci(a){a._valueTracker||(a._valueTracker=lr(a))}function ln(a){if(!a)return!1;var d=a._valueTracker;if(!d)return!0;var x=d.getValue(),C="";return a&&(C=yr(a)?a.checked?"true":"false":a.value),a=C,a!==x?(d.setValue(a),!0):!1}function Vr(a){if(a=a||(typeof document<"u"?document:void 0),typeof a>"u")return null;try{return a.activeElement||a.body}catch{return a.body}}function Nr(a,d){var x=d.checked;return Ct({},d,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:x??a._wrapperState.initialChecked})}function Cn(a,d){var x=d.defaultValue==null?"":d.defaultValue,C=d.checked!=null?d.checked:d.defaultChecked;x=sr(d.value!=null?d.value:x),a._wrapperState={initialChecked:C,initialValue:x,controlled:d.type==="checkbox"||d.type==="radio"?d.checked!=null:d.value!=null}}function jn(a,d){d=d.checked,d!=null&&Ie(a,"checked",d,!1)}function Xi(a,d){jn(a,d);var x=sr(d.value),C=d.type;if(x!=null)C==="number"?(x===0&&a.value===""||a.value!=x)&&(a.value=""+x):a.value!==""+x&&(a.value=""+x);else if(C==="submit"||C==="reset"){a.removeAttribute("value");return}d.hasOwnProperty("value")?Zn(a,d.type,x):d.hasOwnProperty("defaultValue")&&Zn(a,d.type,sr(d.defaultValue)),d.checked==null&&d.defaultChecked!=null&&(a.defaultChecked=!!d.defaultChecked)}function zi(a,d,x){if(d.hasOwnProperty("value")||d.hasOwnProperty("defaultValue")){var C=d.type;if(!(C!=="submit"&&C!=="reset"||d.value!==void 0&&d.value!==null))return;d=""+a._wrapperState.initialValue,x||d===a.value||(a.value=d),a.defaultValue=d}x=a.name,x!==""&&(a.name=""),a.defaultChecked=!!a._wrapperState.initialChecked,x!==""&&(a.name=x)}function Zn(a,d,x){(d!=="number"||Vr(a.ownerDocument)!==a)&&(x==null?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+x&&(a.defaultValue=""+x))}var xe=Array.isArray;function Be(a,d,x,C){if(a=a.options,d){d={};for(var z=0;z"+d.valueOf().toString()+"",d=Sr.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;d.firstChild;)a.appendChild(d.firstChild)}});function K(a,d){if(d){var x=a.firstChild;if(x&&x===a.lastChild&&x.nodeType===3){x.nodeValue=d;return}}a.textContent=d}var X={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ne=["Webkit","ms","Moz","O"];Object.keys(X).forEach(function(a){ne.forEach(function(d){d=d+a.charAt(0).toUpperCase()+a.substring(1),X[d]=X[a]})});function ue(a,d,x){return d==null||typeof d=="boolean"||d===""?"":x||typeof d!="number"||d===0||X.hasOwnProperty(a)&&X[a]?(""+d).trim():d+"px"}function ve(a,d){a=a.style;for(var x in d)if(d.hasOwnProperty(x)){var C=x.indexOf("--")===0,z=ue(x,d[x],C);x==="float"&&(x="cssFloat"),C?a.setProperty(x,z):a[x]=z}}var Ae=Ct({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Oe(a,d){if(d){if(Ae[a]&&(d.children!=null||d.dangerouslySetInnerHTML!=null))throw Error(u(137,a));if(d.dangerouslySetInnerHTML!=null){if(d.children!=null)throw Error(u(60));if(typeof d.dangerouslySetInnerHTML!="object"||!("__html"in d.dangerouslySetInnerHTML))throw Error(u(61))}if(d.style!=null&&typeof d.style!="object")throw Error(u(62))}}function ze(a,d){if(a.indexOf("-")===-1)return typeof d.is=="string";switch(a){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var qe=null;function rt(a){return a=a.target||a.srcElement||window,a.correspondingUseElement&&(a=a.correspondingUseElement),a.nodeType===3?a.parentNode:a}var Je=null,zt=null,Pe=null;function $t(a){if(a=Vl(a)){if(typeof Je!="function")throw Error(u(280));var d=a.stateNode;d&&(d=Fc(d),Je(a.stateNode,a.type,d))}}function rr(a){zt?Pe?Pe.push(a):Pe=[a]:zt=a}function Dt(){if(zt){var a=zt,d=Pe;if(Pe=zt=null,$t(a),d)for(a=0;a>>=0,a===0?32:31-(Ud(a)/xl|0)|0}var pu=64,mu=4194304;function pc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function bl(a,d){var x=a.pendingLanes;if(x===0)return 0;var C=0,z=a.suspendedLanes,O=a.pingedLanes,Q=x&268435455;if(Q!==0){var me=Q&~z;me!==0?C=pc(me):(O&=Q,O!==0&&(C=pc(O)))}else Q=x&~z,Q!==0?C=pc(Q):O!==0&&(C=pc(O));if(C===0)return 0;if(d!==0&&d!==C&&(d&z)===0&&(z=C&-C,O=d&-d,z>=O||z===16&&(O&4194240)!==0))return d;if((C&4)!==0&&(C|=x&16),d=a.entangledLanes,d!==0)for(a=a.entanglements,d&=C;0x;x++)d.push(a);return d}function ja(a,d,x){a.pendingLanes|=d,d!==536870912&&(a.suspendedLanes=0,a.pingedLanes=0),a=a.eventTimes,d=31-Es(d),a[d]=x}function Yf(a,d){var x=a.pendingLanes&~d;a.pendingLanes=d,a.suspendedLanes=0,a.pingedLanes=0,a.expiredLanes&=d,a.mutableReadLanes&=d,a.entangledLanes&=d,d=a.entanglements;var C=a.eventTimes;for(a=a.expirationTimes;0=Il),bc=" ",kh=!1;function Mh(a,d){switch(a){case"keyup":return Th.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ma(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var ga=!1;function Ih(a,d){switch(a){case"compositionend":return ma(d);case"keypress":return d.which!==32?null:(kh=!0,bc);case"textInput":return a=d.data,a===bc&&kh?null:a;default:return null}}function wc(a,d){if(ga)return a==="compositionend"||!Ph&&Mh(a,d)?(a=Ms(),Na=Cl=Us=null,ga=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:x,offset:d-a};a=C}e:{for(;x;){if(x.nextSibling){x=x.nextSibling;break e}x=x.parentNode}x=void 0}x=Kd(x)}}function Xd(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?Xd(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function Oh(){for(var a=window,d=Vr();d instanceof a.HTMLIFrameElement;){try{var x=typeof d.contentWindow.location.href=="string"}catch{x=!1}if(x)a=d.contentWindow;else break;d=Vr(a.document)}return d}function Ll(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}function Su(a){var d=Oh(),x=a.focusedElem,C=a.selectionRange;if(d!==x&&x&&x.ownerDocument&&Xd(x.ownerDocument.documentElement,x)){if(C!==null&&Ll(x)){if(d=C.start,a=C.end,a===void 0&&(a=d),"selectionStart"in x)x.selectionStart=d,x.selectionEnd=Math.min(a,x.value.length);else if(a=(d=x.ownerDocument||document)&&d.defaultView||window,a.getSelection){a=a.getSelection();var z=x.textContent.length,O=Math.min(C.start,z);C=C.end===void 0?O:Math.min(C.end,z),!a.extend&&O>C&&(z=C,C=O,O=z),z=Fh(x,O);var Q=Fh(x,C);z&&Q&&(a.rangeCount!==1||a.anchorNode!==z.node||a.anchorOffset!==z.offset||a.focusNode!==Q.node||a.focusOffset!==Q.offset)&&(d=d.createRange(),d.setStart(z.node,z.offset),a.removeAllRanges(),O>C?(a.addRange(d),a.extend(Q.node,Q.offset)):(d.setEnd(Q.node,Q.offset),a.addRange(d)))}}for(d=[],a=x;a=a.parentNode;)a.nodeType===1&&d.push({element:a,left:a.scrollLeft,top:a.scrollTop});for(typeof x.focus=="function"&&x.focus(),x=0;x=document.documentMode,ya=null,Lo=null,Fo=null,_a=!1;function ps(a,d,x){var C=x.window===x?x.document:x.nodeType===9?x:x.ownerDocument;_a||ya==null||ya!==Vr(C)||(C=ya,"selectionStart"in C&&Ll(C)?C={start:C.selectionStart,end:C.selectionEnd}:(C=(C.ownerDocument&&C.ownerDocument.defaultView||window).getSelection(),C={anchorNode:C.anchorNode,anchorOffset:C.anchorOffset,focusNode:C.focusNode,focusOffset:C.focusOffset}),Fo&&Tc(Fo,C)||(Fo=C,C=Ac(Lo,"onSelect"),0Ka||(a.current=Mu[Ka],Mu[Ka]=null,Ka--)}function Or(a,d){Ka++,Mu[Ka]=a.current,a.current=d}var gs={},ii=ni(gs),Ri=ni(!1),wa=gs;function Hs(a,d){var x=a.type.contextTypes;if(!x)return gs;var C=a.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===d)return C.__reactInternalMemoizedMaskedChildContext;var z={},O;for(O in x)z[O]=d[O];return C&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=d,a.__reactInternalMemoizedMaskedChildContext=z),z}function wi(a){return a=a.childContextTypes,a!=null}function Zo(){rn(Ri),rn(ii)}function Uh(a,d,x){if(ii.current!==gs)throw Error(u(168));Or(ii,d),Or(Ri,x)}function Di(a,d,x){var C=a.stateNode;if(d=d.childContextTypes,typeof C.getChildContext!="function")return x;C=C.getChildContext();for(var z in C)if(!(z in d))throw Error(u(108,Lr(a)||"Unknown",z));return Ct({},x,C)}function Yi(a){return a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||gs,wa=ii.current,Or(ii,a),Or(Ri,Ri.current),!0}function Xa(a,d,x){var C=a.stateNode;if(!C)throw Error(u(169));x?(a=Di(a,d,wa),C.__reactInternalMemoizedMergedChildContext=a,rn(Ri),rn(ii),Or(ii,a)):rn(Ri),Or(Ri,x)}var Hn=null,Go=!1,Vh=!1;function Zl(a){Hn===null?Hn=[a]:Hn.push(a)}function ap(a){Go=!0,Zl(a)}function Sa(){if(!Vh&&Hn!==null){Vh=!0;var a=0,d=qr;try{var x=Hn;for(qr=1;a>=Q,z-=Q,es=1<<32-Es(d)+z|x<vr?(gi=dr,dr=null):gi=dr.sibling;var Hr=ot(Ve,dr,Ge[vr],bt);if(Hr===null){dr===null&&(dr=gi);break}a&&dr&&Hr.alternate===null&&d(Ve,dr),De=O(Hr,De,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr,dr=gi}if(vr===Ge.length)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;vrvr?(gi=dr,dr=null):gi=dr.sibling;var ol=ot(Ve,dr,Hr.value,bt);if(ol===null){dr===null&&(dr=gi);break}a&&dr&&ol.alternate===null&&d(Ve,dr),De=O(ol,De,vr),hr===null?Gt=ol:hr.sibling=ol,hr=ol,dr=gi}if(Hr.done)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;!Hr.done;vr++,Hr=Ge.next())Hr=lt(Ve,Hr.value,bt),Hr!==null&&(De=O(Hr,De,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return vn&&vo(Ve,vr),Gt}for(dr=C(Ve,dr);!Hr.done;vr++,Hr=Ge.next())Hr=Ot(dr,Ve,vr,Hr.value,bt),Hr!==null&&(a&&Hr.alternate!==null&&dr.delete(Hr.key===null?vr:Hr.key),De=O(Hr,De,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return a&&dr.forEach(function(cy){return d(Ve,cy)}),vn&&vo(Ve,vr),Gt}function Mn(Ve,De,Ge,bt){if(typeof Ge=="object"&&Ge!==null&&Ge.type===yt&&Ge.key===null&&(Ge=Ge.props.children),typeof Ge=="object"&&Ge!==null){switch(Ge.$$typeof){case Xe:e:{for(var Gt=Ge.key,hr=De;hr!==null;){if(hr.key===Gt){if(Gt=Ge.type,Gt===yt){if(hr.tag===7){x(Ve,hr.sibling),De=z(hr,Ge.props.children),De.return=Ve,Ve=De;break e}}else if(hr.elementType===Gt||typeof Gt=="object"&&Gt!==null&&Gt.$$typeof===ar&&zu(Gt)===hr.type){x(Ve,hr.sibling),De=z(hr,Ge.props),De.ref=ql(Ve,hr,Ge),De.return=Ve,Ve=De;break e}x(Ve,hr);break}else d(Ve,hr);hr=hr.sibling}Ge.type===yt?(De=Xc(Ge.props.children,Ve.mode,bt,Ge.key),De.return=Ve,Ve=De):(bt=hf(Ge.type,Ge.key,Ge.props,null,Ve.mode,bt),bt.ref=ql(Ve,De,Ge),bt.return=Ve,Ve=bt)}return Q(Ve);case mt:e:{for(hr=Ge.key;De!==null;){if(De.key===hr)if(De.tag===4&&De.stateNode.containerInfo===Ge.containerInfo&&De.stateNode.implementation===Ge.implementation){x(Ve,De.sibling),De=z(De,Ge.children||[]),De.return=Ve,Ve=De;break e}else{x(Ve,De);break}else d(Ve,De);De=De.sibling}De=Rp(Ge,Ve.mode,bt),De.return=Ve,Ve=De}return Q(Ve);case ar:return hr=Ge._init,Mn(Ve,De,hr(Ge._payload),bt)}if(xe(Ge))return Vt(Ve,De,Ge,bt);if(ht(Ge))return Qt(Ve,De,Ge,bt);Wl(Ve,Ge)}return typeof Ge=="string"&&Ge!==""||typeof Ge=="number"?(Ge=""+Ge,De!==null&&De.tag===6?(x(Ve,De.sibling),De=z(De,Ge),De.return=Ve,Ve=De):(x(Ve,De),De=zp(Ge,Ve.mode,bt),De.return=Ve,Ve=De),Q(Ve)):x(Ve,De)}return Mn}var Oi=Hh(!0),Nc=Hh(!1),Lt=ni(null),Yt=null,Wo=null,xo=null;function Hl(){xo=Wo=Yt=null}function fi(a){var d=Lt.current;rn(Lt),a._currentValue=d}function Ru(a,d,x){for(;a!==null;){var C=a.alternate;if((a.childLanes&d)!==d?(a.childLanes|=d,C!==null&&(C.childLanes|=d)):C!==null&&(C.childLanes&d)!==d&&(C.childLanes|=d),a===x)break;a=a.return}}function pi(a,d){Yt=a,xo=Wo=null,a=a.dependencies,a!==null&&a.firstContext!==null&&((a.lanes&d)!==0&&(Ds=!0),a.firstContext=null)}function As(a){var d=a._currentValue;if(xo!==a)if(a={context:a,memoizedValue:d,next:null},Wo===null){if(Yt===null)throw Error(u(308));Wo=a,Yt.dependencies={lanes:0,firstContext:a}}else Wo=Wo.next=a;return d}var Ks=null;function Yr(a){Ks===null?Ks=[a]:Ks.push(a)}function wn(a,d,x,C){var z=d.interleaved;return z===null?(x.next=x,Yr(d)):(x.next=z.next,z.next=x),d.interleaved=x,zs(a,C)}function zs(a,d){a.lanes|=d;var x=a.alternate;for(x!==null&&(x.lanes|=d),x=a,a=a.return;a!==null;)a.childLanes|=d,x=a.alternate,x!==null&&(x.childLanes|=d),x=a,a=a.return;return x.tag===3?x.stateNode:null}var vs=!1;function Pa(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Du(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function ts(a,d){return{eventTime:a,lane:d,tag:0,payload:null,callback:null,next:null}}function rs(a,d,x){var C=a.updateQueue;if(C===null)return null;if(C=C.shared,(Br&2)!==0){var z=C.pending;return z===null?d.next=d:(d.next=z.next,z.next=d),C.pending=d,zs(a,x)}return z=C.interleaved,z===null?(d.next=d,Yr(C)):(d.next=z.next,z.next=d),C.interleaved=d,zs(a,x)}function Ca(a,d,x){if(d=d.updateQueue,d!==null&&(d=d.shared,(x&4194240)!==0)){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ca(a,x)}}function Bc(a,d){var x=a.updateQueue,C=a.alternate;if(C!==null&&(C=C.updateQueue,x===C)){var z=null,O=null;if(x=x.firstBaseUpdate,x!==null){do{var Q={eventTime:x.eventTime,lane:x.lane,tag:x.tag,payload:x.payload,callback:x.callback,next:null};O===null?z=O=Q:O=O.next=Q,x=x.next}while(x!==null);O===null?z=O=d:O=O.next=d}else z=O=d;x={baseState:C.baseState,firstBaseUpdate:z,lastBaseUpdate:O,shared:C.shared,effects:C.effects},a.updateQueue=x;return}a=x.lastBaseUpdate,a===null?x.firstBaseUpdate=d:a.next=d,x.lastBaseUpdate=d}function bo(a,d,x,C){var z=a.updateQueue;vs=!1;var O=z.firstBaseUpdate,Q=z.lastBaseUpdate,me=z.shared.pending;if(me!==null){z.shared.pending=null;var ke=me,He=ke.next;ke.next=null,Q===null?O=He:Q.next=He,Q=ke;var ut=a.alternate;ut!==null&&(ut=ut.updateQueue,me=ut.lastBaseUpdate,me!==Q&&(me===null?ut.firstBaseUpdate=He:me.next=He,ut.lastBaseUpdate=ke))}if(O!==null){var lt=z.baseState;Q=0,ut=He=ke=null,me=O;do{var ot=me.lane,Ot=me.eventTime;if((C&ot)===ot){ut!==null&&(ut=ut.next={eventTime:Ot,lane:0,tag:me.tag,payload:me.payload,callback:me.callback,next:null});e:{var Vt=a,Qt=me;switch(ot=d,Ot=x,Qt.tag){case 1:if(Vt=Qt.payload,typeof Vt=="function"){lt=Vt.call(Ot,lt,ot);break e}lt=Vt;break e;case 3:Vt.flags=Vt.flags&-65537|128;case 0:if(Vt=Qt.payload,ot=typeof Vt=="function"?Vt.call(Ot,lt,ot):Vt,ot==null)break e;lt=Ct({},lt,ot);break e;case 2:vs=!0}}me.callback!==null&&me.lane!==0&&(a.flags|=64,ot=z.effects,ot===null?z.effects=[me]:ot.push(me))}else Ot={eventTime:Ot,lane:ot,tag:me.tag,payload:me.payload,callback:me.callback,next:null},ut===null?(He=ut=Ot,ke=lt):ut=ut.next=Ot,Q|=ot;if(me=me.next,me===null){if(me=z.shared.pending,me===null)break;ot=me,me=ot.next,ot.next=null,z.lastBaseUpdate=ot,z.shared.pending=null}}while(!0);if(ut===null&&(ke=lt),z.baseState=ke,z.firstBaseUpdate=He,z.lastBaseUpdate=ut,d=z.shared.interleaved,d!==null){z=d;do Q|=z.lane,z=z.next;while(z!==d)}else O===null&&(z.shared.lanes=0);Yl|=Q,a.lanes=Q,a.memoizedState=lt}}function Lu(a,d,x){if(a=d.effects,d.effects=null,a!==null)for(d=0;dx?x:4,a(!0);var C=N.transition;N.transition={};try{a(!1),d()}finally{qr=x,N.transition=C}}function ji(){return $e().memoizedState}function Xs(a,d,x){var C=Ia(a);if(x={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null},Ko(a))Fn(d,x);else if(x=wn(a,d,x,C),x!==null){var z=mi();Jo(x,a,C,z),Nn(x,d,C)}}function Ni(a,d,x){var C=Ia(a),z={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null};if(Ko(a))Fn(d,z);else{var O=a.alternate;if(a.lanes===0&&(O===null||O.lanes===0)&&(O=d.lastRenderedReducer,O!==null))try{var Q=d.lastRenderedState,me=O(Q,x);if(z.hasEagerState=!0,z.eagerState=me,qs(me,Q)){var ke=d.interleaved;ke===null?(z.next=z,Yr(d)):(z.next=ke.next,ke.next=z),d.interleaved=z;return}}catch{}finally{}x=wn(a,d,z,C),x!==null&&(z=mi(),Jo(x,a,C,z),Nn(x,d,C))}}function Ko(a){var d=a.alternate;return a===$||d!==null&&d===$}function Fn(a,d){oe=se=!0;var x=a.pending;x===null?d.next=d:(d.next=x.next,x.next=d),a.pending=d}function Nn(a,d,x){if((x&4194240)!==0){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ca(a,x)}}var Bi={readContext:As,useCallback:le,useContext:le,useEffect:le,useImperativeHandle:le,useInsertionEffect:le,useLayoutEffect:le,useMemo:le,useReducer:le,useRef:le,useState:le,useDebugValue:le,useDeferredValue:le,useTransition:le,useMutableSource:le,useSyncExternalStore:le,useId:le,unstable_isNewReconciler:!1},Bn={readContext:As,useCallback:function(a,d){return Ee().memoizedState=[a,d===void 0?null:d],a},useContext:As,useEffect:Dn,useImperativeHandle:function(a,d,x){return x=x!=null?x.concat([a]):null,mr(4194308,4,Rs.bind(null,d,a),x)},useLayoutEffect:function(a,d){return mr(4194308,4,a,d)},useInsertionEffect:function(a,d){return mr(4,2,a,d)},useMemo:function(a,d){var x=Ee();return d=d===void 0?null:d,a=a(),x.memoizedState=[a,d],a},useReducer:function(a,d,x){var C=Ee();return d=x!==void 0?x(d):d,C.memoizedState=C.baseState=d,a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:d},C.queue=a,a=a.dispatch=Xs.bind(null,$,a),[C.memoizedState,a]},useRef:function(a){var d=Ee();return a={current:a},d.memoizedState=a},useState:Qr,useDebugValue:wo,useDeferredValue:function(a){return Ee().memoizedState=a},useTransition:function(){var a=Qr(!1),d=a[0];return a=tl.bind(null,a[1]),Ee().memoizedState=a,[d,a]},useMutableSource:function(){},useSyncExternalStore:function(a,d,x){var C=$,z=Ee();if(vn){if(x===void 0)throw Error(u(407));x=x()}else{if(x=d(),Yn===null)throw Error(u(349));(j&30)!==0||tt(C,d,x)}z.memoizedState=x;var O={value:x,getSnapshot:d};return z.queue=O,Dn(br.bind(null,C,O,a),[a]),C.flags|=2048,un(9,Ft.bind(null,C,O,x,d),void 0,null),x},useId:function(){var a=Ee(),d=Yn.identifierPrefix;if(vn){var x=Fi,C=es;x=(C&~(1<<32-Es(C)-1)).toString(32)+x,d=":"+d+"R"+x,x=ae++,0me||R[Q]!==O[me]){var Ee=` +`+R[Q].replace(" at new "," at ");return a.displayName&&Ee.includes("")&&(Ee=Ee.replace("",a.displayName)),Ee}while(1<=Q&&0<=me);break}}}finally{jt=!1,Error.prepareStackTrace=x}return(a=a?a.displayName||a.name:"")?Qe(a):""}function fr(a){switch(a.tag){case 5:return Qe(a.type);case 16:return Qe("Lazy");case 13:return Qe("Suspense");case 19:return Qe("SuspenseList");case 0:case 2:case 15:return a=tr(a.type,!1),a;case 11:return a=tr(a.type.render,!1),a;case 1:return a=tr(a.type,!0),a;default:return""}}function Kt(a){if(a==null)return null;if(typeof a=="function")return a.displayName||a.name||null;if(typeof a=="string")return a;switch(a){case ct:return"Fragment";case at:return"Portal";case ot:return"Profiler";case dt:return"StrictMode";case Mt:return"Suspense";case Ht:return"SuspenseList"}if(typeof a=="object")switch(a.$$typeof){case Rt:return(a.displayName||"Context")+".Consumer";case Xe:return(a._context.displayName||"Context")+".Provider";case At:var d=a.render;return a=a.displayName,a||(a=d.displayName||d.name||"",a=a!==""?"ForwardRef("+a+")":"ForwardRef"),a;case zr:return d=a.displayName||null,d!==null?d:Kt(a.type)||"Memo";case ar:d=a._payload,a=a._init;try{return Kt(a(d))}catch{}}return null}function Lr(a){var d=a.type;switch(a.tag){case 24:return"Cache";case 9:return(d.displayName||"Context")+".Consumer";case 10:return(d._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=d.render,a=a.displayName||a.name||"",d.displayName||(a!==""?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return d;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Kt(d);case 8:return d===dt?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof d=="function")return d.displayName||d.name||null;if(typeof d=="string")return d}return null}function sr(a){switch(typeof a){case"boolean":case"number":case"string":case"undefined":return a;case"object":return a;default:return""}}function yr(a){var d=a.type;return(a=a.nodeName)&&a.toLowerCase()==="input"&&(d==="checkbox"||d==="radio")}function lr(a){var d=yr(a)?"checked":"value",x=Object.getOwnPropertyDescriptor(a.constructor.prototype,d),C=""+a[d];if(!a.hasOwnProperty(d)&&typeof x<"u"&&typeof x.get=="function"&&typeof x.set=="function"){var R=x.get,O=x.set;return Object.defineProperty(a,d,{configurable:!0,get:function(){return R.call(this)},set:function(Q){C=""+Q,O.call(this,Q)}}),Object.defineProperty(a,d,{enumerable:x.enumerable}),{getValue:function(){return C},setValue:function(Q){C=""+Q},stopTracking:function(){a._valueTracker=null,delete a[d]}}}}function ci(a){a._valueTracker||(a._valueTracker=lr(a))}function ln(a){if(!a)return!1;var d=a._valueTracker;if(!d)return!0;var x=d.getValue(),C="";return a&&(C=yr(a)?a.checked?"true":"false":a.value),a=C,a!==x?(d.setValue(a),!0):!1}function Vr(a){if(a=a||(typeof document<"u"?document:void 0),typeof a>"u")return null;try{return a.activeElement||a.body}catch{return a.body}}function Nr(a,d){var x=d.checked;return Ct({},d,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:x??a._wrapperState.initialChecked})}function Cn(a,d){var x=d.defaultValue==null?"":d.defaultValue,C=d.checked!=null?d.checked:d.defaultChecked;x=sr(d.value!=null?d.value:x),a._wrapperState={initialChecked:C,initialValue:x,controlled:d.type==="checkbox"||d.type==="radio"?d.checked!=null:d.value!=null}}function jn(a,d){d=d.checked,d!=null&&Te(a,"checked",d,!1)}function Yi(a,d){jn(a,d);var x=sr(d.value),C=d.type;if(x!=null)C==="number"?(x===0&&a.value===""||a.value!=x)&&(a.value=""+x):a.value!==""+x&&(a.value=""+x);else if(C==="submit"||C==="reset"){a.removeAttribute("value");return}d.hasOwnProperty("value")?Zn(a,d.type,x):d.hasOwnProperty("defaultValue")&&Zn(a,d.type,sr(d.defaultValue)),d.checked==null&&d.defaultChecked!=null&&(a.defaultChecked=!!d.defaultChecked)}function Ri(a,d,x){if(d.hasOwnProperty("value")||d.hasOwnProperty("defaultValue")){var C=d.type;if(!(C!=="submit"&&C!=="reset"||d.value!==void 0&&d.value!==null))return;d=""+a._wrapperState.initialValue,x||d===a.value||(a.value=d),a.defaultValue=d}x=a.name,x!==""&&(a.name=""),a.defaultChecked=!!a._wrapperState.initialChecked,x!==""&&(a.name=x)}function Zn(a,d,x){(d!=="number"||Vr(a.ownerDocument)!==a)&&(x==null?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+x&&(a.defaultValue=""+x))}var ve=Array.isArray;function Be(a,d,x,C){if(a=a.options,d){d={};for(var R=0;R"+d.valueOf().toString()+"",d=Sr.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;d.firstChild;)a.appendChild(d.firstChild)}});function K(a,d){if(d){var x=a.firstChild;if(x&&x===a.lastChild&&x.nodeType===3){x.nodeValue=d;return}}a.textContent=d}var X={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ne=["Webkit","ms","Moz","O"];Object.keys(X).forEach(function(a){ne.forEach(function(d){d=d+a.charAt(0).toUpperCase()+a.substring(1),X[d]=X[a]})});function ue(a,d,x){return d==null||typeof d=="boolean"||d===""?"":x||typeof d!="number"||d===0||X.hasOwnProperty(a)&&X[a]?(""+d).trim():d+"px"}function _e(a,d){a=a.style;for(var x in d)if(d.hasOwnProperty(x)){var C=x.indexOf("--")===0,R=ue(x,d[x],C);x==="float"&&(x="cssFloat"),C?a.setProperty(x,R):a[x]=R}}var Me=Ct({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Oe(a,d){if(d){if(Me[a]&&(d.children!=null||d.dangerouslySetInnerHTML!=null))throw Error(u(137,a));if(d.dangerouslySetInnerHTML!=null){if(d.children!=null)throw Error(u(60));if(typeof d.dangerouslySetInnerHTML!="object"||!("__html"in d.dangerouslySetInnerHTML))throw Error(u(61))}if(d.style!=null&&typeof d.style!="object")throw Error(u(62))}}function Ae(a,d){if(a.indexOf("-")===-1)return typeof d.is=="string";switch(a){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var qe=null;function rt(a){return a=a.target||a.srcElement||window,a.correspondingUseElement&&(a=a.correspondingUseElement),a.nodeType===3?a.parentNode:a}var Je=null,zt=null,Ce=null;function $t(a){if(a=Zl(a)){if(typeof Je!="function")throw Error(u(280));var d=a.stateNode;d&&(d=Oc(d),Je(a.stateNode,a.type,d))}}function rr(a){zt?Ce?Ce.push(a):Ce=[a]:zt=a}function Dt(){if(zt){var a=zt,d=Ce;if(Ce=zt=null,$t(a),d)for(a=0;a>>=0,a===0?32:31-(Vd(a)/bl|0)|0}var gu=64,yu=4194304;function mc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function wl(a,d){var x=a.pendingLanes;if(x===0)return 0;var C=0,R=a.suspendedLanes,O=a.pingedLanes,Q=x&268435455;if(Q!==0){var me=Q&~R;me!==0?C=mc(me):(O&=Q,O!==0&&(C=mc(O)))}else Q=x&~R,Q!==0?C=mc(Q):O!==0&&(C=mc(O));if(C===0)return 0;if(d!==0&&d!==C&&(d&R)===0&&(R=C&-C,O=d&-d,R>=O||R===16&&(O&4194240)!==0))return d;if((C&4)!==0&&(C|=x&16),d=a.entangledLanes,d!==0)for(a=a.entanglements,d&=C;0x;x++)d.push(a);return d}function Na(a,d,x){a.pendingLanes|=d,d!==536870912&&(a.suspendedLanes=0,a.pingedLanes=0),a=a.eventTimes,d=31-ks(d),a[d]=x}function Qf(a,d){var x=a.pendingLanes&~d;a.pendingLanes=d,a.suspendedLanes=0,a.pingedLanes=0,a.expiredLanes&=d,a.mutableReadLanes&=d,a.entangledLanes&=d,d=a.entanglements;var C=a.eventTimes;for(a=a.expirationTimes;0=Al),wc=" ",Mh=!1;function Ah(a,d){switch(a){case"keyup":return Ch.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ga(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var ya=!1;function Rh(a,d){switch(a){case"compositionend":return ga(d);case"keypress":return d.which!==32?null:(Mh=!0,wc);case"textInput":return a=d.data,a===wc&&Mh?null:a;default:return null}}function Sc(a,d){if(ya)return a==="compositionend"||!kh&&Ah(a,d)?(a=Is(),Ba=kl=Us=null,ya=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:x,offset:d-a};a=C}e:{for(;x;){if(x.nextSibling){x=x.nextSibling;break e}x=x.parentNode}x=void 0}x=Xd(x)}}function Yd(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?Yd(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function Nh(){for(var a=window,d=Vr();d instanceof a.HTMLIFrameElement;){try{var x=typeof d.contentWindow.location.href=="string"}catch{x=!1}if(x)a=d.contentWindow;else break;d=Vr(a.document)}return d}function Fl(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}function Pu(a){var d=Nh(),x=a.focusedElem,C=a.selectionRange;if(d!==x&&x&&x.ownerDocument&&Yd(x.ownerDocument.documentElement,x)){if(C!==null&&Fl(x)){if(d=C.start,a=C.end,a===void 0&&(a=d),"selectionStart"in x)x.selectionStart=d,x.selectionEnd=Math.min(a,x.value.length);else if(a=(d=x.ownerDocument||document)&&d.defaultView||window,a.getSelection){a=a.getSelection();var R=x.textContent.length,O=Math.min(C.start,R);C=C.end===void 0?O:Math.min(C.end,R),!a.extend&&O>C&&(R=C,C=O,O=R),R=jh(x,O);var Q=jh(x,C);R&&Q&&(a.rangeCount!==1||a.anchorNode!==R.node||a.anchorOffset!==R.offset||a.focusNode!==Q.node||a.focusOffset!==Q.offset)&&(d=d.createRange(),d.setStart(R.node,R.offset),a.removeAllRanges(),O>C?(a.addRange(d),a.extend(Q.node,Q.offset)):(d.setEnd(Q.node,Q.offset),a.addRange(d)))}}for(d=[],a=x;a=a.parentNode;)a.nodeType===1&&d.push({element:a,left:a.scrollLeft,top:a.scrollTop});for(typeof x.focus=="function"&&x.focus(),x=0;x=document.documentMode,_a=null,Fo=null,Oo=null,va=!1;function ps(a,d,x){var C=x.window===x?x.document:x.nodeType===9?x:x.ownerDocument;va||_a==null||_a!==Vr(C)||(C=_a,"selectionStart"in C&&Fl(C)?C={start:C.selectionStart,end:C.selectionEnd}:(C=(C.ownerDocument&&C.ownerDocument.defaultView||window).getSelection(),C={anchorNode:C.anchorNode,anchorOffset:C.anchorOffset,focusNode:C.focusNode,focusOffset:C.focusOffset}),Oo&&Pc(Oo,C)||(Oo=C,C=Rc(Fo,"onSelect"),0Xa||(a.current=Au[Xa],Au[Xa]=null,Xa--)}function Or(a,d){Xa++,Au[Xa]=a.current,a.current=d}var gs={},ii=ni(gs),zi=ni(!1),Sa=gs;function Hs(a,d){var x=a.type.contextTypes;if(!x)return gs;var C=a.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===d)return C.__reactInternalMemoizedMaskedChildContext;var R={},O;for(O in x)R[O]=d[O];return C&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=d,a.__reactInternalMemoizedMaskedChildContext=R),R}function wi(a){return a=a.childContextTypes,a!=null}function Go(){rn(zi),rn(ii)}function Zh(a,d,x){if(ii.current!==gs)throw Error(u(168));Or(ii,d),Or(zi,x)}function Di(a,d,x){var C=a.stateNode;if(d=d.childContextTypes,typeof C.getChildContext!="function")return x;C=C.getChildContext();for(var R in C)if(!(R in d))throw Error(u(108,Lr(a)||"Unknown",R));return Ct({},x,C)}function Qi(a){return a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||gs,Sa=ii.current,Or(ii,a),Or(zi,zi.current),!0}function Ya(a,d,x){var C=a.stateNode;if(!C)throw Error(u(169));x?(a=Di(a,d,Sa),C.__reactInternalMemoizedMergedChildContext=a,rn(zi),rn(ii),Or(ii,a)):rn(zi),Or(zi,x)}var Hn=null,qo=!1,Gh=!1;function Gl(a){Hn===null?Hn=[a]:Hn.push(a)}function lp(a){qo=!0,Gl(a)}function Ta(){if(!Gh&&Hn!==null){Gh=!0;var a=0,d=qr;try{var x=Hn;for(qr=1;a>=Q,R-=Q,ts=1<<32-ks(d)+R|x<vr?(gi=dr,dr=null):gi=dr.sibling;var Hr=st(Ve,dr,Ge[vr],bt);if(Hr===null){dr===null&&(dr=gi);break}a&&dr&&Hr.alternate===null&&d(Ve,dr),Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr,dr=gi}if(vr===Ge.length)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;vrvr?(gi=dr,dr=null):gi=dr.sibling;var al=st(Ve,dr,Hr.value,bt);if(al===null){dr===null&&(dr=gi);break}a&&dr&&al.alternate===null&&d(Ve,dr),Le=O(al,Le,vr),hr===null?Gt=al:hr.sibling=al,hr=al,dr=gi}if(Hr.done)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;!Hr.done;vr++,Hr=Ge.next())Hr=lt(Ve,Hr.value,bt),Hr!==null&&(Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return vn&&vo(Ve,vr),Gt}for(dr=C(Ve,dr);!Hr.done;vr++,Hr=Ge.next())Hr=Ot(dr,Ve,vr,Hr.value,bt),Hr!==null&&(a&&Hr.alternate!==null&&dr.delete(Hr.key===null?vr:Hr.key),Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return a&&dr.forEach(function(hy){return d(Ve,hy)}),vn&&vo(Ve,vr),Gt}function In(Ve,Le,Ge,bt){if(typeof Ge=="object"&&Ge!==null&&Ge.type===ct&&Ge.key===null&&(Ge=Ge.props.children),typeof Ge=="object"&&Ge!==null){switch(Ge.$$typeof){case Ye:e:{for(var Gt=Ge.key,hr=Le;hr!==null;){if(hr.key===Gt){if(Gt=Ge.type,Gt===ct){if(hr.tag===7){x(Ve,hr.sibling),Le=R(hr,Ge.props.children),Le.return=Ve,Ve=Le;break e}}else if(hr.elementType===Gt||typeof Gt=="object"&&Gt!==null&&Gt.$$typeof===ar&&Du(Gt)===hr.type){x(Ve,hr.sibling),Le=R(hr,Ge.props),Le.ref=Wl(Ve,hr,Ge),Le.return=Ve,Ve=Le;break e}x(Ve,hr);break}else d(Ve,hr);hr=hr.sibling}Ge.type===ct?(Le=Yc(Ge.props.children,Ve.mode,bt,Ge.key),Le.return=Ve,Ve=Le):(bt=df(Ge.type,Ge.key,Ge.props,null,Ve.mode,bt),bt.ref=Wl(Ve,Le,Ge),bt.return=Ve,Ve=bt)}return Q(Ve);case at:e:{for(hr=Ge.key;Le!==null;){if(Le.key===hr)if(Le.tag===4&&Le.stateNode.containerInfo===Ge.containerInfo&&Le.stateNode.implementation===Ge.implementation){x(Ve,Le.sibling),Le=R(Le,Ge.children||[]),Le.return=Ve,Ve=Le;break e}else{x(Ve,Le);break}else d(Ve,Le);Le=Le.sibling}Le=Dp(Ge,Ve.mode,bt),Le.return=Ve,Ve=Le}return Q(Ve);case ar:return hr=Ge._init,In(Ve,Le,hr(Ge._payload),bt)}if(ve(Ge))return Vt(Ve,Le,Ge,bt);if(ft(Ge))return Qt(Ve,Le,Ge,bt);Hl(Ve,Ge)}return typeof Ge=="string"&&Ge!==""||typeof Ge=="number"?(Ge=""+Ge,Le!==null&&Le.tag===6?(x(Ve,Le.sibling),Le=R(Le,Ge),Le.return=Ve,Ve=Le):(x(Ve,Le),Le=zp(Ge,Ve.mode,bt),Le.return=Ve,Ve=Le),Q(Ve)):x(Ve,Le)}return In}var Oi=Xh(!0),Bc=Xh(!1),Lt=ni(null),Yt=null,Ho=null,xo=null;function Kl(){xo=Ho=Yt=null}function fi(a){var d=Lt.current;rn(Lt),a._currentValue=d}function Lu(a,d,x){for(;a!==null;){var C=a.alternate;if((a.childLanes&d)!==d?(a.childLanes|=d,C!==null&&(C.childLanes|=d)):C!==null&&(C.childLanes&d)!==d&&(C.childLanes|=d),a===x)break;a=a.return}}function pi(a,d){Yt=a,xo=Ho=null,a=a.dependencies,a!==null&&a.firstContext!==null&&((a.lanes&d)!==0&&(Ds=!0),a.firstContext=null)}function As(a){var d=a._currentValue;if(xo!==a)if(a={context:a,memoizedValue:d,next:null},Ho===null){if(Yt===null)throw Error(u(308));Ho=a,Yt.dependencies={lanes:0,firstContext:a}}else Ho=Ho.next=a;return d}var Ks=null;function Yr(a){Ks===null?Ks=[a]:Ks.push(a)}function wn(a,d,x,C){var R=d.interleaved;return R===null?(x.next=x,Yr(d)):(x.next=R.next,R.next=x),d.interleaved=x,Rs(a,C)}function Rs(a,d){a.lanes|=d;var x=a.alternate;for(x!==null&&(x.lanes|=d),x=a,a=a.return;a!==null;)a.childLanes|=d,x=a.alternate,x!==null&&(x.childLanes|=d),x=a,a=a.return;return x.tag===3?x.stateNode:null}var vs=!1;function Ca(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fu(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function rs(a,d){return{eventTime:a,lane:d,tag:0,payload:null,callback:null,next:null}}function ns(a,d,x){var C=a.updateQueue;if(C===null)return null;if(C=C.shared,(Br&2)!==0){var R=C.pending;return R===null?d.next=d:(d.next=R.next,R.next=d),C.pending=d,Rs(a,x)}return R=C.interleaved,R===null?(d.next=d,Yr(C)):(d.next=R.next,R.next=d),C.interleaved=d,Rs(a,x)}function ka(a,d,x){if(d=d.updateQueue,d!==null&&(d=d.shared,(x&4194240)!==0)){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ua(a,x)}}function $c(a,d){var x=a.updateQueue,C=a.alternate;if(C!==null&&(C=C.updateQueue,x===C)){var R=null,O=null;if(x=x.firstBaseUpdate,x!==null){do{var Q={eventTime:x.eventTime,lane:x.lane,tag:x.tag,payload:x.payload,callback:x.callback,next:null};O===null?R=O=Q:O=O.next=Q,x=x.next}while(x!==null);O===null?R=O=d:O=O.next=d}else R=O=d;x={baseState:C.baseState,firstBaseUpdate:R,lastBaseUpdate:O,shared:C.shared,effects:C.effects},a.updateQueue=x;return}a=x.lastBaseUpdate,a===null?x.firstBaseUpdate=d:a.next=d,x.lastBaseUpdate=d}function bo(a,d,x,C){var R=a.updateQueue;vs=!1;var O=R.firstBaseUpdate,Q=R.lastBaseUpdate,me=R.shared.pending;if(me!==null){R.shared.pending=null;var Ee=me,He=Ee.next;Ee.next=null,Q===null?O=He:Q.next=He,Q=Ee;var ht=a.alternate;ht!==null&&(ht=ht.updateQueue,me=ht.lastBaseUpdate,me!==Q&&(me===null?ht.firstBaseUpdate=He:me.next=He,ht.lastBaseUpdate=Ee))}if(O!==null){var lt=R.baseState;Q=0,ht=He=Ee=null,me=O;do{var st=me.lane,Ot=me.eventTime;if((C&st)===st){ht!==null&&(ht=ht.next={eventTime:Ot,lane:0,tag:me.tag,payload:me.payload,callback:me.callback,next:null});e:{var Vt=a,Qt=me;switch(st=d,Ot=x,Qt.tag){case 1:if(Vt=Qt.payload,typeof Vt=="function"){lt=Vt.call(Ot,lt,st);break e}lt=Vt;break e;case 3:Vt.flags=Vt.flags&-65537|128;case 0:if(Vt=Qt.payload,st=typeof Vt=="function"?Vt.call(Ot,lt,st):Vt,st==null)break e;lt=Ct({},lt,st);break e;case 2:vs=!0}}me.callback!==null&&me.lane!==0&&(a.flags|=64,st=R.effects,st===null?R.effects=[me]:st.push(me))}else Ot={eventTime:Ot,lane:st,tag:me.tag,payload:me.payload,callback:me.callback,next:null},ht===null?(He=ht=Ot,Ee=lt):ht=ht.next=Ot,Q|=st;if(me=me.next,me===null){if(me=R.shared.pending,me===null)break;st=me,me=st.next,st.next=null,R.lastBaseUpdate=st,R.shared.pending=null}}while(!0);if(ht===null&&(Ee=lt),R.baseState=Ee,R.firstBaseUpdate=He,R.lastBaseUpdate=ht,d=R.shared.interleaved,d!==null){R=d;do Q|=R.lane,R=R.next;while(R!==d)}else O===null&&(R.shared.lanes=0);Ql|=Q,a.lanes=Q,a.memoizedState=lt}}function Ou(a,d,x){if(a=d.effects,d.effects=null,a!==null)for(d=0;dx?x:4,a(!0);var C=B.transition;B.transition={};try{a(!1),d()}finally{qr=x,B.transition=C}}function ji(){return $e().memoizedState}function Xs(a,d,x){var C=Aa(a);if(x={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null},Xo(a))Fn(d,x);else if(x=wn(a,d,x,C),x!==null){var R=mi();ea(x,a,C,R),Nn(x,d,C)}}function Ni(a,d,x){var C=Aa(a),R={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null};if(Xo(a))Fn(d,R);else{var O=a.alternate;if(a.lanes===0&&(O===null||O.lanes===0)&&(O=d.lastRenderedReducer,O!==null))try{var Q=d.lastRenderedState,me=O(Q,x);if(R.hasEagerState=!0,R.eagerState=me,qs(me,Q)){var Ee=d.interleaved;Ee===null?(R.next=R,Yr(d)):(R.next=Ee.next,Ee.next=R),d.interleaved=R;return}}catch{}finally{}x=wn(a,d,R,C),x!==null&&(R=mi(),ea(x,a,C,R),Nn(x,d,C))}}function Xo(a){var d=a.alternate;return a===$||d!==null&&d===$}function Fn(a,d){oe=se=!0;var x=a.pending;x===null?d.next=d:(d.next=x.next,x.next=d),a.pending=d}function Nn(a,d,x){if((x&4194240)!==0){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ua(a,x)}}var Bi={readContext:As,useCallback:le,useContext:le,useEffect:le,useImperativeHandle:le,useInsertionEffect:le,useLayoutEffect:le,useMemo:le,useReducer:le,useRef:le,useState:le,useDebugValue:le,useDeferredValue:le,useTransition:le,useMutableSource:le,useSyncExternalStore:le,useId:le,unstable_isNewReconciler:!1},Bn={readContext:As,useCallback:function(a,d){return ke().memoizedState=[a,d===void 0?null:d],a},useContext:As,useEffect:Dn,useImperativeHandle:function(a,d,x){return x=x!=null?x.concat([a]):null,mr(4194308,4,zs.bind(null,d,a),x)},useLayoutEffect:function(a,d){return mr(4194308,4,a,d)},useInsertionEffect:function(a,d){return mr(4,2,a,d)},useMemo:function(a,d){var x=ke();return d=d===void 0?null:d,a=a(),x.memoizedState=[a,d],a},useReducer:function(a,d,x){var C=ke();return d=x!==void 0?x(d):d,C.memoizedState=C.baseState=d,a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:d},C.queue=a,a=a.dispatch=Xs.bind(null,$,a),[C.memoizedState,a]},useRef:function(a){var d=ke();return a={current:a},d.memoizedState=a},useState:Qr,useDebugValue:wo,useDeferredValue:function(a){return ke().memoizedState=a},useTransition:function(){var a=Qr(!1),d=a[0];return a=rl.bind(null,a[1]),ke().memoizedState=a,[d,a]},useMutableSource:function(){},useSyncExternalStore:function(a,d,x){var C=$,R=ke();if(vn){if(x===void 0)throw Error(u(407));x=x()}else{if(x=d(),Yn===null)throw Error(u(349));(j&30)!==0||tt(C,d,x)}R.memoizedState=x;var O={value:x,getSnapshot:d};return R.queue=O,Dn(br.bind(null,C,O,a),[a]),C.flags|=2048,un(9,Ft.bind(null,C,O,x,d),void 0,null),x},useId:function(){var a=ke(),d=Yn.identifierPrefix;if(vn){var x=Fi,C=ts;x=(C&~(1<<32-ks(C)-1)).toString(32)+x,d=":"+d+"R"+x,x=ae++,0<\/script>",a=a.removeChild(a.firstChild)):typeof C.is=="string"?a=Q.createElement(x,{is:C.is}):(a=Q.createElement(x),x==="select"&&(Q=a,C.multiple?Q.multiple=!0:C.size&&(Q.size=C.size))):a=Q.createElementNS(a,x),a[ms]=d,a[Bo]=C,gp(a,d,!1,!1),d.stateNode=a;e:{switch(Q=ze(x,C),x){case"dialog":gn("cancel",a),gn("close",a),z=C;break;case"iframe":case"object":case"embed":gn("load",a),z=C;break;case"video":case"audio":for(z=0;zGu&&(d.flags|=128,C=!0,td(O,!1),d.lanes=4194304)}else{if(!C)if(a=k(Q),a!==null){if(d.flags|=128,C=!0,x=a.updateQueue,x!==null&&(d.updateQueue=x,d.flags|=4),td(O,!0),O.tail===null&&O.tailMode==="hidden"&&!Q.alternate&&!vn)return is(d),null}else 2*Er()-O.renderingStartTime>Gu&&x!==1073741824&&(d.flags|=128,C=!0,td(O,!1),d.lanes=4194304);O.isBackwards?(Q.sibling=d.child,d.child=Q):(x=O.last,x!==null?x.sibling=Q:d.child=Q,O.last=Q)}return O.tail!==null?(d=O.tail,O.rendering=d,O.tail=d.sibling,O.renderingStartTime=Er(),d.sibling=null,x=S.current,Or(S,C?x&1|2:x&1),d):(is(d),null);case 22:case 23:return Ip(),C=d.memoizedState!==null,a!==null&&a.memoizedState!==null!==C&&(d.flags|=8192),C&&(d.mode&1)!==0?(Js&1073741824)!==0&&(is(d),d.subtreeFlags&6&&(d.flags|=8192)):is(d),null;case 24:return null;case 25:return null}throw Error(u(156,d.tag))}function ny(a,d){switch(Zh(d),d.tag){case 1:return wi(d.type)&&Zo(),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return h(),rn(Ri),rn(ii),R(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 5:return y(d),null;case 13:if(rn(S),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(u(340));Wr()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return rn(S),null;case 4:return h(),null;case 10:return fi(d.type._context),null;case 22:case 23:return Ip(),null;case 24:return null;default:return null}}var Nu=!1,Ui=!1,Rm=typeof WeakSet=="function"?WeakSet:Set,Ut=null;function Bu(a,d){var x=a.ref;if(x!==null)if(typeof x=="function")try{x(null)}catch(C){$n(a,d,C)}else x.current=null}function _p(a,d,x){try{x()}catch(C){$n(a,d,C)}}var nf=!1;function vp(a,d){if(Ul=bi,a=Oh(),Ll(a)){if("selectionStart"in a)var x={start:a.selectionStart,end:a.selectionEnd};else e:{x=(x=a.ownerDocument)&&x.defaultView||window;var C=x.getSelection&&x.getSelection();if(C&&C.rangeCount!==0){x=C.anchorNode;var z=C.anchorOffset,O=C.focusNode;C=C.focusOffset;try{x.nodeType,O.nodeType}catch{x=null;break e}var Q=0,me=-1,ke=-1,He=0,ut=0,lt=a,ot=null;t:for(;;){for(var Ot;lt!==x||z!==0&<.nodeType!==3||(me=Q+z),lt!==O||C!==0&<.nodeType!==3||(ke=Q+C),lt.nodeType===3&&(Q+=lt.nodeValue.length),(Ot=lt.firstChild)!==null;)ot=lt,lt=Ot;for(;;){if(lt===a)break t;if(ot===x&&++He===z&&(me=Q),ot===O&&++ut===C&&(ke=Q),(Ot=lt.nextSibling)!==null)break;lt=ot,ot=lt.parentNode}lt=Ot}x=me===-1||ke===-1?null:{start:me,end:ke}}else x=null}x=x||{start:0,end:0}}else x=null;for(No={focusedElem:a,selectionRange:x},bi=!1,Ut=d;Ut!==null;)if(d=Ut,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Ut=a;else for(;Ut!==null;){d=Ut;try{var Vt=d.alternate;if((d.flags&1024)!==0)switch(d.tag){case 0:case 11:case 15:break;case 1:if(Vt!==null){var Qt=Vt.memoizedProps,Mn=Vt.memoizedState,Ve=d.stateNode,De=Ve.getSnapshotBeforeUpdate(d.elementType===d.type?Qt:Xn(d.type,Qt),Mn);Ve.__reactInternalSnapshotBeforeUpdate=De}break;case 3:var Ge=d.stateNode.containerInfo;Ge.nodeType===1?Ge.textContent="":Ge.nodeType===9&&Ge.documentElement&&Ge.removeChild(Ge.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch(bt){$n(d,d.return,bt)}if(a=d.sibling,a!==null){a.return=d.return,Ut=a;break}Ut=d.return}return Vt=nf,nf=!1,Vt}function $u(a,d,x){var C=d.updateQueue;if(C=C!==null?C.lastEffect:null,C!==null){var z=C=C.next;do{if((z.tag&a)===a){var O=z.destroy;z.destroy=void 0,O!==void 0&&_p(d,x,O)}z=z.next}while(z!==C)}}function ka(a,d){if(d=d.updateQueue,d=d!==null?d.lastEffect:null,d!==null){var x=d=d.next;do{if((x.tag&a)===a){var C=x.create;x.destroy=C()}x=x.next}while(x!==d)}}function Zc(a){var d=a.ref;if(d!==null){var x=a.stateNode;switch(a.tag){case 5:a=x;break;default:a=x}typeof d=="function"?d(a):d.current=a}}function Dm(a){var d=a.alternate;d!==null&&(a.alternate=null,Dm(d)),a.child=null,a.deletions=null,a.sibling=null,a.tag===5&&(d=a.stateNode,d!==null&&(delete d[ms],delete d[Bo],delete d[$o],delete d[ku],delete d[Uo])),a.stateNode=null,a.return=null,a.dependencies=null,a.memoizedProps=null,a.memoizedState=null,a.pendingProps=null,a.stateNode=null,a.updateQueue=null}function Lm(a){return a.tag===5||a.tag===3||a.tag===4}function Fm(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||Lm(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function xp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.nodeType===8?x.parentNode.insertBefore(a,d):x.insertBefore(a,d):(x.nodeType===8?(d=x.parentNode,d.insertBefore(a,x)):(d=x,d.appendChild(a)),x=x._reactRootContainer,x!=null||d.onclick!==null||(d.onclick=$l));else if(C!==4&&(a=a.child,a!==null))for(xp(a,d,x),a=a.sibling;a!==null;)xp(a,d,x),a=a.sibling}function bp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.insertBefore(a,d):x.appendChild(a);else if(C!==4&&(a=a.child,a!==null))for(bp(a,d,x),a=a.sibling;a!==null;)bp(a,d,x),a=a.sibling}var Vi=null,Qo=!1;function Ma(a,d,x){for(x=x.child;x!==null;)Om(a,d,x),x=x.sibling}function Om(a,d,x){if(ui&&typeof ui.onCommitFiberUnmount=="function")try{ui.onCommitFiberUnmount(Ns,x)}catch{}switch(x.tag){case 5:Ui||Bu(x,d);case 6:var C=Vi,z=Qo;Vi=null,Ma(a,d,x),Vi=C,Qo=z,Vi!==null&&(Qo?(a=Vi,x=x.stateNode,a.nodeType===8?a.parentNode.removeChild(x):a.removeChild(x)):Vi.removeChild(x.stateNode));break;case 18:Vi!==null&&(Qo?(a=Vi,x=x.stateNode,a.nodeType===8?Lc(a.parentNode,x):a.nodeType===1&&Lc(a,x),fs(a)):Lc(Vi,x.stateNode));break;case 4:C=Vi,z=Qo,Vi=x.stateNode.containerInfo,Qo=!0,Ma(a,d,x),Vi=C,Qo=z;break;case 0:case 11:case 14:case 15:if(!Ui&&(C=x.updateQueue,C!==null&&(C=C.lastEffect,C!==null))){z=C=C.next;do{var O=z,Q=O.destroy;O=O.tag,Q!==void 0&&((O&2)!==0||(O&4)!==0)&&_p(x,d,Q),z=z.next}while(z!==C)}Ma(a,d,x);break;case 1:if(!Ui&&(Bu(x,d),C=x.stateNode,typeof C.componentWillUnmount=="function"))try{C.props=x.memoizedProps,C.state=x.memoizedState,C.componentWillUnmount()}catch(me){$n(x,d,me)}Ma(a,d,x);break;case 21:Ma(a,d,x);break;case 22:x.mode&1?(Ui=(C=Ui)||x.memoizedState!==null,Ma(a,d,x),Ui=C):Ma(a,d,x);break;default:Ma(a,d,x)}}function wp(a){var d=a.updateQueue;if(d!==null){a.updateQueue=null;var x=a.stateNode;x===null&&(x=a.stateNode=new Rm),d.forEach(function(C){var z=dd.bind(null,a,C);x.has(C)||(x.add(C),C.then(z,z))})}}function Ss(a,d){var x=d.deletions;if(x!==null)for(var C=0;Cz&&(z=Q),C&=~O}if(C=z,C=Er()-C,C=(120>C?120:480>C?480:1080>C?1080:1920>C?1920:3e3>C?3e3:4320>C?4320:1960*Nm(C/1960))-C,10a?16:a,Jl===null)var C=!1;else{if(a=Jl,Jl=null,ad=0,(Br&6)!==0)throw Error(u(331));var z=Br;for(Br|=4,Ut=a.current;Ut!==null;){var O=Ut,Q=O.child;if((Ut.flags&16)!==0){var me=O.deletions;if(me!==null){for(var ke=0;keEr()-Tp?Hc(a,0):od|=x),Fs(a,d)}function Wm(a,d){d===0&&((a.mode&1)===0?d=1:(d=mu,mu<<=1,(mu&130023424)===0&&(mu=4194304)));var x=mi();a=zs(a,d),a!==null&&(ja(a,d,x),Fs(a,x))}function sy(a){var d=a.memoizedState,x=0;d!==null&&(x=d.retryLane),Wm(a,x)}function dd(a,d){var x=0;switch(a.tag){case 13:var C=a.stateNode,z=a.memoizedState;z!==null&&(x=z.retryLane);break;case 19:C=a.stateNode;break;default:throw Error(u(314))}C!==null&&C.delete(d),Wm(a,x)}var Jn;Jn=function(a,d,x){if(a!==null)if(a.memoizedProps!==d.pendingProps||Ri.current)Ds=!0;else{if((a.lanes&x)===0&&(d.flags&128)===0)return Ds=!1,ws(a,d,x);Ds=(a.flags&131072)!==0}else Ds=!1,vn&&(d.flags&1048576)!==0&&Fr(d,Et,d.index);switch(d.lanes=0,d.tag){case 2:var C=d.type;ju(a,d),a=d.pendingProps;var z=Hs(d,ii.current);pi(d,x),z=we(null,d,C,a,z,x);var O=he();return d.flags|=1,typeof z=="object"&&z!==null&&typeof z.render=="function"&&z.$$typeof===void 0?(d.tag=1,d.memoizedState=null,d.updateQueue=null,wi(C)?(O=!0,Yi(d)):O=!1,d.memoizedState=z.state!==null&&z.state!==void 0?z.state:null,Pa(d),z.updater=Ys,d.stateNode=z,z._reactInternals=d,Uc(d,C,a,x),d=Ou(null,d,C,!0,O,x)):(d.tag=0,vn&&O&&Gl(d),$i(null,d,z,x),d=d.child),d;case 16:C=d.elementType;e:{switch(ju(a,d),a=d.pendingProps,z=C._init,C=z(C._payload),d.type=C,z=d.tag=Hu(C),a=Xn(C,a),z){case 0:d=dp(null,d,C,a,x);break e;case 1:d=Fu(null,d,C,a,x);break e;case 11:d=Em(null,d,C,a,x);break e;case 14:d=ef(null,d,C,Xn(C.type,a),x);break e}throw Error(u(306,C,""))}return d;case 0:return C=d.type,z=d.pendingProps,z=d.elementType===C?z:Xn(C,z),dp(a,d,C,z,x);case 1:return C=d.type,z=d.pendingProps,z=d.elementType===C?z:Xn(C,z),Fu(a,d,C,z,x);case 3:e:{if(Yh(d),a===null)throw Error(u(387));C=d.pendingProps,O=d.memoizedState,z=O.element,Du(a,d),bo(d,C,null,x);var Q=d.memoizedState;if(C=Q.element,O.isDehydrated)if(O={element:C,isDehydrated:!1,cache:Q.cache,pendingSuspenseBoundaries:Q.pendingSuspenseBoundaries,transitions:Q.transitions},d.updateQueue.baseState=O,d.memoizedState=O,d.flags&256){z=Xo(Error(u(423)),d),d=fp(a,d,C,x,z);break e}else if(C!==z){z=Xo(Error(u(424)),d),d=fp(a,d,C,x,z);break e}else for(ys=tn(d.stateNode.containerInfo.firstChild),Si=d,vn=!0,_s=null,x=Nc(d,null,C,x),d.child=x;x;)x.flags=x.flags&-3|4096,x=x.sibling;else{if(Wr(),C===z){d=Qs(a,d,x);break e}$i(a,d,C,x)}d=d.child}return d;case 5:return p(d),a===null&&Oc(d),C=d.type,z=d.pendingProps,O=a!==null?a.memoizedProps:null,Q=z.children,Rc(C,z)?Q=null:O!==null&&Rc(C,O)&&(d.flags|=32),Vc(a,d),$i(a,d,Q,x),d.child;case 6:return a===null&&Oc(d),null;case 13:return km(a,d,x);case 4:return s(d,d.stateNode.containerInfo),C=d.pendingProps,a===null?d.child=Oi(d,null,C,x):$i(a,d,C,x),d.child;case 11:return C=d.type,z=d.pendingProps,z=d.elementType===C?z:Xn(C,z),Em(a,d,C,z,x);case 7:return $i(a,d,d.pendingProps,x),d.child;case 8:return $i(a,d,d.pendingProps.children,x),d.child;case 12:return $i(a,d,d.pendingProps.children,x),d.child;case 10:e:{if(C=d.type._context,z=d.pendingProps,O=d.memoizedProps,Q=z.value,Or(Lt,C._currentValue),C._currentValue=Q,O!==null)if(qs(O.value,Q)){if(O.children===z.children&&!Ri.current){d=Qs(a,d,x);break e}}else for(O=d.child,O!==null&&(O.return=d);O!==null;){var me=O.dependencies;if(me!==null){Q=O.child;for(var ke=me.firstContext;ke!==null;){if(ke.context===C){if(O.tag===1){ke=ts(-1,x&-x),ke.tag=2;var He=O.updateQueue;if(He!==null){He=He.shared;var ut=He.pending;ut===null?ke.next=ke:(ke.next=ut.next,ut.next=ke),He.pending=ke}}O.lanes|=x,ke=O.alternate,ke!==null&&(ke.lanes|=x),Ru(O.return,x,d),me.lanes|=x;break}ke=ke.next}}else if(O.tag===10)Q=O.type===d.type?null:O.child;else if(O.tag===18){if(Q=O.return,Q===null)throw Error(u(341));Q.lanes|=x,me=Q.alternate,me!==null&&(me.lanes|=x),Ru(Q,x,d),Q=O.sibling}else Q=O.child;if(Q!==null)Q.return=O;else for(Q=O;Q!==null;){if(Q===d){Q=null;break}if(O=Q.sibling,O!==null){O.return=Q.return,Q=O;break}Q=Q.return}O=Q}$i(a,d,z.children,x),d=d.child}return d;case 9:return z=d.type,C=d.pendingProps.children,pi(d,x),z=As(z),C=C(z),d.flags|=1,$i(a,d,C,x),d.child;case 14:return C=d.type,z=Xn(C,d.pendingProps),z=Xn(C.type,z),ef(a,d,C,z,x);case 15:return up(a,d,d.type,d.pendingProps,x);case 17:return C=d.type,z=d.pendingProps,z=d.elementType===C?z:Xn(C,z),ju(a,d),d.tag=1,wi(C)?(a=!0,Yi(d)):a=!1,pi(d,x),ns(d,C,z),Uc(d,C,z,x),Ou(null,d,C,!0,a,x);case 19:return mp(a,d,x);case 22:return hp(a,d,x)}throw Error(u(156,d.tag))};function uf(a,d){return Io(a,d)}function Hm(a,d,x,C){this.tag=a,this.key=x,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=d,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=C,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function to(a,d,x,C){return new Hm(a,d,x,C)}function fd(a){return a=a.prototype,!(!a||!a.isReactComponent)}function Hu(a){if(typeof a=="function")return fd(a)?1:0;if(a!=null){if(a=a.$$typeof,a===Rt)return 11;if(a===Rr)return 14}return 2}function nl(a,d){var x=a.alternate;return x===null?(x=to(a.tag,d,a.key,a.mode),x.elementType=a.elementType,x.type=a.type,x.stateNode=a.stateNode,x.alternate=a,a.alternate=x):(x.pendingProps=d,x.type=a.type,x.flags=0,x.subtreeFlags=0,x.deletions=null),x.flags=a.flags&14680064,x.childLanes=a.childLanes,x.lanes=a.lanes,x.child=a.child,x.memoizedProps=a.memoizedProps,x.memoizedState=a.memoizedState,x.updateQueue=a.updateQueue,d=a.dependencies,x.dependencies=d===null?null:{lanes:d.lanes,firstContext:d.firstContext},x.sibling=a.sibling,x.index=a.index,x.ref=a.ref,x}function hf(a,d,x,C,z,O){var Q=2;if(C=a,typeof a=="function")fd(a)&&(Q=1);else if(typeof a=="string")Q=5;else e:switch(a){case yt:return Xc(x.children,z,O,d);case Qe:Q=8,z|=8;break;case it:return a=to(12,x,d,z|2),a.elementType=it,a.lanes=O,a;case At:return a=to(13,x,d,z),a.elementType=At,a.lanes=O,a;case Ht:return a=to(19,x,d,z),a.elementType=Ht,a.lanes=O,a;case Zt:return df(x,z,O,d);default:if(typeof a=="object"&&a!==null)switch(a.$$typeof){case at:Q=10;break e;case It:Q=9;break e;case Rt:Q=11;break e;case Rr:Q=14;break e;case ar:Q=16,C=null;break e}throw Error(u(130,a==null?a:typeof a,""))}return d=to(Q,x,d,z),d.elementType=a,d.type=C,d.lanes=O,d}function Xc(a,d,x,C){return a=to(7,a,C,d),a.lanes=x,a}function df(a,d,x,C){return a=to(22,a,C,d),a.elementType=Zt,a.lanes=x,a.stateNode={isHidden:!1},a}function zp(a,d,x){return a=to(6,a,null,d),a.lanes=x,a}function Rp(a,d,x){return d=to(4,a.children!==null?a.children:[],a.key,d),d.lanes=x,d.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation},d}function oy(a,d,x,C,z){this.tag=d,this.containerInfo=a,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=gc(0),this.expirationTimes=gc(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=gc(0),this.identifierPrefix=C,this.onRecoverableError=z,this.mutableSourceEagerHydrationData=null}function Dp(a,d,x,C,z,O,Q,me,ke){return a=new oy(a,d,x,me,ke),d===1?(d=1,O===!0&&(d|=8)):d=0,O=to(3,null,null,d),a.current=O,O.stateNode=a,O.memoizedState={element:C,isDehydrated:x,cache:null,transitions:null,pendingSuspenseBoundaries:null},Pa(O),a}function ay(a,d,x){var C=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(o){console.error(o)}}return n(),Ey.exports=ww(),Ey.exports}var sx;function Sw(){if(sx)return lg;sx=1;var n=X0();return lg.createRoot=n.createRoot,lg.hydrateRoot=n.hydrateRoot,lg}var Tw=Sw();const Pw=k_(Tw);/** +`+O.stack}return{value:a,source:d,stack:R,digest:null}}function Yl(a,d,x){return{value:a,source:null,stack:x??null,digest:d??null}}function So(a,d){try{console.error(d.value)}catch(x){setTimeout(function(){throw x})}}var Qo=typeof WeakMap=="function"?WeakMap:Map;function cp(a,d,x){x=rs(-1,x),x.tag=3,x.payload={element:null};var C=d.value;return x.callback=function(){of||(of=!0,Cp=C),So(a,d)},x}function Pm(a,d,x){x=rs(-1,x),x.tag=3;var C=a.type.getDerivedStateFromError;if(typeof C=="function"){var R=d.value;x.payload=function(){return C(R)},x.callback=function(){So(a,d)}}var O=a.stateNode;return O!==null&&typeof O.componentDidCatch=="function"&&(x.callback=function(){So(a,d),typeof C!="function"&&(eo===null?eo=new Set([this]):eo.add(this));var Q=d.stack;this.componentDidCatch(d.value,{componentStack:Q!==null?Q:""})}),x}function ef(a,d,x){var C=a.pingCache;if(C===null){C=a.pingCache=new Qo;var R=new Set;C.set(d,R)}else R=C.get(d),R===void 0&&(R=new Set,C.set(d,R));R.has(x)||(R.add(x),a=Wm.bind(null,a,d,x),d.then(a,a))}function up(a){do{var d;if((d=a.tag===13)&&(d=a.memoizedState,d=d!==null?d.dehydrated!==null:!0),d)return a;a=a.return}while(a!==null);return null}function Cm(a,d,x,C,R){return(a.mode&1)===0?(a===d?a.flags|=65536:(a.flags|=128,x.flags|=131072,x.flags&=-52805,x.tag===1&&(x.alternate===null?x.tag=17:(d=rs(-1,1),d.tag=2,ns(x,d,1))),x.lanes|=1),a):(a.flags|=65536,a.lanes=R,a)}var km=Fe.ReactCurrentOwner,Ds=!1;function $i(a,d,x,C){d.child=a===null?Bc(d,null,x,C):Oi(d,a.child,x,C)}function Em(a,d,x,C,R){x=x.render;var O=d.ref;return pi(d,R),C=we(a,d,x,C,O,R),x=he(),a!==null&&!Ds?(d.updateQueue=a.updateQueue,d.flags&=-2053,a.lanes&=~R,Qs(a,d,R)):(vn&&x&&ql(d),d.flags|=1,$i(a,d,C,R),d.child)}function tf(a,d,x,C,R){if(a===null){var O=x.type;return typeof O=="function"&&!md(O)&&O.defaultProps===void 0&&x.compare===null&&x.defaultProps===void 0?(d.tag=15,d.type=O,hp(a,d,O,C,R)):(a=df(x.type,null,C,d,d.mode,R),a.ref=d.ref,a.return=d,d.child=a)}if(O=a.child,(a.lanes&R)===0){var Q=O.memoizedProps;if(x=x.compare,x=x!==null?x:Pc,x(Q,C)&&a.ref===d.ref)return Qs(a,d,R)}return d.flags|=1,a=il(O,C),a.ref=d.ref,a.return=d,d.child=a}function hp(a,d,x,C,R){if(a!==null){var O=a.memoizedProps;if(Pc(O,C)&&a.ref===d.ref)if(Ds=!1,d.pendingProps=C=O,(a.lanes&R)!==0)(a.flags&131072)!==0&&(Ds=!0);else return d.lanes=a.lanes,Qs(a,d,R)}return fp(a,d,x,C,R)}function dp(a,d,x){var C=d.pendingProps,R=C.children,O=a!==null?a.memoizedState:null;if(C.mode==="hidden")if((d.mode&1)===0)d.memoizedState={baseLanes:0,cachePool:null,transitions:null},Or(Wc,Js),Js|=x;else{if((x&1073741824)===0)return a=O!==null?O.baseLanes|x:x,d.lanes=d.childLanes=1073741824,d.memoizedState={baseLanes:a,cachePool:null,transitions:null},d.updateQueue=null,Or(Wc,Js),Js|=a,null;d.memoizedState={baseLanes:0,cachePool:null,transitions:null},C=O!==null?O.baseLanes:x,Or(Wc,Js),Js|=C}else O!==null?(C=O.baseLanes|x,d.memoizedState=null):C=x,Or(Wc,Js),Js|=C;return $i(a,d,R,x),d.child}function Zc(a,d){var x=d.ref;(a===null&&x!==null||a!==null&&a.ref!==x)&&(d.flags|=512,d.flags|=2097152)}function fp(a,d,x,C,R){var O=wi(x)?Sa:ii.current;return O=Hs(d,O),pi(d,R),x=we(a,d,x,C,O,R),C=he(),a!==null&&!Ds?(d.updateQueue=a.updateQueue,d.flags&=-2053,a.lanes&=~R,Qs(a,d,R)):(vn&&C&&ql(d),d.flags|=1,$i(a,d,x,R),d.child)}function ju(a,d,x,C,R){if(wi(x)){var O=!0;Qi(d)}else O=!1;if(pi(d,R),d.stateNode===null)Bu(a,d),is(d,x,C),Vc(d,x,C,R),C=!0;else if(a===null){var Q=d.stateNode,me=d.memoizedProps;Q.props=me;var Ee=Q.context,He=x.contextType;typeof He=="object"&&He!==null?He=As(He):(He=wi(x)?Sa:ii.current,He=Hs(d,He));var ht=x.getDerivedStateFromProps,lt=typeof ht=="function"||typeof Q.getSnapshotBeforeUpdate=="function";lt||typeof Q.UNSAFE_componentWillReceiveProps!="function"&&typeof Q.componentWillReceiveProps!="function"||(me!==C||Ee!==He)&&Qh(d,Q,C,He),vs=!1;var st=d.memoizedState;Q.state=st,bo(d,C,Q,R),Ee=d.memoizedState,me!==C||st!==Ee||zi.current||vs?(typeof ht=="function"&&(bs(d,x,ht,C),Ee=d.memoizedState),(me=vs||Yh(d,x,me,C,st,Ee,He))?(lt||typeof Q.UNSAFE_componentWillMount!="function"&&typeof Q.componentWillMount!="function"||(typeof Q.componentWillMount=="function"&&Q.componentWillMount(),typeof Q.UNSAFE_componentWillMount=="function"&&Q.UNSAFE_componentWillMount()),typeof Q.componentDidMount=="function"&&(d.flags|=4194308)):(typeof Q.componentDidMount=="function"&&(d.flags|=4194308),d.memoizedProps=C,d.memoizedState=Ee),Q.props=C,Q.state=Ee,Q.context=He,C=me):(typeof Q.componentDidMount=="function"&&(d.flags|=4194308),C=!1)}else{Q=d.stateNode,Fu(a,d),me=d.memoizedProps,He=d.type===d.elementType?me:Xn(d.type,me),Q.props=He,lt=d.pendingProps,st=Q.context,Ee=x.contextType,typeof Ee=="object"&&Ee!==null?Ee=As(Ee):(Ee=wi(x)?Sa:ii.current,Ee=Hs(d,Ee));var Ot=x.getDerivedStateFromProps;(ht=typeof Ot=="function"||typeof Q.getSnapshotBeforeUpdate=="function")||typeof Q.UNSAFE_componentWillReceiveProps!="function"&&typeof Q.componentWillReceiveProps!="function"||(me!==lt||st!==Ee)&&Qh(d,Q,C,Ee),vs=!1,st=d.memoizedState,Q.state=st,bo(d,C,Q,R);var Vt=d.memoizedState;me!==lt||st!==Vt||zi.current||vs?(typeof Ot=="function"&&(bs(d,x,Ot,C),Vt=d.memoizedState),(He=vs||Yh(d,x,He,C,st,Vt,Ee)||!1)?(ht||typeof Q.UNSAFE_componentWillUpdate!="function"&&typeof Q.componentWillUpdate!="function"||(typeof Q.componentWillUpdate=="function"&&Q.componentWillUpdate(C,Vt,Ee),typeof Q.UNSAFE_componentWillUpdate=="function"&&Q.UNSAFE_componentWillUpdate(C,Vt,Ee)),typeof Q.componentDidUpdate=="function"&&(d.flags|=4),typeof Q.getSnapshotBeforeUpdate=="function"&&(d.flags|=1024)):(typeof Q.componentDidUpdate!="function"||me===a.memoizedProps&&st===a.memoizedState||(d.flags|=4),typeof Q.getSnapshotBeforeUpdate!="function"||me===a.memoizedProps&&st===a.memoizedState||(d.flags|=1024),d.memoizedProps=C,d.memoizedState=Vt),Q.props=C,Q.state=Vt,Q.context=Ee,C=He):(typeof Q.componentDidUpdate!="function"||me===a.memoizedProps&&st===a.memoizedState||(d.flags|=4),typeof Q.getSnapshotBeforeUpdate!="function"||me===a.memoizedProps&&st===a.memoizedState||(d.flags|=1024),C=!1)}return Nu(a,d,x,C,O,R)}function Nu(a,d,x,C,R,O){Zc(a,d);var Q=(d.flags&128)!==0;if(!C&&!Q)return R&&Ya(d,x,!1),Qs(a,d,O);C=d.stateNode,km.current=d;var me=Q&&typeof x.getDerivedStateFromError!="function"?null:C.render();return d.flags|=1,a!==null&&Q?(d.child=Oi(d,a.child,null,O),d.child=Oi(d,null,me,O)):$i(a,d,me,O),d.memoizedState=C.state,R&&Ya(d,x,!0),d.child}function Jh(a){var d=a.stateNode;d.pendingContext?Zh(a,d.pendingContext,d.pendingContext!==d.context):d.context&&Zh(a,d.context,!1),s(a,d.containerInfo)}function pp(a,d,x,C,R){return Wr(),Pa(R),d.flags|=256,$i(a,d,x,C),d.child}var rf={dehydrated:null,treeContext:null,retryLane:0};function mp(a){return{baseLanes:a,cachePool:null,transitions:null}}function Im(a,d,x){var C=d.pendingProps,R=S.current,O=!1,Q=(d.flags&128)!==0,me;if((me=Q)||(me=a!==null&&a.memoizedState===null?!1:(R&2)!==0),me?(O=!0,d.flags&=-129):(a===null||a.memoizedState!==null)&&(R|=1),Or(S,R&1),a===null)return jc(d),a=d.memoizedState,a!==null&&(a=a.dehydrated,a!==null)?((d.mode&1)===0?d.lanes=1:a.data==="$!"?d.lanes=8:d.lanes=1073741824,null):(Q=C.children,a=C.fallback,O?(C=d.mode,O=d.child,Q={mode:"hidden",children:Q},(C&1)===0&&O!==null?(O.childLanes=0,O.pendingProps=Q):O=ff(Q,C,0,null),a=Yc(a,C,x,null),O.return=d,a.return=d,O.sibling=a,d.child=O,d.child.memoizedState=mp(x),d.memoizedState=rf,a):nf(d,Q));if(R=a.memoizedState,R!==null&&(me=R.dehydrated,me!==null))return Mm(a,d,Q,C,me,R,x);if(O){O=C.fallback,Q=d.mode,R=a.child,me=R.sibling;var Ee={mode:"hidden",children:C.children};return(Q&1)===0&&d.child!==R?(C=d.child,C.childLanes=0,C.pendingProps=Ee,d.deletions=null):(C=il(R,Ee),C.subtreeFlags=R.subtreeFlags&14680064),me!==null?O=il(me,O):(O=Yc(O,Q,x,null),O.flags|=2),O.return=d,C.return=d,C.sibling=O,d.child=C,C=O,O=d.child,Q=a.child.memoizedState,Q=Q===null?mp(x):{baseLanes:Q.baseLanes|x,cachePool:null,transitions:Q.transitions},O.memoizedState=Q,O.childLanes=a.childLanes&~x,d.memoizedState=rf,C}return O=a.child,a=O.sibling,C=il(O,{mode:"visible",children:C.children}),(d.mode&1)===0&&(C.lanes=x),C.return=d,C.sibling=null,a!==null&&(x=d.deletions,x===null?(d.deletions=[a],d.flags|=16):x.push(a)),d.child=C,d.memoizedState=null,C}function nf(a,d){return d=ff({mode:"visible",children:d},a.mode,0,null),d.return=a,a.child=d}function ed(a,d,x,C){return C!==null&&Pa(C),Oi(d,a.child,null,x),a=nf(d,d.pendingProps.children),a.flags|=2,d.memoizedState=null,a}function Mm(a,d,x,C,R,O,Q){if(x)return d.flags&256?(d.flags&=-257,C=Yl(Error(u(422))),ed(a,d,Q,C)):d.memoizedState!==null?(d.child=a.child,d.flags|=128,null):(O=C.fallback,R=d.mode,C=ff({mode:"visible",children:C.children},R,0,null),O=Yc(O,R,Q,null),O.flags|=2,C.return=d,O.return=d,C.sibling=O,d.child=C,(d.mode&1)!==0&&Oi(d,a.child,null,Q),d.child.memoizedState=mp(Q),d.memoizedState=rf,O);if((d.mode&1)===0)return ed(a,d,Q,null);if(R.data==="$!"){if(C=R.nextSibling&&R.nextSibling.dataset,C)var me=C.dgst;return C=me,O=Error(u(419)),C=Yl(O,C,void 0),ed(a,d,Q,C)}if(me=(Q&a.childLanes)!==0,Ds||me){if(C=Yn,C!==null){switch(Q&-Q){case 4:R=2;break;case 16:R=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:R=32;break;case 536870912:R=268435456;break;default:R=0}R=(R&(C.suspendedLanes|Q))!==0?0:R,R!==0&&R!==O.retryLane&&(O.retryLane=R,Rs(a,R),ea(C,a,R,-1))}return hd(),C=Yl(Error(u(421))),ed(a,d,Q,C)}return R.data==="$?"?(d.flags|=128,d.child=a.child,d=ay.bind(null,a),R._reactRetry=d,null):(a=O.treeContext,ys=tn(R.nextSibling),Si=d,vn=!0,_s=null,a!==null&&(es[Li++]=ts,es[Li++]=Fi,es[Li++]=Wo,ts=a.id,Fi=a.overflow,Wo=d),d=nf(d,C.children),d.flags|=4096,d)}function Am(a,d,x){a.lanes|=d;var C=a.alternate;C!==null&&(C.lanes|=d),Lu(a.return,d,x)}function td(a,d,x,C,R){var O=a.memoizedState;O===null?a.memoizedState={isBackwards:d,rendering:null,renderingStartTime:0,last:C,tail:x,tailMode:R}:(O.isBackwards=d,O.rendering=null,O.renderingStartTime=0,O.last=C,O.tail=x,O.tailMode=R)}function gp(a,d,x){var C=d.pendingProps,R=C.revealOrder,O=C.tail;if($i(a,d,C.children,x),C=S.current,(C&2)!==0)C=C&1|2,d.flags|=128;else{if(a!==null&&(a.flags&128)!==0)e:for(a=d.child;a!==null;){if(a.tag===13)a.memoizedState!==null&&Am(a,x,d);else if(a.tag===19)Am(a,x,d);else if(a.child!==null){a.child.return=a,a=a.child;continue}if(a===d)break e;for(;a.sibling===null;){if(a.return===null||a.return===d)break e;a=a.return}a.sibling.return=a.return,a=a.sibling}C&=1}if(Or(S,C),(d.mode&1)===0)d.memoizedState=null;else switch(R){case"forwards":for(x=d.child,R=null;x!==null;)a=x.alternate,a!==null&&E(a)===null&&(R=x),x=x.sibling;x=R,x===null?(R=d.child,d.child=null):(R=x.sibling,x.sibling=null),td(d,!1,R,x,O);break;case"backwards":for(x=null,R=d.child,d.child=null;R!==null;){if(a=R.alternate,a!==null&&E(a)===null){d.child=R;break}a=R.sibling,R.sibling=x,x=R,R=a}td(d,!0,x,null,O);break;case"together":td(d,!1,null,null,void 0);break;default:d.memoizedState=null}return d.child}function Bu(a,d){(d.mode&1)===0&&a!==null&&(a.alternate=null,d.alternate=null,d.flags|=2)}function Qs(a,d,x){if(a!==null&&(d.dependencies=a.dependencies),Ql|=d.lanes,(x&d.childLanes)===0)return null;if(a!==null&&d.child!==a.child)throw Error(u(153));if(d.child!==null){for(a=d.child,x=il(a,a.pendingProps),d.child=x,x.return=d;a.sibling!==null;)a=a.sibling,x=x.sibling=il(a,a.pendingProps),x.return=d;x.sibling=null}return d.child}function ws(a,d,x){switch(d.tag){case 3:Jh(d),Wr();break;case 5:p(d);break;case 1:wi(d.type)&&Qi(d);break;case 4:s(d,d.stateNode.containerInfo);break;case 10:var C=d.type._context,R=d.memoizedProps.value;Or(Lt,C._currentValue),C._currentValue=R;break;case 13:if(C=d.memoizedState,C!==null)return C.dehydrated!==null?(Or(S,S.current&1),d.flags|=128,null):(x&d.child.childLanes)!==0?Im(a,d,x):(Or(S,S.current&1),a=Qs(a,d,x),a!==null?a.sibling:null);Or(S,S.current&1);break;case 19:if(C=(x&d.childLanes)!==0,(a.flags&128)!==0){if(C)return gp(a,d,x);d.flags|=128}if(R=d.memoizedState,R!==null&&(R.rendering=null,R.tail=null,R.lastEffect=null),Or(S,S.current),C)break;return null;case 22:case 23:return d.lanes=0,dp(a,d,x)}return Qs(a,d,x)}var yp,rd,Rm,_p;yp=function(a,d){for(var x=d.child;x!==null;){if(x.tag===5||x.tag===6)a.appendChild(x.stateNode);else if(x.tag!==4&&x.child!==null){x.child.return=x,x=x.child;continue}if(x===d)break;for(;x.sibling===null;){if(x.return===null||x.return===d)return;x=x.return}x.sibling.return=x.return,x=x.sibling}},rd=function(){},Rm=function(a,d,x,C){var R=a.memoizedProps;if(R!==C){a=d.stateNode,e(xs.current);var O=null;switch(x){case"input":R=Nr(a,R),C=Nr(a,C),O=[];break;case"select":R=Ct({},R,{value:void 0}),C=Ct({},C,{value:void 0}),O=[];break;case"textarea":R=Ke(a,R),C=Ke(a,C),O=[];break;default:typeof R.onClick!="function"&&typeof C.onClick=="function"&&(a.onclick=Ul)}Oe(x,C);var Q;x=null;for(He in R)if(!C.hasOwnProperty(He)&&R.hasOwnProperty(He)&&R[He]!=null)if(He==="style"){var me=R[He];for(Q in me)me.hasOwnProperty(Q)&&(x||(x={}),x[Q]="")}else He!=="dangerouslySetInnerHTML"&&He!=="children"&&He!=="suppressContentEditableWarning"&&He!=="suppressHydrationWarning"&&He!=="autoFocus"&&(b.hasOwnProperty(He)?O||(O=[]):(O=O||[]).push(He,null));for(He in C){var Ee=C[He];if(me=R!=null?R[He]:void 0,C.hasOwnProperty(He)&&Ee!==me&&(Ee!=null||me!=null))if(He==="style")if(me){for(Q in me)!me.hasOwnProperty(Q)||Ee&&Ee.hasOwnProperty(Q)||(x||(x={}),x[Q]="");for(Q in Ee)Ee.hasOwnProperty(Q)&&me[Q]!==Ee[Q]&&(x||(x={}),x[Q]=Ee[Q])}else x||(O||(O=[]),O.push(He,x)),x=Ee;else He==="dangerouslySetInnerHTML"?(Ee=Ee?Ee.__html:void 0,me=me?me.__html:void 0,Ee!=null&&me!==Ee&&(O=O||[]).push(He,Ee)):He==="children"?typeof Ee!="string"&&typeof Ee!="number"||(O=O||[]).push(He,""+Ee):He!=="suppressContentEditableWarning"&&He!=="suppressHydrationWarning"&&(b.hasOwnProperty(He)?(Ee!=null&&He==="onScroll"&&gn("scroll",a),O||me===Ee||(O=[])):(O=O||[]).push(He,Ee))}x&&(O=O||[]).push("style",x);var He=O;(d.updateQueue=He)&&(d.flags|=4)}},_p=function(a,d,x,C){x!==C&&(d.flags|=4)};function nd(a,d){if(!vn)switch(a.tailMode){case"hidden":d=a.tail;for(var x=null;d!==null;)d.alternate!==null&&(x=d),d=d.sibling;x===null?a.tail=null:x.sibling=null;break;case"collapsed":x=a.tail;for(var C=null;x!==null;)x.alternate!==null&&(C=x),x=x.sibling;C===null?d||a.tail===null?a.tail=null:a.tail.sibling=null:C.sibling=null}}function ss(a){var d=a.alternate!==null&&a.alternate.child===a.child,x=0,C=0;if(d)for(var R=a.child;R!==null;)x|=R.lanes|R.childLanes,C|=R.subtreeFlags&14680064,C|=R.flags&14680064,R.return=a,R=R.sibling;else for(R=a.child;R!==null;)x|=R.lanes|R.childLanes,C|=R.subtreeFlags,C|=R.flags,R.return=a,R=R.sibling;return a.subtreeFlags|=C,a.childLanes=x,d}function zm(a,d,x){var C=d.pendingProps;switch(qh(d),d.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return ss(d),null;case 1:return wi(d.type)&&Go(),ss(d),null;case 3:return C=d.stateNode,h(),rn(zi),rn(ii),z(),C.pendingContext&&(C.context=C.pendingContext,C.pendingContext=null),(a===null||a.child===null)&&(Nc(d)?d.flags|=4:a===null||a.memoizedState.isDehydrated&&(d.flags&256)===0||(d.flags|=1024,_s!==null&&(Ep(_s),_s=null))),rd(a,d),ss(d),null;case 5:y(d);var R=e(g.current);if(x=d.type,a!==null&&d.stateNode!=null)Rm(a,d,x,C,R),a.ref!==d.ref&&(d.flags|=512,d.flags|=2097152);else{if(!C){if(d.stateNode===null)throw Error(u(166));return ss(d),null}if(a=e(xs.current),Nc(d)){C=d.stateNode,x=d.type;var O=d.memoizedProps;switch(C[ms]=d,C[$o]=O,a=(d.mode&1)!==0,x){case"dialog":gn("cancel",C),gn("close",C);break;case"iframe":case"object":case"embed":gn("load",C);break;case"video":case"audio":for(R=0;R<\/script>",a=a.removeChild(a.firstChild)):typeof C.is=="string"?a=Q.createElement(x,{is:C.is}):(a=Q.createElement(x),x==="select"&&(Q=a,C.multiple?Q.multiple=!0:C.size&&(Q.size=C.size))):a=Q.createElementNS(a,x),a[ms]=d,a[$o]=C,yp(a,d,!1,!1),d.stateNode=a;e:{switch(Q=Ae(x,C),x){case"dialog":gn("cancel",a),gn("close",a),R=C;break;case"iframe":case"object":case"embed":gn("load",a),R=C;break;case"video":case"audio":for(R=0;RWu&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304)}else{if(!C)if(a=E(Q),a!==null){if(d.flags|=128,C=!0,x=a.updateQueue,x!==null&&(d.updateQueue=x,d.flags|=4),nd(O,!0),O.tail===null&&O.tailMode==="hidden"&&!Q.alternate&&!vn)return ss(d),null}else 2*kr()-O.renderingStartTime>Wu&&x!==1073741824&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304);O.isBackwards?(Q.sibling=d.child,d.child=Q):(x=O.last,x!==null?x.sibling=Q:d.child=Q,O.last=Q)}return O.tail!==null?(d=O.tail,O.rendering=d,O.tail=d.sibling,O.renderingStartTime=kr(),d.sibling=null,x=S.current,Or(S,C?x&1|2:x&1),d):(ss(d),null);case 22:case 23:return Ap(),C=d.memoizedState!==null,a!==null&&a.memoizedState!==null!==C&&(d.flags|=8192),C&&(d.mode&1)!==0?(Js&1073741824)!==0&&(ss(d),d.subtreeFlags&6&&(d.flags|=8192)):ss(d),null;case 24:return null;case 25:return null}throw Error(u(156,d.tag))}function sy(a,d){switch(qh(d),d.tag){case 1:return wi(d.type)&&Go(),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return h(),rn(zi),rn(ii),z(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 5:return y(d),null;case 13:if(rn(S),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(u(340));Wr()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return rn(S),null;case 4:return h(),null;case 10:return fi(d.type._context),null;case 22:case 23:return Ap(),null;case 24:return null;default:return null}}var $u=!1,Ui=!1,Dm=typeof WeakSet=="function"?WeakSet:Set,Ut=null;function Uu(a,d){var x=a.ref;if(x!==null)if(typeof x=="function")try{x(null)}catch(C){$n(a,d,C)}else x.current=null}function vp(a,d,x){try{x()}catch(C){$n(a,d,C)}}var sf=!1;function xp(a,d){if(Vl=bi,a=Nh(),Fl(a)){if("selectionStart"in a)var x={start:a.selectionStart,end:a.selectionEnd};else e:{x=(x=a.ownerDocument)&&x.defaultView||window;var C=x.getSelection&&x.getSelection();if(C&&C.rangeCount!==0){x=C.anchorNode;var R=C.anchorOffset,O=C.focusNode;C=C.focusOffset;try{x.nodeType,O.nodeType}catch{x=null;break e}var Q=0,me=-1,Ee=-1,He=0,ht=0,lt=a,st=null;t:for(;;){for(var Ot;lt!==x||R!==0&<.nodeType!==3||(me=Q+R),lt!==O||C!==0&<.nodeType!==3||(Ee=Q+C),lt.nodeType===3&&(Q+=lt.nodeValue.length),(Ot=lt.firstChild)!==null;)st=lt,lt=Ot;for(;;){if(lt===a)break t;if(st===x&&++He===R&&(me=Q),st===O&&++ht===C&&(Ee=Q),(Ot=lt.nextSibling)!==null)break;lt=st,st=lt.parentNode}lt=Ot}x=me===-1||Ee===-1?null:{start:me,end:Ee}}else x=null}x=x||{start:0,end:0}}else x=null;for(Bo={focusedElem:a,selectionRange:x},bi=!1,Ut=d;Ut!==null;)if(d=Ut,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Ut=a;else for(;Ut!==null;){d=Ut;try{var Vt=d.alternate;if((d.flags&1024)!==0)switch(d.tag){case 0:case 11:case 15:break;case 1:if(Vt!==null){var Qt=Vt.memoizedProps,In=Vt.memoizedState,Ve=d.stateNode,Le=Ve.getSnapshotBeforeUpdate(d.elementType===d.type?Qt:Xn(d.type,Qt),In);Ve.__reactInternalSnapshotBeforeUpdate=Le}break;case 3:var Ge=d.stateNode.containerInfo;Ge.nodeType===1?Ge.textContent="":Ge.nodeType===9&&Ge.documentElement&&Ge.removeChild(Ge.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch(bt){$n(d,d.return,bt)}if(a=d.sibling,a!==null){a.return=d.return,Ut=a;break}Ut=d.return}return Vt=sf,sf=!1,Vt}function Vu(a,d,x){var C=d.updateQueue;if(C=C!==null?C.lastEffect:null,C!==null){var R=C=C.next;do{if((R.tag&a)===a){var O=R.destroy;R.destroy=void 0,O!==void 0&&vp(d,x,O)}R=R.next}while(R!==C)}}function Ia(a,d){if(d=d.updateQueue,d=d!==null?d.lastEffect:null,d!==null){var x=d=d.next;do{if((x.tag&a)===a){var C=x.create;x.destroy=C()}x=x.next}while(x!==d)}}function Gc(a){var d=a.ref;if(d!==null){var x=a.stateNode;switch(a.tag){case 5:a=x;break;default:a=x}typeof d=="function"?d(a):d.current=a}}function Lm(a){var d=a.alternate;d!==null&&(a.alternate=null,Lm(d)),a.child=null,a.deletions=null,a.sibling=null,a.tag===5&&(d=a.stateNode,d!==null&&(delete d[ms],delete d[$o],delete d[Uo],delete d[Mu],delete d[Vo])),a.stateNode=null,a.return=null,a.dependencies=null,a.memoizedProps=null,a.memoizedState=null,a.pendingProps=null,a.stateNode=null,a.updateQueue=null}function Fm(a){return a.tag===5||a.tag===3||a.tag===4}function Om(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||Fm(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function bp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.nodeType===8?x.parentNode.insertBefore(a,d):x.insertBefore(a,d):(x.nodeType===8?(d=x.parentNode,d.insertBefore(a,x)):(d=x,d.appendChild(a)),x=x._reactRootContainer,x!=null||d.onclick!==null||(d.onclick=Ul));else if(C!==4&&(a=a.child,a!==null))for(bp(a,d,x),a=a.sibling;a!==null;)bp(a,d,x),a=a.sibling}function wp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.insertBefore(a,d):x.appendChild(a);else if(C!==4&&(a=a.child,a!==null))for(wp(a,d,x),a=a.sibling;a!==null;)wp(a,d,x),a=a.sibling}var Vi=null,Jo=!1;function Ma(a,d,x){for(x=x.child;x!==null;)jm(a,d,x),x=x.sibling}function jm(a,d,x){if(ui&&typeof ui.onCommitFiberUnmount=="function")try{ui.onCommitFiberUnmount(Ns,x)}catch{}switch(x.tag){case 5:Ui||Uu(x,d);case 6:var C=Vi,R=Jo;Vi=null,Ma(a,d,x),Vi=C,Jo=R,Vi!==null&&(Jo?(a=Vi,x=x.stateNode,a.nodeType===8?a.parentNode.removeChild(x):a.removeChild(x)):Vi.removeChild(x.stateNode));break;case 18:Vi!==null&&(Jo?(a=Vi,x=x.stateNode,a.nodeType===8?Fc(a.parentNode,x):a.nodeType===1&&Fc(a,x),fs(a)):Fc(Vi,x.stateNode));break;case 4:C=Vi,R=Jo,Vi=x.stateNode.containerInfo,Jo=!0,Ma(a,d,x),Vi=C,Jo=R;break;case 0:case 11:case 14:case 15:if(!Ui&&(C=x.updateQueue,C!==null&&(C=C.lastEffect,C!==null))){R=C=C.next;do{var O=R,Q=O.destroy;O=O.tag,Q!==void 0&&((O&2)!==0||(O&4)!==0)&&vp(x,d,Q),R=R.next}while(R!==C)}Ma(a,d,x);break;case 1:if(!Ui&&(Uu(x,d),C=x.stateNode,typeof C.componentWillUnmount=="function"))try{C.props=x.memoizedProps,C.state=x.memoizedState,C.componentWillUnmount()}catch(me){$n(x,d,me)}Ma(a,d,x);break;case 21:Ma(a,d,x);break;case 22:x.mode&1?(Ui=(C=Ui)||x.memoizedState!==null,Ma(a,d,x),Ui=C):Ma(a,d,x);break;default:Ma(a,d,x)}}function Sp(a){var d=a.updateQueue;if(d!==null){a.updateQueue=null;var x=a.stateNode;x===null&&(x=a.stateNode=new Dm),d.forEach(function(C){var R=pd.bind(null,a,C);x.has(C)||(x.add(C),C.then(R,R))})}}function Ss(a,d){var x=d.deletions;if(x!==null)for(var C=0;CR&&(R=Q),C&=~O}if(C=R,C=kr()-C,C=(120>C?120:480>C?480:1080>C?1080:1920>C?1920:3e3>C?3e3:4320>C?4320:1960*Bm(C/1960))-C,10a?16:a,ec===null)var C=!1;else{if(a=ec,ec=null,cd=0,(Br&6)!==0)throw Error(u(331));var R=Br;for(Br|=4,Ut=a.current;Ut!==null;){var O=Ut,Q=O.child;if((Ut.flags&16)!==0){var me=O.deletions;if(me!==null){for(var Ee=0;Eekr()-Pp?Kc(a,0):ld|=x),Fs(a,d)}function Hm(a,d){d===0&&((a.mode&1)===0?d=1:(d=yu,yu<<=1,(yu&130023424)===0&&(yu=4194304)));var x=mi();a=Rs(a,d),a!==null&&(Na(a,d,x),Fs(a,x))}function ay(a){var d=a.memoizedState,x=0;d!==null&&(x=d.retryLane),Hm(a,x)}function pd(a,d){var x=0;switch(a.tag){case 13:var C=a.stateNode,R=a.memoizedState;R!==null&&(x=R.retryLane);break;case 19:C=a.stateNode;break;default:throw Error(u(314))}C!==null&&C.delete(d),Hm(a,x)}var Jn;Jn=function(a,d,x){if(a!==null)if(a.memoizedProps!==d.pendingProps||zi.current)Ds=!0;else{if((a.lanes&x)===0&&(d.flags&128)===0)return Ds=!1,ws(a,d,x);Ds=(a.flags&131072)!==0}else Ds=!1,vn&&(d.flags&1048576)!==0&&Fr(d,kt,d.index);switch(d.lanes=0,d.tag){case 2:var C=d.type;Bu(a,d),a=d.pendingProps;var R=Hs(d,ii.current);pi(d,x),R=we(null,d,C,a,R,x);var O=he();return d.flags|=1,typeof R=="object"&&R!==null&&typeof R.render=="function"&&R.$$typeof===void 0?(d.tag=1,d.memoizedState=null,d.updateQueue=null,wi(C)?(O=!0,Qi(d)):O=!1,d.memoizedState=R.state!==null&&R.state!==void 0?R.state:null,Ca(d),R.updater=Ys,d.stateNode=R,R._reactInternals=d,Vc(d,C,a,x),d=Nu(null,d,C,!0,O,x)):(d.tag=0,vn&&O&&ql(d),$i(null,d,R,x),d=d.child),d;case 16:C=d.elementType;e:{switch(Bu(a,d),a=d.pendingProps,R=C._init,C=R(C._payload),d.type=C,R=d.tag=Xu(C),a=Xn(C,a),R){case 0:d=fp(null,d,C,a,x);break e;case 1:d=ju(null,d,C,a,x);break e;case 11:d=Em(null,d,C,a,x);break e;case 14:d=tf(null,d,C,Xn(C.type,a),x);break e}throw Error(u(306,C,""))}return d;case 0:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),fp(a,d,C,R,x);case 1:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),ju(a,d,C,R,x);case 3:e:{if(Jh(d),a===null)throw Error(u(387));C=d.pendingProps,O=d.memoizedState,R=O.element,Fu(a,d),bo(d,C,null,x);var Q=d.memoizedState;if(C=Q.element,O.isDehydrated)if(O={element:C,isDehydrated:!1,cache:Q.cache,pendingSuspenseBoundaries:Q.pendingSuspenseBoundaries,transitions:Q.transitions},d.updateQueue.baseState=O,d.memoizedState=O,d.flags&256){R=Yo(Error(u(423)),d),d=pp(a,d,C,x,R);break e}else if(C!==R){R=Yo(Error(u(424)),d),d=pp(a,d,C,x,R);break e}else for(ys=tn(d.stateNode.containerInfo.firstChild),Si=d,vn=!0,_s=null,x=Bc(d,null,C,x),d.child=x;x;)x.flags=x.flags&-3|4096,x=x.sibling;else{if(Wr(),C===R){d=Qs(a,d,x);break e}$i(a,d,C,x)}d=d.child}return d;case 5:return p(d),a===null&&jc(d),C=d.type,R=d.pendingProps,O=a!==null?a.memoizedProps:null,Q=R.children,Dc(C,R)?Q=null:O!==null&&Dc(C,O)&&(d.flags|=32),Zc(a,d),$i(a,d,Q,x),d.child;case 6:return a===null&&jc(d),null;case 13:return Im(a,d,x);case 4:return s(d,d.stateNode.containerInfo),C=d.pendingProps,a===null?d.child=Oi(d,null,C,x):$i(a,d,C,x),d.child;case 11:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Em(a,d,C,R,x);case 7:return $i(a,d,d.pendingProps,x),d.child;case 8:return $i(a,d,d.pendingProps.children,x),d.child;case 12:return $i(a,d,d.pendingProps.children,x),d.child;case 10:e:{if(C=d.type._context,R=d.pendingProps,O=d.memoizedProps,Q=R.value,Or(Lt,C._currentValue),C._currentValue=Q,O!==null)if(qs(O.value,Q)){if(O.children===R.children&&!zi.current){d=Qs(a,d,x);break e}}else for(O=d.child,O!==null&&(O.return=d);O!==null;){var me=O.dependencies;if(me!==null){Q=O.child;for(var Ee=me.firstContext;Ee!==null;){if(Ee.context===C){if(O.tag===1){Ee=rs(-1,x&-x),Ee.tag=2;var He=O.updateQueue;if(He!==null){He=He.shared;var ht=He.pending;ht===null?Ee.next=Ee:(Ee.next=ht.next,ht.next=Ee),He.pending=Ee}}O.lanes|=x,Ee=O.alternate,Ee!==null&&(Ee.lanes|=x),Lu(O.return,x,d),me.lanes|=x;break}Ee=Ee.next}}else if(O.tag===10)Q=O.type===d.type?null:O.child;else if(O.tag===18){if(Q=O.return,Q===null)throw Error(u(341));Q.lanes|=x,me=Q.alternate,me!==null&&(me.lanes|=x),Lu(Q,x,d),Q=O.sibling}else Q=O.child;if(Q!==null)Q.return=O;else for(Q=O;Q!==null;){if(Q===d){Q=null;break}if(O=Q.sibling,O!==null){O.return=Q.return,Q=O;break}Q=Q.return}O=Q}$i(a,d,R.children,x),d=d.child}return d;case 9:return R=d.type,C=d.pendingProps.children,pi(d,x),R=As(R),C=C(R),d.flags|=1,$i(a,d,C,x),d.child;case 14:return C=d.type,R=Xn(C,d.pendingProps),R=Xn(C.type,R),tf(a,d,C,R,x);case 15:return hp(a,d,d.type,d.pendingProps,x);case 17:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Bu(a,d),d.tag=1,wi(C)?(a=!0,Qi(d)):a=!1,pi(d,x),is(d,C,R),Vc(d,C,R,x),Nu(null,d,C,!0,a,x);case 19:return gp(a,d,x);case 22:return dp(a,d,x)}throw Error(u(156,d.tag))};function hf(a,d){return Ao(a,d)}function Km(a,d,x,C){this.tag=a,this.key=x,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=d,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=C,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function to(a,d,x,C){return new Km(a,d,x,C)}function md(a){return a=a.prototype,!(!a||!a.isReactComponent)}function Xu(a){if(typeof a=="function")return md(a)?1:0;if(a!=null){if(a=a.$$typeof,a===At)return 11;if(a===zr)return 14}return 2}function il(a,d){var x=a.alternate;return x===null?(x=to(a.tag,d,a.key,a.mode),x.elementType=a.elementType,x.type=a.type,x.stateNode=a.stateNode,x.alternate=a,a.alternate=x):(x.pendingProps=d,x.type=a.type,x.flags=0,x.subtreeFlags=0,x.deletions=null),x.flags=a.flags&14680064,x.childLanes=a.childLanes,x.lanes=a.lanes,x.child=a.child,x.memoizedProps=a.memoizedProps,x.memoizedState=a.memoizedState,x.updateQueue=a.updateQueue,d=a.dependencies,x.dependencies=d===null?null:{lanes:d.lanes,firstContext:d.firstContext},x.sibling=a.sibling,x.index=a.index,x.ref=a.ref,x}function df(a,d,x,C,R,O){var Q=2;if(C=a,typeof a=="function")md(a)&&(Q=1);else if(typeof a=="string")Q=5;else e:switch(a){case ct:return Yc(x.children,R,O,d);case dt:Q=8,R|=8;break;case ot:return a=to(12,x,d,R|2),a.elementType=ot,a.lanes=O,a;case Mt:return a=to(13,x,d,R),a.elementType=Mt,a.lanes=O,a;case Ht:return a=to(19,x,d,R),a.elementType=Ht,a.lanes=O,a;case Zt:return ff(x,R,O,d);default:if(typeof a=="object"&&a!==null)switch(a.$$typeof){case Xe:Q=10;break e;case Rt:Q=9;break e;case At:Q=11;break e;case zr:Q=14;break e;case ar:Q=16,C=null;break e}throw Error(u(130,a==null?a:typeof a,""))}return d=to(Q,x,d,R),d.elementType=a,d.type=C,d.lanes=O,d}function Yc(a,d,x,C){return a=to(7,a,C,d),a.lanes=x,a}function ff(a,d,x,C){return a=to(22,a,C,d),a.elementType=Zt,a.lanes=x,a.stateNode={isHidden:!1},a}function zp(a,d,x){return a=to(6,a,null,d),a.lanes=x,a}function Dp(a,d,x){return d=to(4,a.children!==null?a.children:[],a.key,d),d.lanes=x,d.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation},d}function ly(a,d,x,C,R){this.tag=d,this.containerInfo=a,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=yc(0),this.expirationTimes=yc(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=yc(0),this.identifierPrefix=C,this.onRecoverableError=R,this.mutableSourceEagerHydrationData=null}function Lp(a,d,x,C,R,O,Q,me,Ee){return a=new ly(a,d,x,me,Ee),d===1?(d=1,O===!0&&(d|=8)):d=0,O=to(3,null,null,d),a.current=O,O.stateNode=a,O.memoizedState={element:C,isDehydrated:x,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ca(O),a}function cy(a,d,x){var C=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(o){console.error(o)}}return r(),Iy.exports=Tw(),Iy.exports}var ox;function Pw(){if(ox)return cg;ox=1;var r=Y0();return cg.createRoot=r.createRoot,cg.hydrateRoot=r.hydrateRoot,cg}var Cw=Pw();const kw=M_(Cw);/** * react-router v7.9.1 * * Copyright (c) Remix Software Inc. @@ -46,9 +46,9 @@ Error generating stack: `+O.message+` * LICENSE.md file in the root directory of this source tree. * * @license MIT - */var ox="popstate";function Cw(n={}){function o(m,b){let{pathname:P,search:w,hash:l}=m.location;return By("",{pathname:P,search:w,hash:l},b.state&&b.state.usr||null,b.state&&b.state.key||"default")}function u(m,b){return typeof b=="string"?b:Jp(b)}return kw(o,u,null,n)}function Un(n,o){if(n===!1||n===null||typeof n>"u")throw new Error(o)}function ml(n,o){if(!n){typeof console<"u"&&console.warn(o);try{throw new Error(o)}catch{}}}function Ew(){return Math.random().toString(36).substring(2,10)}function ax(n,o){return{usr:n.state,key:n.key,idx:o}}function By(n,o,u=null,m){return{pathname:typeof n=="string"?n:n.pathname,search:"",hash:"",...typeof o=="string"?Vf(o):o,state:u,key:o&&o.key||m||Ew()}}function Jp({pathname:n="/",search:o="",hash:u=""}){return o&&o!=="?"&&(n+=o.charAt(0)==="?"?o:"?"+o),u&&u!=="#"&&(n+=u.charAt(0)==="#"?u:"#"+u),n}function Vf(n){let o={};if(n){let u=n.indexOf("#");u>=0&&(o.hash=n.substring(u),n=n.substring(0,u));let m=n.indexOf("?");m>=0&&(o.search=n.substring(m),n=n.substring(0,m)),n&&(o.pathname=n)}return o}function kw(n,o,u,m={}){let{window:b=document.defaultView,v5Compat:P=!1}=m,w=b.history,l="POP",L=null,B=V();B==null&&(B=0,w.replaceState({...w.state,idx:B},""));function V(){return(w.state||{idx:null}).idx}function G(){l="POP";let fe=V(),Fe=fe==null?null:fe-B;B=fe,L&&L({action:l,location:ye.location,delta:Fe})}function te(fe,Fe){l="PUSH";let Ce=By(ye.location,fe,Fe);B=V()+1;let Ie=ax(Ce,B),Le=ye.createHref(Ce);try{w.pushState(Ie,"",Le)}catch(Xe){if(Xe instanceof DOMException&&Xe.name==="DataCloneError")throw Xe;b.location.assign(Le)}P&&L&&L({action:l,location:ye.location,delta:1})}function q(fe,Fe){l="REPLACE";let Ce=By(ye.location,fe,Fe);B=V();let Ie=ax(Ce,B),Le=ye.createHref(Ce);w.replaceState(Ie,"",Le),P&&L&&L({action:l,location:ye.location,delta:0})}function Se(fe){return Mw(fe)}let ye={get action(){return l},get location(){return n(b,w)},listen(fe){if(L)throw new Error("A history only accepts one active listener");return b.addEventListener(ox,G),L=fe,()=>{b.removeEventListener(ox,G),L=null}},createHref(fe){return o(b,fe)},createURL:Se,encodeLocation(fe){let Fe=Se(fe);return{pathname:Fe.pathname,search:Fe.search,hash:Fe.hash}},push:te,replace:q,go(fe){return w.go(fe)}};return ye}function Mw(n,o=!1){let u="http://localhost";typeof window<"u"&&(u=window.location.origin!=="null"?window.location.origin:window.location.href),Un(u,"No window.location.(origin|href) available to create URL");let m=typeof n=="string"?n:Jp(n);return m=m.replace(/ $/,"%20"),!o&&m.startsWith("//")&&(m=u+m),new URL(m,u)}function Y0(n,o,u="/"){return Iw(n,o,u,!1)}function Iw(n,o,u,m){let b=typeof o=="string"?Vf(o):o,P=cu(b.pathname||"/",u);if(P==null)return null;let w=Q0(n);Aw(w);let l=null;for(let L=0;l==null&&L{let V={relativePath:B===void 0?w.path||"":B,caseSensitive:w.caseSensitive===!0,childrenIndex:l,route:w};if(V.relativePath.startsWith("/")){if(!V.relativePath.startsWith(m)&&L)return;Un(V.relativePath.startsWith(m),`Absolute route path "${V.relativePath}" nested under path "${m}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),V.relativePath=V.relativePath.slice(m.length)}let G=au([m,V.relativePath]),te=u.concat(V);w.children&&w.children.length>0&&(Un(w.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${G}".`),Q0(w.children,o,te,G,L)),!(w.path==null&&!w.index)&&o.push({path:G,score:jw(G,w.index),routesMeta:te})};return n.forEach((w,l)=>{var L;if(w.path===""||!((L=w.path)!=null&&L.includes("?")))P(w,l);else for(let B of J0(w.path))P(w,l,!0,B)}),o}function J0(n){let o=n.split("/");if(o.length===0)return[];let[u,...m]=o,b=u.endsWith("?"),P=u.replace(/\?$/,"");if(m.length===0)return b?[P,""]:[P];let w=J0(m.join("/")),l=[];return l.push(...w.map(L=>L===""?P:[P,L].join("/"))),b&&l.push(...w),l.map(L=>n.startsWith("/")&&L===""?"/":L)}function Aw(n){n.sort((o,u)=>o.score!==u.score?u.score-o.score:Nw(o.routesMeta.map(m=>m.childrenIndex),u.routesMeta.map(m=>m.childrenIndex)))}var zw=/^:[\w-]+$/,Rw=3,Dw=2,Lw=1,Fw=10,Ow=-2,lx=n=>n==="*";function jw(n,o){let u=n.split("/"),m=u.length;return u.some(lx)&&(m+=Ow),o&&(m+=Dw),u.filter(b=>!lx(b)).reduce((b,P)=>b+(zw.test(P)?Rw:P===""?Lw:Fw),m)}function Nw(n,o){return n.length===o.length&&n.slice(0,-1).every((m,b)=>m===o[b])?n[n.length-1]-o[o.length-1]:0}function Bw(n,o,u=!1){let{routesMeta:m}=n,b={},P="/",w=[];for(let l=0;l{if(V==="*"){let Se=l[te]||"";w=P.slice(0,P.length-Se.length).replace(/(.)\/+$/,"$1")}const q=l[te];return G&&!q?B[V]=void 0:B[V]=(q||"").replace(/%2F/g,"/"),B},{}),pathname:P,pathnameBase:w,pattern:n}}function $w(n,o=!1,u=!0){ml(n==="*"||!n.endsWith("*")||n.endsWith("/*"),`Route path "${n}" will be treated as if it were "${n.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${n.replace(/\*$/,"/*")}".`);let m=[],b="^"+n.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(w,l,L)=>(m.push({paramName:l,isOptional:L!=null}),L?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return n.endsWith("*")?(m.push({paramName:"*"}),b+=n==="*"||n==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):u?b+="\\/*$":n!==""&&n!=="/"&&(b+="(?:(?=\\/|$))"),[new RegExp(b,o?void 0:"i"),m]}function Uw(n){try{return n.split("/").map(o=>decodeURIComponent(o).replace(/\//g,"%2F")).join("/")}catch(o){return ml(!1,`The URL path "${n}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${o}).`),n}}function cu(n,o){if(o==="/")return n;if(!n.toLowerCase().startsWith(o.toLowerCase()))return null;let u=o.endsWith("/")?o.length-1:o.length,m=n.charAt(u);return m&&m!=="/"?null:n.slice(u)||"/"}function Vw(n,o="/"){let{pathname:u,search:m="",hash:b=""}=typeof n=="string"?Vf(n):n;return{pathname:u?u.startsWith("/")?u:Zw(u,o):o,search:Ww(m),hash:Hw(b)}}function Zw(n,o){let u=o.replace(/\/+$/,"").split("/");return n.split("/").forEach(b=>{b===".."?u.length>1&&u.pop():b!=="."&&u.push(b)}),u.length>1?u.join("/"):"/"}function Iy(n,o,u,m){return`Cannot include a '${n}' character in a manually specified \`to.${o}\` field [${JSON.stringify(m)}]. Please separate it out to the \`to.${u}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Gw(n){return n.filter((o,u)=>u===0||o.route.path&&o.route.path.length>0)}function I_(n){let o=Gw(n);return o.map((u,m)=>m===o.length-1?u.pathname:u.pathnameBase)}function A_(n,o,u,m=!1){let b;typeof n=="string"?b=Vf(n):(b={...n},Un(!b.pathname||!b.pathname.includes("?"),Iy("?","pathname","search",b)),Un(!b.pathname||!b.pathname.includes("#"),Iy("#","pathname","hash",b)),Un(!b.search||!b.search.includes("#"),Iy("#","search","hash",b)));let P=n===""||b.pathname==="",w=P?"/":b.pathname,l;if(w==null)l=u;else{let G=o.length-1;if(!m&&w.startsWith("..")){let te=w.split("/");for(;te[0]==="..";)te.shift(),G-=1;b.pathname=te.join("/")}l=G>=0?o[G]:"/"}let L=Vw(b,l),B=w&&w!=="/"&&w.endsWith("/"),V=(P||w===".")&&u.endsWith("/");return!L.pathname.endsWith("/")&&(B||V)&&(L.pathname+="/"),L}var au=n=>n.join("/").replace(/\/\/+/g,"/"),qw=n=>n.replace(/\/+$/,"").replace(/^\/*/,"/"),Ww=n=>!n||n==="?"?"":n.startsWith("?")?n:"?"+n,Hw=n=>!n||n==="#"?"":n.startsWith("#")?n:"#"+n;function Kw(n){return n!=null&&typeof n.status=="number"&&typeof n.statusText=="string"&&typeof n.internal=="boolean"&&"data"in n}var eb=["POST","PUT","PATCH","DELETE"];new Set(eb);var Xw=["GET",...eb];new Set(Xw);var Zf=ee.createContext(null);Zf.displayName="DataRouter";var $g=ee.createContext(null);$g.displayName="DataRouterState";ee.createContext(!1);var tb=ee.createContext({isTransitioning:!1});tb.displayName="ViewTransition";var Yw=ee.createContext(new Map);Yw.displayName="Fetchers";var Qw=ee.createContext(null);Qw.displayName="Await";var yl=ee.createContext(null);yl.displayName="Navigation";var gm=ee.createContext(null);gm.displayName="Location";var La=ee.createContext({outlet:null,matches:[],isDataRoute:!1});La.displayName="Route";var z_=ee.createContext(null);z_.displayName="RouteError";function Jw(n,{relative:o}={}){Un(Gf(),"useHref() may be used only in the context of a component.");let{basename:u,navigator:m}=ee.useContext(yl),{hash:b,pathname:P,search:w}=ym(n,{relative:o}),l=P;return u!=="/"&&(l=P==="/"?u:au([u,P])),m.createHref({pathname:l,search:w,hash:b})}function Gf(){return ee.useContext(gm)!=null}function _l(){return Un(Gf(),"useLocation() may be used only in the context of a component."),ee.useContext(gm).location}var rb="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function nb(n){ee.useContext(yl).static||ee.useLayoutEffect(n)}function qf(){let{isDataRoute:n}=ee.useContext(La);return n?m2():e2()}function e2(){Un(Gf(),"useNavigate() may be used only in the context of a component.");let n=ee.useContext(Zf),{basename:o,navigator:u}=ee.useContext(yl),{matches:m}=ee.useContext(La),{pathname:b}=_l(),P=JSON.stringify(I_(m)),w=ee.useRef(!1);return nb(()=>{w.current=!0}),ee.useCallback((L,B={})=>{if(ml(w.current,rb),!w.current)return;if(typeof L=="number"){u.go(L);return}let V=A_(L,JSON.parse(P),b,B.relative==="path");n==null&&o!=="/"&&(V.pathname=V.pathname==="/"?o:au([o,V.pathname])),(B.replace?u.replace:u.push)(V,B.state,B)},[o,u,P,b,n])}var t2=ee.createContext(null);function r2(n){let o=ee.useContext(La).outlet;return o&&ee.createElement(t2.Provider,{value:n},o)}function n2(){let{matches:n}=ee.useContext(La),o=n[n.length-1];return o?o.params:{}}function ym(n,{relative:o}={}){let{matches:u}=ee.useContext(La),{pathname:m}=_l(),b=JSON.stringify(I_(u));return ee.useMemo(()=>A_(n,JSON.parse(b),m,o==="path"),[n,b,m,o])}function i2(n,o){return ib(n,o)}function ib(n,o,u,m,b){var Ce;Un(Gf(),"useRoutes() may be used only in the context of a component.");let{navigator:P}=ee.useContext(yl),{matches:w}=ee.useContext(La),l=w[w.length-1],L=l?l.params:{},B=l?l.pathname:"/",V=l?l.pathnameBase:"/",G=l&&l.route;{let Ie=G&&G.path||"";sb(B,!G||Ie.endsWith("*")||Ie.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${B}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + */var ax="popstate";function Ew(r={}){function o(m,b){let{pathname:P,search:w,hash:l}=m.location;return Uy("",{pathname:P,search:w,hash:l},b.state&&b.state.usr||null,b.state&&b.state.key||"default")}function u(m,b){return typeof b=="string"?b:em(b)}return Mw(o,u,null,r)}function Un(r,o){if(r===!1||r===null||typeof r>"u")throw new Error(o)}function gl(r,o){if(!r){typeof console<"u"&&console.warn(o);try{throw new Error(o)}catch{}}}function Iw(){return Math.random().toString(36).substring(2,10)}function lx(r,o){return{usr:r.state,key:r.key,idx:o}}function Uy(r,o,u=null,m){return{pathname:typeof r=="string"?r:r.pathname,search:"",hash:"",...typeof o=="string"?Zf(o):o,state:u,key:o&&o.key||m||Iw()}}function em({pathname:r="/",search:o="",hash:u=""}){return o&&o!=="?"&&(r+=o.charAt(0)==="?"?o:"?"+o),u&&u!=="#"&&(r+=u.charAt(0)==="#"?u:"#"+u),r}function Zf(r){let o={};if(r){let u=r.indexOf("#");u>=0&&(o.hash=r.substring(u),r=r.substring(0,u));let m=r.indexOf("?");m>=0&&(o.search=r.substring(m),r=r.substring(0,m)),r&&(o.pathname=r)}return o}function Mw(r,o,u,m={}){let{window:b=document.defaultView,v5Compat:P=!1}=m,w=b.history,l="POP",L=null,N=Z();N==null&&(N=0,w.replaceState({...w.state,idx:N},""));function Z(){return(w.state||{idx:null}).idx}function V(){l="POP";let Pe=Z(),ze=Pe==null?null:Pe-N;N=Pe,L&&L({action:l,location:pe.location,delta:ze})}function ee(Pe,ze){l="PUSH";let Re=Uy(pe.location,Pe,ze);N=Z()+1;let Te=lx(Re,N),Fe=pe.createHref(Re);try{w.pushState(Te,"",Fe)}catch(Ye){if(Ye instanceof DOMException&&Ye.name==="DataCloneError")throw Ye;b.location.assign(Fe)}P&&L&&L({action:l,location:pe.location,delta:1})}function q(Pe,ze){l="REPLACE";let Re=Uy(pe.location,Pe,ze);N=Z();let Te=lx(Re,N),Fe=pe.createHref(Re);w.replaceState(Te,"",Fe),P&&L&&L({action:l,location:pe.location,delta:0})}function be(Pe){return Aw(Pe)}let pe={get action(){return l},get location(){return r(b,w)},listen(Pe){if(L)throw new Error("A history only accepts one active listener");return b.addEventListener(ax,V),L=Pe,()=>{b.removeEventListener(ax,V),L=null}},createHref(Pe){return o(b,Pe)},createURL:be,encodeLocation(Pe){let ze=be(Pe);return{pathname:ze.pathname,search:ze.search,hash:ze.hash}},push:ee,replace:q,go(Pe){return w.go(Pe)}};return pe}function Aw(r,o=!1){let u="http://localhost";typeof window<"u"&&(u=window.location.origin!=="null"?window.location.origin:window.location.href),Un(u,"No window.location.(origin|href) available to create URL");let m=typeof r=="string"?r:em(r);return m=m.replace(/ $/,"%20"),!o&&m.startsWith("//")&&(m=u+m),new URL(m,u)}function Q0(r,o,u="/"){return Rw(r,o,u,!1)}function Rw(r,o,u,m){let b=typeof o=="string"?Zf(o):o,P=hu(b.pathname||"/",u);if(P==null)return null;let w=J0(r);zw(w);let l=null;for(let L=0;l==null&&L{let Z={relativePath:N===void 0?w.path||"":N,caseSensitive:w.caseSensitive===!0,childrenIndex:l,route:w};if(Z.relativePath.startsWith("/")){if(!Z.relativePath.startsWith(m)&&L)return;Un(Z.relativePath.startsWith(m),`Absolute route path "${Z.relativePath}" nested under path "${m}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),Z.relativePath=Z.relativePath.slice(m.length)}let V=lu([m,Z.relativePath]),ee=u.concat(Z);w.children&&w.children.length>0&&(Un(w.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${V}".`),J0(w.children,o,ee,V,L)),!(w.path==null&&!w.index)&&o.push({path:V,score:Bw(V,w.index),routesMeta:ee})};return r.forEach((w,l)=>{var L;if(w.path===""||!((L=w.path)!=null&&L.includes("?")))P(w,l);else for(let N of eb(w.path))P(w,l,!0,N)}),o}function eb(r){let o=r.split("/");if(o.length===0)return[];let[u,...m]=o,b=u.endsWith("?"),P=u.replace(/\?$/,"");if(m.length===0)return b?[P,""]:[P];let w=eb(m.join("/")),l=[];return l.push(...w.map(L=>L===""?P:[P,L].join("/"))),b&&l.push(...w),l.map(L=>r.startsWith("/")&&L===""?"/":L)}function zw(r){r.sort((o,u)=>o.score!==u.score?u.score-o.score:$w(o.routesMeta.map(m=>m.childrenIndex),u.routesMeta.map(m=>m.childrenIndex)))}var Dw=/^:[\w-]+$/,Lw=3,Fw=2,Ow=1,jw=10,Nw=-2,cx=r=>r==="*";function Bw(r,o){let u=r.split("/"),m=u.length;return u.some(cx)&&(m+=Nw),o&&(m+=Fw),u.filter(b=>!cx(b)).reduce((b,P)=>b+(Dw.test(P)?Lw:P===""?Ow:jw),m)}function $w(r,o){return r.length===o.length&&r.slice(0,-1).every((m,b)=>m===o[b])?r[r.length-1]-o[o.length-1]:0}function Uw(r,o,u=!1){let{routesMeta:m}=r,b={},P="/",w=[];for(let l=0;l{if(Z==="*"){let be=l[ee]||"";w=P.slice(0,P.length-be.length).replace(/(.)\/+$/,"$1")}const q=l[ee];return V&&!q?N[Z]=void 0:N[Z]=(q||"").replace(/%2F/g,"/"),N},{}),pathname:P,pathnameBase:w,pattern:r}}function Vw(r,o=!1,u=!0){gl(r==="*"||!r.endsWith("*")||r.endsWith("/*"),`Route path "${r}" will be treated as if it were "${r.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${r.replace(/\*$/,"/*")}".`);let m=[],b="^"+r.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(w,l,L)=>(m.push({paramName:l,isOptional:L!=null}),L?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return r.endsWith("*")?(m.push({paramName:"*"}),b+=r==="*"||r==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):u?b+="\\/*$":r!==""&&r!=="/"&&(b+="(?:(?=\\/|$))"),[new RegExp(b,o?void 0:"i"),m]}function Zw(r){try{return r.split("/").map(o=>decodeURIComponent(o).replace(/\//g,"%2F")).join("/")}catch(o){return gl(!1,`The URL path "${r}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${o}).`),r}}function hu(r,o){if(o==="/")return r;if(!r.toLowerCase().startsWith(o.toLowerCase()))return null;let u=o.endsWith("/")?o.length-1:o.length,m=r.charAt(u);return m&&m!=="/"?null:r.slice(u)||"/"}function Gw(r,o="/"){let{pathname:u,search:m="",hash:b=""}=typeof r=="string"?Zf(r):r;return{pathname:u?u.startsWith("/")?u:qw(u,o):o,search:Kw(m),hash:Xw(b)}}function qw(r,o){let u=o.replace(/\/+$/,"").split("/");return r.split("/").forEach(b=>{b===".."?u.length>1&&u.pop():b!=="."&&u.push(b)}),u.length>1?u.join("/"):"/"}function Ry(r,o,u,m){return`Cannot include a '${r}' character in a manually specified \`to.${o}\` field [${JSON.stringify(m)}]. Please separate it out to the \`to.${u}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Ww(r){return r.filter((o,u)=>u===0||o.route.path&&o.route.path.length>0)}function R_(r){let o=Ww(r);return o.map((u,m)=>m===o.length-1?u.pathname:u.pathnameBase)}function z_(r,o,u,m=!1){let b;typeof r=="string"?b=Zf(r):(b={...r},Un(!b.pathname||!b.pathname.includes("?"),Ry("?","pathname","search",b)),Un(!b.pathname||!b.pathname.includes("#"),Ry("#","pathname","hash",b)),Un(!b.search||!b.search.includes("#"),Ry("#","search","hash",b)));let P=r===""||b.pathname==="",w=P?"/":b.pathname,l;if(w==null)l=u;else{let V=o.length-1;if(!m&&w.startsWith("..")){let ee=w.split("/");for(;ee[0]==="..";)ee.shift(),V-=1;b.pathname=ee.join("/")}l=V>=0?o[V]:"/"}let L=Gw(b,l),N=w&&w!=="/"&&w.endsWith("/"),Z=(P||w===".")&&u.endsWith("/");return!L.pathname.endsWith("/")&&(N||Z)&&(L.pathname+="/"),L}var lu=r=>r.join("/").replace(/\/\/+/g,"/"),Hw=r=>r.replace(/\/+$/,"").replace(/^\/*/,"/"),Kw=r=>!r||r==="?"?"":r.startsWith("?")?r:"?"+r,Xw=r=>!r||r==="#"?"":r.startsWith("#")?r:"#"+r;function Yw(r){return r!=null&&typeof r.status=="number"&&typeof r.statusText=="string"&&typeof r.internal=="boolean"&&"data"in r}var tb=["POST","PUT","PATCH","DELETE"];new Set(tb);var Qw=["GET",...tb];new Set(Qw);var Gf=te.createContext(null);Gf.displayName="DataRouter";var Vg=te.createContext(null);Vg.displayName="DataRouterState";te.createContext(!1);var rb=te.createContext({isTransitioning:!1});rb.displayName="ViewTransition";var Jw=te.createContext(new Map);Jw.displayName="Fetchers";var e2=te.createContext(null);e2.displayName="Await";var _l=te.createContext(null);_l.displayName="Navigation";var ym=te.createContext(null);ym.displayName="Location";var Fa=te.createContext({outlet:null,matches:[],isDataRoute:!1});Fa.displayName="Route";var D_=te.createContext(null);D_.displayName="RouteError";function t2(r,{relative:o}={}){Un(qf(),"useHref() may be used only in the context of a component.");let{basename:u,navigator:m}=te.useContext(_l),{hash:b,pathname:P,search:w}=_m(r,{relative:o}),l=P;return u!=="/"&&(l=P==="/"?u:lu([u,P])),m.createHref({pathname:l,search:w,hash:b})}function qf(){return te.useContext(ym)!=null}function vl(){return Un(qf(),"useLocation() may be used only in the context of a component."),te.useContext(ym).location}var nb="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function ib(r){te.useContext(_l).static||te.useLayoutEffect(r)}function Wf(){let{isDataRoute:r}=te.useContext(Fa);return r?y2():r2()}function r2(){Un(qf(),"useNavigate() may be used only in the context of a component.");let r=te.useContext(Gf),{basename:o,navigator:u}=te.useContext(_l),{matches:m}=te.useContext(Fa),{pathname:b}=vl(),P=JSON.stringify(R_(m)),w=te.useRef(!1);return ib(()=>{w.current=!0}),te.useCallback((L,N={})=>{if(gl(w.current,nb),!w.current)return;if(typeof L=="number"){u.go(L);return}let Z=z_(L,JSON.parse(P),b,N.relative==="path");r==null&&o!=="/"&&(Z.pathname=Z.pathname==="/"?o:lu([o,Z.pathname])),(N.replace?u.replace:u.push)(Z,N.state,N)},[o,u,P,b,r])}var n2=te.createContext(null);function i2(r){let o=te.useContext(Fa).outlet;return o&&te.createElement(n2.Provider,{value:r},o)}function s2(){let{matches:r}=te.useContext(Fa),o=r[r.length-1];return o?o.params:{}}function _m(r,{relative:o}={}){let{matches:u}=te.useContext(Fa),{pathname:m}=vl(),b=JSON.stringify(R_(u));return te.useMemo(()=>z_(r,JSON.parse(b),m,o==="path"),[r,b,m,o])}function o2(r,o){return sb(r,o)}function sb(r,o,u,m,b){var Re;Un(qf(),"useRoutes() may be used only in the context of a component.");let{navigator:P}=te.useContext(_l),{matches:w}=te.useContext(Fa),l=w[w.length-1],L=l?l.params:{},N=l?l.pathname:"/",Z=l?l.pathnameBase:"/",V=l&&l.route;{let Te=V&&V.path||"";ob(N,!V||Te.endsWith("*")||Te.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${N}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. -Please change the parent to .`)}let te=_l(),q;if(o){let Ie=typeof o=="string"?Vf(o):o;Un(V==="/"||((Ce=Ie.pathname)==null?void 0:Ce.startsWith(V)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${V}" but pathname "${Ie.pathname}" was given in the \`location\` prop.`),q=Ie}else q=te;let Se=q.pathname||"/",ye=Se;if(V!=="/"){let Ie=V.replace(/^\//,"").split("/");ye="/"+Se.replace(/^\//,"").split("/").slice(Ie.length).join("/")}let fe=Y0(n,{pathname:ye});ml(G||fe!=null,`No routes matched location "${q.pathname}${q.search}${q.hash}" `),ml(fe==null||fe[fe.length-1].route.element!==void 0||fe[fe.length-1].route.Component!==void 0||fe[fe.length-1].route.lazy!==void 0,`Matched leaf route at location "${q.pathname}${q.search}${q.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let Fe=c2(fe&&fe.map(Ie=>Object.assign({},Ie,{params:Object.assign({},L,Ie.params),pathname:au([V,P.encodeLocation?P.encodeLocation(Ie.pathname).pathname:Ie.pathname]),pathnameBase:Ie.pathnameBase==="/"?V:au([V,P.encodeLocation?P.encodeLocation(Ie.pathnameBase).pathname:Ie.pathnameBase])})),w,u,m,b);return o&&Fe?ee.createElement(gm.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...q},navigationType:"POP"}},Fe):Fe}function s2(){let n=p2(),o=Kw(n)?`${n.status} ${n.statusText}`:n instanceof Error?n.message:JSON.stringify(n),u=n instanceof Error?n.stack:null,m="rgba(200,200,200, 0.5)",b={padding:"0.5rem",backgroundColor:m},P={padding:"2px 4px",backgroundColor:m},w=null;return console.error("Error handled by React Router default ErrorBoundary:",n),w=ee.createElement(ee.Fragment,null,ee.createElement("p",null,"💿 Hey developer 👋"),ee.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",ee.createElement("code",{style:P},"ErrorBoundary")," or"," ",ee.createElement("code",{style:P},"errorElement")," prop on your route.")),ee.createElement(ee.Fragment,null,ee.createElement("h2",null,"Unexpected Application Error!"),ee.createElement("h3",{style:{fontStyle:"italic"}},o),u?ee.createElement("pre",{style:b},u):null,w)}var o2=ee.createElement(s2,null),a2=class extends ee.Component{constructor(n){super(n),this.state={location:n.location,revalidation:n.revalidation,error:n.error}}static getDerivedStateFromError(n){return{error:n}}static getDerivedStateFromProps(n,o){return o.location!==n.location||o.revalidation!=="idle"&&n.revalidation==="idle"?{error:n.error,location:n.location,revalidation:n.revalidation}:{error:n.error!==void 0?n.error:o.error,location:o.location,revalidation:n.revalidation||o.revalidation}}componentDidCatch(n,o){this.props.unstable_onError?this.props.unstable_onError(n,o):console.error("React Router caught the following error during render",n)}render(){return this.state.error!==void 0?ee.createElement(La.Provider,{value:this.props.routeContext},ee.createElement(z_.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function l2({routeContext:n,match:o,children:u}){let m=ee.useContext(Zf);return m&&m.static&&m.staticContext&&(o.route.errorElement||o.route.ErrorBoundary)&&(m.staticContext._deepestRenderedBoundaryId=o.route.id),ee.createElement(La.Provider,{value:n},u)}function c2(n,o=[],u=null,m=null,b=null){if(n==null){if(!u)return null;if(u.errors)n=u.matches;else if(o.length===0&&!u.initialized&&u.matches.length>0)n=u.matches;else return null}let P=n,w=u==null?void 0:u.errors;if(w!=null){let B=P.findIndex(V=>V.route.id&&(w==null?void 0:w[V.route.id])!==void 0);Un(B>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(w).join(",")}`),P=P.slice(0,Math.min(P.length,B+1))}let l=!1,L=-1;if(u)for(let B=0;B=0?P=P.slice(0,L+1):P=[P[0]];break}}}return P.reduceRight((B,V,G)=>{let te,q=!1,Se=null,ye=null;u&&(te=w&&V.route.id?w[V.route.id]:void 0,Se=V.route.errorElement||o2,l&&(L<0&&G===0?(sb("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),q=!0,ye=null):L===G&&(q=!0,ye=V.route.hydrateFallbackElement||null)));let fe=o.concat(P.slice(0,G+1)),Fe=()=>{let Ce;return te?Ce=Se:q?Ce=ye:V.route.Component?Ce=ee.createElement(V.route.Component,null):V.route.element?Ce=V.route.element:Ce=B,ee.createElement(l2,{match:V,routeContext:{outlet:B,matches:fe,isDataRoute:u!=null},children:Ce})};return u&&(V.route.ErrorBoundary||V.route.errorElement||G===0)?ee.createElement(a2,{location:u.location,revalidation:u.revalidation,component:Se,error:te,children:Fe(),routeContext:{outlet:null,matches:fe,isDataRoute:!0},unstable_onError:m}):Fe()},null)}function R_(n){return`${n} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function u2(n){let o=ee.useContext(Zf);return Un(o,R_(n)),o}function h2(n){let o=ee.useContext($g);return Un(o,R_(n)),o}function d2(n){let o=ee.useContext(La);return Un(o,R_(n)),o}function D_(n){let o=d2(n),u=o.matches[o.matches.length-1];return Un(u.route.id,`${n} can only be used on routes that contain a unique "id"`),u.route.id}function f2(){return D_("useRouteId")}function p2(){var m;let n=ee.useContext(z_),o=h2("useRouteError"),u=D_("useRouteError");return n!==void 0?n:(m=o.errors)==null?void 0:m[u]}function m2(){let{router:n}=u2("useNavigate"),o=D_("useNavigate"),u=ee.useRef(!1);return nb(()=>{u.current=!0}),ee.useCallback(async(b,P={})=>{ml(u.current,rb),u.current&&(typeof b=="number"?n.navigate(b):await n.navigate(b,{fromRouteId:o,...P}))},[n,o])}var cx={};function sb(n,o,u){!o&&!cx[n]&&(cx[n]=!0,ml(!1,u))}ee.memo(g2);function g2({routes:n,future:o,state:u,unstable_onError:m}){return ib(n,void 0,u,m,o)}function y2({to:n,replace:o,state:u,relative:m}){Un(Gf()," may be used only in the context of a component.");let{static:b}=ee.useContext(yl);ml(!b," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:P}=ee.useContext(La),{pathname:w}=_l(),l=qf(),L=A_(n,I_(P),w,m==="path"),B=JSON.stringify(L);return ee.useEffect(()=>{l(JSON.parse(B),{replace:o,state:u,relative:m})},[l,B,m,o,u]),null}function _2(n){return r2(n.context)}function ia(n){Un(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function v2({basename:n="/",children:o=null,location:u,navigationType:m="POP",navigator:b,static:P=!1}){Un(!Gf(),"You cannot render a inside another . You should never have more than one in your app.");let w=n.replace(/^\/*/,"/"),l=ee.useMemo(()=>({basename:w,navigator:b,static:P,future:{}}),[w,b,P]);typeof u=="string"&&(u=Vf(u));let{pathname:L="/",search:B="",hash:V="",state:G=null,key:te="default"}=u,q=ee.useMemo(()=>{let Se=cu(L,w);return Se==null?null:{location:{pathname:Se,search:B,hash:V,state:G,key:te},navigationType:m}},[w,L,B,V,G,te,m]);return ml(q!=null,` is not able to match the URL "${L}${B}${V}" because it does not start with the basename, so the won't render anything.`),q==null?null:ee.createElement(yl.Provider,{value:l},ee.createElement(gm.Provider,{children:o,value:q}))}function x2({children:n,location:o}){return i2($y(n),o)}function $y(n,o=[]){let u=[];return ee.Children.forEach(n,(m,b)=>{if(!ee.isValidElement(m))return;let P=[...o,b];if(m.type===ee.Fragment){u.push.apply(u,$y(m.props.children,P));return}Un(m.type===ia,`[${typeof m.type=="string"?m.type:m.type.name}] is not a component. All component children of must be a or `),Un(!m.props.index||!m.props.children,"An index route cannot have child routes.");let w={id:m.props.id||P.join("-"),caseSensitive:m.props.caseSensitive,element:m.props.element,Component:m.props.Component,index:m.props.index,path:m.props.path,loader:m.props.loader,action:m.props.action,hydrateFallbackElement:m.props.hydrateFallbackElement,HydrateFallback:m.props.HydrateFallback,errorElement:m.props.errorElement,ErrorBoundary:m.props.ErrorBoundary,hasErrorBoundary:m.props.hasErrorBoundary===!0||m.props.ErrorBoundary!=null||m.props.errorElement!=null,shouldRevalidate:m.props.shouldRevalidate,handle:m.props.handle,lazy:m.props.lazy};m.props.children&&(w.children=$y(m.props.children,P)),u.push(w)}),u}var vg="get",xg="application/x-www-form-urlencoded";function Ug(n){return n!=null&&typeof n.tagName=="string"}function b2(n){return Ug(n)&&n.tagName.toLowerCase()==="button"}function w2(n){return Ug(n)&&n.tagName.toLowerCase()==="form"}function S2(n){return Ug(n)&&n.tagName.toLowerCase()==="input"}function T2(n){return!!(n.metaKey||n.altKey||n.ctrlKey||n.shiftKey)}function P2(n,o){return n.button===0&&(!o||o==="_self")&&!T2(n)}var cg=null;function C2(){if(cg===null)try{new FormData(document.createElement("form"),0),cg=!1}catch{cg=!0}return cg}var E2=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Ay(n){return n!=null&&!E2.has(n)?(ml(!1,`"${n}" is not a valid \`encType\` for \`
            \`/\`\` and will default to "${xg}"`),null):n}function k2(n,o){let u,m,b,P,w;if(w2(n)){let l=n.getAttribute("action");m=l?cu(l,o):null,u=n.getAttribute("method")||vg,b=Ay(n.getAttribute("enctype"))||xg,P=new FormData(n)}else if(b2(n)||S2(n)&&(n.type==="submit"||n.type==="image")){let l=n.form;if(l==null)throw new Error('Cannot submit a - - {menuOpen && ( -
            setMenuOpen((v) => !v)} > -
            {t("header.menu")}
            -
            -
            - {t("header.language")} - {languageSwitcher ?? ( - SV / EN - )} +
            + )} +
            - {/* Center: Brand block */} -
            -
            - - BADA - -
            - {t("header.euBeaches")} -
            - {t("header.inSweden")} + {/* Center: Brand block */} +
            +
            + + BADA + +
            + {t("header.euBeaches")} +
            + {t("header.inSweden")} +
            -
            - {/* Right: User menu */} -
            - - - {userOpen && ( -
            + + + {userOpen && ( +
            +
            + {authed ? t("nav.account") : t("nav.user")} +
            +
            + {!authed ? ( + <> + {t("nav.signIn")} + {t("nav.register")} + + ) : ( + <> + + {t("nav.favoriteBeaches")} + + {t("nav.profile")} + {t("nav.settings")} +
            + + + )} +
            -
            - {!authed ? ( - <> - {t("nav.signIn")} - {t("nav.register")} - - ) : ( - <> - {t("nav.favoriteBeaches")} - {t("nav.profile")} - {t("nav.settings")} -
            - - - )} + )} +
            +
            + + {/* Search bar */} +
            +
            + handleSearchChange(e.target.value)} + /> + {search && ( -
            - )} -
            -
            - - {/* Search bar */} -
            -
            - setSearch(e.target.value)} - /> - {search && ( - - )} + )} +
            -
            - + ); } From 8b5be41532cc4e55947e9e2de56f3f80c1cc6009 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:59:13 +0100 Subject: [PATCH 192/215] feat(header): add search autocomplete with inline beach results - Show dropdown with up to 10 filtered beaches while typing - Click any result to navigate directly to beach detail page - Add "View all results on map" link for full search view - Implement outside-click detection to close dropdown - Add translations for "viewAllResults" (EN/SV) Allows users to search and navigate to beaches from any page without leaving their current context, improving UX. --- frontend/src/components/Header.tsx | 127 ++++++++++++++++++++++------ frontend/src/locales/en/common.json | 3 +- frontend/src/locales/sv/common.json | 3 +- 3 files changed, 105 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index 5b5b0d968c..fc0cab9bc9 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -1,9 +1,10 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useMemo } from "react"; import { Link, useLocation, useNavigate } from "react-router"; import { useTranslation } from "react-i18next"; import { useUI } from "../store/ui"; import { useAuth } from "@/store/auth"; -import { useQueryClient } from "@tanstack/react-query"; +import { useQueryClient, useQuery } from "@tanstack/react-query"; +import { fetchBeaches } from "../api/beaches"; import MenuIcon from "../assets/menu_icon.svg?react"; import UserIcon from "../assets/user_icon.svg?react"; @@ -41,8 +42,10 @@ export default function Header({ languageSwitcher }: HeaderProps) { const navigate = useNavigate(); const [menuOpen, setMenuOpen] = useState(false); const [userOpen, setUserOpen] = useState(false); + const [searchOpen, setSearchOpen] = useState(false); const menuRef = useOutsideClose(() => setMenuOpen(false)); const userRef = useOutsideClose(() => setUserOpen(false)); + const searchRef = useOutsideClose(() => setSearchOpen(false)); const { isDark, setIsDark } = useDarkMode(); const search = useUI((s) => s.search); @@ -52,19 +55,42 @@ export default function Header({ languageSwitcher }: HeaderProps) { const authed = !!token; const qc = useQueryClient(); + // Fetch beaches for search dropdown + const { data: beaches } = useQuery({ + queryKey: ["beaches"], + queryFn: fetchBeaches, + staleTime: 5 * 60 * 1000, + }); + + // Filter beaches based on search + const filteredBeaches = useMemo(() => { + if (!search.trim() || !beaches) return []; + const q = search.trim().toLowerCase(); + return beaches + .filter((b) => { + const hay = `${b.name} ${b.municipality ?? ""}`.toLowerCase(); + return hay.includes(q); + }) + .slice(0, 10); // Limit to 10 results + }, [search, beaches]); + function handleLogout() { clearToken(); qc.clear(); // clear cached queries (favorites, etc.) setUserOpen(false); } - // Navigate to home page when user starts searching from a non-home page + // Handle search input change function handleSearchChange(value: string) { setSearch(value); - // If user starts typing and not on home page, navigate to home - if (value && location.pathname !== "/") { - navigate("/"); - } + setSearchOpen(!!value); // Show dropdown when there's input + } + + // Handle beach selection from dropdown + function handleBeachSelect(beachId: string) { + setSearch(""); // Clear search + setSearchOpen(false); + navigate(`/beach/${beachId}`); } return ( @@ -185,26 +211,75 @@ export default function Header({ languageSwitcher }: HeaderProps) {
            - {/* Search bar */} + {/* Search bar with dropdown */}
            -
            - handleSearchChange(e.target.value)} - /> - {search && ( - +
            +
            + handleSearchChange(e.target.value)} + onFocus={() => search && setSearchOpen(true)} + /> + {search && ( + + )} +
            + + {/* Search results dropdown */} + {searchOpen && search && ( +
            + {filteredBeaches.length > 0 ? ( +
              + {filteredBeaches.map((beach) => ( +
            • + +
            • + ))} +
            + ) : ( +
            + {t("beachesList.noMatches")} +
            + )} + + {/* View all results link */} + {filteredBeaches.length > 0 && ( + <> +
            + { + setSearchOpen(false); + }} + className="block px-4 py-2 text-sm text-accent hover:bg-surface-muted text-center" + > + {t("header.viewAllResults")} + + + )} +
            )}
            diff --git a/frontend/src/locales/en/common.json b/frontend/src/locales/en/common.json index e5a566995f..db49727267 100644 --- a/frontend/src/locales/en/common.json +++ b/frontend/src/locales/en/common.json @@ -25,7 +25,8 @@ "userMenu": "User menu", "skipToMain": "Skip to main content", "euBeaches": "EU Beaches", - "inSweden": "in Sweden" + "inSweden": "in Sweden", + "viewAllResults": "View all results on map" }, "nav": { "whatIsEUBeach": "What is an EU Beach?", diff --git a/frontend/src/locales/sv/common.json b/frontend/src/locales/sv/common.json index 0d4445704a..d9724d3450 100644 --- a/frontend/src/locales/sv/common.json +++ b/frontend/src/locales/sv/common.json @@ -25,7 +25,8 @@ "userMenu": "Användarmenyn", "skipToMain": "Hoppa till huvudinnehållet", "euBeaches": "EU-bad", - "inSweden": "i Sverige" + "inSweden": "i Sverige", + "viewAllResults": "Visa alla resultat på kartan" }, "nav": { "whatIsEUBeach": "Vad är en EU-badplats?", From ab212a0870e26be076b280b86e88f2134bf96129 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 01:08:14 +0100 Subject: [PATCH 193/215] fix: achieve 100% Lighthouse scores for accessibility and SEO --- frontend/dist/_redirects | 4 ++ frontend/dist/assets/main-BSfwhuEJ.css | 1 + .../{main-voNoOY85.js => main-DNC_d3kL.js} | 60 +++++++++---------- frontend/dist/assets/main-p_R7C9Fr.css | 1 - frontend/dist/index.html | 4 +- frontend/dist/robots.txt | 4 ++ frontend/public/_redirects | 4 ++ frontend/public/robots.txt | 4 ++ frontend/src/components/MapView.tsx | 22 ++++++- 9 files changed, 69 insertions(+), 35 deletions(-) create mode 100644 frontend/dist/assets/main-BSfwhuEJ.css rename frontend/dist/assets/{main-voNoOY85.js => main-DNC_d3kL.js} (66%) delete mode 100644 frontend/dist/assets/main-p_R7C9Fr.css create mode 100644 frontend/dist/robots.txt create mode 100644 frontend/public/robots.txt diff --git a/frontend/dist/_redirects b/frontend/dist/_redirects index 9fe800e1b2..05916b5e7f 100644 --- a/frontend/dist/_redirects +++ b/frontend/dist/_redirects @@ -1 +1,5 @@ +# Don't redirect these files - serve them directly +/robots.txt /robots.txt 200 + +# Catch-all: serve index.html for client-side routing /* /index.html 200 \ No newline at end of file diff --git a/frontend/dist/assets/main-BSfwhuEJ.css b/frontend/dist/assets/main-BSfwhuEJ.css new file mode 100644 index 0000000000..8c61a3d9f6 --- /dev/null +++ b/frontend/dist/assets/main-BSfwhuEJ.css @@ -0,0 +1 @@ +.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0,0,0,0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:maplibregl-spin 2s linear infinite}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;cursor:pointer;display:block;height:23px;margin:0 0 -4px -4px;overflow:hidden;width:88px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:transparent;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{background-color:#fff;border-radius:12px;box-sizing:content-box;color:#000;margin:10px;min-height:20px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:2px 28px 2px 8px;visibility:visible}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;box-sizing:border-box;cursor:pointer;display:none;height:24px;outline:none;position:absolute;right:0;top:0;width:24px}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{right:0;top:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{left:0;top:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-scale{background-color:#ffffffbf;border:2px solid #333;border-top:#333;box-sizing:border-box;color:#333;font-size:10px;padding:0 5px}.maplibregl-popup{display:flex;left:0;pointer-events:none;position:absolute;top:0;will-change:transform}.maplibregl-popup-tip{border:10px solid transparent;height:0;width:0;z-index:1}.maplibregl-popup-anchor-top .maplibregl-popup-tip{align-self:center;border-bottom-color:#fff;border-top:none}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{align-self:flex-start;border-bottom-color:#fff;border-left:none;border-top:none}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{align-self:flex-end;border-bottom-color:#fff;border-right:none;border-top:none}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{align-self:center;border-bottom:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{align-self:flex-start;border-bottom:none;border-left:none;border-top-color:#fff}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{align-self:flex-end;border-bottom:none;border-right:none;border-top-color:#fff}.maplibregl-popup-anchor-left .maplibregl-popup-tip{align-self:center;border-left:none;border-right-color:#fff}.maplibregl-popup-anchor-right .maplibregl-popup-tip{align-self:center;border-left-color:#fff;border-right:none}.maplibregl-popup-close-button{background-color:transparent;border:0;border-radius:0 3px 0 0;cursor:pointer;position:absolute;right:0;top:0}.maplibregl-popup-content{background:#fff;border-radius:3px;box-shadow:0 1px 2px #0000001a;padding:15px 10px;pointer-events:auto;position:relative}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-marker{left:0;position:absolute;top:0;transition:opacity .2s;will-change:transform}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;height:15px;width:15px}.maplibregl-user-location-dot:before{animation:maplibregl-user-location-dot-pulse 2s infinite;content:"";position:absolute}.maplibregl-user-location-dot:after{border:2px solid #fff;border-radius:50%;box-shadow:0 0 3px #00000059;box-sizing:border-box;content:"";height:19px;left:-2px;position:absolute;top:-2px;width:19px}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;height:1px;width:1px}.maplibregl-boxzoom{background:#fff;border:2px dotted #202020;height:0;left:0;opacity:.5;position:absolute;top:0;width:0}.maplibregl-cooperative-gesture-screen{align-items:center;background:#0006;color:#fff;display:flex;font-size:1.4em;top:0;right:0;bottom:0;left:0;justify-content:center;line-height:1.2;opacity:0;padding:1rem;pointer-events:none;position:absolute;transition:opacity 1s ease 1s;z-index:99999}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity .05s}.maplibregl-pseudo-fullscreen{height:100%!important;left:0!important;position:fixed!important;top:0!important;width:100%!important;z-index:99999}/*! tailwindcss v4.1.13 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-600:oklch(57.7% .245 27.325);--color-red-950:oklch(25.8% .092 26.042);--color-black:#000;--color-white:#fff;--spacing:.25rem;--breakpoint-lg:64rem;--container-md:28rem;--container-2xl:42rem;--container-3xl:48rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5/2.25);--font-weight-medium:500;--font-weight-bold:700;--leading-tight:1.25;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-lg:.5rem;--radius-2xl:1rem;--ease-out:cubic-bezier(0,0,.2,1);--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-surface:#efebe4;--color-surface-muted:#f6f2ec;--color-ink:#1a1a1a;--color-ink-muted:#64686c;--color-border:#cdc8c0;--color-accent:#0a5a82;--color-badge:#e6e4dc;--color-quality-excellent:#1f6fb2;--color-quality-good:#2b8a3e;--color-quality-sufficient:#b08900;--color-quality-poor:#c0392b;--color-quality-unknown:#6b7280;--font-spectral:"Spectral",serif;--font-inter:"Inter",system-ui,sans-serif}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}html,body,#root{height:100%}body{background-color:var(--color-surface);font-family:var(--font-inter);color:var(--color-ink);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}h1,h2,h3,h4{font-family:var(--font-spectral);color:var(--color-ink)}a{border-radius:var(--radius-sm);color:var(--color-accent);text-underline-offset:2px}@media (hover:hover){a:hover{text-decoration-line:underline}}a:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){a:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}a:focus-visible{--tw-outline-style:none;outline-style:none}}@layer components{.card{border-radius:var(--radius-2xl);border-style:var(--tw-border-style);border-width:1px;border-color:var(--color-border);background-color:var(--color-surface-muted);--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.badge{align-items:center;gap:calc(var(--spacing)*1);background-color:var(--color-badge);padding-inline:calc(var(--spacing)*2.5);padding-block:calc(var(--spacing)*1);font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height));--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium);color:var(--color-ink);border-radius:3.40282e38px;display:inline-flex}.kpi-excellent{color:var(--color-quality-excellent)}.kpi-good{color:var(--color-quality-good)}.kpi-sufficient{color:var(--color-quality-sufficient)}.kpi-poor{color:var(--color-quality-poor)}.kpi-unknown{color:var(--color-quality-unknown)}}@layer utilities{.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-full{top:100%}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.ml-1{margin-left:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-block{display:inline-block}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-3{height:calc(var(--spacing)*3)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-9{height:calc(var(--spacing)*9)}.h-\[260px\]{height:260px}.h-px{height:1px}.max-h-96{max-height:calc(var(--spacing)*96)}.min-h-screen{min-height:100vh}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-1\/2{width:50%}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3{width:calc(var(--spacing)*3)}.w-6{width:calc(var(--spacing)*6)}.w-40{width:calc(var(--spacing)*40)}.w-56{width:calc(var(--spacing)*56)}.w-60{width:calc(var(--spacing)*60)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-\[200px\]{max-width:200px}.max-w-md{max-width:var(--container-md)}.max-w-screen-lg{max-width:var(--breakpoint-lg)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.cursor-grab{cursor:grab}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-border{border-color:var(--color-border)}.border-red-600{border-color:var(--color-red-600)}.bg-accent{background-color:var(--color-accent)}.bg-border{background-color:var(--color-border)}.bg-ink\/60{background-color:#1a1a1a99}@supports (color:color-mix(in lab,red,red)){.bg-ink\/60{background-color:color-mix(in oklab,var(--color-ink)60%,transparent)}}.bg-surface{background-color:var(--color-surface)}.bg-surface-muted{background-color:var(--color-surface-muted)}.p-0{padding:calc(var(--spacing)*0)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.px-0{padding-inline:calc(var(--spacing)*0)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-4{padding-top:calc(var(--spacing)*4)}.pt-6{padding-top:calc(var(--spacing)*6)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.text-center{text-align:center}.text-justify{text-align:justify}.text-left{text-align:left}.font-spectral{font-family:var(--font-spectral)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-\[1\.1\]{--tw-leading:1.1;line-height:1.1}.leading-none{--tw-leading:1;line-height:1}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.tracking-\[-0\.06em\]{--tw-tracking:-.06em;letter-spacing:-.06em}.whitespace-pre-line{white-space:pre-line}.whitespace-pre-wrap{white-space:pre-wrap}.text-accent{color:var(--color-accent)}.text-ink{color:var(--color-ink)}.text-ink-muted{color:var(--color-ink-muted)}.text-red-600{color:var(--color-red-600)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-black\/60{--tw-ring-color:#0009}@supports (color:color-mix(in lab,red,red)){.ring-black\/60{--tw-ring-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.ring-white\/85{--tw-ring-color:#ffffffd9}@supports (color:color-mix(in lab,red,red)){.ring-white\/85{--tw-ring-color:color-mix(in oklab,var(--color-white)85%,transparent)}}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition\!{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}@media (hover:hover){.hover\:bg-accent\/90:hover{background-color:#0a5a82e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/90:hover{background-color:color-mix(in oklab,var(--color-accent)90%,transparent)}}.hover\:bg-red-50:hover{background-color:var(--color-red-50)}.hover\:bg-surface:hover{background-color:var(--color-surface)}.hover\:bg-surface-muted:hover{background-color:var(--color-surface-muted)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:not-sr-only:focus{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.focus\:absolute:focus{position:absolute}.focus\:top-4:focus{top:calc(var(--spacing)*4)}.focus\:left-4:focus{left:calc(var(--spacing)*4)}.focus\:z-50:focus{z-index:50}.focus\:rounded:focus{border-radius:.25rem}.focus\:bg-accent:focus{background-color:var(--color-accent)}.focus\:bg-surface-muted:focus{background-color:var(--color-surface-muted)}.focus\:px-4:focus{padding-inline:calc(var(--spacing)*4)}.focus\:py-2:focus{padding-block:calc(var(--spacing)*2)}.focus\:text-white:focus{color:var(--color-white)}.focus\:underline:focus{text-decoration-line:underline}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-accent\/40:focus{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus\:ring-accent\/40:focus{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:#0a5a8266}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/40:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)40%,transparent)}}.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:#0a5a8280}@supports (color:color-mix(in lab,red,red)){.focus-visible\:ring-accent\/50:focus-visible{--tw-ring-color:color-mix(in oklab,var(--color-accent)50%,transparent)}}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}@media (min-width:40rem){.sm\:max-w-none{max-width:none}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-4{gap:calc(var(--spacing)*4)}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:64rem){.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme:dark){@media (hover:hover){.dark\:hover\:bg-red-950:hover{background-color:var(--color-red-950)}}}.aspect-square{aspect-ratio:1}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.sr-only:focus{width:auto;height:auto;padding:inherit;margin:inherit;clip:auto;white-space:normal;position:static;overflow:visible}}.maplibregl-map{-webkit-tap-highlight-color:#0000;font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;position:relative;overflow:hidden}.maplibregl-canvas{position:absolute;top:0;left:0}.maplibregl-map:fullscreen{width:100%;height:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;z-index:2;position:absolute}.maplibregl-ctrl-top-left{top:0;left:0}.maplibregl-ctrl-top-right{top:0;right:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px #0000001a}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px buttontext}}.maplibregl-ctrl-group button{box-sizing:border-box;cursor:pointer;background-color:#0000;border:0;outline:none;width:29px;height:29px;padding:0;display:block}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;width:100%;height:100%;display:block}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:#0000}.maplibregl-ctrl-group button+button{border-top:1px solid buttontext}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}@media (hover:hover){.maplibregl-ctrl button:not(:disabled):hover{background-color:#0000000d}}.maplibregl-ctrl button:not(:disabled):active{background-color:#0000000d}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-fullscreen .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M24 16v5.5c0 1.75-.75 2.5-2.5 2.5H16v-1l3-1.5-4-5.5 1-1 5.5 4 1.5-3zM6 16l1.5 3 5.5-4 1 1-4 5.5 3 1.5v1H7.5C5.75 24 5 23.25 5 21.5V16zm7-11v1l-3 1.5 4 5.5-1 1-5.5-4L6 13H5V7.5C5 5.75 5.75 5 7.5 5zm11 2.5c0-1.75-.75-2.5-2.5-2.5H16v1l3 1.5-4 5.5 1 1 5.5-4 1.5 3h1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-shrink .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='M18.5 16c-1.75 0-2.5.75-2.5 2.5V24h1l1.5-3 5.5 4 1-1-4-5.5 3-1.5v-1zM13 18.5c0-1.75-.75-2.5-2.5-2.5H5v1l3 1.5L4 24l1 1 5.5-4 1.5 3h1zm3-8c0 1.75.75 2.5 2.5 2.5H24v-1l-3-1.5L25 5l-1-1-5.5 4L17 5h-1zM10.5 13c1.75 0 2.5-.75 2.5-2.5V5h-1l-1.5 3L5 4 4 5l4 5.5L5 12v1z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-compass .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 29 29'%3E%3Cpath d='m10.5 14 4-8 4 8z'/%3E%3Cpath fill='%23ccc' d='m10.5 16 4 8 4-8z'/%3E%3C/svg%3E")}}.maplibregl-ctrl button.maplibregl-ctrl-globe .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%23333' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-globe-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='none' stroke='%2333b5e5' viewBox='0 0 22 22'%3E%3Ccircle cx='11' cy='11' r='8.5'/%3E%3Cpath d='M17.5 11c0 4.819-3.02 8.5-6.5 8.5S4.5 15.819 4.5 11 7.52 2.5 11 2.5s6.5 3.681 6.5 8.5Z'/%3E%3Cpath d='M13.5 11c0 2.447-.331 4.64-.853 6.206-.262.785-.562 1.384-.872 1.777-.314.399-.58.517-.775.517s-.461-.118-.775-.517c-.31-.393-.61-.992-.872-1.777C8.831 15.64 8.5 13.446 8.5 11s.331-4.64.853-6.206c.262-.785.562-1.384.872-1.777.314-.399.58-.517.775-.517s.461.118.775.517c.31.393.61.992.872 1.777.522 1.565.853 3.76.853 6.206Z'/%3E%3Cpath d='M11 7.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138q.07-.058.224-.138c.299-.151.763-.302 1.379-.434C7.378 5.666 9.091 5.5 11 5.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138q-.07.058-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428ZM4.486 6.436ZM11 16.5c-1.909 0-3.622-.166-4.845-.428-.616-.132-1.08-.283-1.379-.434a1.3 1.3 0 0 1-.224-.138 1.3 1.3 0 0 1 .224-.138c.299-.151.763-.302 1.379-.434C7.378 14.666 9.091 14.5 11 14.5s3.622.166 4.845.428c.616.132 1.08.283 1.379.434.105.053.177.1.224.138a1.3 1.3 0 0 1-.224.138c-.299.151-.763.302-1.379.434-1.223.262-2.936.428-4.845.428Zm-6.514-1.064ZM11 12.5c-2.46 0-4.672-.222-6.255-.574-.796-.177-1.406-.38-1.805-.59a1.5 1.5 0 0 1-.39-.272.3.3 0 0 1-.047-.064.3.3 0 0 1 .048-.064c.066-.073.189-.167.389-.272.399-.21 1.009-.413 1.805-.59C6.328 9.722 8.54 9.5 11 9.5s4.672.222 6.256.574c.795.177 1.405.38 1.804.59.2.105.323.2.39.272a.3.3 0 0 1 .047.064.3.3 0 0 1-.048.064 1.4 1.4 0 0 1-.389.272c-.399.21-1.009.413-1.804.59-1.584.352-3.796.574-6.256.574Zm-8.501-1.51v.002zm0 .018v.002zm17.002.002v-.002zm0-.018v-.002z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%23333' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-terrain-enabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='22' height='22' fill='%2333b5e5' viewBox='0 0 22 22'%3E%3Cpath d='m1.754 13.406 4.453-4.851 3.09 3.09 3.281 3.277.969-.969-3.309-3.312 3.844-4.121 6.148 6.886h1.082v-.855l-7.207-8.07-4.84 5.187L6.169 6.57l-5.48 5.965v.871ZM.688 16.844h20.625v1.375H.688Zm0 0'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23aaa' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-waiting .maplibregl-ctrl-icon{animation:2s linear infinite maplibregl-spin}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23999' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-active-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e58978' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%2333b5e5' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate.maplibregl-ctrl-geolocate-background-error .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23e54e33' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl button.maplibregl-ctrl-geolocate .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-geolocate:disabled .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23666' viewBox='0 0 20 20'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 0 0 5.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 0 0 9 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 0 0 3.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0 0 11 5.1V5s0-1-1-1m0 2.5a3.5 3.5 0 1 1 0 7 3.5 3.5 0 1 1 0-7'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3Cpath fill='red' d='m14 5 1 1-9 9-1-1z'/%3E%3C/svg%3E")}}@keyframes maplibregl-spin{0%{transform:rotate(0)}to{transform:rotate(1turn)}}a.maplibregl-ctrl-logo{cursor:pointer;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:88px;height:23px;margin:0 0 -4px -4px;display:block;overflow:hidden}a.maplibregl-ctrl-logo.maplibregl-compact{width:14px}@media (forced-colors:active){a.maplibregl-ctrl-logo{background-color:#0000;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}@media (forced-colors:active) and (prefers-color-scheme:light){a.maplibregl-ctrl-logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='88' height='23' fill='none'%3E%3Cpath fill='%23000' fill-opacity='.4' fill-rule='evenodd' d='M17.408 16.796h-1.827l2.501-12.095h.198l3.324 6.533.988 2.19.988-2.19 3.258-6.533h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.929 5.644h-.098l-2.914-5.644-.757-1.71-.345 1.71zm1.958-3.42-.726 3.663a1.255 1.255 0 0 1-1.232 1.011h-1.827a1.255 1.255 0 0 1-1.229-1.509l2.501-12.095a1.255 1.255 0 0 1 1.23-1.001h.197a1.25 1.25 0 0 1 1.12.685l3.19 6.273 3.125-6.263a1.25 1.25 0 0 1 1.123-.695h.181a1.255 1.255 0 0 1 1.227.991l1.443 6.71a5 5 0 0 1 .314-.787l.009-.016a4.6 4.6 0 0 1 1.777-1.887c.782-.46 1.668-.667 2.611-.667a4.6 4.6 0 0 1 1.7.32l.306.134c.21-.16.474-.256.759-.256h1.694a1.255 1.255 0 0 1 1.212.925 1.255 1.255 0 0 1 1.212-.925h1.711c.284 0 .545.094.755.252.613-.3 1.312-.45 2.075-.45 1.356 0 2.557.445 3.482 1.4q.47.48.763 1.064V4.701a1.255 1.255 0 0 1 1.255-1.255h1.86A1.255 1.255 0 0 1 54.44 4.7v9.194h2.217c.19 0 .37.043.532.118v-4.77c0-.356.147-.678.385-.906a2.42 2.42 0 0 1-.682-1.71c0-.665.267-1.253.735-1.7a2.45 2.45 0 0 1 1.722-.674 2.43 2.43 0 0 1 1.705.675q.318.302.504.683V4.7a1.255 1.255 0 0 1 1.255-1.255h1.744A1.255 1.255 0 0 1 65.812 4.7v3.335a4.8 4.8 0 0 1 1.526-.246c.938 0 1.817.214 2.59.69a4.47 4.47 0 0 1 1.67 1.743v-.98a1.255 1.255 0 0 1 1.256-1.256h1.777c.233 0 .451.064.639.174a3.4 3.4 0 0 1 1.567-.372c.346 0 .861.02 1.285.232a1.25 1.25 0 0 1 .689 1.004 4.7 4.7 0 0 1 .853-.588c.795-.44 1.675-.647 2.61-.647 1.385 0 2.65.39 3.525 1.396.836.938 1.168 2.173 1.168 3.528q-.001.515-.056 1.051a1.255 1.255 0 0 1-.947 1.09l.408.952a1.255 1.255 0 0 1-.477 1.552c-.418.268-.92.463-1.458.612-.613.171-1.304.244-2.049.244-1.06 0-2.043-.207-2.886-.698l-.015-.008c-.798-.48-1.419-1.135-1.818-1.963l-.004-.008a5.8 5.8 0 0 1-.548-2.512q0-.429.053-.843a1.3 1.3 0 0 1-.333-.086l-.166-.004c-.223 0-.426.062-.643.228-.03.024-.142.139-.142.59v3.883a1.255 1.255 0 0 1-1.256 1.256h-1.777a1.255 1.255 0 0 1-1.256-1.256V15.69l-.032.057a4.8 4.8 0 0 1-1.86 1.833 5.04 5.04 0 0 1-2.484.634 4.5 4.5 0 0 1-1.935-.424 1.25 1.25 0 0 1-.764.258h-1.71a1.255 1.255 0 0 1-1.256-1.255V7.687a2.4 2.4 0 0 1-.428.625c.253.23.412.561.412.93v7.553a1.255 1.255 0 0 1-1.256 1.255h-1.843a1.25 1.25 0 0 1-.894-.373c-.228.23-.544.373-.894.373H51.32a1.255 1.255 0 0 1-1.256-1.255v-1.251l-.061.117a4.7 4.7 0 0 1-1.782 1.884 4.77 4.77 0 0 1-2.485.67 5.6 5.6 0 0 1-1.485-.188l.009 2.764a1.255 1.255 0 0 1-1.255 1.259h-1.729a1.255 1.255 0 0 1-1.255-1.255v-3.537a1.255 1.255 0 0 1-1.167.793h-1.679a1.25 1.25 0 0 1-.77-.263 4.5 4.5 0 0 1-1.945.429c-.885 0-1.724-.21-2.495-.632l-.017-.01a5 5 0 0 1-1.081-.836 1.255 1.255 0 0 1-1.254 1.312h-1.81a1.255 1.255 0 0 1-1.228-.99l-.782-3.625-2.044 3.939a1.25 1.25 0 0 1-1.115.676h-.098a1.25 1.25 0 0 1-1.116-.68l-2.061-3.994zM35.92 16.63l.207-.114.223-.15q.493-.356.735-.785l.061-.118.033 1.332h1.678V9.242h-1.694l-.033 1.267q-.133-.329-.526-.658l-.032-.028a3.2 3.2 0 0 0-.668-.428l-.27-.12a3.3 3.3 0 0 0-1.235-.23q-1.136-.001-1.974.493a3.36 3.36 0 0 0-1.3 1.382q-.445.89-.444 2.074 0 1.2.51 2.107a3.8 3.8 0 0 0 1.382 1.381 3.9 3.9 0 0 0 1.893.477q.795 0 1.455-.33zm-2.789-5.38q-.576.675-.575 1.762 0 1.102.559 1.794.576.675 1.645.675a2.25 2.25 0 0 0 .934-.19 2.2 2.2 0 0 0 .468-.29l.178-.161a2.2 2.2 0 0 0 .397-.561q.244-.5.244-1.15v-.115q0-.708-.296-1.267l-.043-.077a2.2 2.2 0 0 0-.633-.709l-.13-.086-.047-.028a2.1 2.1 0 0 0-1.073-.285q-1.052 0-1.629.692zm2.316 2.706c.163-.17.28-.407.28-.83v-.114c0-.292-.06-.508-.15-.68a.96.96 0 0 0-.353-.389.85.85 0 0 0-.464-.127c-.4 0-.56.114-.664.239l-.01.012c-.148.174-.275.45-.275.945 0 .506.122.801.27.99.097.11.266.224.68.224.303 0 .504-.09.687-.269zm7.545 1.705a2.6 2.6 0 0 0 .331.423q.319.33.755.548l.173.074q.65.255 1.49.255 1.02 0 1.844-.493a3.45 3.45 0 0 0 1.316-1.4q.493-.904.493-2.089 0-1.909-.988-2.913-.988-1.02-2.584-1.02-.898 0-1.575.347a3 3 0 0 0-.415.262l-.199.166a3.4 3.4 0 0 0-.64.82V9.242h-1.712v11.553h1.729l-.017-5.134zm.53-1.138q.206.29.48.5l.155.11.053.034q.51.296 1.119.297 1.07 0 1.645-.675.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.435 0-.835.16a2 2 0 0 0-.284.136 2 2 0 0 0-.363.254 2.2 2.2 0 0 0-.46.569l-.082.162a2.6 2.6 0 0 0-.213 1.072v.115q0 .707.296 1.267l.135.211zm.964-.818a1.1 1.1 0 0 0 .367.385.94.94 0 0 0 .476.118c.423 0 .59-.117.687-.23.159-.194.28-.478.28-.95 0-.53-.133-.8-.266-.952l-.021-.025c-.078-.094-.231-.221-.68-.221a1 1 0 0 0-.503.135l-.012.007a.86.86 0 0 0-.335.343c-.073.133-.132.324-.132.614v.115a1.4 1.4 0 0 0 .14.66zm15.7-6.222q.347-.346.346-.856a1.05 1.05 0 0 0-.345-.79 1.18 1.18 0 0 0-.84-.329q-.51 0-.855.33a1.05 1.05 0 0 0-.346.79q0 .51.346.855.345.346.856.346.51 0 .839-.346zm4.337 9.314.033-1.332q.191.403.59.747l.098.081a4 4 0 0 0 .316.224l.223.122a3.2 3.2 0 0 0 1.44.322 3.8 3.8 0 0 0 1.875-.477 3.5 3.5 0 0 0 1.382-1.366q.527-.89.526-2.09 0-1.184-.444-2.073a3.24 3.24 0 0 0-1.283-1.399q-.823-.51-1.942-.51a3.5 3.5 0 0 0-1.527.344l-.086.043-.165.09a3 3 0 0 0-.33.214q-.432.315-.656.707a2 2 0 0 0-.099.198l.082-1.283V4.701h-1.744v12.095zm.473-2.509a2.5 2.5 0 0 0 .566.7q.117.098.245.18l.144.08a2.1 2.1 0 0 0 .975.232q1.07 0 1.645-.675.576-.69.576-1.778 0-1.102-.576-1.777-.56-.691-1.645-.692a2.2 2.2 0 0 0-1.015.235q-.22.113-.415.282l-.15.142a2.1 2.1 0 0 0-.42.594q-.223.479-.223 1.1v.115q0 .705.293 1.26zm2.616-.293c.157-.191.28-.479.28-.967 0-.51-.13-.79-.276-.961l-.021-.026c-.082-.1-.232-.225-.67-.225a.87.87 0 0 0-.681.279l-.012.011c-.154.155-.274.38-.274.807v.115c0 .285.057.499.144.669a1.1 1.1 0 0 0 .367.405c.137.082.28.123.455.123.423 0 .59-.118.686-.23zm8.266-3.013q.345-.13.724-.14l.069-.002q.493 0 .642.099l.247-1.794q-.196-.099-.717-.099a2.3 2.3 0 0 0-.545.063 2 2 0 0 0-.411.148 2.2 2.2 0 0 0-.4.249 2.5 2.5 0 0 0-.485.499 2.7 2.7 0 0 0-.32.581l-.05.137v-1.48h-1.778v7.553h1.777v-3.884q0-.546.159-.943a1.5 1.5 0 0 1 .466-.636 2.5 2.5 0 0 1 .399-.253 2 2 0 0 1 .224-.099zm9.784 2.656.05-.922q0-1.743-.856-2.698-.838-.97-2.584-.97-1.119-.001-2.007.493a3.46 3.46 0 0 0-1.4 1.382q-.493.906-.493 2.106 0 1.07.428 1.975.428.89 1.332 1.432.906.526 2.255.526.973 0 1.668-.185l.044-.012.135-.04q.613-.184.984-.421l-.542-1.267q-.3.162-.642.274l-.297.087q-.51.131-1.3.131-.954 0-1.497-.444a1.6 1.6 0 0 1-.192-.193q-.366-.44-.512-1.234l-.004-.021zm-5.427-1.256-.003.022h3.752v-.138q-.011-.727-.288-1.118a1 1 0 0 0-.156-.176q-.46-.428-1.316-.428-.986 0-1.494.604-.379.45-.494 1.234zm-27.053 2.77V4.7h-1.86v12.095h5.333V15.15zm7.103-5.908v7.553h-1.843V9.242h1.843z'/%3E%3Cpath fill='%23fff' d='m19.63 11.151-.757-1.71-.345 1.71-1.12 5.644h-1.827L18.083 4.7h.197l3.325 6.533.988 2.19.988-2.19L26.839 4.7h.181l2.6 12.095h-1.81l-1.218-5.644-.362-1.71-.658 1.71-2.93 5.644h-.098l-2.913-5.644zm14.836 5.81q-1.02 0-1.893-.478a3.8 3.8 0 0 1-1.381-1.382q-.51-.906-.51-2.106 0-1.185.444-2.074a3.36 3.36 0 0 1 1.3-1.382q.839-.494 1.974-.494a3.3 3.3 0 0 1 1.234.231 3.3 3.3 0 0 1 .97.575q.396.33.527.659l.033-1.267h1.694v7.553H37.18l-.033-1.332q-.279.593-1.02 1.053a3.17 3.17 0 0 1-1.662.444zm.296-1.482q.938 0 1.58-.642.642-.66.642-1.711v-.115q0-.708-.296-1.267a2.2 2.2 0 0 0-.807-.872 2.1 2.1 0 0 0-1.119-.313q-1.053 0-1.629.692-.575.675-.575 1.76 0 1.103.559 1.795.577.675 1.645.675zm6.521-6.237h1.711v1.4q.906-1.597 2.83-1.597 1.596 0 2.584 1.02.988 1.005.988 2.914 0 1.185-.493 2.09a3.46 3.46 0 0 1-1.316 1.399 3.5 3.5 0 0 1-1.844.493q-.954 0-1.662-.329a2.67 2.67 0 0 1-1.086-.97l.017 5.134h-1.728zm4.048 6.22q1.07 0 1.645-.674.577-.69.576-1.762 0-1.119-.576-1.777-.558-.675-1.645-.675-.592 0-1.12.296-.51.28-.822.823-.296.527-.296 1.234v.115q0 .708.296 1.267.313.543.823.855.51.296 1.119.297z'/%3E%3Cpath fill='%23e1e3e9' d='M51.325 4.7h1.86v10.45h3.473v1.646h-5.333zm7.12 4.542h1.843v7.553h-1.843zm.905-1.415a1.16 1.16 0 0 1-.856-.346 1.17 1.17 0 0 1-.346-.856 1.05 1.05 0 0 1 .346-.79q.346-.329.856-.329.494 0 .839.33a1.05 1.05 0 0 1 .345.79 1.16 1.16 0 0 1-.345.855q-.33.346-.84.346zm7.875 9.133a3.17 3.17 0 0 1-1.662-.444q-.723-.46-1.004-1.053l-.033 1.332h-1.71V4.701h1.743v4.657l-.082 1.283q.279-.658 1.086-1.119a3.5 3.5 0 0 1 1.778-.477q1.119 0 1.942.51a3.24 3.24 0 0 1 1.283 1.4q.445.888.444 2.072 0 1.201-.526 2.09a3.5 3.5 0 0 1-1.382 1.366 3.8 3.8 0 0 1-1.876.477zm-.296-1.481q1.069 0 1.645-.675.577-.69.577-1.778 0-1.102-.577-1.776-.56-.691-1.645-.692a2.12 2.12 0 0 0-1.58.659q-.642.641-.642 1.694v.115q0 .71.296 1.267a2.4 2.4 0 0 0 .807.872 2.1 2.1 0 0 0 1.119.313zm5.927-6.237h1.777v1.481q.263-.757.856-1.217a2.14 2.14 0 0 1 1.349-.46q.527 0 .724.098l-.247 1.794q-.149-.099-.642-.099-.774 0-1.416.494-.626.493-.626 1.58v3.883h-1.777V9.242zm9.534 7.718q-1.35 0-2.255-.526-.904-.543-1.332-1.432a4.6 4.6 0 0 1-.428-1.975q0-1.2.493-2.106a3.46 3.46 0 0 1 1.4-1.382q.889-.495 2.007-.494 1.744 0 2.584.97.855.956.856 2.7 0 .444-.05.92h-5.43q.18 1.005.708 1.45.542.443 1.497.443.79 0 1.3-.131a4 4 0 0 0 .938-.362l.542 1.267q-.411.263-1.119.46-.708.198-1.711.197zm1.596-4.558q.016-1.02-.444-1.432-.46-.428-1.316-.428-1.728 0-1.991 1.86z'/%3E%3Cpath d='M5.074 15.948a.484.657 0 0 0-.486.659v1.84a.484.657 0 0 0 .486.659h4.101a.484.657 0 0 0 .486-.659v-1.84a.484.657 0 0 0-.486-.659zm3.56 1.16H5.617v.838h3.017z' style='fill:%23fff;fill-rule:evenodd;stroke-width:1.03600001'/%3E%3Cg style='stroke-width:1.12603545'%3E%3Cpath d='M-9.408-1.416c-3.833-.025-7.056 2.912-7.08 6.615-.02 3.08 1.653 4.832 3.107 6.268.903.892 1.721 1.74 2.32 2.902l-.525-.004c-.543-.003-.992.304-1.24.639a1.87 1.87 0 0 0-.362 1.121l-.011 1.877c-.003.402.104.787.347 1.125.244.338.688.653 1.23.656l4.142.028c.542.003.99-.306 1.238-.641a1.87 1.87 0 0 0 .363-1.121l.012-1.875a1.87 1.87 0 0 0-.348-1.127c-.243-.338-.688-.653-1.23-.656l-.518-.004c.597-1.145 1.425-1.983 2.348-2.87 1.473-1.414 3.18-3.149 3.2-6.226-.016-3.59-2.923-6.684-6.993-6.707m-.006 1.1v.002c3.274.02 5.92 2.532 5.9 5.6-.017 2.706-1.39 4.026-2.863 5.44-1.034.994-2.118 2.033-2.814 3.633-.018.041-.052.055-.075.065q-.013.004-.02.01a.34.34 0 0 1-.226.084.34.34 0 0 1-.224-.086l-.092-.077c-.699-1.615-1.768-2.669-2.781-3.67-1.454-1.435-2.797-2.762-2.78-5.478.02-3.067 2.7-5.545 5.975-5.523m-.02 2.826c-1.62-.01-2.944 1.315-2.955 2.96-.01 1.646 1.295 2.988 2.916 2.999h.002c1.621.01 2.943-1.316 2.953-2.961.011-1.646-1.294-2.988-2.916-2.998m-.005 1.1c1.017.006 1.829.83 1.822 1.89s-.83 1.874-1.848 1.867c-1.018-.006-1.829-.83-1.822-1.89s.83-1.874 1.848-1.868m-2.155 11.857 4.14.025c.271.002.49.305.487.676l-.013 1.875c-.003.37-.224.67-.495.668l-4.14-.025c-.27-.002-.487-.306-.485-.676l.012-1.875c.003-.37.224-.67.494-.668' style='color:%23000;font-style:normal;font-variant:normal;font-weight:400;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:%23000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:evenodd;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:%23000;solid-opacity:1;vector-effect:none;fill:%23000;fill-opacity:.4;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-9.415-.316C-12.69-.338-15.37 2.14-15.39 5.207c-.017 2.716 1.326 4.041 2.78 5.477 1.013 1 2.081 2.055 2.78 3.67l.092.076a.34.34 0 0 0 .225.086.34.34 0 0 0 .227-.083l.019-.01c.022-.009.057-.024.074-.064.697-1.6 1.78-2.64 2.814-3.634 1.473-1.414 2.847-2.733 2.864-5.44.02-3.067-2.627-5.58-5.901-5.601m-.057 8.784c1.621.011 2.944-1.315 2.955-2.96.01-1.646-1.295-2.988-2.916-2.999-1.622-.01-2.945 1.315-2.955 2.96s1.295 2.989 2.916 3' style='clip-rule:evenodd;fill:%23e1e3e9;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3Cpath d='M-11.594 15.465c-.27-.002-.492.297-.494.668l-.012 1.876c-.003.371.214.673.485.675l4.14.027c.271.002.492-.298.495-.668l.012-1.877c.003-.37-.215-.672-.485-.674z' style='clip-rule:evenodd;fill:%23fff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.47727823;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:.4' transform='translate(15.553 2.85)scale(.88807)'/%3E%3C/g%3E%3C/svg%3E")}}.maplibregl-ctrl.maplibregl-ctrl-attrib{background-color:#ffffff80;margin:0;padding:0 5px}@media screen{.maplibregl-ctrl-attrib.maplibregl-compact{box-sizing:content-box;color:#000;background-color:#fff;border-radius:12px;min-height:20px;margin:10px;padding:2px 24px 2px 0;position:relative}.maplibregl-ctrl-attrib.maplibregl-compact-show{visibility:visible;padding:2px 28px 2px 8px}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact-show,.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact-show{border-radius:12px;padding:2px 8px 2px 28px}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}.maplibregl-ctrl-attrib-button{box-sizing:border-box;cursor:pointer;background-color:#ffffff80;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E");border:0;border-radius:12px;outline:none;width:24px;height:24px;display:none;position:absolute;top:0;right:0}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button{-webkit-appearance:none;-moz-appearance:none;appearance:none;list-style:none}.maplibregl-ctrl-attrib summary.maplibregl-ctrl-attrib-button::-webkit-details-marker{display:none}.maplibregl-ctrl-bottom-left .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-top-left .maplibregl-ctrl-attrib-button{left:0}.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-button,.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block}.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{background-color:#0000000d}.maplibregl-ctrl-bottom-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;right:0}.maplibregl-ctrl-top-right>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;right:0}.maplibregl-ctrl-top-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{top:0;left:0}.maplibregl-ctrl-bottom-left>.maplibregl-ctrl-attrib.maplibregl-compact:after{bottom:0;left:0}}@media screen and (forced-colors:active){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='%23fff' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}@media screen and (forced-colors:active) and (prefers-color-scheme:light){.maplibregl-ctrl-attrib.maplibregl-compact:after{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill-rule='evenodd' viewBox='0 0 20 20'%3E%3Cpath d='M4 10a6 6 0 1 0 12 0 6 6 0 1 0-12 0m5-3a1 1 0 1 0 2 0 1 1 0 1 0-2 0m0 3a1 1 0 1 1 2 0v3a1 1 0 1 1-2 0'/%3E%3C/svg%3E")}}.maplibregl-ctrl-attrib a{color:#000000bf;text-decoration:none}.maplibregl-ctrl-attrib a:hover{color:inherit;text-decoration:underline}.maplibregl-attrib-empty{display:none}.maplibregl-ctrl-scale{box-sizing:border-box;color:#333;background-color:#ffffffbf;border:2px solid #333;border-top:#333;padding:0 5px;font-size:10px}.maplibregl-popup{pointer-events:none;will-change:transform;display:flex;position:absolute;top:0;left:0}.maplibregl-popup-anchor-top,.maplibregl-popup-anchor-top-left,.maplibregl-popup-anchor-top-right{flex-direction:column}.maplibregl-popup-anchor-bottom,.maplibregl-popup-anchor-bottom-left,.maplibregl-popup-anchor-bottom-right{flex-direction:column-reverse}.maplibregl-popup-anchor-left{flex-direction:row}.maplibregl-popup-anchor-right{flex-direction:row-reverse}.maplibregl-popup-tip{z-index:1;border:10px solid #0000;width:0;height:0}.maplibregl-popup-anchor-top .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;align-self:center}.maplibregl-popup-anchor-top-left .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-top-right .maplibregl-popup-tip{border-top:none;border-bottom-color:#fff;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-bottom .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;align-self:center}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-left:none;align-self:flex-start}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{border-top-color:#fff;border-bottom:none;border-right:none;align-self:flex-end}.maplibregl-popup-anchor-left .maplibregl-popup-tip{border-left:none;border-right-color:#fff;align-self:center}.maplibregl-popup-anchor-right .maplibregl-popup-tip{border-left-color:#fff;border-right:none;align-self:center}.maplibregl-popup-close-button{cursor:pointer;background-color:#0000;border:0;border-radius:0 3px 0 0;position:absolute;top:0;right:0}.maplibregl-popup-close-button:hover{background-color:#0000000d}.maplibregl-popup-content{pointer-events:auto;background:#fff;border-radius:3px;padding:15px 10px;position:relative;box-shadow:0 1px 2px #0000001a}.maplibregl-popup-anchor-top-left .maplibregl-popup-content{border-top-left-radius:0}.maplibregl-popup-anchor-top-right .maplibregl-popup-content{border-top-right-radius:0}.maplibregl-popup-anchor-bottom-left .maplibregl-popup-content{border-bottom-left-radius:0}.maplibregl-popup-anchor-bottom-right .maplibregl-popup-content{border-bottom-right-radius:0}.maplibregl-popup-track-pointer{display:none}.maplibregl-popup-track-pointer *{pointer-events:none;-webkit-user-select:none;user-select:none}.maplibregl-map:hover .maplibregl-popup-track-pointer{display:flex}.maplibregl-map:active .maplibregl-popup-track-pointer{display:none}.maplibregl-marker{will-change:transform;transition:opacity .2s;position:absolute;top:0;left:0}.maplibregl-user-location-dot,.maplibregl-user-location-dot:before{background-color:#1da1f2;border-radius:50%;width:15px;height:15px}.maplibregl-user-location-dot:before{content:"";animation:2s infinite maplibregl-user-location-dot-pulse;position:absolute}.maplibregl-user-location-dot:after{box-sizing:border-box;content:"";border:2px solid #fff;border-radius:50%;width:19px;height:19px;position:absolute;top:-2px;left:-2px;box-shadow:0 0 3px #00000059}@keyframes maplibregl-user-location-dot-pulse{0%{opacity:1;transform:scale(1)}70%{opacity:0;transform:scale(3)}to{opacity:0;transform:scale(1)}}.maplibregl-user-location-dot-stale{background-color:#aaa}.maplibregl-user-location-dot-stale:after{display:none}.maplibregl-user-location-accuracy-circle{background-color:#1da1f233;border-radius:100%;width:1px;height:1px}.maplibregl-crosshair,.maplibregl-crosshair .maplibregl-interactive,.maplibregl-crosshair .maplibregl-interactive:active{cursor:crosshair}.maplibregl-boxzoom{opacity:.5;background:#fff;border:2px dotted #202020;width:0;height:0;position:absolute;top:0;left:0}.maplibregl-cooperative-gesture-screen{color:#fff;opacity:0;pointer-events:none;z-index:99999;background:#0006;justify-content:center;align-items:center;padding:1rem;font-size:1.4em;line-height:1.2;transition:opacity 1s 1s;display:flex;position:absolute;top:0;right:0;bottom:0;left:0}.maplibregl-cooperative-gesture-screen.maplibregl-show{opacity:1;transition:opacity 50ms}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:none}@media (hover:none),(pointer:coarse){.maplibregl-cooperative-gesture-screen .maplibregl-desktop-message{display:none}.maplibregl-cooperative-gesture-screen .maplibregl-mobile-message{display:block}}.maplibregl-pseudo-fullscreen{z-index:99999;width:100%!important;height:100%!important;position:fixed!important;top:0!important;left:0!important}.dark{--color-surface:#161011;--color-surface-muted:#1e1819;--color-ink:#f0ede6;--color-ink-muted:#b8b2aa;--color-border:#40383a;--color-accent:#5ea5d2;--color-badge:#302a2c}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes pulse{50%{opacity:.5}} diff --git a/frontend/dist/assets/main-voNoOY85.js b/frontend/dist/assets/main-DNC_d3kL.js similarity index 66% rename from frontend/dist/assets/main-voNoOY85.js rename to frontend/dist/assets/main-DNC_d3kL.js index 037bdc434f..7f4a36a17e 100644 --- a/frontend/dist/assets/main-voNoOY85.js +++ b/frontend/dist/assets/main-DNC_d3kL.js @@ -1,4 +1,4 @@ -var yw=Object.defineProperty;var Xv=r=>{throw TypeError(r)};var _w=(r,o,u)=>o in r?yw(r,o,{enumerable:!0,configurable:!0,writable:!0,value:u}):r[o]=u;var Yv=(r,o,u)=>_w(r,typeof o!="symbol"?o+"":o,u),Cy=(r,o,u)=>o.has(r)||Xv("Cannot "+u);var ye=(r,o,u)=>(Cy(r,o,"read from private field"),u?u.call(r):o.get(r)),Wt=(r,o,u)=>o.has(r)?Xv("Cannot add the same private member more than once"):o instanceof WeakSet?o.add(r):o.set(r,u),It=(r,o,u,m)=>(Cy(r,o,"write to private field"),m?m.call(r,u):o.set(r,u),u),wr=(r,o,u)=>(Cy(r,o,"access private method"),u);var lg=(r,o,u,m)=>({set _(b){It(r,o,b,u)},get _(){return ye(r,o,m)}});(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const b of document.querySelectorAll('link[rel="modulepreload"]'))m(b);new MutationObserver(b=>{for(const P of b)if(P.type==="childList")for(const w of P.addedNodes)w.tagName==="LINK"&&w.rel==="modulepreload"&&m(w)}).observe(document,{childList:!0,subtree:!0});function u(b){const P={};return b.integrity&&(P.integrity=b.integrity),b.referrerPolicy&&(P.referrerPolicy=b.referrerPolicy),b.crossOrigin==="use-credentials"?P.credentials="include":b.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function m(b){if(b.ep)return;b.ep=!0;const P=u(b);fetch(b.href,P)}})();function M_(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var ky={exports:{}},$p={},Ey={exports:{}},Gr={};/** +var _w=Object.defineProperty;var Yv=r=>{throw TypeError(r)};var vw=(r,a,u)=>a in r?_w(r,a,{enumerable:!0,configurable:!0,writable:!0,value:u}):r[a]=u;var Qv=(r,a,u)=>vw(r,typeof a!="symbol"?a+"":a,u),ky=(r,a,u)=>a.has(r)||Yv("Cannot "+u);var ye=(r,a,u)=>(ky(r,a,"read from private field"),u?u.call(r):a.get(r)),Wt=(r,a,u)=>a.has(r)?Yv("Cannot add the same private member more than once"):a instanceof WeakSet?a.add(r):a.set(r,u),It=(r,a,u,m)=>(ky(r,a,"write to private field"),m?m.call(r,u):a.set(r,u),u),wr=(r,a,u)=>(ky(r,a,"access private method"),u);var lg=(r,a,u,m)=>({set _(b){It(r,a,b,u)},get _(){return ye(r,a,m)}});(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const b of document.querySelectorAll('link[rel="modulepreload"]'))m(b);new MutationObserver(b=>{for(const P of b)if(P.type==="childList")for(const w of P.addedNodes)w.tagName==="LINK"&&w.rel==="modulepreload"&&m(w)}).observe(document,{childList:!0,subtree:!0});function u(b){const P={};return b.integrity&&(P.integrity=b.integrity),b.referrerPolicy&&(P.referrerPolicy=b.referrerPolicy),b.crossOrigin==="use-credentials"?P.credentials="include":b.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function m(b){if(b.ep)return;b.ep=!0;const P=u(b);fetch(b.href,P)}})();function R_(r){return r&&r.__esModule&&Object.prototype.hasOwnProperty.call(r,"default")?r.default:r}var Ey={exports:{}},$p={},Iy={exports:{}},Gr={};/** * @license React * react.production.min.js * @@ -6,7 +6,7 @@ var yw=Object.defineProperty;var Xv=r=>{throw TypeError(r)};var _w=(r,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var Qv;function vw(){if(Qv)return Gr;Qv=1;var r=Symbol.for("react.element"),o=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),m=Symbol.for("react.strict_mode"),b=Symbol.for("react.profiler"),P=Symbol.for("react.provider"),w=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),L=Symbol.for("react.suspense"),N=Symbol.for("react.memo"),Z=Symbol.for("react.lazy"),V=Symbol.iterator;function ee(Ie){return Ie===null||typeof Ie!="object"?null:(Ie=V&&Ie[V]||Ie["@@iterator"],typeof Ie=="function"?Ie:null)}var q={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},be=Object.assign,pe={};function Pe(Ie,Qe,jt){this.props=Ie,this.context=Qe,this.refs=pe,this.updater=jt||q}Pe.prototype.isReactComponent={},Pe.prototype.setState=function(Ie,Qe){if(typeof Ie!="object"&&typeof Ie!="function"&&Ie!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Ie,Qe,"setState")},Pe.prototype.forceUpdate=function(Ie){this.updater.enqueueForceUpdate(this,Ie,"forceUpdate")};function ze(){}ze.prototype=Pe.prototype;function Re(Ie,Qe,jt){this.props=Ie,this.context=Qe,this.refs=pe,this.updater=jt||q}var Te=Re.prototype=new ze;Te.constructor=Re,be(Te,Pe.prototype),Te.isPureReactComponent=!0;var Fe=Array.isArray,Ye=Object.prototype.hasOwnProperty,at={current:null},ct={key:!0,ref:!0,__self:!0,__source:!0};function dt(Ie,Qe,jt){var tr,fr={},Kt=null,Lr=null;if(Qe!=null)for(tr in Qe.ref!==void 0&&(Lr=Qe.ref),Qe.key!==void 0&&(Kt=""+Qe.key),Qe)Ye.call(Qe,tr)&&!ct.hasOwnProperty(tr)&&(fr[tr]=Qe[tr]);var sr=arguments.length-2;if(sr===1)fr.children=jt;else if(1{throw TypeError(r)};var _w=(r,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var ex;function xw(){if(ex)return $p;ex=1;var r=A_(),o=Symbol.for("react.element"),u=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,b=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,P={key:!0,ref:!0,__self:!0,__source:!0};function w(l,L,N){var Z,V={},ee=null,q=null;N!==void 0&&(ee=""+N),L.key!==void 0&&(ee=""+L.key),L.ref!==void 0&&(q=L.ref);for(Z in L)m.call(L,Z)&&!P.hasOwnProperty(Z)&&(V[Z]=L[Z]);if(l&&l.defaultProps)for(Z in L=l.defaultProps,L)V[Z]===void 0&&(V[Z]=L[Z]);return{$$typeof:o,type:l,key:ee,ref:q,props:V,_owner:b.current}}return $p.Fragment=u,$p.jsx=w,$p.jsxs=w,$p}var tx;function bw(){return tx||(tx=1,ky.exports=xw()),ky.exports}var J=bw(),te=A_();const hn=M_(te);var cg={},Iy={exports:{}},Po={},My={exports:{}},Ay={};/** + */var tx;function bw(){if(tx)return $p;tx=1;var r=z_(),a=Symbol.for("react.element"),u=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,b=r.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,P={key:!0,ref:!0,__self:!0,__source:!0};function w(l,L,N){var Z,V={},ee=null,q=null;N!==void 0&&(ee=""+N),L.key!==void 0&&(ee=""+L.key),L.ref!==void 0&&(q=L.ref);for(Z in L)m.call(L,Z)&&!P.hasOwnProperty(Z)&&(V[Z]=L[Z]);if(l&&l.defaultProps)for(Z in L=l.defaultProps,L)V[Z]===void 0&&(V[Z]=L[Z]);return{$$typeof:a,type:l,key:ee,ref:q,props:V,_owner:b.current}}return $p.Fragment=u,$p.jsx=w,$p.jsxs=w,$p}var rx;function ww(){return rx||(rx=1,Ey.exports=bw()),Ey.exports}var Y=ww(),te=z_();const hn=R_(te);var cg={},My={exports:{}},Ta={},Ay={exports:{}},Ry={};/** * @license React * scheduler.production.min.js * @@ -22,7 +22,7 @@ var yw=Object.defineProperty;var Xv=r=>{throw TypeError(r)};var _w=(r,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var rx;function ww(){return rx||(rx=1,(function(r){function o(_t,ft){var Ct=_t.length;_t.push(ft);e:for(;0>>1,Qe=_t[Ie];if(0>>1;Ieb(fr,Ct))Ktb(Lr,fr)?(_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt):(_t[Ie]=fr,_t[tr]=Ct,Ie=tr);else if(Ktb(Lr,Ct))_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt;else break e}}return ft}function b(_t,ft){var Ct=_t.sortIndex-ft.sortIndex;return Ct!==0?Ct:_t.id-ft.id}if(typeof performance=="object"&&typeof performance.now=="function"){var P=performance;r.unstable_now=function(){return P.now()}}else{var w=Date,l=w.now();r.unstable_now=function(){return w.now()-l}}var L=[],N=[],Z=1,V=null,ee=3,q=!1,be=!1,pe=!1,Pe=typeof setTimeout=="function"?setTimeout:null,ze=typeof clearTimeout=="function"?clearTimeout:null,Re=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Te(_t){for(var ft=u(N);ft!==null;){if(ft.callback===null)m(N);else if(ft.startTime<=_t)m(N),ft.sortIndex=ft.expirationTime,o(L,ft);else break;ft=u(N)}}function Fe(_t){if(pe=!1,Te(_t),!be)if(u(L)!==null)be=!0,ar(Ye);else{var ft=u(N);ft!==null&&Zt(Fe,ft.startTime-_t)}}function Ye(_t,ft){be=!1,pe&&(pe=!1,ze(dt),dt=-1),q=!0;var Ct=ee;try{for(Te(ft),V=u(L);V!==null&&(!(V.expirationTime>ft)||_t&&!Rt());){var Ie=V.callback;if(typeof Ie=="function"){V.callback=null,ee=V.priorityLevel;var Qe=Ie(V.expirationTime<=ft);ft=r.unstable_now(),typeof Qe=="function"?V.callback=Qe:V===u(L)&&m(L),Te(ft)}else m(L);V=u(L)}if(V!==null)var jt=!0;else{var tr=u(N);tr!==null&&Zt(Fe,tr.startTime-ft),jt=!1}return jt}finally{V=null,ee=Ct,q=!1}}var at=!1,ct=null,dt=-1,ot=5,Xe=-1;function Rt(){return!(r.unstable_now()-Xe_t||125<_t?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ot=0<_t?Math.floor(1e3/_t):5},r.unstable_getCurrentPriorityLevel=function(){return ee},r.unstable_getFirstCallbackNode=function(){return u(L)},r.unstable_next=function(_t){switch(ee){case 1:case 2:case 3:var ft=3;break;default:ft=ee}var Ct=ee;ee=ft;try{return _t()}finally{ee=Ct}},r.unstable_pauseExecution=function(){},r.unstable_requestPaint=function(){},r.unstable_runWithPriority=function(_t,ft){switch(_t){case 1:case 2:case 3:case 4:case 5:break;default:_t=3}var Ct=ee;ee=_t;try{return ft()}finally{ee=Ct}},r.unstable_scheduleCallback=function(_t,ft,Ct){var Ie=r.unstable_now();switch(typeof Ct=="object"&&Ct!==null?(Ct=Ct.delay,Ct=typeof Ct=="number"&&0Ie?(_t.sortIndex=Ct,o(N,_t),u(L)===null&&_t===u(N)&&(pe?(ze(dt),dt=-1):pe=!0,Zt(Fe,Ct-Ie))):(_t.sortIndex=Qe,o(L,_t),be||q||(be=!0,ar(Ye))),_t},r.unstable_shouldYield=Rt,r.unstable_wrapCallback=function(_t){var ft=ee;return function(){var Ct=ee;ee=ft;try{return _t.apply(this,arguments)}finally{ee=Ct}}}})(Ay)),Ay}var nx;function Sw(){return nx||(nx=1,My.exports=ww()),My.exports}/** + */var nx;function Sw(){return nx||(nx=1,(function(r){function a(_t,ft){var Ct=_t.length;_t.push(ft);e:for(;0>>1,Qe=_t[Ie];if(0>>1;Ieb(fr,Ct))Ktb(Lr,fr)?(_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt):(_t[Ie]=fr,_t[tr]=Ct,Ie=tr);else if(Ktb(Lr,Ct))_t[Ie]=Lr,_t[Kt]=Ct,Ie=Kt;else break e}}return ft}function b(_t,ft){var Ct=_t.sortIndex-ft.sortIndex;return Ct!==0?Ct:_t.id-ft.id}if(typeof performance=="object"&&typeof performance.now=="function"){var P=performance;r.unstable_now=function(){return P.now()}}else{var w=Date,l=w.now();r.unstable_now=function(){return w.now()-l}}var L=[],N=[],Z=1,V=null,ee=3,q=!1,_e=!1,fe=!1,Pe=typeof setTimeout=="function"?setTimeout:null,Le=typeof clearTimeout=="function"?clearTimeout:null,Me=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Se(_t){for(var ft=u(N);ft!==null;){if(ft.callback===null)m(N);else if(ft.startTime<=_t)m(N),ft.sortIndex=ft.expirationTime,a(L,ft);else break;ft=u(N)}}function De(_t){if(fe=!1,Se(_t),!_e)if(u(L)!==null)_e=!0,or(Ye);else{var ft=u(N);ft!==null&&Zt(De,ft.startTime-_t)}}function Ye(_t,ft){_e=!1,fe&&(fe=!1,Le(ut),ut=-1),q=!0;var Ct=ee;try{for(Se(ft),V=u(L);V!==null&&(!(V.expirationTime>ft)||_t&&!At());){var Ie=V.callback;if(typeof Ie=="function"){V.callback=null,ee=V.priorityLevel;var Qe=Ie(V.expirationTime<=ft);ft=r.unstable_now(),typeof Qe=="function"?V.callback=Qe:V===u(L)&&m(L),Se(ft)}else m(L);V=u(L)}if(V!==null)var jt=!0;else{var tr=u(N);tr!==null&&Zt(De,tr.startTime-ft),jt=!1}return jt}finally{V=null,ee=Ct,q=!1}}var ot=!1,lt=null,ut=-1,Je=5,Ke=-1;function At(){return!(r.unstable_now()-Ke_t||125<_t?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):Je=0<_t?Math.floor(1e3/_t):5},r.unstable_getCurrentPriorityLevel=function(){return ee},r.unstable_getFirstCallbackNode=function(){return u(L)},r.unstable_next=function(_t){switch(ee){case 1:case 2:case 3:var ft=3;break;default:ft=ee}var Ct=ee;ee=ft;try{return _t()}finally{ee=Ct}},r.unstable_pauseExecution=function(){},r.unstable_requestPaint=function(){},r.unstable_runWithPriority=function(_t,ft){switch(_t){case 1:case 2:case 3:case 4:case 5:break;default:_t=3}var Ct=ee;ee=_t;try{return ft()}finally{ee=Ct}},r.unstable_scheduleCallback=function(_t,ft,Ct){var Ie=r.unstable_now();switch(typeof Ct=="object"&&Ct!==null?(Ct=Ct.delay,Ct=typeof Ct=="number"&&0Ie?(_t.sortIndex=Ct,a(N,_t),u(L)===null&&_t===u(N)&&(fe?(Le(ut),ut=-1):fe=!0,Zt(De,Ct-Ie))):(_t.sortIndex=Qe,a(L,_t),_e||q||(_e=!0,or(Ye))),_t},r.unstable_shouldYield=At,r.unstable_wrapCallback=function(_t){var ft=ee;return function(){var Ct=ee;ee=ft;try{return _t.apply(this,arguments)}finally{ee=Ct}}}})(Ry)),Ry}var ix;function Tw(){return ix||(ix=1,Ay.exports=Sw()),Ay.exports}/** * @license React * react-dom.production.min.js * @@ -30,14 +30,14 @@ var yw=Object.defineProperty;var Xv=r=>{throw TypeError(r)};var _w=(r,o,u)=>o in * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. - */var ix;function Tw(){if(ix)return Po;ix=1;var r=A_(),o=Sw();function u(a){for(var d="https://reactjs.org/docs/error-decoder.html?invariant="+a,x=1;x"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),L=Object.prototype.hasOwnProperty,N=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Z={},V={};function ee(a){return L.call(V,a)?!0:L.call(Z,a)?!1:N.test(a)?V[a]=!0:(Z[a]=!0,!1)}function q(a,d,x,C){if(x!==null&&x.type===0)return!1;switch(typeof d){case"function":case"symbol":return!0;case"boolean":return C?!1:x!==null?!x.acceptsBooleans:(a=a.toLowerCase().slice(0,5),a!=="data-"&&a!=="aria-");default:return!1}}function be(a,d,x,C){if(d===null||typeof d>"u"||q(a,d,x,C))return!0;if(C)return!1;if(x!==null)switch(x.type){case 3:return!d;case 4:return d===!1;case 5:return isNaN(d);case 6:return isNaN(d)||1>d}return!1}function pe(a,d,x,C,R,O,Q){this.acceptsBooleans=d===2||d===3||d===4,this.attributeName=C,this.attributeNamespace=R,this.mustUseProperty=x,this.propertyName=a,this.type=d,this.sanitizeURL=O,this.removeEmptyString=Q}var Pe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){Pe[a]=new pe(a,0,!1,a,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var d=a[0];Pe[d]=new pe(d,1,!1,a[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(a){Pe[a]=new pe(a,2,!1,a.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){Pe[a]=new pe(a,2,!1,a,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){Pe[a]=new pe(a,3,!1,a.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(a){Pe[a]=new pe(a,3,!0,a,null,!1,!1)}),["capture","download"].forEach(function(a){Pe[a]=new pe(a,4,!1,a,null,!1,!1)}),["cols","rows","size","span"].forEach(function(a){Pe[a]=new pe(a,6,!1,a,null,!1,!1)}),["rowSpan","start"].forEach(function(a){Pe[a]=new pe(a,5,!1,a.toLowerCase(),null,!1,!1)});var ze=/[\-:]([a-z])/g;function Re(a){return a[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(a){var d=a.replace(ze,Re);Pe[d]=new pe(d,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(a){Pe[a]=new pe(a,1,!1,a.toLowerCase(),null,!1,!1)}),Pe.xlinkHref=new pe("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(a){Pe[a]=new pe(a,1,!1,a.toLowerCase(),null,!0,!0)});function Te(a,d,x,C){var R=Pe.hasOwnProperty(d)?Pe[d]:null;(R!==null?R.type!==0:C||!(2"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),L=Object.prototype.hasOwnProperty,N=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,Z={},V={};function ee(o){return L.call(V,o)?!0:L.call(Z,o)?!1:N.test(o)?V[o]=!0:(Z[o]=!0,!1)}function q(o,d,x,C){if(x!==null&&x.type===0)return!1;switch(typeof d){case"function":case"symbol":return!0;case"boolean":return C?!1:x!==null?!x.acceptsBooleans:(o=o.toLowerCase().slice(0,5),o!=="data-"&&o!=="aria-");default:return!1}}function _e(o,d,x,C){if(d===null||typeof d>"u"||q(o,d,x,C))return!0;if(C)return!1;if(x!==null)switch(x.type){case 3:return!d;case 4:return d===!1;case 5:return isNaN(d);case 6:return isNaN(d)||1>d}return!1}function fe(o,d,x,C,R,O,J){this.acceptsBooleans=d===2||d===3||d===4,this.attributeName=C,this.attributeNamespace=R,this.mustUseProperty=x,this.propertyName=o,this.type=d,this.sanitizeURL=O,this.removeEmptyString=J}var Pe={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(o){Pe[o]=new fe(o,0,!1,o,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(o){var d=o[0];Pe[d]=new fe(d,1,!1,o[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(o){Pe[o]=new fe(o,2,!1,o.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(o){Pe[o]=new fe(o,2,!1,o,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(o){Pe[o]=new fe(o,3,!1,o.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(o){Pe[o]=new fe(o,3,!0,o,null,!1,!1)}),["capture","download"].forEach(function(o){Pe[o]=new fe(o,4,!1,o,null,!1,!1)}),["cols","rows","size","span"].forEach(function(o){Pe[o]=new fe(o,6,!1,o,null,!1,!1)}),["rowSpan","start"].forEach(function(o){Pe[o]=new fe(o,5,!1,o.toLowerCase(),null,!1,!1)});var Le=/[\-:]([a-z])/g;function Me(o){return o[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(o){var d=o.replace(Le,Me);Pe[d]=new fe(d,1,!1,o,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(o){var d=o.replace(Le,Me);Pe[d]=new fe(d,1,!1,o,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(o){var d=o.replace(Le,Me);Pe[d]=new fe(d,1,!1,o,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(o){Pe[o]=new fe(o,1,!1,o.toLowerCase(),null,!1,!1)}),Pe.xlinkHref=new fe("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(o){Pe[o]=new fe(o,1,!1,o.toLowerCase(),null,!0,!0)});function Se(o,d,x,C){var R=Pe.hasOwnProperty(d)?Pe[d]:null;(R!==null?R.type!==0:C||!(2me||R[Q]!==O[me]){var Ee=` -`+R[Q].replace(" at new "," at ");return a.displayName&&Ee.includes("")&&(Ee=Ee.replace("",a.displayName)),Ee}while(1<=Q&&0<=me);break}}}finally{jt=!1,Error.prepareStackTrace=x}return(a=a?a.displayName||a.name:"")?Qe(a):""}function fr(a){switch(a.tag){case 5:return Qe(a.type);case 16:return Qe("Lazy");case 13:return Qe("Suspense");case 19:return Qe("SuspenseList");case 0:case 2:case 15:return a=tr(a.type,!1),a;case 11:return a=tr(a.type.render,!1),a;case 1:return a=tr(a.type,!0),a;default:return""}}function Kt(a){if(a==null)return null;if(typeof a=="function")return a.displayName||a.name||null;if(typeof a=="string")return a;switch(a){case ct:return"Fragment";case at:return"Portal";case ot:return"Profiler";case dt:return"StrictMode";case Mt:return"Suspense";case Ht:return"SuspenseList"}if(typeof a=="object")switch(a.$$typeof){case Rt:return(a.displayName||"Context")+".Consumer";case Xe:return(a._context.displayName||"Context")+".Provider";case At:var d=a.render;return a=a.displayName,a||(a=d.displayName||d.name||"",a=a!==""?"ForwardRef("+a+")":"ForwardRef"),a;case zr:return d=a.displayName||null,d!==null?d:Kt(a.type)||"Memo";case ar:d=a._payload,a=a._init;try{return Kt(a(d))}catch{}}return null}function Lr(a){var d=a.type;switch(a.tag){case 24:return"Cache";case 9:return(d.displayName||"Context")+".Consumer";case 10:return(d._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=d.render,a=a.displayName||a.name||"",d.displayName||(a!==""?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return d;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Kt(d);case 8:return d===dt?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof d=="function")return d.displayName||d.name||null;if(typeof d=="string")return d}return null}function sr(a){switch(typeof a){case"boolean":case"number":case"string":case"undefined":return a;case"object":return a;default:return""}}function yr(a){var d=a.type;return(a=a.nodeName)&&a.toLowerCase()==="input"&&(d==="checkbox"||d==="radio")}function lr(a){var d=yr(a)?"checked":"value",x=Object.getOwnPropertyDescriptor(a.constructor.prototype,d),C=""+a[d];if(!a.hasOwnProperty(d)&&typeof x<"u"&&typeof x.get=="function"&&typeof x.set=="function"){var R=x.get,O=x.set;return Object.defineProperty(a,d,{configurable:!0,get:function(){return R.call(this)},set:function(Q){C=""+Q,O.call(this,Q)}}),Object.defineProperty(a,d,{enumerable:x.enumerable}),{getValue:function(){return C},setValue:function(Q){C=""+Q},stopTracking:function(){a._valueTracker=null,delete a[d]}}}}function ci(a){a._valueTracker||(a._valueTracker=lr(a))}function ln(a){if(!a)return!1;var d=a._valueTracker;if(!d)return!0;var x=d.getValue(),C="";return a&&(C=yr(a)?a.checked?"true":"false":a.value),a=C,a!==x?(d.setValue(a),!0):!1}function Vr(a){if(a=a||(typeof document<"u"?document:void 0),typeof a>"u")return null;try{return a.activeElement||a.body}catch{return a.body}}function Nr(a,d){var x=d.checked;return Ct({},d,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:x??a._wrapperState.initialChecked})}function Cn(a,d){var x=d.defaultValue==null?"":d.defaultValue,C=d.checked!=null?d.checked:d.defaultChecked;x=sr(d.value!=null?d.value:x),a._wrapperState={initialChecked:C,initialValue:x,controlled:d.type==="checkbox"||d.type==="radio"?d.checked!=null:d.value!=null}}function jn(a,d){d=d.checked,d!=null&&Te(a,"checked",d,!1)}function Yi(a,d){jn(a,d);var x=sr(d.value),C=d.type;if(x!=null)C==="number"?(x===0&&a.value===""||a.value!=x)&&(a.value=""+x):a.value!==""+x&&(a.value=""+x);else if(C==="submit"||C==="reset"){a.removeAttribute("value");return}d.hasOwnProperty("value")?Zn(a,d.type,x):d.hasOwnProperty("defaultValue")&&Zn(a,d.type,sr(d.defaultValue)),d.checked==null&&d.defaultChecked!=null&&(a.defaultChecked=!!d.defaultChecked)}function Ri(a,d,x){if(d.hasOwnProperty("value")||d.hasOwnProperty("defaultValue")){var C=d.type;if(!(C!=="submit"&&C!=="reset"||d.value!==void 0&&d.value!==null))return;d=""+a._wrapperState.initialValue,x||d===a.value||(a.value=d),a.defaultValue=d}x=a.name,x!==""&&(a.name=""),a.defaultChecked=!!a._wrapperState.initialChecked,x!==""&&(a.name=x)}function Zn(a,d,x){(d!=="number"||Vr(a.ownerDocument)!==a)&&(x==null?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+x&&(a.defaultValue=""+x))}var ve=Array.isArray;function Be(a,d,x,C){if(a=a.options,d){d={};for(var R=0;R"+d.valueOf().toString()+"",d=Sr.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;d.firstChild;)a.appendChild(d.firstChild)}});function K(a,d){if(d){var x=a.firstChild;if(x&&x===a.lastChild&&x.nodeType===3){x.nodeValue=d;return}}a.textContent=d}var X={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ne=["Webkit","ms","Moz","O"];Object.keys(X).forEach(function(a){ne.forEach(function(d){d=d+a.charAt(0).toUpperCase()+a.substring(1),X[d]=X[a]})});function ue(a,d,x){return d==null||typeof d=="boolean"||d===""?"":x||typeof d!="number"||d===0||X.hasOwnProperty(a)&&X[a]?(""+d).trim():d+"px"}function _e(a,d){a=a.style;for(var x in d)if(d.hasOwnProperty(x)){var C=x.indexOf("--")===0,R=ue(x,d[x],C);x==="float"&&(x="cssFloat"),C?a.setProperty(x,R):a[x]=R}}var Me=Ct({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Oe(a,d){if(d){if(Me[a]&&(d.children!=null||d.dangerouslySetInnerHTML!=null))throw Error(u(137,a));if(d.dangerouslySetInnerHTML!=null){if(d.children!=null)throw Error(u(60));if(typeof d.dangerouslySetInnerHTML!="object"||!("__html"in d.dangerouslySetInnerHTML))throw Error(u(61))}if(d.style!=null&&typeof d.style!="object")throw Error(u(62))}}function Ae(a,d){if(a.indexOf("-")===-1)return typeof d.is=="string";switch(a){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var qe=null;function rt(a){return a=a.target||a.srcElement||window,a.correspondingUseElement&&(a=a.correspondingUseElement),a.nodeType===3?a.parentNode:a}var Je=null,zt=null,Ce=null;function $t(a){if(a=Zl(a)){if(typeof Je!="function")throw Error(u(280));var d=a.stateNode;d&&(d=Oc(d),Je(a.stateNode,a.type,d))}}function rr(a){zt?Ce?Ce.push(a):Ce=[a]:zt=a}function Dt(){if(zt){var a=zt,d=Ce;if(Ce=zt=null,$t(a),d)for(a=0;a>>=0,a===0?32:31-(Vd(a)/bl|0)|0}var gu=64,yu=4194304;function mc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function wl(a,d){var x=a.pendingLanes;if(x===0)return 0;var C=0,R=a.suspendedLanes,O=a.pingedLanes,Q=x&268435455;if(Q!==0){var me=Q&~R;me!==0?C=mc(me):(O&=Q,O!==0&&(C=mc(O)))}else Q=x&~R,Q!==0?C=mc(Q):O!==0&&(C=mc(O));if(C===0)return 0;if(d!==0&&d!==C&&(d&R)===0&&(R=C&-C,O=d&-d,R>=O||R===16&&(O&4194240)!==0))return d;if((C&4)!==0&&(C|=x&16),d=a.entangledLanes,d!==0)for(a=a.entanglements,d&=C;0x;x++)d.push(a);return d}function Na(a,d,x){a.pendingLanes|=d,d!==536870912&&(a.suspendedLanes=0,a.pingedLanes=0),a=a.eventTimes,d=31-ks(d),a[d]=x}function Qf(a,d){var x=a.pendingLanes&~d;a.pendingLanes=d,a.suspendedLanes=0,a.pingedLanes=0,a.expiredLanes&=d,a.mutableReadLanes&=d,a.entangledLanes&=d,d=a.entanglements;var C=a.eventTimes;for(a=a.expirationTimes;0=Al),wc=" ",Mh=!1;function Ah(a,d){switch(a){case"keyup":return Ch.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function ga(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var ya=!1;function Rh(a,d){switch(a){case"compositionend":return ga(d);case"keypress":return d.which!==32?null:(Mh=!0,wc);case"textInput":return a=d.data,a===wc&&Mh?null:a;default:return null}}function Sc(a,d){if(ya)return a==="compositionend"||!kh&&Ah(a,d)?(a=Is(),Ba=kl=Us=null,ya=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:x,offset:d-a};a=C}e:{for(;x;){if(x.nextSibling){x=x.nextSibling;break e}x=x.parentNode}x=void 0}x=Xd(x)}}function Yd(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?Yd(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function Nh(){for(var a=window,d=Vr();d instanceof a.HTMLIFrameElement;){try{var x=typeof d.contentWindow.location.href=="string"}catch{x=!1}if(x)a=d.contentWindow;else break;d=Vr(a.document)}return d}function Fl(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}function Pu(a){var d=Nh(),x=a.focusedElem,C=a.selectionRange;if(d!==x&&x&&x.ownerDocument&&Yd(x.ownerDocument.documentElement,x)){if(C!==null&&Fl(x)){if(d=C.start,a=C.end,a===void 0&&(a=d),"selectionStart"in x)x.selectionStart=d,x.selectionEnd=Math.min(a,x.value.length);else if(a=(d=x.ownerDocument||document)&&d.defaultView||window,a.getSelection){a=a.getSelection();var R=x.textContent.length,O=Math.min(C.start,R);C=C.end===void 0?O:Math.min(C.end,R),!a.extend&&O>C&&(R=C,C=O,O=R),R=jh(x,O);var Q=jh(x,C);R&&Q&&(a.rangeCount!==1||a.anchorNode!==R.node||a.anchorOffset!==R.offset||a.focusNode!==Q.node||a.focusOffset!==Q.offset)&&(d=d.createRange(),d.setStart(R.node,R.offset),a.removeAllRanges(),O>C?(a.addRange(d),a.extend(Q.node,Q.offset)):(d.setEnd(Q.node,Q.offset),a.addRange(d)))}}for(d=[],a=x;a=a.parentNode;)a.nodeType===1&&d.push({element:a,left:a.scrollLeft,top:a.scrollTop});for(typeof x.focus=="function"&&x.focus(),x=0;x=document.documentMode,_a=null,Fo=null,Oo=null,va=!1;function ps(a,d,x){var C=x.window===x?x.document:x.nodeType===9?x:x.ownerDocument;va||_a==null||_a!==Vr(C)||(C=_a,"selectionStart"in C&&Fl(C)?C={start:C.selectionStart,end:C.selectionEnd}:(C=(C.ownerDocument&&C.ownerDocument.defaultView||window).getSelection(),C={anchorNode:C.anchorNode,anchorOffset:C.anchorOffset,focusNode:C.focusNode,focusOffset:C.focusOffset}),Oo&&Pc(Oo,C)||(Oo=C,C=Rc(Fo,"onSelect"),0Xa||(a.current=Au[Xa],Au[Xa]=null,Xa--)}function Or(a,d){Xa++,Au[Xa]=a.current,a.current=d}var gs={},ii=ni(gs),zi=ni(!1),Sa=gs;function Hs(a,d){var x=a.type.contextTypes;if(!x)return gs;var C=a.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===d)return C.__reactInternalMemoizedMaskedChildContext;var R={},O;for(O in x)R[O]=d[O];return C&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=d,a.__reactInternalMemoizedMaskedChildContext=R),R}function wi(a){return a=a.childContextTypes,a!=null}function Go(){rn(zi),rn(ii)}function Zh(a,d,x){if(ii.current!==gs)throw Error(u(168));Or(ii,d),Or(zi,x)}function Di(a,d,x){var C=a.stateNode;if(d=d.childContextTypes,typeof C.getChildContext!="function")return x;C=C.getChildContext();for(var R in C)if(!(R in d))throw Error(u(108,Lr(a)||"Unknown",R));return Ct({},x,C)}function Qi(a){return a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||gs,Sa=ii.current,Or(ii,a),Or(zi,zi.current),!0}function Ya(a,d,x){var C=a.stateNode;if(!C)throw Error(u(169));x?(a=Di(a,d,Sa),C.__reactInternalMemoizedMergedChildContext=a,rn(zi),rn(ii),Or(ii,a)):rn(zi),Or(zi,x)}var Hn=null,qo=!1,Gh=!1;function Gl(a){Hn===null?Hn=[a]:Hn.push(a)}function lp(a){qo=!0,Gl(a)}function Ta(){if(!Gh&&Hn!==null){Gh=!0;var a=0,d=qr;try{var x=Hn;for(qr=1;a>=Q,R-=Q,ts=1<<32-ks(d)+R|x<vr?(gi=dr,dr=null):gi=dr.sibling;var Hr=st(Ve,dr,Ge[vr],bt);if(Hr===null){dr===null&&(dr=gi);break}a&&dr&&Hr.alternate===null&&d(Ve,dr),Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr,dr=gi}if(vr===Ge.length)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;vrvr?(gi=dr,dr=null):gi=dr.sibling;var al=st(Ve,dr,Hr.value,bt);if(al===null){dr===null&&(dr=gi);break}a&&dr&&al.alternate===null&&d(Ve,dr),Le=O(al,Le,vr),hr===null?Gt=al:hr.sibling=al,hr=al,dr=gi}if(Hr.done)return x(Ve,dr),vn&&vo(Ve,vr),Gt;if(dr===null){for(;!Hr.done;vr++,Hr=Ge.next())Hr=lt(Ve,Hr.value,bt),Hr!==null&&(Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return vn&&vo(Ve,vr),Gt}for(dr=C(Ve,dr);!Hr.done;vr++,Hr=Ge.next())Hr=Ot(dr,Ve,vr,Hr.value,bt),Hr!==null&&(a&&Hr.alternate!==null&&dr.delete(Hr.key===null?vr:Hr.key),Le=O(Hr,Le,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return a&&dr.forEach(function(hy){return d(Ve,hy)}),vn&&vo(Ve,vr),Gt}function In(Ve,Le,Ge,bt){if(typeof Ge=="object"&&Ge!==null&&Ge.type===ct&&Ge.key===null&&(Ge=Ge.props.children),typeof Ge=="object"&&Ge!==null){switch(Ge.$$typeof){case Ye:e:{for(var Gt=Ge.key,hr=Le;hr!==null;){if(hr.key===Gt){if(Gt=Ge.type,Gt===ct){if(hr.tag===7){x(Ve,hr.sibling),Le=R(hr,Ge.props.children),Le.return=Ve,Ve=Le;break e}}else if(hr.elementType===Gt||typeof Gt=="object"&&Gt!==null&&Gt.$$typeof===ar&&Du(Gt)===hr.type){x(Ve,hr.sibling),Le=R(hr,Ge.props),Le.ref=Wl(Ve,hr,Ge),Le.return=Ve,Ve=Le;break e}x(Ve,hr);break}else d(Ve,hr);hr=hr.sibling}Ge.type===ct?(Le=Yc(Ge.props.children,Ve.mode,bt,Ge.key),Le.return=Ve,Ve=Le):(bt=df(Ge.type,Ge.key,Ge.props,null,Ve.mode,bt),bt.ref=Wl(Ve,Le,Ge),bt.return=Ve,Ve=bt)}return Q(Ve);case at:e:{for(hr=Ge.key;Le!==null;){if(Le.key===hr)if(Le.tag===4&&Le.stateNode.containerInfo===Ge.containerInfo&&Le.stateNode.implementation===Ge.implementation){x(Ve,Le.sibling),Le=R(Le,Ge.children||[]),Le.return=Ve,Ve=Le;break e}else{x(Ve,Le);break}else d(Ve,Le);Le=Le.sibling}Le=Dp(Ge,Ve.mode,bt),Le.return=Ve,Ve=Le}return Q(Ve);case ar:return hr=Ge._init,In(Ve,Le,hr(Ge._payload),bt)}if(ve(Ge))return Vt(Ve,Le,Ge,bt);if(ft(Ge))return Qt(Ve,Le,Ge,bt);Hl(Ve,Ge)}return typeof Ge=="string"&&Ge!==""||typeof Ge=="number"?(Ge=""+Ge,Le!==null&&Le.tag===6?(x(Ve,Le.sibling),Le=R(Le,Ge),Le.return=Ve,Ve=Le):(x(Ve,Le),Le=zp(Ge,Ve.mode,bt),Le.return=Ve,Ve=Le),Q(Ve)):x(Ve,Le)}return In}var Oi=Xh(!0),Bc=Xh(!1),Lt=ni(null),Yt=null,Ho=null,xo=null;function Kl(){xo=Ho=Yt=null}function fi(a){var d=Lt.current;rn(Lt),a._currentValue=d}function Lu(a,d,x){for(;a!==null;){var C=a.alternate;if((a.childLanes&d)!==d?(a.childLanes|=d,C!==null&&(C.childLanes|=d)):C!==null&&(C.childLanes&d)!==d&&(C.childLanes|=d),a===x)break;a=a.return}}function pi(a,d){Yt=a,xo=Ho=null,a=a.dependencies,a!==null&&a.firstContext!==null&&((a.lanes&d)!==0&&(Ds=!0),a.firstContext=null)}function As(a){var d=a._currentValue;if(xo!==a)if(a={context:a,memoizedValue:d,next:null},Ho===null){if(Yt===null)throw Error(u(308));Ho=a,Yt.dependencies={lanes:0,firstContext:a}}else Ho=Ho.next=a;return d}var Ks=null;function Yr(a){Ks===null?Ks=[a]:Ks.push(a)}function wn(a,d,x,C){var R=d.interleaved;return R===null?(x.next=x,Yr(d)):(x.next=R.next,R.next=x),d.interleaved=x,Rs(a,C)}function Rs(a,d){a.lanes|=d;var x=a.alternate;for(x!==null&&(x.lanes|=d),x=a,a=a.return;a!==null;)a.childLanes|=d,x=a.alternate,x!==null&&(x.childLanes|=d),x=a,a=a.return;return x.tag===3?x.stateNode:null}var vs=!1;function Ca(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fu(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function rs(a,d){return{eventTime:a,lane:d,tag:0,payload:null,callback:null,next:null}}function ns(a,d,x){var C=a.updateQueue;if(C===null)return null;if(C=C.shared,(Br&2)!==0){var R=C.pending;return R===null?d.next=d:(d.next=R.next,R.next=d),C.pending=d,Rs(a,x)}return R=C.interleaved,R===null?(d.next=d,Yr(C)):(d.next=R.next,R.next=d),C.interleaved=d,Rs(a,x)}function ka(a,d,x){if(d=d.updateQueue,d!==null&&(d=d.shared,(x&4194240)!==0)){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ua(a,x)}}function $c(a,d){var x=a.updateQueue,C=a.alternate;if(C!==null&&(C=C.updateQueue,x===C)){var R=null,O=null;if(x=x.firstBaseUpdate,x!==null){do{var Q={eventTime:x.eventTime,lane:x.lane,tag:x.tag,payload:x.payload,callback:x.callback,next:null};O===null?R=O=Q:O=O.next=Q,x=x.next}while(x!==null);O===null?R=O=d:O=O.next=d}else R=O=d;x={baseState:C.baseState,firstBaseUpdate:R,lastBaseUpdate:O,shared:C.shared,effects:C.effects},a.updateQueue=x;return}a=x.lastBaseUpdate,a===null?x.firstBaseUpdate=d:a.next=d,x.lastBaseUpdate=d}function bo(a,d,x,C){var R=a.updateQueue;vs=!1;var O=R.firstBaseUpdate,Q=R.lastBaseUpdate,me=R.shared.pending;if(me!==null){R.shared.pending=null;var Ee=me,He=Ee.next;Ee.next=null,Q===null?O=He:Q.next=He,Q=Ee;var ht=a.alternate;ht!==null&&(ht=ht.updateQueue,me=ht.lastBaseUpdate,me!==Q&&(me===null?ht.firstBaseUpdate=He:me.next=He,ht.lastBaseUpdate=Ee))}if(O!==null){var lt=R.baseState;Q=0,ht=He=Ee=null,me=O;do{var st=me.lane,Ot=me.eventTime;if((C&st)===st){ht!==null&&(ht=ht.next={eventTime:Ot,lane:0,tag:me.tag,payload:me.payload,callback:me.callback,next:null});e:{var Vt=a,Qt=me;switch(st=d,Ot=x,Qt.tag){case 1:if(Vt=Qt.payload,typeof Vt=="function"){lt=Vt.call(Ot,lt,st);break e}lt=Vt;break e;case 3:Vt.flags=Vt.flags&-65537|128;case 0:if(Vt=Qt.payload,st=typeof Vt=="function"?Vt.call(Ot,lt,st):Vt,st==null)break e;lt=Ct({},lt,st);break e;case 2:vs=!0}}me.callback!==null&&me.lane!==0&&(a.flags|=64,st=R.effects,st===null?R.effects=[me]:st.push(me))}else Ot={eventTime:Ot,lane:st,tag:me.tag,payload:me.payload,callback:me.callback,next:null},ht===null?(He=ht=Ot,Ee=lt):ht=ht.next=Ot,Q|=st;if(me=me.next,me===null){if(me=R.shared.pending,me===null)break;st=me,me=st.next,st.next=null,R.lastBaseUpdate=st,R.shared.pending=null}}while(!0);if(ht===null&&(Ee=lt),R.baseState=Ee,R.firstBaseUpdate=He,R.lastBaseUpdate=ht,d=R.shared.interleaved,d!==null){R=d;do Q|=R.lane,R=R.next;while(R!==d)}else O===null&&(R.shared.lanes=0);Ql|=Q,a.lanes=Q,a.memoizedState=lt}}function Ou(a,d,x){if(a=d.effects,d.effects=null,a!==null)for(d=0;dx?x:4,a(!0);var C=B.transition;B.transition={};try{a(!1),d()}finally{qr=x,B.transition=C}}function ji(){return $e().memoizedState}function Xs(a,d,x){var C=Aa(a);if(x={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null},Xo(a))Fn(d,x);else if(x=wn(a,d,x,C),x!==null){var R=mi();ea(x,a,C,R),Nn(x,d,C)}}function Ni(a,d,x){var C=Aa(a),R={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null};if(Xo(a))Fn(d,R);else{var O=a.alternate;if(a.lanes===0&&(O===null||O.lanes===0)&&(O=d.lastRenderedReducer,O!==null))try{var Q=d.lastRenderedState,me=O(Q,x);if(R.hasEagerState=!0,R.eagerState=me,qs(me,Q)){var Ee=d.interleaved;Ee===null?(R.next=R,Yr(d)):(R.next=Ee.next,Ee.next=R),d.interleaved=R;return}}catch{}finally{}x=wn(a,d,R,C),x!==null&&(R=mi(),ea(x,a,C,R),Nn(x,d,C))}}function Xo(a){var d=a.alternate;return a===$||d!==null&&d===$}function Fn(a,d){oe=se=!0;var x=a.pending;x===null?d.next=d:(d.next=x.next,x.next=d),a.pending=d}function Nn(a,d,x){if((x&4194240)!==0){var C=d.lanes;C&=a.pendingLanes,x|=C,d.lanes=x,ua(a,x)}}var Bi={readContext:As,useCallback:le,useContext:le,useEffect:le,useImperativeHandle:le,useInsertionEffect:le,useLayoutEffect:le,useMemo:le,useReducer:le,useRef:le,useState:le,useDebugValue:le,useDeferredValue:le,useTransition:le,useMutableSource:le,useSyncExternalStore:le,useId:le,unstable_isNewReconciler:!1},Bn={readContext:As,useCallback:function(a,d){return ke().memoizedState=[a,d===void 0?null:d],a},useContext:As,useEffect:Dn,useImperativeHandle:function(a,d,x){return x=x!=null?x.concat([a]):null,mr(4194308,4,zs.bind(null,d,a),x)},useLayoutEffect:function(a,d){return mr(4194308,4,a,d)},useInsertionEffect:function(a,d){return mr(4,2,a,d)},useMemo:function(a,d){var x=ke();return d=d===void 0?null:d,a=a(),x.memoizedState=[a,d],a},useReducer:function(a,d,x){var C=ke();return d=x!==void 0?x(d):d,C.memoizedState=C.baseState=d,a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:d},C.queue=a,a=a.dispatch=Xs.bind(null,$,a),[C.memoizedState,a]},useRef:function(a){var d=ke();return a={current:a},d.memoizedState=a},useState:Qr,useDebugValue:wo,useDeferredValue:function(a){return ke().memoizedState=a},useTransition:function(){var a=Qr(!1),d=a[0];return a=rl.bind(null,a[1]),ke().memoizedState=a,[d,a]},useMutableSource:function(){},useSyncExternalStore:function(a,d,x){var C=$,R=ke();if(vn){if(x===void 0)throw Error(u(407));x=x()}else{if(x=d(),Yn===null)throw Error(u(349));(j&30)!==0||tt(C,d,x)}R.memoizedState=x;var O={value:x,getSnapshot:d};return R.queue=O,Dn(br.bind(null,C,O,a),[a]),C.flags|=2048,un(9,Ft.bind(null,C,O,x,d),void 0,null),x},useId:function(){var a=ke(),d=Yn.identifierPrefix;if(vn){var x=Fi,C=ts;x=(C&~(1<<32-ks(C)-1)).toString(32)+x,d=":"+d+"R"+x,x=ae++,0me||R[J]!==O[me]){var Ee=` +`+R[J].replace(" at new "," at ");return o.displayName&&Ee.includes("")&&(Ee=Ee.replace("",o.displayName)),Ee}while(1<=J&&0<=me);break}}}finally{jt=!1,Error.prepareStackTrace=x}return(o=o?o.displayName||o.name:"")?Qe(o):""}function fr(o){switch(o.tag){case 5:return Qe(o.type);case 16:return Qe("Lazy");case 13:return Qe("Suspense");case 19:return Qe("SuspenseList");case 0:case 2:case 15:return o=tr(o.type,!1),o;case 11:return o=tr(o.type.render,!1),o;case 1:return o=tr(o.type,!0),o;default:return""}}function Kt(o){if(o==null)return null;if(typeof o=="function")return o.displayName||o.name||null;if(typeof o=="string")return o;switch(o){case lt:return"Fragment";case ot:return"Portal";case Je:return"Profiler";case ut:return"StrictMode";case Mt:return"Suspense";case Ht:return"SuspenseList"}if(typeof o=="object")switch(o.$$typeof){case At:return(o.displayName||"Context")+".Consumer";case Ke:return(o._context.displayName||"Context")+".Provider";case Rt:var d=o.render;return o=o.displayName,o||(o=d.displayName||d.name||"",o=o!==""?"ForwardRef("+o+")":"ForwardRef"),o;case zr:return d=o.displayName||null,d!==null?d:Kt(o.type)||"Memo";case or:d=o._payload,o=o._init;try{return Kt(o(d))}catch{}}return null}function Lr(o){var d=o.type;switch(o.tag){case 24:return"Cache";case 9:return(d.displayName||"Context")+".Consumer";case 10:return(d._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return o=d.render,o=o.displayName||o.name||"",d.displayName||(o!==""?"ForwardRef("+o+")":"ForwardRef");case 7:return"Fragment";case 5:return d;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return Kt(d);case 8:return d===ut?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof d=="function")return d.displayName||d.name||null;if(typeof d=="string")return d}return null}function sr(o){switch(typeof o){case"boolean":case"number":case"string":case"undefined":return o;case"object":return o;default:return""}}function yr(o){var d=o.type;return(o=o.nodeName)&&o.toLowerCase()==="input"&&(d==="checkbox"||d==="radio")}function lr(o){var d=yr(o)?"checked":"value",x=Object.getOwnPropertyDescriptor(o.constructor.prototype,d),C=""+o[d];if(!o.hasOwnProperty(d)&&typeof x<"u"&&typeof x.get=="function"&&typeof x.set=="function"){var R=x.get,O=x.set;return Object.defineProperty(o,d,{configurable:!0,get:function(){return R.call(this)},set:function(J){C=""+J,O.call(this,J)}}),Object.defineProperty(o,d,{enumerable:x.enumerable}),{getValue:function(){return C},setValue:function(J){C=""+J},stopTracking:function(){o._valueTracker=null,delete o[d]}}}}function ci(o){o._valueTracker||(o._valueTracker=lr(o))}function ln(o){if(!o)return!1;var d=o._valueTracker;if(!d)return!0;var x=d.getValue(),C="";return o&&(C=yr(o)?o.checked?"true":"false":o.value),o=C,o!==x?(d.setValue(o),!0):!1}function Vr(o){if(o=o||(typeof document<"u"?document:void 0),typeof o>"u")return null;try{return o.activeElement||o.body}catch{return o.body}}function Nr(o,d){var x=d.checked;return Ct({},d,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:x??o._wrapperState.initialChecked})}function Cn(o,d){var x=d.defaultValue==null?"":d.defaultValue,C=d.checked!=null?d.checked:d.defaultChecked;x=sr(d.value!=null?d.value:x),o._wrapperState={initialChecked:C,initialValue:x,controlled:d.type==="checkbox"||d.type==="radio"?d.checked!=null:d.value!=null}}function jn(o,d){d=d.checked,d!=null&&Se(o,"checked",d,!1)}function Yi(o,d){jn(o,d);var x=sr(d.value),C=d.type;if(x!=null)C==="number"?(x===0&&o.value===""||o.value!=x)&&(o.value=""+x):o.value!==""+x&&(o.value=""+x);else if(C==="submit"||C==="reset"){o.removeAttribute("value");return}d.hasOwnProperty("value")?Zn(o,d.type,x):d.hasOwnProperty("defaultValue")&&Zn(o,d.type,sr(d.defaultValue)),d.checked==null&&d.defaultChecked!=null&&(o.defaultChecked=!!d.defaultChecked)}function Ri(o,d,x){if(d.hasOwnProperty("value")||d.hasOwnProperty("defaultValue")){var C=d.type;if(!(C!=="submit"&&C!=="reset"||d.value!==void 0&&d.value!==null))return;d=""+o._wrapperState.initialValue,x||d===o.value||(o.value=d),o.defaultValue=d}x=o.name,x!==""&&(o.name=""),o.defaultChecked=!!o._wrapperState.initialChecked,x!==""&&(o.name=x)}function Zn(o,d,x){(d!=="number"||Vr(o.ownerDocument)!==o)&&(x==null?o.defaultValue=""+o._wrapperState.initialValue:o.defaultValue!==""+x&&(o.defaultValue=""+x))}var xe=Array.isArray;function Be(o,d,x,C){if(o=o.options,d){d={};for(var R=0;R"+d.valueOf().toString()+"",d=Sr.firstChild;o.firstChild;)o.removeChild(o.firstChild);for(;d.firstChild;)o.appendChild(d.firstChild)}});function K(o,d){if(d){var x=o.firstChild;if(x&&x===o.lastChild&&x.nodeType===3){x.nodeValue=d;return}}o.textContent=d}var X={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ne=["Webkit","ms","Moz","O"];Object.keys(X).forEach(function(o){ne.forEach(function(d){d=d+o.charAt(0).toUpperCase()+o.substring(1),X[d]=X[o]})});function ue(o,d,x){return d==null||typeof d=="boolean"||d===""?"":x||typeof d!="number"||d===0||X.hasOwnProperty(o)&&X[o]?(""+d).trim():d+"px"}function ve(o,d){o=o.style;for(var x in d)if(d.hasOwnProperty(x)){var C=x.indexOf("--")===0,R=ue(x,d[x],C);x==="float"&&(x="cssFloat"),C?o.setProperty(x,R):o[x]=R}}var Ae=Ct({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function Oe(o,d){if(d){if(Ae[o]&&(d.children!=null||d.dangerouslySetInnerHTML!=null))throw Error(u(137,o));if(d.dangerouslySetInnerHTML!=null){if(d.children!=null)throw Error(u(60));if(typeof d.dangerouslySetInnerHTML!="object"||!("__html"in d.dangerouslySetInnerHTML))throw Error(u(61))}if(d.style!=null&&typeof d.style!="object")throw Error(u(62))}}function Re(o,d){if(o.indexOf("-")===-1)return typeof d.is=="string";switch(o){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var qe=null;function nt(o){return o=o.target||o.srcElement||window,o.correspondingUseElement&&(o=o.correspondingUseElement),o.nodeType===3?o.parentNode:o}var et=null,zt=null,Ce=null;function $t(o){if(o=Zl(o)){if(typeof et!="function")throw Error(u(280));var d=o.stateNode;d&&(d=Oc(d),et(o.stateNode,o.type,d))}}function rr(o){zt?Ce?Ce.push(o):Ce=[o]:zt=o}function Dt(){if(zt){var o=zt,d=Ce;if(Ce=zt=null,$t(o),d)for(o=0;o>>=0,o===0?32:31-(Zd(o)/bl|0)|0}var gu=64,yu=4194304;function mc(o){switch(o&-o){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return o&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return o&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return o}}function wl(o,d){var x=o.pendingLanes;if(x===0)return 0;var C=0,R=o.suspendedLanes,O=o.pingedLanes,J=x&268435455;if(J!==0){var me=J&~R;me!==0?C=mc(me):(O&=J,O!==0&&(C=mc(O)))}else J=x&~R,J!==0?C=mc(J):O!==0&&(C=mc(O));if(C===0)return 0;if(d!==0&&d!==C&&(d&R)===0&&(R=C&-C,O=d&-d,R>=O||R===16&&(O&4194240)!==0))return d;if((C&4)!==0&&(C|=x&16),d=o.entangledLanes,d!==0)for(o=o.entanglements,d&=C;0x;x++)d.push(o);return d}function Bo(o,d,x){o.pendingLanes|=d,d!==536870912&&(o.suspendedLanes=0,o.pingedLanes=0),o=o.eventTimes,d=31-ks(d),o[d]=x}function Qf(o,d){var x=o.pendingLanes&~d;o.pendingLanes=d,o.suspendedLanes=0,o.pingedLanes=0,o.expiredLanes&=d,o.mutableReadLanes&=d,o.entangledLanes&=d,d=o.entanglements;var C=o.eventTimes;for(o=o.expirationTimes;0=Al),wc=" ",Mh=!1;function Ah(o,d){switch(o){case"keyup":return Ch.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function go(o){return o=o.detail,typeof o=="object"&&"data"in o?o.data:null}var yo=!1;function Rh(o,d){switch(o){case"compositionend":return go(d);case"keypress":return d.which!==32?null:(Mh=!0,wc);case"textInput":return o=d.data,o===wc&&Mh?null:o;default:return null}}function Sc(o,d){if(yo)return o==="compositionend"||!kh&&Ah(o,d)?(o=Is(),$o=kl=Us=null,yo=!1,o):null;switch(o){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:x,offset:d-o};o=C}e:{for(;x;){if(x.nextSibling){x=x.nextSibling;break e}x=x.parentNode}x=void 0}x=Yd(x)}}function Qd(o,d){return o&&d?o===d?!0:o&&o.nodeType===3?!1:d&&d.nodeType===3?Qd(o,d.parentNode):"contains"in o?o.contains(d):o.compareDocumentPosition?!!(o.compareDocumentPosition(d)&16):!1:!1}function Nh(){for(var o=window,d=Vr();d instanceof o.HTMLIFrameElement;){try{var x=typeof d.contentWindow.location.href=="string"}catch{x=!1}if(x)o=d.contentWindow;else break;d=Vr(o.document)}return d}function Fl(o){var d=o&&o.nodeName&&o.nodeName.toLowerCase();return d&&(d==="input"&&(o.type==="text"||o.type==="search"||o.type==="tel"||o.type==="url"||o.type==="password")||d==="textarea"||o.contentEditable==="true")}function Pu(o){var d=Nh(),x=o.focusedElem,C=o.selectionRange;if(d!==x&&x&&x.ownerDocument&&Qd(x.ownerDocument.documentElement,x)){if(C!==null&&Fl(x)){if(d=C.start,o=C.end,o===void 0&&(o=d),"selectionStart"in x)x.selectionStart=d,x.selectionEnd=Math.min(o,x.value.length);else if(o=(d=x.ownerDocument||document)&&d.defaultView||window,o.getSelection){o=o.getSelection();var R=x.textContent.length,O=Math.min(C.start,R);C=C.end===void 0?O:Math.min(C.end,R),!o.extend&&O>C&&(R=C,C=O,O=R),R=jh(x,O);var J=jh(x,C);R&&J&&(o.rangeCount!==1||o.anchorNode!==R.node||o.anchorOffset!==R.offset||o.focusNode!==J.node||o.focusOffset!==J.offset)&&(d=d.createRange(),d.setStart(R.node,R.offset),o.removeAllRanges(),O>C?(o.addRange(d),o.extend(J.node,J.offset)):(d.setEnd(J.node,J.offset),o.addRange(d)))}}for(d=[],o=x;o=o.parentNode;)o.nodeType===1&&d.push({element:o,left:o.scrollLeft,top:o.scrollTop});for(typeof x.focus=="function"&&x.focus(),x=0;x=document.documentMode,_o=null,La=null,Fa=null,vo=!1;function ps(o,d,x){var C=x.window===x?x.document:x.nodeType===9?x:x.ownerDocument;vo||_o==null||_o!==Vr(C)||(C=_o,"selectionStart"in C&&Fl(C)?C={start:C.selectionStart,end:C.selectionEnd}:(C=(C.ownerDocument&&C.ownerDocument.defaultView||window).getSelection(),C={anchorNode:C.anchorNode,anchorOffset:C.anchorOffset,focusNode:C.focusNode,focusOffset:C.focusOffset}),Fa&&Pc(Fa,C)||(Fa=C,C=Rc(La,"onSelect"),0Yo||(o.current=Au[Yo],Au[Yo]=null,Yo--)}function Or(o,d){Yo++,Au[Yo]=o.current,o.current=d}var gs={},si=ii(gs),zi=ii(!1),So=gs;function Hs(o,d){var x=o.type.contextTypes;if(!x)return gs;var C=o.stateNode;if(C&&C.__reactInternalMemoizedUnmaskedChildContext===d)return C.__reactInternalMemoizedMaskedChildContext;var R={},O;for(O in x)R[O]=d[O];return C&&(o=o.stateNode,o.__reactInternalMemoizedUnmaskedChildContext=d,o.__reactInternalMemoizedMaskedChildContext=R),R}function wi(o){return o=o.childContextTypes,o!=null}function Za(){rn(zi),rn(si)}function Zh(o,d,x){if(si.current!==gs)throw Error(u(168));Or(si,d),Or(zi,x)}function Di(o,d,x){var C=o.stateNode;if(d=d.childContextTypes,typeof C.getChildContext!="function")return x;C=C.getChildContext();for(var R in C)if(!(R in d))throw Error(u(108,Lr(o)||"Unknown",R));return Ct({},x,C)}function Qi(o){return o=(o=o.stateNode)&&o.__reactInternalMemoizedMergedChildContext||gs,So=si.current,Or(si,o),Or(zi,zi.current),!0}function Qo(o,d,x){var C=o.stateNode;if(!C)throw Error(u(169));x?(o=Di(o,d,So),C.__reactInternalMemoizedMergedChildContext=o,rn(zi),rn(si),Or(si,o)):rn(zi),Or(zi,x)}var Hn=null,Ga=!1,Gh=!1;function Gl(o){Hn===null?Hn=[o]:Hn.push(o)}function lp(o){Ga=!0,Gl(o)}function To(){if(!Gh&&Hn!==null){Gh=!0;var o=0,d=qr;try{var x=Hn;for(qr=1;o>=J,R-=J,ts=1<<32-ks(d)+R|x<vr?(gi=dr,dr=null):gi=dr.sibling;var Hr=at(Ve,dr,Ge[vr],bt);if(Hr===null){dr===null&&(dr=gi);break}o&&dr&&Hr.alternate===null&&d(Ve,dr),Fe=O(Hr,Fe,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr,dr=gi}if(vr===Ge.length)return x(Ve,dr),vn&&_a(Ve,vr),Gt;if(dr===null){for(;vrvr?(gi=dr,dr=null):gi=dr.sibling;var ll=at(Ve,dr,Hr.value,bt);if(ll===null){dr===null&&(dr=gi);break}o&&dr&&ll.alternate===null&&d(Ve,dr),Fe=O(ll,Fe,vr),hr===null?Gt=ll:hr.sibling=ll,hr=ll,dr=gi}if(Hr.done)return x(Ve,dr),vn&&_a(Ve,vr),Gt;if(dr===null){for(;!Hr.done;vr++,Hr=Ge.next())Hr=ct(Ve,Hr.value,bt),Hr!==null&&(Fe=O(Hr,Fe,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return vn&&_a(Ve,vr),Gt}for(dr=C(Ve,dr);!Hr.done;vr++,Hr=Ge.next())Hr=Ot(dr,Ve,vr,Hr.value,bt),Hr!==null&&(o&&Hr.alternate!==null&&dr.delete(Hr.key===null?vr:Hr.key),Fe=O(Hr,Fe,vr),hr===null?Gt=Hr:hr.sibling=Hr,hr=Hr);return o&&dr.forEach(function(dy){return d(Ve,dy)}),vn&&_a(Ve,vr),Gt}function In(Ve,Fe,Ge,bt){if(typeof Ge=="object"&&Ge!==null&&Ge.type===lt&&Ge.key===null&&(Ge=Ge.props.children),typeof Ge=="object"&&Ge!==null){switch(Ge.$$typeof){case Ye:e:{for(var Gt=Ge.key,hr=Fe;hr!==null;){if(hr.key===Gt){if(Gt=Ge.type,Gt===lt){if(hr.tag===7){x(Ve,hr.sibling),Fe=R(hr,Ge.props.children),Fe.return=Ve,Ve=Fe;break e}}else if(hr.elementType===Gt||typeof Gt=="object"&&Gt!==null&&Gt.$$typeof===or&&Du(Gt)===hr.type){x(Ve,hr.sibling),Fe=R(hr,Ge.props),Fe.ref=Wl(Ve,hr,Ge),Fe.return=Ve,Ve=Fe;break e}x(Ve,hr);break}else d(Ve,hr);hr=hr.sibling}Ge.type===lt?(Fe=Yc(Ge.props.children,Ve.mode,bt,Ge.key),Fe.return=Ve,Ve=Fe):(bt=ff(Ge.type,Ge.key,Ge.props,null,Ve.mode,bt),bt.ref=Wl(Ve,Fe,Ge),bt.return=Ve,Ve=bt)}return J(Ve);case ot:e:{for(hr=Ge.key;Fe!==null;){if(Fe.key===hr)if(Fe.tag===4&&Fe.stateNode.containerInfo===Ge.containerInfo&&Fe.stateNode.implementation===Ge.implementation){x(Ve,Fe.sibling),Fe=R(Fe,Ge.children||[]),Fe.return=Ve,Ve=Fe;break e}else{x(Ve,Fe);break}else d(Ve,Fe);Fe=Fe.sibling}Fe=Dp(Ge,Ve.mode,bt),Fe.return=Ve,Ve=Fe}return J(Ve);case or:return hr=Ge._init,In(Ve,Fe,hr(Ge._payload),bt)}if(xe(Ge))return Vt(Ve,Fe,Ge,bt);if(ft(Ge))return Qt(Ve,Fe,Ge,bt);Hl(Ve,Ge)}return typeof Ge=="string"&&Ge!==""||typeof Ge=="number"?(Ge=""+Ge,Fe!==null&&Fe.tag===6?(x(Ve,Fe.sibling),Fe=R(Fe,Ge),Fe.return=Ve,Ve=Fe):(x(Ve,Fe),Fe=zp(Ge,Ve.mode,bt),Fe.return=Ve,Ve=Fe),J(Ve)):x(Ve,Fe)}return In}var Oi=Xh(!0),Bc=Xh(!1),Lt=ii(null),Yt=null,Wa=null,va=null;function Kl(){va=Wa=Yt=null}function fi(o){var d=Lt.current;rn(Lt),o._currentValue=d}function Lu(o,d,x){for(;o!==null;){var C=o.alternate;if((o.childLanes&d)!==d?(o.childLanes|=d,C!==null&&(C.childLanes|=d)):C!==null&&(C.childLanes&d)!==d&&(C.childLanes|=d),o===x)break;o=o.return}}function pi(o,d){Yt=o,va=Wa=null,o=o.dependencies,o!==null&&o.firstContext!==null&&((o.lanes&d)!==0&&(Ds=!0),o.firstContext=null)}function As(o){var d=o._currentValue;if(va!==o)if(o={context:o,memoizedValue:d,next:null},Wa===null){if(Yt===null)throw Error(u(308));Wa=o,Yt.dependencies={lanes:0,firstContext:o}}else Wa=Wa.next=o;return d}var Ks=null;function Yr(o){Ks===null?Ks=[o]:Ks.push(o)}function wn(o,d,x,C){var R=d.interleaved;return R===null?(x.next=x,Yr(d)):(x.next=R.next,R.next=x),d.interleaved=x,Rs(o,C)}function Rs(o,d){o.lanes|=d;var x=o.alternate;for(x!==null&&(x.lanes|=d),x=o,o=o.return;o!==null;)o.childLanes|=d,x=o.alternate,x!==null&&(x.childLanes|=d),x=o,o=o.return;return x.tag===3?x.stateNode:null}var vs=!1;function Co(o){o.updateQueue={baseState:o.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fu(o,d){o=o.updateQueue,d.updateQueue===o&&(d.updateQueue={baseState:o.baseState,firstBaseUpdate:o.firstBaseUpdate,lastBaseUpdate:o.lastBaseUpdate,shared:o.shared,effects:o.effects})}function rs(o,d){return{eventTime:o,lane:d,tag:0,payload:null,callback:null,next:null}}function ns(o,d,x){var C=o.updateQueue;if(C===null)return null;if(C=C.shared,(Br&2)!==0){var R=C.pending;return R===null?d.next=d:(d.next=R.next,R.next=d),C.pending=d,Rs(o,x)}return R=C.interleaved,R===null?(d.next=d,Yr(C)):(d.next=R.next,R.next=d),C.interleaved=d,Rs(o,x)}function ko(o,d,x){if(d=d.updateQueue,d!==null&&(d=d.shared,(x&4194240)!==0)){var C=d.lanes;C&=o.pendingLanes,x|=C,d.lanes=x,co(o,x)}}function $c(o,d){var x=o.updateQueue,C=o.alternate;if(C!==null&&(C=C.updateQueue,x===C)){var R=null,O=null;if(x=x.firstBaseUpdate,x!==null){do{var J={eventTime:x.eventTime,lane:x.lane,tag:x.tag,payload:x.payload,callback:x.callback,next:null};O===null?R=O=J:O=O.next=J,x=x.next}while(x!==null);O===null?R=O=d:O=O.next=d}else R=O=d;x={baseState:C.baseState,firstBaseUpdate:R,lastBaseUpdate:O,shared:C.shared,effects:C.effects},o.updateQueue=x;return}o=x.lastBaseUpdate,o===null?x.firstBaseUpdate=d:o.next=d,x.lastBaseUpdate=d}function xa(o,d,x,C){var R=o.updateQueue;vs=!1;var O=R.firstBaseUpdate,J=R.lastBaseUpdate,me=R.shared.pending;if(me!==null){R.shared.pending=null;var Ee=me,He=Ee.next;Ee.next=null,J===null?O=He:J.next=He,J=Ee;var dt=o.alternate;dt!==null&&(dt=dt.updateQueue,me=dt.lastBaseUpdate,me!==J&&(me===null?dt.firstBaseUpdate=He:me.next=He,dt.lastBaseUpdate=Ee))}if(O!==null){var ct=R.baseState;J=0,dt=He=Ee=null,me=O;do{var at=me.lane,Ot=me.eventTime;if((C&at)===at){dt!==null&&(dt=dt.next={eventTime:Ot,lane:0,tag:me.tag,payload:me.payload,callback:me.callback,next:null});e:{var Vt=o,Qt=me;switch(at=d,Ot=x,Qt.tag){case 1:if(Vt=Qt.payload,typeof Vt=="function"){ct=Vt.call(Ot,ct,at);break e}ct=Vt;break e;case 3:Vt.flags=Vt.flags&-65537|128;case 0:if(Vt=Qt.payload,at=typeof Vt=="function"?Vt.call(Ot,ct,at):Vt,at==null)break e;ct=Ct({},ct,at);break e;case 2:vs=!0}}me.callback!==null&&me.lane!==0&&(o.flags|=64,at=R.effects,at===null?R.effects=[me]:at.push(me))}else Ot={eventTime:Ot,lane:at,tag:me.tag,payload:me.payload,callback:me.callback,next:null},dt===null?(He=dt=Ot,Ee=ct):dt=dt.next=Ot,J|=at;if(me=me.next,me===null){if(me=R.shared.pending,me===null)break;at=me,me=at.next,at.next=null,R.lastBaseUpdate=at,R.shared.pending=null}}while(!0);if(dt===null&&(Ee=ct),R.baseState=Ee,R.firstBaseUpdate=He,R.lastBaseUpdate=dt,d=R.shared.interleaved,d!==null){R=d;do J|=R.lane,R=R.next;while(R!==d)}else O===null&&(R.shared.lanes=0);Ql|=J,o.lanes=J,o.memoizedState=ct}}function Ou(o,d,x){if(o=d.effects,d.effects=null,o!==null)for(d=0;dx?x:4,o(!0);var C=B.transition;B.transition={};try{o(!1),d()}finally{qr=x,B.transition=C}}function ji(){return $e().memoizedState}function Xs(o,d,x){var C=Ao(o);if(x={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null},Ka(o))Fn(d,x);else if(x=wn(o,d,x,C),x!==null){var R=mi();Ja(x,o,C,R),Nn(x,d,C)}}function Ni(o,d,x){var C=Ao(o),R={lane:C,action:x,hasEagerState:!1,eagerState:null,next:null};if(Ka(o))Fn(d,R);else{var O=o.alternate;if(o.lanes===0&&(O===null||O.lanes===0)&&(O=d.lastRenderedReducer,O!==null))try{var J=d.lastRenderedState,me=O(J,x);if(R.hasEagerState=!0,R.eagerState=me,qs(me,J)){var Ee=d.interleaved;Ee===null?(R.next=R,Yr(d)):(R.next=Ee.next,Ee.next=R),d.interleaved=R;return}}catch{}finally{}x=wn(o,d,R,C),x!==null&&(R=mi(),Ja(x,o,C,R),Nn(x,d,C))}}function Ka(o){var d=o.alternate;return o===$||d!==null&&d===$}function Fn(o,d){ae=se=!0;var x=o.pending;x===null?d.next=d:(d.next=x.next,x.next=d),o.pending=d}function Nn(o,d,x){if((x&4194240)!==0){var C=d.lanes;C&=o.pendingLanes,x|=C,d.lanes=x,co(o,x)}}var Bi={readContext:As,useCallback:le,useContext:le,useEffect:le,useImperativeHandle:le,useInsertionEffect:le,useLayoutEffect:le,useMemo:le,useReducer:le,useRef:le,useState:le,useDebugValue:le,useDeferredValue:le,useTransition:le,useMutableSource:le,useSyncExternalStore:le,useId:le,unstable_isNewReconciler:!1},Bn={readContext:As,useCallback:function(o,d){return ke().memoizedState=[o,d===void 0?null:d],o},useContext:As,useEffect:Dn,useImperativeHandle:function(o,d,x){return x=x!=null?x.concat([o]):null,mr(4194308,4,zs.bind(null,d,o),x)},useLayoutEffect:function(o,d){return mr(4194308,4,o,d)},useInsertionEffect:function(o,d){return mr(4,2,o,d)},useMemo:function(o,d){var x=ke();return d=d===void 0?null:d,o=o(),x.memoizedState=[o,d],o},useReducer:function(o,d,x){var C=ke();return d=x!==void 0?x(d):d,C.memoizedState=C.baseState=d,o={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:o,lastRenderedState:d},C.queue=o,o=o.dispatch=Xs.bind(null,$,o),[C.memoizedState,o]},useRef:function(o){var d=ke();return o={current:o},d.memoizedState=o},useState:Qr,useDebugValue:ba,useDeferredValue:function(o){return ke().memoizedState=o},useTransition:function(){var o=Qr(!1),d=o[0];return o=nl.bind(null,o[1]),ke().memoizedState=o,[d,o]},useMutableSource:function(){},useSyncExternalStore:function(o,d,x){var C=$,R=ke();if(vn){if(x===void 0)throw Error(u(407));x=x()}else{if(x=d(),Yn===null)throw Error(u(349));(j&30)!==0||rt(C,d,x)}R.memoizedState=x;var O={value:x,getSnapshot:d};return R.queue=O,Dn(br.bind(null,C,O,o),[o]),C.flags|=2048,un(9,Ft.bind(null,C,O,x,d),void 0,null),x},useId:function(){var o=ke(),d=Yn.identifierPrefix;if(vn){var x=Fi,C=ts;x=(C&~(1<<32-ks(C)-1)).toString(32)+x,d=":"+d+"R"+x,x=oe++,0<\/script>",a=a.removeChild(a.firstChild)):typeof C.is=="string"?a=Q.createElement(x,{is:C.is}):(a=Q.createElement(x),x==="select"&&(Q=a,C.multiple?Q.multiple=!0:C.size&&(Q.size=C.size))):a=Q.createElementNS(a,x),a[ms]=d,a[$o]=C,yp(a,d,!1,!1),d.stateNode=a;e:{switch(Q=Ae(x,C),x){case"dialog":gn("cancel",a),gn("close",a),R=C;break;case"iframe":case"object":case"embed":gn("load",a),R=C;break;case"video":case"audio":for(R=0;RWu&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304)}else{if(!C)if(a=E(Q),a!==null){if(d.flags|=128,C=!0,x=a.updateQueue,x!==null&&(d.updateQueue=x,d.flags|=4),nd(O,!0),O.tail===null&&O.tailMode==="hidden"&&!Q.alternate&&!vn)return ss(d),null}else 2*kr()-O.renderingStartTime>Wu&&x!==1073741824&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304);O.isBackwards?(Q.sibling=d.child,d.child=Q):(x=O.last,x!==null?x.sibling=Q:d.child=Q,O.last=Q)}return O.tail!==null?(d=O.tail,O.rendering=d,O.tail=d.sibling,O.renderingStartTime=kr(),d.sibling=null,x=S.current,Or(S,C?x&1|2:x&1),d):(ss(d),null);case 22:case 23:return Ap(),C=d.memoizedState!==null,a!==null&&a.memoizedState!==null!==C&&(d.flags|=8192),C&&(d.mode&1)!==0?(Js&1073741824)!==0&&(ss(d),d.subtreeFlags&6&&(d.flags|=8192)):ss(d),null;case 24:return null;case 25:return null}throw Error(u(156,d.tag))}function sy(a,d){switch(qh(d),d.tag){case 1:return wi(d.type)&&Go(),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return h(),rn(zi),rn(ii),z(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 5:return y(d),null;case 13:if(rn(S),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(u(340));Wr()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return rn(S),null;case 4:return h(),null;case 10:return fi(d.type._context),null;case 22:case 23:return Ap(),null;case 24:return null;default:return null}}var $u=!1,Ui=!1,Dm=typeof WeakSet=="function"?WeakSet:Set,Ut=null;function Uu(a,d){var x=a.ref;if(x!==null)if(typeof x=="function")try{x(null)}catch(C){$n(a,d,C)}else x.current=null}function vp(a,d,x){try{x()}catch(C){$n(a,d,C)}}var sf=!1;function xp(a,d){if(Vl=bi,a=Nh(),Fl(a)){if("selectionStart"in a)var x={start:a.selectionStart,end:a.selectionEnd};else e:{x=(x=a.ownerDocument)&&x.defaultView||window;var C=x.getSelection&&x.getSelection();if(C&&C.rangeCount!==0){x=C.anchorNode;var R=C.anchorOffset,O=C.focusNode;C=C.focusOffset;try{x.nodeType,O.nodeType}catch{x=null;break e}var Q=0,me=-1,Ee=-1,He=0,ht=0,lt=a,st=null;t:for(;;){for(var Ot;lt!==x||R!==0&<.nodeType!==3||(me=Q+R),lt!==O||C!==0&<.nodeType!==3||(Ee=Q+C),lt.nodeType===3&&(Q+=lt.nodeValue.length),(Ot=lt.firstChild)!==null;)st=lt,lt=Ot;for(;;){if(lt===a)break t;if(st===x&&++He===R&&(me=Q),st===O&&++ht===C&&(Ee=Q),(Ot=lt.nextSibling)!==null)break;lt=st,st=lt.parentNode}lt=Ot}x=me===-1||Ee===-1?null:{start:me,end:Ee}}else x=null}x=x||{start:0,end:0}}else x=null;for(Bo={focusedElem:a,selectionRange:x},bi=!1,Ut=d;Ut!==null;)if(d=Ut,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Ut=a;else for(;Ut!==null;){d=Ut;try{var Vt=d.alternate;if((d.flags&1024)!==0)switch(d.tag){case 0:case 11:case 15:break;case 1:if(Vt!==null){var Qt=Vt.memoizedProps,In=Vt.memoizedState,Ve=d.stateNode,Le=Ve.getSnapshotBeforeUpdate(d.elementType===d.type?Qt:Xn(d.type,Qt),In);Ve.__reactInternalSnapshotBeforeUpdate=Le}break;case 3:var Ge=d.stateNode.containerInfo;Ge.nodeType===1?Ge.textContent="":Ge.nodeType===9&&Ge.documentElement&&Ge.removeChild(Ge.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch(bt){$n(d,d.return,bt)}if(a=d.sibling,a!==null){a.return=d.return,Ut=a;break}Ut=d.return}return Vt=sf,sf=!1,Vt}function Vu(a,d,x){var C=d.updateQueue;if(C=C!==null?C.lastEffect:null,C!==null){var R=C=C.next;do{if((R.tag&a)===a){var O=R.destroy;R.destroy=void 0,O!==void 0&&vp(d,x,O)}R=R.next}while(R!==C)}}function Ia(a,d){if(d=d.updateQueue,d=d!==null?d.lastEffect:null,d!==null){var x=d=d.next;do{if((x.tag&a)===a){var C=x.create;x.destroy=C()}x=x.next}while(x!==d)}}function Gc(a){var d=a.ref;if(d!==null){var x=a.stateNode;switch(a.tag){case 5:a=x;break;default:a=x}typeof d=="function"?d(a):d.current=a}}function Lm(a){var d=a.alternate;d!==null&&(a.alternate=null,Lm(d)),a.child=null,a.deletions=null,a.sibling=null,a.tag===5&&(d=a.stateNode,d!==null&&(delete d[ms],delete d[$o],delete d[Uo],delete d[Mu],delete d[Vo])),a.stateNode=null,a.return=null,a.dependencies=null,a.memoizedProps=null,a.memoizedState=null,a.pendingProps=null,a.stateNode=null,a.updateQueue=null}function Fm(a){return a.tag===5||a.tag===3||a.tag===4}function Om(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||Fm(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function bp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.nodeType===8?x.parentNode.insertBefore(a,d):x.insertBefore(a,d):(x.nodeType===8?(d=x.parentNode,d.insertBefore(a,x)):(d=x,d.appendChild(a)),x=x._reactRootContainer,x!=null||d.onclick!==null||(d.onclick=Ul));else if(C!==4&&(a=a.child,a!==null))for(bp(a,d,x),a=a.sibling;a!==null;)bp(a,d,x),a=a.sibling}function wp(a,d,x){var C=a.tag;if(C===5||C===6)a=a.stateNode,d?x.insertBefore(a,d):x.appendChild(a);else if(C!==4&&(a=a.child,a!==null))for(wp(a,d,x),a=a.sibling;a!==null;)wp(a,d,x),a=a.sibling}var Vi=null,Jo=!1;function Ma(a,d,x){for(x=x.child;x!==null;)jm(a,d,x),x=x.sibling}function jm(a,d,x){if(ui&&typeof ui.onCommitFiberUnmount=="function")try{ui.onCommitFiberUnmount(Ns,x)}catch{}switch(x.tag){case 5:Ui||Uu(x,d);case 6:var C=Vi,R=Jo;Vi=null,Ma(a,d,x),Vi=C,Jo=R,Vi!==null&&(Jo?(a=Vi,x=x.stateNode,a.nodeType===8?a.parentNode.removeChild(x):a.removeChild(x)):Vi.removeChild(x.stateNode));break;case 18:Vi!==null&&(Jo?(a=Vi,x=x.stateNode,a.nodeType===8?Fc(a.parentNode,x):a.nodeType===1&&Fc(a,x),fs(a)):Fc(Vi,x.stateNode));break;case 4:C=Vi,R=Jo,Vi=x.stateNode.containerInfo,Jo=!0,Ma(a,d,x),Vi=C,Jo=R;break;case 0:case 11:case 14:case 15:if(!Ui&&(C=x.updateQueue,C!==null&&(C=C.lastEffect,C!==null))){R=C=C.next;do{var O=R,Q=O.destroy;O=O.tag,Q!==void 0&&((O&2)!==0||(O&4)!==0)&&vp(x,d,Q),R=R.next}while(R!==C)}Ma(a,d,x);break;case 1:if(!Ui&&(Uu(x,d),C=x.stateNode,typeof C.componentWillUnmount=="function"))try{C.props=x.memoizedProps,C.state=x.memoizedState,C.componentWillUnmount()}catch(me){$n(x,d,me)}Ma(a,d,x);break;case 21:Ma(a,d,x);break;case 22:x.mode&1?(Ui=(C=Ui)||x.memoizedState!==null,Ma(a,d,x),Ui=C):Ma(a,d,x);break;default:Ma(a,d,x)}}function Sp(a){var d=a.updateQueue;if(d!==null){a.updateQueue=null;var x=a.stateNode;x===null&&(x=a.stateNode=new Dm),d.forEach(function(C){var R=pd.bind(null,a,C);x.has(C)||(x.add(C),C.then(R,R))})}}function Ss(a,d){var x=d.deletions;if(x!==null)for(var C=0;CR&&(R=Q),C&=~O}if(C=R,C=kr()-C,C=(120>C?120:480>C?480:1080>C?1080:1920>C?1920:3e3>C?3e3:4320>C?4320:1960*Bm(C/1960))-C,10a?16:a,ec===null)var C=!1;else{if(a=ec,ec=null,cd=0,(Br&6)!==0)throw Error(u(331));var R=Br;for(Br|=4,Ut=a.current;Ut!==null;){var O=Ut,Q=O.child;if((Ut.flags&16)!==0){var me=O.deletions;if(me!==null){for(var Ee=0;Eekr()-Pp?Kc(a,0):ld|=x),Fs(a,d)}function Hm(a,d){d===0&&((a.mode&1)===0?d=1:(d=yu,yu<<=1,(yu&130023424)===0&&(yu=4194304)));var x=mi();a=Rs(a,d),a!==null&&(Na(a,d,x),Fs(a,x))}function ay(a){var d=a.memoizedState,x=0;d!==null&&(x=d.retryLane),Hm(a,x)}function pd(a,d){var x=0;switch(a.tag){case 13:var C=a.stateNode,R=a.memoizedState;R!==null&&(x=R.retryLane);break;case 19:C=a.stateNode;break;default:throw Error(u(314))}C!==null&&C.delete(d),Hm(a,x)}var Jn;Jn=function(a,d,x){if(a!==null)if(a.memoizedProps!==d.pendingProps||zi.current)Ds=!0;else{if((a.lanes&x)===0&&(d.flags&128)===0)return Ds=!1,ws(a,d,x);Ds=(a.flags&131072)!==0}else Ds=!1,vn&&(d.flags&1048576)!==0&&Fr(d,kt,d.index);switch(d.lanes=0,d.tag){case 2:var C=d.type;Bu(a,d),a=d.pendingProps;var R=Hs(d,ii.current);pi(d,x),R=we(null,d,C,a,R,x);var O=he();return d.flags|=1,typeof R=="object"&&R!==null&&typeof R.render=="function"&&R.$$typeof===void 0?(d.tag=1,d.memoizedState=null,d.updateQueue=null,wi(C)?(O=!0,Qi(d)):O=!1,d.memoizedState=R.state!==null&&R.state!==void 0?R.state:null,Ca(d),R.updater=Ys,d.stateNode=R,R._reactInternals=d,Vc(d,C,a,x),d=Nu(null,d,C,!0,O,x)):(d.tag=0,vn&&O&&ql(d),$i(null,d,R,x),d=d.child),d;case 16:C=d.elementType;e:{switch(Bu(a,d),a=d.pendingProps,R=C._init,C=R(C._payload),d.type=C,R=d.tag=Xu(C),a=Xn(C,a),R){case 0:d=fp(null,d,C,a,x);break e;case 1:d=ju(null,d,C,a,x);break e;case 11:d=Em(null,d,C,a,x);break e;case 14:d=tf(null,d,C,Xn(C.type,a),x);break e}throw Error(u(306,C,""))}return d;case 0:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),fp(a,d,C,R,x);case 1:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),ju(a,d,C,R,x);case 3:e:{if(Jh(d),a===null)throw Error(u(387));C=d.pendingProps,O=d.memoizedState,R=O.element,Fu(a,d),bo(d,C,null,x);var Q=d.memoizedState;if(C=Q.element,O.isDehydrated)if(O={element:C,isDehydrated:!1,cache:Q.cache,pendingSuspenseBoundaries:Q.pendingSuspenseBoundaries,transitions:Q.transitions},d.updateQueue.baseState=O,d.memoizedState=O,d.flags&256){R=Yo(Error(u(423)),d),d=pp(a,d,C,x,R);break e}else if(C!==R){R=Yo(Error(u(424)),d),d=pp(a,d,C,x,R);break e}else for(ys=tn(d.stateNode.containerInfo.firstChild),Si=d,vn=!0,_s=null,x=Bc(d,null,C,x),d.child=x;x;)x.flags=x.flags&-3|4096,x=x.sibling;else{if(Wr(),C===R){d=Qs(a,d,x);break e}$i(a,d,C,x)}d=d.child}return d;case 5:return p(d),a===null&&jc(d),C=d.type,R=d.pendingProps,O=a!==null?a.memoizedProps:null,Q=R.children,Dc(C,R)?Q=null:O!==null&&Dc(C,O)&&(d.flags|=32),Zc(a,d),$i(a,d,Q,x),d.child;case 6:return a===null&&jc(d),null;case 13:return Im(a,d,x);case 4:return s(d,d.stateNode.containerInfo),C=d.pendingProps,a===null?d.child=Oi(d,null,C,x):$i(a,d,C,x),d.child;case 11:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Em(a,d,C,R,x);case 7:return $i(a,d,d.pendingProps,x),d.child;case 8:return $i(a,d,d.pendingProps.children,x),d.child;case 12:return $i(a,d,d.pendingProps.children,x),d.child;case 10:e:{if(C=d.type._context,R=d.pendingProps,O=d.memoizedProps,Q=R.value,Or(Lt,C._currentValue),C._currentValue=Q,O!==null)if(qs(O.value,Q)){if(O.children===R.children&&!zi.current){d=Qs(a,d,x);break e}}else for(O=d.child,O!==null&&(O.return=d);O!==null;){var me=O.dependencies;if(me!==null){Q=O.child;for(var Ee=me.firstContext;Ee!==null;){if(Ee.context===C){if(O.tag===1){Ee=rs(-1,x&-x),Ee.tag=2;var He=O.updateQueue;if(He!==null){He=He.shared;var ht=He.pending;ht===null?Ee.next=Ee:(Ee.next=ht.next,ht.next=Ee),He.pending=Ee}}O.lanes|=x,Ee=O.alternate,Ee!==null&&(Ee.lanes|=x),Lu(O.return,x,d),me.lanes|=x;break}Ee=Ee.next}}else if(O.tag===10)Q=O.type===d.type?null:O.child;else if(O.tag===18){if(Q=O.return,Q===null)throw Error(u(341));Q.lanes|=x,me=Q.alternate,me!==null&&(me.lanes|=x),Lu(Q,x,d),Q=O.sibling}else Q=O.child;if(Q!==null)Q.return=O;else for(Q=O;Q!==null;){if(Q===d){Q=null;break}if(O=Q.sibling,O!==null){O.return=Q.return,Q=O;break}Q=Q.return}O=Q}$i(a,d,R.children,x),d=d.child}return d;case 9:return R=d.type,C=d.pendingProps.children,pi(d,x),R=As(R),C=C(R),d.flags|=1,$i(a,d,C,x),d.child;case 14:return C=d.type,R=Xn(C,d.pendingProps),R=Xn(C.type,R),tf(a,d,C,R,x);case 15:return hp(a,d,d.type,d.pendingProps,x);case 17:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Bu(a,d),d.tag=1,wi(C)?(a=!0,Qi(d)):a=!1,pi(d,x),is(d,C,R),Vc(d,C,R,x),Nu(null,d,C,!0,a,x);case 19:return gp(a,d,x);case 22:return dp(a,d,x)}throw Error(u(156,d.tag))};function hf(a,d){return Ao(a,d)}function Km(a,d,x,C){this.tag=a,this.key=x,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=d,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=C,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function to(a,d,x,C){return new Km(a,d,x,C)}function md(a){return a=a.prototype,!(!a||!a.isReactComponent)}function Xu(a){if(typeof a=="function")return md(a)?1:0;if(a!=null){if(a=a.$$typeof,a===At)return 11;if(a===zr)return 14}return 2}function il(a,d){var x=a.alternate;return x===null?(x=to(a.tag,d,a.key,a.mode),x.elementType=a.elementType,x.type=a.type,x.stateNode=a.stateNode,x.alternate=a,a.alternate=x):(x.pendingProps=d,x.type=a.type,x.flags=0,x.subtreeFlags=0,x.deletions=null),x.flags=a.flags&14680064,x.childLanes=a.childLanes,x.lanes=a.lanes,x.child=a.child,x.memoizedProps=a.memoizedProps,x.memoizedState=a.memoizedState,x.updateQueue=a.updateQueue,d=a.dependencies,x.dependencies=d===null?null:{lanes:d.lanes,firstContext:d.firstContext},x.sibling=a.sibling,x.index=a.index,x.ref=a.ref,x}function df(a,d,x,C,R,O){var Q=2;if(C=a,typeof a=="function")md(a)&&(Q=1);else if(typeof a=="string")Q=5;else e:switch(a){case ct:return Yc(x.children,R,O,d);case dt:Q=8,R|=8;break;case ot:return a=to(12,x,d,R|2),a.elementType=ot,a.lanes=O,a;case Mt:return a=to(13,x,d,R),a.elementType=Mt,a.lanes=O,a;case Ht:return a=to(19,x,d,R),a.elementType=Ht,a.lanes=O,a;case Zt:return ff(x,R,O,d);default:if(typeof a=="object"&&a!==null)switch(a.$$typeof){case Xe:Q=10;break e;case Rt:Q=9;break e;case At:Q=11;break e;case zr:Q=14;break e;case ar:Q=16,C=null;break e}throw Error(u(130,a==null?a:typeof a,""))}return d=to(Q,x,d,R),d.elementType=a,d.type=C,d.lanes=O,d}function Yc(a,d,x,C){return a=to(7,a,C,d),a.lanes=x,a}function ff(a,d,x,C){return a=to(22,a,C,d),a.elementType=Zt,a.lanes=x,a.stateNode={isHidden:!1},a}function zp(a,d,x){return a=to(6,a,null,d),a.lanes=x,a}function Dp(a,d,x){return d=to(4,a.children!==null?a.children:[],a.key,d),d.lanes=x,d.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation},d}function ly(a,d,x,C,R){this.tag=d,this.containerInfo=a,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=yc(0),this.expirationTimes=yc(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=yc(0),this.identifierPrefix=C,this.onRecoverableError=R,this.mutableSourceEagerHydrationData=null}function Lp(a,d,x,C,R,O,Q,me,Ee){return a=new ly(a,d,x,me,Ee),d===1?(d=1,O===!0&&(d|=8)):d=0,O=to(3,null,null,d),a.current=O,O.stateNode=a,O.memoizedState={element:C,isDehydrated:x,cache:null,transitions:null,pendingSuspenseBoundaries:null},Ca(O),a}function cy(a,d,x){var C=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(o){console.error(o)}}return r(),Iy.exports=Tw(),Iy.exports}var ox;function Pw(){if(ox)return cg;ox=1;var r=Y0();return cg.createRoot=r.createRoot,cg.hydrateRoot=r.hydrateRoot,cg}var Cw=Pw();const kw=M_(Cw);/** +`+O.stack}return{value:o,source:d,stack:R,digest:null}}function Yl(o,d,x){return{value:o,source:null,stack:x??null,digest:d??null}}function wa(o,d){try{console.error(d.value)}catch(x){setTimeout(function(){throw x})}}var Ya=typeof WeakMap=="function"?WeakMap:Map;function cp(o,d,x){x=rs(-1,x),x.tag=3,x.payload={element:null};var C=d.value;return x.callback=function(){of||(of=!0,Cp=C),wa(o,d)},x}function Pm(o,d,x){x=rs(-1,x),x.tag=3;var C=o.type.getDerivedStateFromError;if(typeof C=="function"){var R=d.value;x.payload=function(){return C(R)},x.callback=function(){wa(o,d)}}var O=o.stateNode;return O!==null&&typeof O.componentDidCatch=="function"&&(x.callback=function(){wa(o,d),typeof C!="function"&&(ea===null?ea=new Set([this]):ea.add(this));var J=d.stack;this.componentDidCatch(d.value,{componentStack:J!==null?J:""})}),x}function tf(o,d,x){var C=o.pingCache;if(C===null){C=o.pingCache=new Ya;var R=new Set;C.set(d,R)}else R=C.get(d),R===void 0&&(R=new Set,C.set(d,R));R.has(x)||(R.add(x),o=Wm.bind(null,o,d,x),d.then(o,o))}function up(o){do{var d;if((d=o.tag===13)&&(d=o.memoizedState,d=d!==null?d.dehydrated!==null:!0),d)return o;o=o.return}while(o!==null);return null}function Cm(o,d,x,C,R){return(o.mode&1)===0?(o===d?o.flags|=65536:(o.flags|=128,x.flags|=131072,x.flags&=-52805,x.tag===1&&(x.alternate===null?x.tag=17:(d=rs(-1,1),d.tag=2,ns(x,d,1))),x.lanes|=1),o):(o.flags|=65536,o.lanes=R,o)}var km=De.ReactCurrentOwner,Ds=!1;function $i(o,d,x,C){d.child=o===null?Bc(d,null,x,C):Oi(d,o.child,x,C)}function Em(o,d,x,C,R){x=x.render;var O=d.ref;return pi(d,R),C=we(o,d,x,C,O,R),x=he(),o!==null&&!Ds?(d.updateQueue=o.updateQueue,d.flags&=-2053,o.lanes&=~R,Qs(o,d,R)):(vn&&x&&ql(d),d.flags|=1,$i(o,d,C,R),d.child)}function rf(o,d,x,C,R){if(o===null){var O=x.type;return typeof O=="function"&&!md(O)&&O.defaultProps===void 0&&x.compare===null&&x.defaultProps===void 0?(d.tag=15,d.type=O,hp(o,d,O,C,R)):(o=ff(x.type,null,C,d,d.mode,R),o.ref=d.ref,o.return=d,d.child=o)}if(O=o.child,(o.lanes&R)===0){var J=O.memoizedProps;if(x=x.compare,x=x!==null?x:Pc,x(J,C)&&o.ref===d.ref)return Qs(o,d,R)}return d.flags|=1,o=sl(O,C),o.ref=d.ref,o.return=d,d.child=o}function hp(o,d,x,C,R){if(o!==null){var O=o.memoizedProps;if(Pc(O,C)&&o.ref===d.ref)if(Ds=!1,d.pendingProps=C=O,(o.lanes&R)!==0)(o.flags&131072)!==0&&(Ds=!0);else return d.lanes=o.lanes,Qs(o,d,R)}return fp(o,d,x,C,R)}function dp(o,d,x){var C=d.pendingProps,R=C.children,O=o!==null?o.memoizedState:null;if(C.mode==="hidden")if((d.mode&1)===0)d.memoizedState={baseLanes:0,cachePool:null,transitions:null},Or(Wc,Js),Js|=x;else{if((x&1073741824)===0)return o=O!==null?O.baseLanes|x:x,d.lanes=d.childLanes=1073741824,d.memoizedState={baseLanes:o,cachePool:null,transitions:null},d.updateQueue=null,Or(Wc,Js),Js|=o,null;d.memoizedState={baseLanes:0,cachePool:null,transitions:null},C=O!==null?O.baseLanes:x,Or(Wc,Js),Js|=C}else O!==null?(C=O.baseLanes|x,d.memoizedState=null):C=x,Or(Wc,Js),Js|=C;return $i(o,d,R,x),d.child}function Zc(o,d){var x=d.ref;(o===null&&x!==null||o!==null&&o.ref!==x)&&(d.flags|=512,d.flags|=2097152)}function fp(o,d,x,C,R){var O=wi(x)?So:si.current;return O=Hs(d,O),pi(d,R),x=we(o,d,x,C,O,R),C=he(),o!==null&&!Ds?(d.updateQueue=o.updateQueue,d.flags&=-2053,o.lanes&=~R,Qs(o,d,R)):(vn&&C&&ql(d),d.flags|=1,$i(o,d,x,R),d.child)}function ju(o,d,x,C,R){if(wi(x)){var O=!0;Qi(d)}else O=!1;if(pi(d,R),d.stateNode===null)Bu(o,d),is(d,x,C),Vc(d,x,C,R),C=!0;else if(o===null){var J=d.stateNode,me=d.memoizedProps;J.props=me;var Ee=J.context,He=x.contextType;typeof He=="object"&&He!==null?He=As(He):(He=wi(x)?So:si.current,He=Hs(d,He));var dt=x.getDerivedStateFromProps,ct=typeof dt=="function"||typeof J.getSnapshotBeforeUpdate=="function";ct||typeof J.UNSAFE_componentWillReceiveProps!="function"&&typeof J.componentWillReceiveProps!="function"||(me!==C||Ee!==He)&&Qh(d,J,C,He),vs=!1;var at=d.memoizedState;J.state=at,xa(d,C,J,R),Ee=d.memoizedState,me!==C||at!==Ee||zi.current||vs?(typeof dt=="function"&&(bs(d,x,dt,C),Ee=d.memoizedState),(me=vs||Yh(d,x,me,C,at,Ee,He))?(ct||typeof J.UNSAFE_componentWillMount!="function"&&typeof J.componentWillMount!="function"||(typeof J.componentWillMount=="function"&&J.componentWillMount(),typeof J.UNSAFE_componentWillMount=="function"&&J.UNSAFE_componentWillMount()),typeof J.componentDidMount=="function"&&(d.flags|=4194308)):(typeof J.componentDidMount=="function"&&(d.flags|=4194308),d.memoizedProps=C,d.memoizedState=Ee),J.props=C,J.state=Ee,J.context=He,C=me):(typeof J.componentDidMount=="function"&&(d.flags|=4194308),C=!1)}else{J=d.stateNode,Fu(o,d),me=d.memoizedProps,He=d.type===d.elementType?me:Xn(d.type,me),J.props=He,ct=d.pendingProps,at=J.context,Ee=x.contextType,typeof Ee=="object"&&Ee!==null?Ee=As(Ee):(Ee=wi(x)?So:si.current,Ee=Hs(d,Ee));var Ot=x.getDerivedStateFromProps;(dt=typeof Ot=="function"||typeof J.getSnapshotBeforeUpdate=="function")||typeof J.UNSAFE_componentWillReceiveProps!="function"&&typeof J.componentWillReceiveProps!="function"||(me!==ct||at!==Ee)&&Qh(d,J,C,Ee),vs=!1,at=d.memoizedState,J.state=at,xa(d,C,J,R);var Vt=d.memoizedState;me!==ct||at!==Vt||zi.current||vs?(typeof Ot=="function"&&(bs(d,x,Ot,C),Vt=d.memoizedState),(He=vs||Yh(d,x,He,C,at,Vt,Ee)||!1)?(dt||typeof J.UNSAFE_componentWillUpdate!="function"&&typeof J.componentWillUpdate!="function"||(typeof J.componentWillUpdate=="function"&&J.componentWillUpdate(C,Vt,Ee),typeof J.UNSAFE_componentWillUpdate=="function"&&J.UNSAFE_componentWillUpdate(C,Vt,Ee)),typeof J.componentDidUpdate=="function"&&(d.flags|=4),typeof J.getSnapshotBeforeUpdate=="function"&&(d.flags|=1024)):(typeof J.componentDidUpdate!="function"||me===o.memoizedProps&&at===o.memoizedState||(d.flags|=4),typeof J.getSnapshotBeforeUpdate!="function"||me===o.memoizedProps&&at===o.memoizedState||(d.flags|=1024),d.memoizedProps=C,d.memoizedState=Vt),J.props=C,J.state=Vt,J.context=Ee,C=He):(typeof J.componentDidUpdate!="function"||me===o.memoizedProps&&at===o.memoizedState||(d.flags|=4),typeof J.getSnapshotBeforeUpdate!="function"||me===o.memoizedProps&&at===o.memoizedState||(d.flags|=1024),C=!1)}return Nu(o,d,x,C,O,R)}function Nu(o,d,x,C,R,O){Zc(o,d);var J=(d.flags&128)!==0;if(!C&&!J)return R&&Qo(d,x,!1),Qs(o,d,O);C=d.stateNode,km.current=d;var me=J&&typeof x.getDerivedStateFromError!="function"?null:C.render();return d.flags|=1,o!==null&&J?(d.child=Oi(d,o.child,null,O),d.child=Oi(d,null,me,O)):$i(o,d,me,O),d.memoizedState=C.state,R&&Qo(d,x,!0),d.child}function Jh(o){var d=o.stateNode;d.pendingContext?Zh(o,d.pendingContext,d.pendingContext!==d.context):d.context&&Zh(o,d.context,!1),s(o,d.containerInfo)}function pp(o,d,x,C,R){return Wr(),Po(R),d.flags|=256,$i(o,d,x,C),d.child}var nf={dehydrated:null,treeContext:null,retryLane:0};function mp(o){return{baseLanes:o,cachePool:null,transitions:null}}function Im(o,d,x){var C=d.pendingProps,R=S.current,O=!1,J=(d.flags&128)!==0,me;if((me=J)||(me=o!==null&&o.memoizedState===null?!1:(R&2)!==0),me?(O=!0,d.flags&=-129):(o===null||o.memoizedState!==null)&&(R|=1),Or(S,R&1),o===null)return jc(d),o=d.memoizedState,o!==null&&(o=o.dehydrated,o!==null)?((d.mode&1)===0?d.lanes=1:o.data==="$!"?d.lanes=8:d.lanes=1073741824,null):(J=C.children,o=C.fallback,O?(C=d.mode,O=d.child,J={mode:"hidden",children:J},(C&1)===0&&O!==null?(O.childLanes=0,O.pendingProps=J):O=pf(J,C,0,null),o=Yc(o,C,x,null),O.return=d,o.return=d,O.sibling=o,d.child=O,d.child.memoizedState=mp(x),d.memoizedState=nf,o):sf(d,J));if(R=o.memoizedState,R!==null&&(me=R.dehydrated,me!==null))return Mm(o,d,J,C,me,R,x);if(O){O=C.fallback,J=d.mode,R=o.child,me=R.sibling;var Ee={mode:"hidden",children:C.children};return(J&1)===0&&d.child!==R?(C=d.child,C.childLanes=0,C.pendingProps=Ee,d.deletions=null):(C=sl(R,Ee),C.subtreeFlags=R.subtreeFlags&14680064),me!==null?O=sl(me,O):(O=Yc(O,J,x,null),O.flags|=2),O.return=d,C.return=d,C.sibling=O,d.child=C,C=O,O=d.child,J=o.child.memoizedState,J=J===null?mp(x):{baseLanes:J.baseLanes|x,cachePool:null,transitions:J.transitions},O.memoizedState=J,O.childLanes=o.childLanes&~x,d.memoizedState=nf,C}return O=o.child,o=O.sibling,C=sl(O,{mode:"visible",children:C.children}),(d.mode&1)===0&&(C.lanes=x),C.return=d,C.sibling=null,o!==null&&(x=d.deletions,x===null?(d.deletions=[o],d.flags|=16):x.push(o)),d.child=C,d.memoizedState=null,C}function sf(o,d){return d=pf({mode:"visible",children:d},o.mode,0,null),d.return=o,o.child=d}function ed(o,d,x,C){return C!==null&&Po(C),Oi(d,o.child,null,x),o=sf(d,d.pendingProps.children),o.flags|=2,d.memoizedState=null,o}function Mm(o,d,x,C,R,O,J){if(x)return d.flags&256?(d.flags&=-257,C=Yl(Error(u(422))),ed(o,d,J,C)):d.memoizedState!==null?(d.child=o.child,d.flags|=128,null):(O=C.fallback,R=d.mode,C=pf({mode:"visible",children:C.children},R,0,null),O=Yc(O,R,J,null),O.flags|=2,C.return=d,O.return=d,C.sibling=O,d.child=C,(d.mode&1)!==0&&Oi(d,o.child,null,J),d.child.memoizedState=mp(J),d.memoizedState=nf,O);if((d.mode&1)===0)return ed(o,d,J,null);if(R.data==="$!"){if(C=R.nextSibling&&R.nextSibling.dataset,C)var me=C.dgst;return C=me,O=Error(u(419)),C=Yl(O,C,void 0),ed(o,d,J,C)}if(me=(J&o.childLanes)!==0,Ds||me){if(C=Yn,C!==null){switch(J&-J){case 4:R=2;break;case 16:R=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:R=32;break;case 536870912:R=268435456;break;default:R=0}R=(R&(C.suspendedLanes|J))!==0?0:R,R!==0&&R!==O.retryLane&&(O.retryLane=R,Rs(o,R),Ja(C,o,R,-1))}return hd(),C=Yl(Error(u(421))),ed(o,d,J,C)}return R.data==="$?"?(d.flags|=128,d.child=o.child,d=ly.bind(null,o),R._reactRetry=d,null):(o=O.treeContext,ys=tn(R.nextSibling),Si=d,vn=!0,_s=null,o!==null&&(es[Li++]=ts,es[Li++]=Fi,es[Li++]=qa,ts=o.id,Fi=o.overflow,qa=d),d=sf(d,C.children),d.flags|=4096,d)}function Am(o,d,x){o.lanes|=d;var C=o.alternate;C!==null&&(C.lanes|=d),Lu(o.return,d,x)}function td(o,d,x,C,R){var O=o.memoizedState;O===null?o.memoizedState={isBackwards:d,rendering:null,renderingStartTime:0,last:C,tail:x,tailMode:R}:(O.isBackwards=d,O.rendering=null,O.renderingStartTime=0,O.last=C,O.tail=x,O.tailMode=R)}function gp(o,d,x){var C=d.pendingProps,R=C.revealOrder,O=C.tail;if($i(o,d,C.children,x),C=S.current,(C&2)!==0)C=C&1|2,d.flags|=128;else{if(o!==null&&(o.flags&128)!==0)e:for(o=d.child;o!==null;){if(o.tag===13)o.memoizedState!==null&&Am(o,x,d);else if(o.tag===19)Am(o,x,d);else if(o.child!==null){o.child.return=o,o=o.child;continue}if(o===d)break e;for(;o.sibling===null;){if(o.return===null||o.return===d)break e;o=o.return}o.sibling.return=o.return,o=o.sibling}C&=1}if(Or(S,C),(d.mode&1)===0)d.memoizedState=null;else switch(R){case"forwards":for(x=d.child,R=null;x!==null;)o=x.alternate,o!==null&&E(o)===null&&(R=x),x=x.sibling;x=R,x===null?(R=d.child,d.child=null):(R=x.sibling,x.sibling=null),td(d,!1,R,x,O);break;case"backwards":for(x=null,R=d.child,d.child=null;R!==null;){if(o=R.alternate,o!==null&&E(o)===null){d.child=R;break}o=R.sibling,R.sibling=x,x=R,R=o}td(d,!0,x,null,O);break;case"together":td(d,!1,null,null,void 0);break;default:d.memoizedState=null}return d.child}function Bu(o,d){(d.mode&1)===0&&o!==null&&(o.alternate=null,d.alternate=null,d.flags|=2)}function Qs(o,d,x){if(o!==null&&(d.dependencies=o.dependencies),Ql|=d.lanes,(x&d.childLanes)===0)return null;if(o!==null&&d.child!==o.child)throw Error(u(153));if(d.child!==null){for(o=d.child,x=sl(o,o.pendingProps),d.child=x,x.return=d;o.sibling!==null;)o=o.sibling,x=x.sibling=sl(o,o.pendingProps),x.return=d;x.sibling=null}return d.child}function ws(o,d,x){switch(d.tag){case 3:Jh(d),Wr();break;case 5:p(d);break;case 1:wi(d.type)&&Qi(d);break;case 4:s(d,d.stateNode.containerInfo);break;case 10:var C=d.type._context,R=d.memoizedProps.value;Or(Lt,C._currentValue),C._currentValue=R;break;case 13:if(C=d.memoizedState,C!==null)return C.dehydrated!==null?(Or(S,S.current&1),d.flags|=128,null):(x&d.child.childLanes)!==0?Im(o,d,x):(Or(S,S.current&1),o=Qs(o,d,x),o!==null?o.sibling:null);Or(S,S.current&1);break;case 19:if(C=(x&d.childLanes)!==0,(o.flags&128)!==0){if(C)return gp(o,d,x);d.flags|=128}if(R=d.memoizedState,R!==null&&(R.rendering=null,R.tail=null,R.lastEffect=null),Or(S,S.current),C)break;return null;case 22:case 23:return d.lanes=0,dp(o,d,x)}return Qs(o,d,x)}var yp,rd,Rm,_p;yp=function(o,d){for(var x=d.child;x!==null;){if(x.tag===5||x.tag===6)o.appendChild(x.stateNode);else if(x.tag!==4&&x.child!==null){x.child.return=x,x=x.child;continue}if(x===d)break;for(;x.sibling===null;){if(x.return===null||x.return===d)return;x=x.return}x.sibling.return=x.return,x=x.sibling}},rd=function(){},Rm=function(o,d,x,C){var R=o.memoizedProps;if(R!==C){o=d.stateNode,e(xs.current);var O=null;switch(x){case"input":R=Nr(o,R),C=Nr(o,C),O=[];break;case"select":R=Ct({},R,{value:void 0}),C=Ct({},C,{value:void 0}),O=[];break;case"textarea":R=Xe(o,R),C=Xe(o,C),O=[];break;default:typeof R.onClick!="function"&&typeof C.onClick=="function"&&(o.onclick=Ul)}Oe(x,C);var J;x=null;for(He in R)if(!C.hasOwnProperty(He)&&R.hasOwnProperty(He)&&R[He]!=null)if(He==="style"){var me=R[He];for(J in me)me.hasOwnProperty(J)&&(x||(x={}),x[J]="")}else He!=="dangerouslySetInnerHTML"&&He!=="children"&&He!=="suppressContentEditableWarning"&&He!=="suppressHydrationWarning"&&He!=="autoFocus"&&(b.hasOwnProperty(He)?O||(O=[]):(O=O||[]).push(He,null));for(He in C){var Ee=C[He];if(me=R!=null?R[He]:void 0,C.hasOwnProperty(He)&&Ee!==me&&(Ee!=null||me!=null))if(He==="style")if(me){for(J in me)!me.hasOwnProperty(J)||Ee&&Ee.hasOwnProperty(J)||(x||(x={}),x[J]="");for(J in Ee)Ee.hasOwnProperty(J)&&me[J]!==Ee[J]&&(x||(x={}),x[J]=Ee[J])}else x||(O||(O=[]),O.push(He,x)),x=Ee;else He==="dangerouslySetInnerHTML"?(Ee=Ee?Ee.__html:void 0,me=me?me.__html:void 0,Ee!=null&&me!==Ee&&(O=O||[]).push(He,Ee)):He==="children"?typeof Ee!="string"&&typeof Ee!="number"||(O=O||[]).push(He,""+Ee):He!=="suppressContentEditableWarning"&&He!=="suppressHydrationWarning"&&(b.hasOwnProperty(He)?(Ee!=null&&He==="onScroll"&&gn("scroll",o),O||me===Ee||(O=[])):(O=O||[]).push(He,Ee))}x&&(O=O||[]).push("style",x);var He=O;(d.updateQueue=He)&&(d.flags|=4)}},_p=function(o,d,x,C){x!==C&&(d.flags|=4)};function nd(o,d){if(!vn)switch(o.tailMode){case"hidden":d=o.tail;for(var x=null;d!==null;)d.alternate!==null&&(x=d),d=d.sibling;x===null?o.tail=null:x.sibling=null;break;case"collapsed":x=o.tail;for(var C=null;x!==null;)x.alternate!==null&&(C=x),x=x.sibling;C===null?d||o.tail===null?o.tail=null:o.tail.sibling=null:C.sibling=null}}function ss(o){var d=o.alternate!==null&&o.alternate.child===o.child,x=0,C=0;if(d)for(var R=o.child;R!==null;)x|=R.lanes|R.childLanes,C|=R.subtreeFlags&14680064,C|=R.flags&14680064,R.return=o,R=R.sibling;else for(R=o.child;R!==null;)x|=R.lanes|R.childLanes,C|=R.subtreeFlags,C|=R.flags,R.return=o,R=R.sibling;return o.subtreeFlags|=C,o.childLanes=x,d}function zm(o,d,x){var C=d.pendingProps;switch(qh(d),d.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return ss(d),null;case 1:return wi(d.type)&&Za(),ss(d),null;case 3:return C=d.stateNode,h(),rn(zi),rn(si),z(),C.pendingContext&&(C.context=C.pendingContext,C.pendingContext=null),(o===null||o.child===null)&&(Nc(d)?d.flags|=4:o===null||o.memoizedState.isDehydrated&&(d.flags&256)===0||(d.flags|=1024,_s!==null&&(Ep(_s),_s=null))),rd(o,d),ss(d),null;case 5:y(d);var R=e(g.current);if(x=d.type,o!==null&&d.stateNode!=null)Rm(o,d,x,C,R),o.ref!==d.ref&&(d.flags|=512,d.flags|=2097152);else{if(!C){if(d.stateNode===null)throw Error(u(166));return ss(d),null}if(o=e(xs.current),Nc(d)){C=d.stateNode,x=d.type;var O=d.memoizedProps;switch(C[ms]=d,C[Ba]=O,o=(d.mode&1)!==0,x){case"dialog":gn("cancel",C),gn("close",C);break;case"iframe":case"object":case"embed":gn("load",C);break;case"video":case"audio":for(R=0;R<\/script>",o=o.removeChild(o.firstChild)):typeof C.is=="string"?o=J.createElement(x,{is:C.is}):(o=J.createElement(x),x==="select"&&(J=o,C.multiple?J.multiple=!0:C.size&&(J.size=C.size))):o=J.createElementNS(o,x),o[ms]=d,o[Ba]=C,yp(o,d,!1,!1),d.stateNode=o;e:{switch(J=Re(x,C),x){case"dialog":gn("cancel",o),gn("close",o),R=C;break;case"iframe":case"object":case"embed":gn("load",o),R=C;break;case"video":case"audio":for(R=0;RWu&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304)}else{if(!C)if(o=E(J),o!==null){if(d.flags|=128,C=!0,x=o.updateQueue,x!==null&&(d.updateQueue=x,d.flags|=4),nd(O,!0),O.tail===null&&O.tailMode==="hidden"&&!J.alternate&&!vn)return ss(d),null}else 2*kr()-O.renderingStartTime>Wu&&x!==1073741824&&(d.flags|=128,C=!0,nd(O,!1),d.lanes=4194304);O.isBackwards?(J.sibling=d.child,d.child=J):(x=O.last,x!==null?x.sibling=J:d.child=J,O.last=J)}return O.tail!==null?(d=O.tail,O.rendering=d,O.tail=d.sibling,O.renderingStartTime=kr(),d.sibling=null,x=S.current,Or(S,C?x&1|2:x&1),d):(ss(d),null);case 22:case 23:return Ap(),C=d.memoizedState!==null,o!==null&&o.memoizedState!==null!==C&&(d.flags|=8192),C&&(d.mode&1)!==0?(Js&1073741824)!==0&&(ss(d),d.subtreeFlags&6&&(d.flags|=8192)):ss(d),null;case 24:return null;case 25:return null}throw Error(u(156,d.tag))}function ay(o,d){switch(qh(d),d.tag){case 1:return wi(d.type)&&Za(),o=d.flags,o&65536?(d.flags=o&-65537|128,d):null;case 3:return h(),rn(zi),rn(si),z(),o=d.flags,(o&65536)!==0&&(o&128)===0?(d.flags=o&-65537|128,d):null;case 5:return y(d),null;case 13:if(rn(S),o=d.memoizedState,o!==null&&o.dehydrated!==null){if(d.alternate===null)throw Error(u(340));Wr()}return o=d.flags,o&65536?(d.flags=o&-65537|128,d):null;case 19:return rn(S),null;case 4:return h(),null;case 10:return fi(d.type._context),null;case 22:case 23:return Ap(),null;case 24:return null;default:return null}}var $u=!1,Ui=!1,Dm=typeof WeakSet=="function"?WeakSet:Set,Ut=null;function Uu(o,d){var x=o.ref;if(x!==null)if(typeof x=="function")try{x(null)}catch(C){$n(o,d,C)}else x.current=null}function vp(o,d,x){try{x()}catch(C){$n(o,d,C)}}var af=!1;function xp(o,d){if(Vl=bi,o=Nh(),Fl(o)){if("selectionStart"in o)var x={start:o.selectionStart,end:o.selectionEnd};else e:{x=(x=o.ownerDocument)&&x.defaultView||window;var C=x.getSelection&&x.getSelection();if(C&&C.rangeCount!==0){x=C.anchorNode;var R=C.anchorOffset,O=C.focusNode;C=C.focusOffset;try{x.nodeType,O.nodeType}catch{x=null;break e}var J=0,me=-1,Ee=-1,He=0,dt=0,ct=o,at=null;t:for(;;){for(var Ot;ct!==x||R!==0&&ct.nodeType!==3||(me=J+R),ct!==O||C!==0&&ct.nodeType!==3||(Ee=J+C),ct.nodeType===3&&(J+=ct.nodeValue.length),(Ot=ct.firstChild)!==null;)at=ct,ct=Ot;for(;;){if(ct===o)break t;if(at===x&&++He===R&&(me=J),at===O&&++dt===C&&(Ee=J),(Ot=ct.nextSibling)!==null)break;ct=at,at=ct.parentNode}ct=Ot}x=me===-1||Ee===-1?null:{start:me,end:Ee}}else x=null}x=x||{start:0,end:0}}else x=null;for(Na={focusedElem:o,selectionRange:x},bi=!1,Ut=d;Ut!==null;)if(d=Ut,o=d.child,(d.subtreeFlags&1028)!==0&&o!==null)o.return=d,Ut=o;else for(;Ut!==null;){d=Ut;try{var Vt=d.alternate;if((d.flags&1024)!==0)switch(d.tag){case 0:case 11:case 15:break;case 1:if(Vt!==null){var Qt=Vt.memoizedProps,In=Vt.memoizedState,Ve=d.stateNode,Fe=Ve.getSnapshotBeforeUpdate(d.elementType===d.type?Qt:Xn(d.type,Qt),In);Ve.__reactInternalSnapshotBeforeUpdate=Fe}break;case 3:var Ge=d.stateNode.containerInfo;Ge.nodeType===1?Ge.textContent="":Ge.nodeType===9&&Ge.documentElement&&Ge.removeChild(Ge.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(u(163))}}catch(bt){$n(d,d.return,bt)}if(o=d.sibling,o!==null){o.return=d.return,Ut=o;break}Ut=d.return}return Vt=af,af=!1,Vt}function Vu(o,d,x){var C=d.updateQueue;if(C=C!==null?C.lastEffect:null,C!==null){var R=C=C.next;do{if((R.tag&o)===o){var O=R.destroy;R.destroy=void 0,O!==void 0&&vp(d,x,O)}R=R.next}while(R!==C)}}function Io(o,d){if(d=d.updateQueue,d=d!==null?d.lastEffect:null,d!==null){var x=d=d.next;do{if((x.tag&o)===o){var C=x.create;x.destroy=C()}x=x.next}while(x!==d)}}function Gc(o){var d=o.ref;if(d!==null){var x=o.stateNode;switch(o.tag){case 5:o=x;break;default:o=x}typeof d=="function"?d(o):d.current=o}}function Lm(o){var d=o.alternate;d!==null&&(o.alternate=null,Lm(d)),o.child=null,o.deletions=null,o.sibling=null,o.tag===5&&(d=o.stateNode,d!==null&&(delete d[ms],delete d[Ba],delete d[$a],delete d[Mu],delete d[Ua])),o.stateNode=null,o.return=null,o.dependencies=null,o.memoizedProps=null,o.memoizedState=null,o.pendingProps=null,o.stateNode=null,o.updateQueue=null}function Fm(o){return o.tag===5||o.tag===3||o.tag===4}function Om(o){e:for(;;){for(;o.sibling===null;){if(o.return===null||Fm(o.return))return null;o=o.return}for(o.sibling.return=o.return,o=o.sibling;o.tag!==5&&o.tag!==6&&o.tag!==18;){if(o.flags&2||o.child===null||o.tag===4)continue e;o.child.return=o,o=o.child}if(!(o.flags&2))return o.stateNode}}function bp(o,d,x){var C=o.tag;if(C===5||C===6)o=o.stateNode,d?x.nodeType===8?x.parentNode.insertBefore(o,d):x.insertBefore(o,d):(x.nodeType===8?(d=x.parentNode,d.insertBefore(o,x)):(d=x,d.appendChild(o)),x=x._reactRootContainer,x!=null||d.onclick!==null||(d.onclick=Ul));else if(C!==4&&(o=o.child,o!==null))for(bp(o,d,x),o=o.sibling;o!==null;)bp(o,d,x),o=o.sibling}function wp(o,d,x){var C=o.tag;if(C===5||C===6)o=o.stateNode,d?x.insertBefore(o,d):x.appendChild(o);else if(C!==4&&(o=o.child,o!==null))for(wp(o,d,x),o=o.sibling;o!==null;)wp(o,d,x),o=o.sibling}var Vi=null,Qa=!1;function Mo(o,d,x){for(x=x.child;x!==null;)jm(o,d,x),x=x.sibling}function jm(o,d,x){if(ui&&typeof ui.onCommitFiberUnmount=="function")try{ui.onCommitFiberUnmount(Ns,x)}catch{}switch(x.tag){case 5:Ui||Uu(x,d);case 6:var C=Vi,R=Qa;Vi=null,Mo(o,d,x),Vi=C,Qa=R,Vi!==null&&(Qa?(o=Vi,x=x.stateNode,o.nodeType===8?o.parentNode.removeChild(x):o.removeChild(x)):Vi.removeChild(x.stateNode));break;case 18:Vi!==null&&(Qa?(o=Vi,x=x.stateNode,o.nodeType===8?Fc(o.parentNode,x):o.nodeType===1&&Fc(o,x),fs(o)):Fc(Vi,x.stateNode));break;case 4:C=Vi,R=Qa,Vi=x.stateNode.containerInfo,Qa=!0,Mo(o,d,x),Vi=C,Qa=R;break;case 0:case 11:case 14:case 15:if(!Ui&&(C=x.updateQueue,C!==null&&(C=C.lastEffect,C!==null))){R=C=C.next;do{var O=R,J=O.destroy;O=O.tag,J!==void 0&&((O&2)!==0||(O&4)!==0)&&vp(x,d,J),R=R.next}while(R!==C)}Mo(o,d,x);break;case 1:if(!Ui&&(Uu(x,d),C=x.stateNode,typeof C.componentWillUnmount=="function"))try{C.props=x.memoizedProps,C.state=x.memoizedState,C.componentWillUnmount()}catch(me){$n(x,d,me)}Mo(o,d,x);break;case 21:Mo(o,d,x);break;case 22:x.mode&1?(Ui=(C=Ui)||x.memoizedState!==null,Mo(o,d,x),Ui=C):Mo(o,d,x);break;default:Mo(o,d,x)}}function Sp(o){var d=o.updateQueue;if(d!==null){o.updateQueue=null;var x=o.stateNode;x===null&&(x=o.stateNode=new Dm),d.forEach(function(C){var R=pd.bind(null,o,C);x.has(C)||(x.add(C),C.then(R,R))})}}function Ss(o,d){var x=d.deletions;if(x!==null)for(var C=0;CR&&(R=J),C&=~O}if(C=R,C=kr()-C,C=(120>C?120:480>C?480:1080>C?1080:1920>C?1920:3e3>C?3e3:4320>C?4320:1960*Bm(C/1960))-C,10o?16:o,ec===null)var C=!1;else{if(o=ec,ec=null,cd=0,(Br&6)!==0)throw Error(u(331));var R=Br;for(Br|=4,Ut=o.current;Ut!==null;){var O=Ut,J=O.child;if((Ut.flags&16)!==0){var me=O.deletions;if(me!==null){for(var Ee=0;Eekr()-Pp?Kc(o,0):ld|=x),Fs(o,d)}function Hm(o,d){d===0&&((o.mode&1)===0?d=1:(d=yu,yu<<=1,(yu&130023424)===0&&(yu=4194304)));var x=mi();o=Rs(o,d),o!==null&&(Bo(o,d,x),Fs(o,x))}function ly(o){var d=o.memoizedState,x=0;d!==null&&(x=d.retryLane),Hm(o,x)}function pd(o,d){var x=0;switch(o.tag){case 13:var C=o.stateNode,R=o.memoizedState;R!==null&&(x=R.retryLane);break;case 19:C=o.stateNode;break;default:throw Error(u(314))}C!==null&&C.delete(d),Hm(o,x)}var Jn;Jn=function(o,d,x){if(o!==null)if(o.memoizedProps!==d.pendingProps||zi.current)Ds=!0;else{if((o.lanes&x)===0&&(d.flags&128)===0)return Ds=!1,ws(o,d,x);Ds=(o.flags&131072)!==0}else Ds=!1,vn&&(d.flags&1048576)!==0&&Fr(d,kt,d.index);switch(d.lanes=0,d.tag){case 2:var C=d.type;Bu(o,d),o=d.pendingProps;var R=Hs(d,si.current);pi(d,x),R=we(null,d,C,o,R,x);var O=he();return d.flags|=1,typeof R=="object"&&R!==null&&typeof R.render=="function"&&R.$$typeof===void 0?(d.tag=1,d.memoizedState=null,d.updateQueue=null,wi(C)?(O=!0,Qi(d)):O=!1,d.memoizedState=R.state!==null&&R.state!==void 0?R.state:null,Co(d),R.updater=Ys,d.stateNode=R,R._reactInternals=d,Vc(d,C,o,x),d=Nu(null,d,C,!0,O,x)):(d.tag=0,vn&&O&&ql(d),$i(null,d,R,x),d=d.child),d;case 16:C=d.elementType;e:{switch(Bu(o,d),o=d.pendingProps,R=C._init,C=R(C._payload),d.type=C,R=d.tag=Xu(C),o=Xn(C,o),R){case 0:d=fp(null,d,C,o,x);break e;case 1:d=ju(null,d,C,o,x);break e;case 11:d=Em(null,d,C,o,x);break e;case 14:d=rf(null,d,C,Xn(C.type,o),x);break e}throw Error(u(306,C,""))}return d;case 0:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),fp(o,d,C,R,x);case 1:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),ju(o,d,C,R,x);case 3:e:{if(Jh(d),o===null)throw Error(u(387));C=d.pendingProps,O=d.memoizedState,R=O.element,Fu(o,d),xa(d,C,null,x);var J=d.memoizedState;if(C=J.element,O.isDehydrated)if(O={element:C,isDehydrated:!1,cache:J.cache,pendingSuspenseBoundaries:J.pendingSuspenseBoundaries,transitions:J.transitions},d.updateQueue.baseState=O,d.memoizedState=O,d.flags&256){R=Xa(Error(u(423)),d),d=pp(o,d,C,x,R);break e}else if(C!==R){R=Xa(Error(u(424)),d),d=pp(o,d,C,x,R);break e}else for(ys=tn(d.stateNode.containerInfo.firstChild),Si=d,vn=!0,_s=null,x=Bc(d,null,C,x),d.child=x;x;)x.flags=x.flags&-3|4096,x=x.sibling;else{if(Wr(),C===R){d=Qs(o,d,x);break e}$i(o,d,C,x)}d=d.child}return d;case 5:return p(d),o===null&&jc(d),C=d.type,R=d.pendingProps,O=o!==null?o.memoizedProps:null,J=R.children,Dc(C,R)?J=null:O!==null&&Dc(C,O)&&(d.flags|=32),Zc(o,d),$i(o,d,J,x),d.child;case 6:return o===null&&jc(d),null;case 13:return Im(o,d,x);case 4:return s(d,d.stateNode.containerInfo),C=d.pendingProps,o===null?d.child=Oi(d,null,C,x):$i(o,d,C,x),d.child;case 11:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Em(o,d,C,R,x);case 7:return $i(o,d,d.pendingProps,x),d.child;case 8:return $i(o,d,d.pendingProps.children,x),d.child;case 12:return $i(o,d,d.pendingProps.children,x),d.child;case 10:e:{if(C=d.type._context,R=d.pendingProps,O=d.memoizedProps,J=R.value,Or(Lt,C._currentValue),C._currentValue=J,O!==null)if(qs(O.value,J)){if(O.children===R.children&&!zi.current){d=Qs(o,d,x);break e}}else for(O=d.child,O!==null&&(O.return=d);O!==null;){var me=O.dependencies;if(me!==null){J=O.child;for(var Ee=me.firstContext;Ee!==null;){if(Ee.context===C){if(O.tag===1){Ee=rs(-1,x&-x),Ee.tag=2;var He=O.updateQueue;if(He!==null){He=He.shared;var dt=He.pending;dt===null?Ee.next=Ee:(Ee.next=dt.next,dt.next=Ee),He.pending=Ee}}O.lanes|=x,Ee=O.alternate,Ee!==null&&(Ee.lanes|=x),Lu(O.return,x,d),me.lanes|=x;break}Ee=Ee.next}}else if(O.tag===10)J=O.type===d.type?null:O.child;else if(O.tag===18){if(J=O.return,J===null)throw Error(u(341));J.lanes|=x,me=J.alternate,me!==null&&(me.lanes|=x),Lu(J,x,d),J=O.sibling}else J=O.child;if(J!==null)J.return=O;else for(J=O;J!==null;){if(J===d){J=null;break}if(O=J.sibling,O!==null){O.return=J.return,J=O;break}J=J.return}O=J}$i(o,d,R.children,x),d=d.child}return d;case 9:return R=d.type,C=d.pendingProps.children,pi(d,x),R=As(R),C=C(R),d.flags|=1,$i(o,d,C,x),d.child;case 14:return C=d.type,R=Xn(C,d.pendingProps),R=Xn(C.type,R),rf(o,d,C,R,x);case 15:return hp(o,d,d.type,d.pendingProps,x);case 17:return C=d.type,R=d.pendingProps,R=d.elementType===C?R:Xn(C,R),Bu(o,d),d.tag=1,wi(C)?(o=!0,Qi(d)):o=!1,pi(d,x),is(d,C,R),Vc(d,C,R,x),Nu(null,d,C,!0,o,x);case 19:return gp(o,d,x);case 22:return dp(o,d,x)}throw Error(u(156,d.tag))};function df(o,d){return Ma(o,d)}function Km(o,d,x,C){this.tag=o,this.key=x,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=d,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=C,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function ta(o,d,x,C){return new Km(o,d,x,C)}function md(o){return o=o.prototype,!(!o||!o.isReactComponent)}function Xu(o){if(typeof o=="function")return md(o)?1:0;if(o!=null){if(o=o.$$typeof,o===Rt)return 11;if(o===zr)return 14}return 2}function sl(o,d){var x=o.alternate;return x===null?(x=ta(o.tag,d,o.key,o.mode),x.elementType=o.elementType,x.type=o.type,x.stateNode=o.stateNode,x.alternate=o,o.alternate=x):(x.pendingProps=d,x.type=o.type,x.flags=0,x.subtreeFlags=0,x.deletions=null),x.flags=o.flags&14680064,x.childLanes=o.childLanes,x.lanes=o.lanes,x.child=o.child,x.memoizedProps=o.memoizedProps,x.memoizedState=o.memoizedState,x.updateQueue=o.updateQueue,d=o.dependencies,x.dependencies=d===null?null:{lanes:d.lanes,firstContext:d.firstContext},x.sibling=o.sibling,x.index=o.index,x.ref=o.ref,x}function ff(o,d,x,C,R,O){var J=2;if(C=o,typeof o=="function")md(o)&&(J=1);else if(typeof o=="string")J=5;else e:switch(o){case lt:return Yc(x.children,R,O,d);case ut:J=8,R|=8;break;case Je:return o=ta(12,x,d,R|2),o.elementType=Je,o.lanes=O,o;case Mt:return o=ta(13,x,d,R),o.elementType=Mt,o.lanes=O,o;case Ht:return o=ta(19,x,d,R),o.elementType=Ht,o.lanes=O,o;case Zt:return pf(x,R,O,d);default:if(typeof o=="object"&&o!==null)switch(o.$$typeof){case Ke:J=10;break e;case At:J=9;break e;case Rt:J=11;break e;case zr:J=14;break e;case or:J=16,C=null;break e}throw Error(u(130,o==null?o:typeof o,""))}return d=ta(J,x,d,R),d.elementType=o,d.type=C,d.lanes=O,d}function Yc(o,d,x,C){return o=ta(7,o,C,d),o.lanes=x,o}function pf(o,d,x,C){return o=ta(22,o,C,d),o.elementType=Zt,o.lanes=x,o.stateNode={isHidden:!1},o}function zp(o,d,x){return o=ta(6,o,null,d),o.lanes=x,o}function Dp(o,d,x){return d=ta(4,o.children!==null?o.children:[],o.key,d),d.lanes=x,d.stateNode={containerInfo:o.containerInfo,pendingChildren:null,implementation:o.implementation},d}function cy(o,d,x,C,R){this.tag=d,this.containerInfo=o,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=yc(0),this.expirationTimes=yc(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=yc(0),this.identifierPrefix=C,this.onRecoverableError=R,this.mutableSourceEagerHydrationData=null}function Lp(o,d,x,C,R,O,J,me,Ee){return o=new cy(o,d,x,me,Ee),d===1?(d=1,O===!0&&(d|=8)):d=0,O=ta(3,null,null,d),o.current=O,O.stateNode=o,O.memoizedState={element:C,isDehydrated:x,cache:null,transitions:null,pendingSuspenseBoundaries:null},Co(O),o}function uy(o,d,x){var C=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(r)}catch(a){console.error(a)}}return r(),My.exports=Pw(),My.exports}var ox;function Cw(){if(ox)return cg;ox=1;var r=Y0();return cg.createRoot=r.createRoot,cg.hydrateRoot=r.hydrateRoot,cg}var kw=Cw();const Ew=R_(kw);/** * react-router v7.9.1 * * Copyright (c) Remix Software Inc. @@ -46,9 +46,9 @@ Error generating stack: `+O.message+` * LICENSE.md file in the root directory of this source tree. * * @license MIT - */var ax="popstate";function Ew(r={}){function o(m,b){let{pathname:P,search:w,hash:l}=m.location;return Uy("",{pathname:P,search:w,hash:l},b.state&&b.state.usr||null,b.state&&b.state.key||"default")}function u(m,b){return typeof b=="string"?b:em(b)}return Mw(o,u,null,r)}function Un(r,o){if(r===!1||r===null||typeof r>"u")throw new Error(o)}function gl(r,o){if(!r){typeof console<"u"&&console.warn(o);try{throw new Error(o)}catch{}}}function Iw(){return Math.random().toString(36).substring(2,10)}function lx(r,o){return{usr:r.state,key:r.key,idx:o}}function Uy(r,o,u=null,m){return{pathname:typeof r=="string"?r:r.pathname,search:"",hash:"",...typeof o=="string"?Zf(o):o,state:u,key:o&&o.key||m||Iw()}}function em({pathname:r="/",search:o="",hash:u=""}){return o&&o!=="?"&&(r+=o.charAt(0)==="?"?o:"?"+o),u&&u!=="#"&&(r+=u.charAt(0)==="#"?u:"#"+u),r}function Zf(r){let o={};if(r){let u=r.indexOf("#");u>=0&&(o.hash=r.substring(u),r=r.substring(0,u));let m=r.indexOf("?");m>=0&&(o.search=r.substring(m),r=r.substring(0,m)),r&&(o.pathname=r)}return o}function Mw(r,o,u,m={}){let{window:b=document.defaultView,v5Compat:P=!1}=m,w=b.history,l="POP",L=null,N=Z();N==null&&(N=0,w.replaceState({...w.state,idx:N},""));function Z(){return(w.state||{idx:null}).idx}function V(){l="POP";let Pe=Z(),ze=Pe==null?null:Pe-N;N=Pe,L&&L({action:l,location:pe.location,delta:ze})}function ee(Pe,ze){l="PUSH";let Re=Uy(pe.location,Pe,ze);N=Z()+1;let Te=lx(Re,N),Fe=pe.createHref(Re);try{w.pushState(Te,"",Fe)}catch(Ye){if(Ye instanceof DOMException&&Ye.name==="DataCloneError")throw Ye;b.location.assign(Fe)}P&&L&&L({action:l,location:pe.location,delta:1})}function q(Pe,ze){l="REPLACE";let Re=Uy(pe.location,Pe,ze);N=Z();let Te=lx(Re,N),Fe=pe.createHref(Re);w.replaceState(Te,"",Fe),P&&L&&L({action:l,location:pe.location,delta:0})}function be(Pe){return Aw(Pe)}let pe={get action(){return l},get location(){return r(b,w)},listen(Pe){if(L)throw new Error("A history only accepts one active listener");return b.addEventListener(ax,V),L=Pe,()=>{b.removeEventListener(ax,V),L=null}},createHref(Pe){return o(b,Pe)},createURL:be,encodeLocation(Pe){let ze=be(Pe);return{pathname:ze.pathname,search:ze.search,hash:ze.hash}},push:ee,replace:q,go(Pe){return w.go(Pe)}};return pe}function Aw(r,o=!1){let u="http://localhost";typeof window<"u"&&(u=window.location.origin!=="null"?window.location.origin:window.location.href),Un(u,"No window.location.(origin|href) available to create URL");let m=typeof r=="string"?r:em(r);return m=m.replace(/ $/,"%20"),!o&&m.startsWith("//")&&(m=u+m),new URL(m,u)}function Q0(r,o,u="/"){return Rw(r,o,u,!1)}function Rw(r,o,u,m){let b=typeof o=="string"?Zf(o):o,P=hu(b.pathname||"/",u);if(P==null)return null;let w=J0(r);zw(w);let l=null;for(let L=0;l==null&&L{let Z={relativePath:N===void 0?w.path||"":N,caseSensitive:w.caseSensitive===!0,childrenIndex:l,route:w};if(Z.relativePath.startsWith("/")){if(!Z.relativePath.startsWith(m)&&L)return;Un(Z.relativePath.startsWith(m),`Absolute route path "${Z.relativePath}" nested under path "${m}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),Z.relativePath=Z.relativePath.slice(m.length)}let V=lu([m,Z.relativePath]),ee=u.concat(Z);w.children&&w.children.length>0&&(Un(w.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${V}".`),J0(w.children,o,ee,V,L)),!(w.path==null&&!w.index)&&o.push({path:V,score:Bw(V,w.index),routesMeta:ee})};return r.forEach((w,l)=>{var L;if(w.path===""||!((L=w.path)!=null&&L.includes("?")))P(w,l);else for(let N of eb(w.path))P(w,l,!0,N)}),o}function eb(r){let o=r.split("/");if(o.length===0)return[];let[u,...m]=o,b=u.endsWith("?"),P=u.replace(/\?$/,"");if(m.length===0)return b?[P,""]:[P];let w=eb(m.join("/")),l=[];return l.push(...w.map(L=>L===""?P:[P,L].join("/"))),b&&l.push(...w),l.map(L=>r.startsWith("/")&&L===""?"/":L)}function zw(r){r.sort((o,u)=>o.score!==u.score?u.score-o.score:$w(o.routesMeta.map(m=>m.childrenIndex),u.routesMeta.map(m=>m.childrenIndex)))}var Dw=/^:[\w-]+$/,Lw=3,Fw=2,Ow=1,jw=10,Nw=-2,cx=r=>r==="*";function Bw(r,o){let u=r.split("/"),m=u.length;return u.some(cx)&&(m+=Nw),o&&(m+=Fw),u.filter(b=>!cx(b)).reduce((b,P)=>b+(Dw.test(P)?Lw:P===""?Ow:jw),m)}function $w(r,o){return r.length===o.length&&r.slice(0,-1).every((m,b)=>m===o[b])?r[r.length-1]-o[o.length-1]:0}function Uw(r,o,u=!1){let{routesMeta:m}=r,b={},P="/",w=[];for(let l=0;l{if(Z==="*"){let be=l[ee]||"";w=P.slice(0,P.length-be.length).replace(/(.)\/+$/,"$1")}const q=l[ee];return V&&!q?N[Z]=void 0:N[Z]=(q||"").replace(/%2F/g,"/"),N},{}),pathname:P,pathnameBase:w,pattern:r}}function Vw(r,o=!1,u=!0){gl(r==="*"||!r.endsWith("*")||r.endsWith("/*"),`Route path "${r}" will be treated as if it were "${r.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${r.replace(/\*$/,"/*")}".`);let m=[],b="^"+r.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(w,l,L)=>(m.push({paramName:l,isOptional:L!=null}),L?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return r.endsWith("*")?(m.push({paramName:"*"}),b+=r==="*"||r==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):u?b+="\\/*$":r!==""&&r!=="/"&&(b+="(?:(?=\\/|$))"),[new RegExp(b,o?void 0:"i"),m]}function Zw(r){try{return r.split("/").map(o=>decodeURIComponent(o).replace(/\//g,"%2F")).join("/")}catch(o){return gl(!1,`The URL path "${r}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${o}).`),r}}function hu(r,o){if(o==="/")return r;if(!r.toLowerCase().startsWith(o.toLowerCase()))return null;let u=o.endsWith("/")?o.length-1:o.length,m=r.charAt(u);return m&&m!=="/"?null:r.slice(u)||"/"}function Gw(r,o="/"){let{pathname:u,search:m="",hash:b=""}=typeof r=="string"?Zf(r):r;return{pathname:u?u.startsWith("/")?u:qw(u,o):o,search:Kw(m),hash:Xw(b)}}function qw(r,o){let u=o.replace(/\/+$/,"").split("/");return r.split("/").forEach(b=>{b===".."?u.length>1&&u.pop():b!=="."&&u.push(b)}),u.length>1?u.join("/"):"/"}function Ry(r,o,u,m){return`Cannot include a '${r}' character in a manually specified \`to.${o}\` field [${JSON.stringify(m)}]. Please separate it out to the \`to.${u}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Ww(r){return r.filter((o,u)=>u===0||o.route.path&&o.route.path.length>0)}function R_(r){let o=Ww(r);return o.map((u,m)=>m===o.length-1?u.pathname:u.pathnameBase)}function z_(r,o,u,m=!1){let b;typeof r=="string"?b=Zf(r):(b={...r},Un(!b.pathname||!b.pathname.includes("?"),Ry("?","pathname","search",b)),Un(!b.pathname||!b.pathname.includes("#"),Ry("#","pathname","hash",b)),Un(!b.search||!b.search.includes("#"),Ry("#","search","hash",b)));let P=r===""||b.pathname==="",w=P?"/":b.pathname,l;if(w==null)l=u;else{let V=o.length-1;if(!m&&w.startsWith("..")){let ee=w.split("/");for(;ee[0]==="..";)ee.shift(),V-=1;b.pathname=ee.join("/")}l=V>=0?o[V]:"/"}let L=Gw(b,l),N=w&&w!=="/"&&w.endsWith("/"),Z=(P||w===".")&&u.endsWith("/");return!L.pathname.endsWith("/")&&(N||Z)&&(L.pathname+="/"),L}var lu=r=>r.join("/").replace(/\/\/+/g,"/"),Hw=r=>r.replace(/\/+$/,"").replace(/^\/*/,"/"),Kw=r=>!r||r==="?"?"":r.startsWith("?")?r:"?"+r,Xw=r=>!r||r==="#"?"":r.startsWith("#")?r:"#"+r;function Yw(r){return r!=null&&typeof r.status=="number"&&typeof r.statusText=="string"&&typeof r.internal=="boolean"&&"data"in r}var tb=["POST","PUT","PATCH","DELETE"];new Set(tb);var Qw=["GET",...tb];new Set(Qw);var Gf=te.createContext(null);Gf.displayName="DataRouter";var Vg=te.createContext(null);Vg.displayName="DataRouterState";te.createContext(!1);var rb=te.createContext({isTransitioning:!1});rb.displayName="ViewTransition";var Jw=te.createContext(new Map);Jw.displayName="Fetchers";var e2=te.createContext(null);e2.displayName="Await";var _l=te.createContext(null);_l.displayName="Navigation";var ym=te.createContext(null);ym.displayName="Location";var Fa=te.createContext({outlet:null,matches:[],isDataRoute:!1});Fa.displayName="Route";var D_=te.createContext(null);D_.displayName="RouteError";function t2(r,{relative:o}={}){Un(qf(),"useHref() may be used only in the context of a component.");let{basename:u,navigator:m}=te.useContext(_l),{hash:b,pathname:P,search:w}=_m(r,{relative:o}),l=P;return u!=="/"&&(l=P==="/"?u:lu([u,P])),m.createHref({pathname:l,search:w,hash:b})}function qf(){return te.useContext(ym)!=null}function vl(){return Un(qf(),"useLocation() may be used only in the context of a component."),te.useContext(ym).location}var nb="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function ib(r){te.useContext(_l).static||te.useLayoutEffect(r)}function Wf(){let{isDataRoute:r}=te.useContext(Fa);return r?y2():r2()}function r2(){Un(qf(),"useNavigate() may be used only in the context of a component.");let r=te.useContext(Gf),{basename:o,navigator:u}=te.useContext(_l),{matches:m}=te.useContext(Fa),{pathname:b}=vl(),P=JSON.stringify(R_(m)),w=te.useRef(!1);return ib(()=>{w.current=!0}),te.useCallback((L,N={})=>{if(gl(w.current,nb),!w.current)return;if(typeof L=="number"){u.go(L);return}let Z=z_(L,JSON.parse(P),b,N.relative==="path");r==null&&o!=="/"&&(Z.pathname=Z.pathname==="/"?o:lu([o,Z.pathname])),(N.replace?u.replace:u.push)(Z,N.state,N)},[o,u,P,b,r])}var n2=te.createContext(null);function i2(r){let o=te.useContext(Fa).outlet;return o&&te.createElement(n2.Provider,{value:r},o)}function s2(){let{matches:r}=te.useContext(Fa),o=r[r.length-1];return o?o.params:{}}function _m(r,{relative:o}={}){let{matches:u}=te.useContext(Fa),{pathname:m}=vl(),b=JSON.stringify(R_(u));return te.useMemo(()=>z_(r,JSON.parse(b),m,o==="path"),[r,b,m,o])}function o2(r,o){return sb(r,o)}function sb(r,o,u,m,b){var Re;Un(qf(),"useRoutes() may be used only in the context of a component.");let{navigator:P}=te.useContext(_l),{matches:w}=te.useContext(Fa),l=w[w.length-1],L=l?l.params:{},N=l?l.pathname:"/",Z=l?l.pathnameBase:"/",V=l&&l.route;{let Te=V&&V.path||"";ob(N,!V||Te.endsWith("*")||Te.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${N}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + */var lx="popstate";function Iw(r={}){function a(m,b){let{pathname:P,search:w,hash:l}=m.location;return Zy("",{pathname:P,search:w,hash:l},b.state&&b.state.usr||null,b.state&&b.state.key||"default")}function u(m,b){return typeof b=="string"?b:em(b)}return Aw(a,u,null,r)}function Un(r,a){if(r===!1||r===null||typeof r>"u")throw new Error(a)}function yl(r,a){if(!r){typeof console<"u"&&console.warn(a);try{throw new Error(a)}catch{}}}function Mw(){return Math.random().toString(36).substring(2,10)}function cx(r,a){return{usr:r.state,key:r.key,idx:a}}function Zy(r,a,u=null,m){return{pathname:typeof r=="string"?r:r.pathname,search:"",hash:"",...typeof a=="string"?Gf(a):a,state:u,key:a&&a.key||m||Mw()}}function em({pathname:r="/",search:a="",hash:u=""}){return a&&a!=="?"&&(r+=a.charAt(0)==="?"?a:"?"+a),u&&u!=="#"&&(r+=u.charAt(0)==="#"?u:"#"+u),r}function Gf(r){let a={};if(r){let u=r.indexOf("#");u>=0&&(a.hash=r.substring(u),r=r.substring(0,u));let m=r.indexOf("?");m>=0&&(a.search=r.substring(m),r=r.substring(0,m)),r&&(a.pathname=r)}return a}function Aw(r,a,u,m={}){let{window:b=document.defaultView,v5Compat:P=!1}=m,w=b.history,l="POP",L=null,N=Z();N==null&&(N=0,w.replaceState({...w.state,idx:N},""));function Z(){return(w.state||{idx:null}).idx}function V(){l="POP";let Pe=Z(),Le=Pe==null?null:Pe-N;N=Pe,L&&L({action:l,location:fe.location,delta:Le})}function ee(Pe,Le){l="PUSH";let Me=Zy(fe.location,Pe,Le);N=Z()+1;let Se=cx(Me,N),De=fe.createHref(Me);try{w.pushState(Se,"",De)}catch(Ye){if(Ye instanceof DOMException&&Ye.name==="DataCloneError")throw Ye;b.location.assign(De)}P&&L&&L({action:l,location:fe.location,delta:1})}function q(Pe,Le){l="REPLACE";let Me=Zy(fe.location,Pe,Le);N=Z();let Se=cx(Me,N),De=fe.createHref(Me);w.replaceState(Se,"",De),P&&L&&L({action:l,location:fe.location,delta:0})}function _e(Pe){return Rw(Pe)}let fe={get action(){return l},get location(){return r(b,w)},listen(Pe){if(L)throw new Error("A history only accepts one active listener");return b.addEventListener(lx,V),L=Pe,()=>{b.removeEventListener(lx,V),L=null}},createHref(Pe){return a(b,Pe)},createURL:_e,encodeLocation(Pe){let Le=_e(Pe);return{pathname:Le.pathname,search:Le.search,hash:Le.hash}},push:ee,replace:q,go(Pe){return w.go(Pe)}};return fe}function Rw(r,a=!1){let u="http://localhost";typeof window<"u"&&(u=window.location.origin!=="null"?window.location.origin:window.location.href),Un(u,"No window.location.(origin|href) available to create URL");let m=typeof r=="string"?r:em(r);return m=m.replace(/ $/,"%20"),!a&&m.startsWith("//")&&(m=u+m),new URL(m,u)}function Q0(r,a,u="/"){return zw(r,a,u,!1)}function zw(r,a,u,m){let b=typeof a=="string"?Gf(a):a,P=hu(b.pathname||"/",u);if(P==null)return null;let w=J0(r);Dw(w);let l=null;for(let L=0;l==null&&L{let Z={relativePath:N===void 0?w.path||"":N,caseSensitive:w.caseSensitive===!0,childrenIndex:l,route:w};if(Z.relativePath.startsWith("/")){if(!Z.relativePath.startsWith(m)&&L)return;Un(Z.relativePath.startsWith(m),`Absolute route path "${Z.relativePath}" nested under path "${m}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),Z.relativePath=Z.relativePath.slice(m.length)}let V=lu([m,Z.relativePath]),ee=u.concat(Z);w.children&&w.children.length>0&&(Un(w.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${V}".`),J0(w.children,a,ee,V,L)),!(w.path==null&&!w.index)&&a.push({path:V,score:$w(V,w.index),routesMeta:ee})};return r.forEach((w,l)=>{var L;if(w.path===""||!((L=w.path)!=null&&L.includes("?")))P(w,l);else for(let N of eb(w.path))P(w,l,!0,N)}),a}function eb(r){let a=r.split("/");if(a.length===0)return[];let[u,...m]=a,b=u.endsWith("?"),P=u.replace(/\?$/,"");if(m.length===0)return b?[P,""]:[P];let w=eb(m.join("/")),l=[];return l.push(...w.map(L=>L===""?P:[P,L].join("/"))),b&&l.push(...w),l.map(L=>r.startsWith("/")&&L===""?"/":L)}function Dw(r){r.sort((a,u)=>a.score!==u.score?u.score-a.score:Uw(a.routesMeta.map(m=>m.childrenIndex),u.routesMeta.map(m=>m.childrenIndex)))}var Lw=/^:[\w-]+$/,Fw=3,Ow=2,jw=1,Nw=10,Bw=-2,ux=r=>r==="*";function $w(r,a){let u=r.split("/"),m=u.length;return u.some(ux)&&(m+=Bw),a&&(m+=Ow),u.filter(b=>!ux(b)).reduce((b,P)=>b+(Lw.test(P)?Fw:P===""?jw:Nw),m)}function Uw(r,a){return r.length===a.length&&r.slice(0,-1).every((m,b)=>m===a[b])?r[r.length-1]-a[a.length-1]:0}function Vw(r,a,u=!1){let{routesMeta:m}=r,b={},P="/",w=[];for(let l=0;l{if(Z==="*"){let _e=l[ee]||"";w=P.slice(0,P.length-_e.length).replace(/(.)\/+$/,"$1")}const q=l[ee];return V&&!q?N[Z]=void 0:N[Z]=(q||"").replace(/%2F/g,"/"),N},{}),pathname:P,pathnameBase:w,pattern:r}}function Zw(r,a=!1,u=!0){yl(r==="*"||!r.endsWith("*")||r.endsWith("/*"),`Route path "${r}" will be treated as if it were "${r.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${r.replace(/\*$/,"/*")}".`);let m=[],b="^"+r.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(w,l,L)=>(m.push({paramName:l,isOptional:L!=null}),L?"/?([^\\/]+)?":"/([^\\/]+)")).replace(/\/([\w-]+)\?(\/|$)/g,"(/$1)?$2");return r.endsWith("*")?(m.push({paramName:"*"}),b+=r==="*"||r==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):u?b+="\\/*$":r!==""&&r!=="/"&&(b+="(?:(?=\\/|$))"),[new RegExp(b,a?void 0:"i"),m]}function Gw(r){try{return r.split("/").map(a=>decodeURIComponent(a).replace(/\//g,"%2F")).join("/")}catch(a){return yl(!1,`The URL path "${r}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${a}).`),r}}function hu(r,a){if(a==="/")return r;if(!r.toLowerCase().startsWith(a.toLowerCase()))return null;let u=a.endsWith("/")?a.length-1:a.length,m=r.charAt(u);return m&&m!=="/"?null:r.slice(u)||"/"}function qw(r,a="/"){let{pathname:u,search:m="",hash:b=""}=typeof r=="string"?Gf(r):r;return{pathname:u?u.startsWith("/")?u:Ww(u,a):a,search:Xw(m),hash:Yw(b)}}function Ww(r,a){let u=a.replace(/\/+$/,"").split("/");return r.split("/").forEach(b=>{b===".."?u.length>1&&u.pop():b!=="."&&u.push(b)}),u.length>1?u.join("/"):"/"}function zy(r,a,u,m){return`Cannot include a '${r}' character in a manually specified \`to.${a}\` field [${JSON.stringify(m)}]. Please separate it out to the \`to.${u}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Hw(r){return r.filter((a,u)=>u===0||a.route.path&&a.route.path.length>0)}function D_(r){let a=Hw(r);return a.map((u,m)=>m===a.length-1?u.pathname:u.pathnameBase)}function L_(r,a,u,m=!1){let b;typeof r=="string"?b=Gf(r):(b={...r},Un(!b.pathname||!b.pathname.includes("?"),zy("?","pathname","search",b)),Un(!b.pathname||!b.pathname.includes("#"),zy("#","pathname","hash",b)),Un(!b.search||!b.search.includes("#"),zy("#","search","hash",b)));let P=r===""||b.pathname==="",w=P?"/":b.pathname,l;if(w==null)l=u;else{let V=a.length-1;if(!m&&w.startsWith("..")){let ee=w.split("/");for(;ee[0]==="..";)ee.shift(),V-=1;b.pathname=ee.join("/")}l=V>=0?a[V]:"/"}let L=qw(b,l),N=w&&w!=="/"&&w.endsWith("/"),Z=(P||w===".")&&u.endsWith("/");return!L.pathname.endsWith("/")&&(N||Z)&&(L.pathname+="/"),L}var lu=r=>r.join("/").replace(/\/\/+/g,"/"),Kw=r=>r.replace(/\/+$/,"").replace(/^\/*/,"/"),Xw=r=>!r||r==="?"?"":r.startsWith("?")?r:"?"+r,Yw=r=>!r||r==="#"?"":r.startsWith("#")?r:"#"+r;function Qw(r){return r!=null&&typeof r.status=="number"&&typeof r.statusText=="string"&&typeof r.internal=="boolean"&&"data"in r}var tb=["POST","PUT","PATCH","DELETE"];new Set(tb);var Jw=["GET",...tb];new Set(Jw);var qf=te.createContext(null);qf.displayName="DataRouter";var Vg=te.createContext(null);Vg.displayName="DataRouterState";te.createContext(!1);var rb=te.createContext({isTransitioning:!1});rb.displayName="ViewTransition";var e2=te.createContext(new Map);e2.displayName="Fetchers";var t2=te.createContext(null);t2.displayName="Await";var vl=te.createContext(null);vl.displayName="Navigation";var ym=te.createContext(null);ym.displayName="Location";var Fo=te.createContext({outlet:null,matches:[],isDataRoute:!1});Fo.displayName="Route";var F_=te.createContext(null);F_.displayName="RouteError";function r2(r,{relative:a}={}){Un(Wf(),"useHref() may be used only in the context of a component.");let{basename:u,navigator:m}=te.useContext(vl),{hash:b,pathname:P,search:w}=_m(r,{relative:a}),l=P;return u!=="/"&&(l=P==="/"?u:lu([u,P])),m.createHref({pathname:l,search:w,hash:b})}function Wf(){return te.useContext(ym)!=null}function Oo(){return Un(Wf(),"useLocation() may be used only in the context of a component."),te.useContext(ym).location}var nb="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function ib(r){te.useContext(vl).static||te.useLayoutEffect(r)}function Bd(){let{isDataRoute:r}=te.useContext(Fo);return r?_2():n2()}function n2(){Un(Wf(),"useNavigate() may be used only in the context of a component.");let r=te.useContext(qf),{basename:a,navigator:u}=te.useContext(vl),{matches:m}=te.useContext(Fo),{pathname:b}=Oo(),P=JSON.stringify(D_(m)),w=te.useRef(!1);return ib(()=>{w.current=!0}),te.useCallback((L,N={})=>{if(yl(w.current,nb),!w.current)return;if(typeof L=="number"){u.go(L);return}let Z=L_(L,JSON.parse(P),b,N.relative==="path");r==null&&a!=="/"&&(Z.pathname=Z.pathname==="/"?a:lu([a,Z.pathname])),(N.replace?u.replace:u.push)(Z,N.state,N)},[a,u,P,b,r])}var i2=te.createContext(null);function s2(r){let a=te.useContext(Fo).outlet;return a&&te.createElement(i2.Provider,{value:r},a)}function a2(){let{matches:r}=te.useContext(Fo),a=r[r.length-1];return a?a.params:{}}function _m(r,{relative:a}={}){let{matches:u}=te.useContext(Fo),{pathname:m}=Oo(),b=JSON.stringify(D_(u));return te.useMemo(()=>L_(r,JSON.parse(b),m,a==="path"),[r,b,m,a])}function o2(r,a){return sb(r,a)}function sb(r,a,u,m,b){var Me;Un(Wf(),"useRoutes() may be used only in the context of a component.");let{navigator:P}=te.useContext(vl),{matches:w}=te.useContext(Fo),l=w[w.length-1],L=l?l.params:{},N=l?l.pathname:"/",Z=l?l.pathnameBase:"/",V=l&&l.route;{let Se=V&&V.path||"";ab(N,!V||Se.endsWith("*")||Se.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${N}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. -Please change the parent to .`)}let ee=vl(),q;if(o){let Te=typeof o=="string"?Zf(o):o;Un(Z==="/"||((Re=Te.pathname)==null?void 0:Re.startsWith(Z)),`When overriding the location using \`\` or \`useRoutes(routes, location)\`, the location pathname must begin with the portion of the URL pathname that was matched by all parent routes. The current pathname base is "${Z}" but pathname "${Te.pathname}" was given in the \`location\` prop.`),q=Te}else q=ee;let be=q.pathname||"/",pe=be;if(Z!=="/"){let Te=Z.replace(/^\//,"").split("/");pe="/"+be.replace(/^\//,"").split("/").slice(Te.length).join("/")}let Pe=Q0(r,{pathname:pe});gl(V||Pe!=null,`No routes matched location "${q.pathname}${q.search}${q.hash}" `),gl(Pe==null||Pe[Pe.length-1].route.element!==void 0||Pe[Pe.length-1].route.Component!==void 0||Pe[Pe.length-1].route.lazy!==void 0,`Matched leaf route at location "${q.pathname}${q.search}${q.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`);let ze=h2(Pe&&Pe.map(Te=>Object.assign({},Te,{params:Object.assign({},L,Te.params),pathname:lu([Z,P.encodeLocation?P.encodeLocation(Te.pathname).pathname:Te.pathname]),pathnameBase:Te.pathnameBase==="/"?Z:lu([Z,P.encodeLocation?P.encodeLocation(Te.pathnameBase).pathname:Te.pathnameBase])})),w,u,m,b);return o&&ze?te.createElement(ym.Provider,{value:{location:{pathname:"/",search:"",hash:"",state:null,key:"default",...q},navigationType:"POP"}},ze):ze}function a2(){let r=g2(),o=Yw(r)?`${r.status} ${r.statusText}`:r instanceof Error?r.message:JSON.stringify(r),u=r instanceof Error?r.stack:null,m="rgba(200,200,200, 0.5)",b={padding:"0.5rem",backgroundColor:m},P={padding:"2px 4px",backgroundColor:m},w=null;return console.error("Error handled by React Router default ErrorBoundary:",r),w=te.createElement(te.Fragment,null,te.createElement("p",null,"💿 Hey developer 👋"),te.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",te.createElement("code",{style:P},"ErrorBoundary")," or"," ",te.createElement("code",{style:P},"errorElement")," prop on your route.")),te.createElement(te.Fragment,null,te.createElement("h2",null,"Unexpected Application Error!"),te.createElement("h3",{style:{fontStyle:"italic"}},o),u?te.createElement("pre",{style:b},u):null,w)}var l2=te.createElement(a2,null),c2=class extends te.Component{constructor(r){super(r),this.state={location:r.location,revalidation:r.revalidation,error:r.error}}static getDerivedStateFromError(r){return{error:r}}static getDerivedStateFromProps(r,o){return o.location!==r.location||o.revalidation!=="idle"&&r.revalidation==="idle"?{error:r.error,location:r.location,revalidation:r.revalidation}:{error:r.error!==void 0?r.error:o.error,location:o.location,revalidation:r.revalidation||o.revalidation}}componentDidCatch(r,o){this.props.unstable_onError?this.props.unstable_onError(r,o):console.error("React Router caught the following error during render",r)}render(){return this.state.error!==void 0?te.createElement(Fa.Provider,{value:this.props.routeContext},te.createElement(D_.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function u2({routeContext:r,match:o,children:u}){let m=te.useContext(Gf);return m&&m.static&&m.staticContext&&(o.route.errorElement||o.route.ErrorBoundary)&&(m.staticContext._deepestRenderedBoundaryId=o.route.id),te.createElement(Fa.Provider,{value:r},u)}function h2(r,o=[],u=null,m=null,b=null){if(r==null){if(!u)return null;if(u.errors)r=u.matches;else if(o.length===0&&!u.initialized&&u.matches.length>0)r=u.matches;else return null}let P=r,w=u==null?void 0:u.errors;if(w!=null){let N=P.findIndex(Z=>Z.route.id&&(w==null?void 0:w[Z.route.id])!==void 0);Un(N>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(w).join(",")}`),P=P.slice(0,Math.min(P.length,N+1))}let l=!1,L=-1;if(u)for(let N=0;N=0?P=P.slice(0,L+1):P=[P[0]];break}}}return P.reduceRight((N,Z,V)=>{let ee,q=!1,be=null,pe=null;u&&(ee=w&&Z.route.id?w[Z.route.id]:void 0,be=Z.route.errorElement||l2,l&&(L<0&&V===0?(ob("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),q=!0,pe=null):L===V&&(q=!0,pe=Z.route.hydrateFallbackElement||null)));let Pe=o.concat(P.slice(0,V+1)),ze=()=>{let Re;return ee?Re=be:q?Re=pe:Z.route.Component?Re=te.createElement(Z.route.Component,null):Z.route.element?Re=Z.route.element:Re=N,te.createElement(u2,{match:Z,routeContext:{outlet:N,matches:Pe,isDataRoute:u!=null},children:Re})};return u&&(Z.route.ErrorBoundary||Z.route.errorElement||V===0)?te.createElement(c2,{location:u.location,revalidation:u.revalidation,component:be,error:ee,children:ze(),routeContext:{outlet:null,matches:Pe,isDataRoute:!0},unstable_onError:m}):ze()},null)}function L_(r){return`${r} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function d2(r){let o=te.useContext(Gf);return Un(o,L_(r)),o}function f2(r){let o=te.useContext(Vg);return Un(o,L_(r)),o}function p2(r){let o=te.useContext(Fa);return Un(o,L_(r)),o}function F_(r){let o=p2(r),u=o.matches[o.matches.length-1];return Un(u.route.id,`${r} can only be used on routes that contain a unique "id"`),u.route.id}function m2(){return F_("useRouteId")}function g2(){var m;let r=te.useContext(D_),o=f2("useRouteError"),u=F_("useRouteError");return r!==void 0?r:(m=o.errors)==null?void 0:m[u]}function y2(){let{router:r}=d2("useNavigate"),o=F_("useNavigate"),u=te.useRef(!1);return ib(()=>{u.current=!0}),te.useCallback(async(b,P={})=>{gl(u.current,nb),u.current&&(typeof b=="number"?r.navigate(b):await r.navigate(b,{fromRouteId:o,...P}))},[r,o])}var ux={};function ob(r,o,u){!o&&!ux[r]&&(ux[r]=!0,gl(!1,u))}te.memo(_2);function _2({routes:r,future:o,state:u,unstable_onError:m}){return sb(r,void 0,u,m,o)}function v2({to:r,replace:o,state:u,relative:m}){Un(qf()," may be used only in the context of a component.");let{static:b}=te.useContext(_l);gl(!b," must not be used on the initial render in a . This is a no-op, but you should modify your code so the is only ever rendered in response to some user interaction or state change.");let{matches:P}=te.useContext(Fa),{pathname:w}=vl(),l=Wf(),L=z_(r,R_(P),w,m==="path"),N=JSON.stringify(L);return te.useEffect(()=>{l(JSON.parse(N),{replace:o,state:u,relative:m})},[l,N,m,o,u]),null}function x2(r){return i2(r.context)}function sa(r){Un(!1,"A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .")}function b2({basename:r="/",children:o=null,location:u,navigationType:m="POP",navigator:b,static:P=!1}){Un(!qf(),"You cannot render a inside another . You should never have more than one in your app.");let w=r.replace(/^\/*/,"/"),l=te.useMemo(()=>({basename:w,navigator:b,static:P,future:{}}),[w,b,P]);typeof u=="string"&&(u=Zf(u));let{pathname:L="/",search:N="",hash:Z="",state:V=null,key:ee="default"}=u,q=te.useMemo(()=>{let be=hu(L,w);return be==null?null:{location:{pathname:be,search:N,hash:Z,state:V,key:ee},navigationType:m}},[w,L,N,Z,V,ee,m]);return gl(q!=null,` is not able to match the URL "${L}${N}${Z}" because it does not start with the basename, so the won't render anything.`),q==null?null:te.createElement(_l.Provider,{value:l},te.createElement(ym.Provider,{children:o,value:q}))}function w2({children:r,location:o}){return o2(Vy(r),o)}function Vy(r,o=[]){let u=[];return te.Children.forEach(r,(m,b)=>{if(!te.isValidElement(m))return;let P=[...o,b];if(m.type===te.Fragment){u.push.apply(u,Vy(m.props.children,P));return}Un(m.type===sa,`[${typeof m.type=="string"?m.type:m.type.name}] is not a component. All component children of must be a or `),Un(!m.props.index||!m.props.children,"An index route cannot have child routes.");let w={id:m.props.id||P.join("-"),caseSensitive:m.props.caseSensitive,element:m.props.element,Component:m.props.Component,index:m.props.index,path:m.props.path,loader:m.props.loader,action:m.props.action,hydrateFallbackElement:m.props.hydrateFallbackElement,HydrateFallback:m.props.HydrateFallback,errorElement:m.props.errorElement,ErrorBoundary:m.props.ErrorBoundary,hasErrorBoundary:m.props.hasErrorBoundary===!0||m.props.ErrorBoundary!=null||m.props.errorElement!=null,shouldRevalidate:m.props.shouldRevalidate,handle:m.props.handle,lazy:m.props.lazy};m.props.children&&(w.children=Vy(m.props.children,P)),u.push(w)}),u}var bg="get",wg="application/x-www-form-urlencoded";function Zg(r){return r!=null&&typeof r.tagName=="string"}function S2(r){return Zg(r)&&r.tagName.toLowerCase()==="button"}function T2(r){return Zg(r)&&r.tagName.toLowerCase()==="form"}function P2(r){return Zg(r)&&r.tagName.toLowerCase()==="input"}function C2(r){return!!(r.metaKey||r.altKey||r.ctrlKey||r.shiftKey)}function k2(r,o){return r.button===0&&(!o||o==="_self")&&!C2(r)}var ug=null;function E2(){if(ug===null)try{new FormData(document.createElement("form"),0),ug=!1}catch{ug=!0}return ug}var I2=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function zy(r){return r!=null&&!I2.has(r)?(gl(!1,`"${r}" is not a valid \`encType\` for \`\`/\`\` and will default to "${wg}"`),null):r}function M2(r,o){let u,m,b,P,w;if(T2(r)){let l=r.getAttribute("action");m=l?hu(l,o):null,u=r.getAttribute("method")||bg,b=zy(r.getAttribute("enctype"))||wg,P=new FormData(r)}else if(S2(r)||P2(r)&&(r.type==="submit"||r.type==="image")){let l=r.form;if(l==null)throw new Error('Cannot submit a
            -
            -
            {t("beachDetail.nutsCode")}
            -
            {data.nutsCode}
            -
            {/* Actions row */} diff --git a/frontend/src/pages/FavoritesPage.tsx b/frontend/src/pages/FavoritesPage.tsx index a0d3244c45..de09dc84e4 100644 --- a/frontend/src/pages/FavoritesPage.tsx +++ b/frontend/src/pages/FavoritesPage.tsx @@ -5,6 +5,7 @@ import { useReorderFavorites, } from "@/api/favorites"; import { useBeachDetails } from "@/api/useBeachDetails"; +import { getClassificationKey } from "@/utils/quality"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; import { useState, useMemo, useEffect } from "react"; @@ -101,12 +102,15 @@ export default function FavoritesPage() { for (const f of favorites) { const info = details.byId.get(f.beachId); + const rawClassification = info?.classification ?? info?.classificationText; + const classificationKey = getClassificationKey(rawClassification); + map.set(f.beachId, { fav: f, name: info?.locationName ?? f.beachId, muni: info?.locationArea ?? "", - classification: info?.classification ?? info?.classificationText, - classificationText: info?.classificationText ?? t("classification.unknown"), + classification: rawClassification, + classificationText: t(classificationKey), }); } return map; diff --git a/frontend/src/utils/quality.ts b/frontend/src/utils/quality.ts index cda4f3029f..92443bebc5 100644 --- a/frontend/src/utils/quality.ts +++ b/frontend/src/utils/quality.ts @@ -43,3 +43,59 @@ export function deriveClassificationCode(detail: { return "unknown"; } } + +/** + * Maps classification text (Swedish from API or numeric) to i18n translation key. + * This ensures classification badges are displayed in the user's selected language. + */ +export function getClassificationKey( + classification?: number | string +): string { + // Handle numeric classification + if (typeof classification === "number") { + switch (classification) { + case 1: + return "classification.excellent"; + case 2: + return "classification.good"; + case 3: + return "classification.sufficient"; + case 4: + return "classification.poor"; + default: + return "classification.unknown"; + } + } + + // Handle text classification (Swedish from API) + if (typeof classification === "string") { + const text = classification.toLowerCase().trim(); + + // Excellent + if (text.includes("utmärkt")) { + return "classification.excellent"; + } + // Good + if (text.includes("bra")) { + return "classification.good"; + } + // Sufficient + if (text.includes("tillfreds")) { + return "classification.sufficient"; + } + // Poor + if (text.includes("dålig")) { + return "classification.poor"; + } + // Classification in progress + if (text.includes("klassificering") && text.includes("pågår")) { + return "classification.inProgress"; + } + // Not classified + if (text.includes("ej klassificerad") || text.includes("inte klassificerad")) { + return "classification.notClassified"; + } + } + + return "classification.unknown"; +} From 028954b469cb482745d629d218c3d48d3b9619f0 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:49:34 +0100 Subject: [PATCH 195/215] feat: add marker clustering and expand touch targets for accessibility --- frontend/src/components/MapView.tsx | 181 +++++++++++++++++++++++----- 1 file changed, 153 insertions(+), 28 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 9d540c1867..6b988208c1 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -145,47 +145,172 @@ export default function MapView({ }; }, []); // ← no dependencies (don’t recreate the map) - // Update markers when points change (NO map re-init, NO auto-fit here) + // Update markers with clustering support useEffect(() => { const map = mapRef.current; - if (!map) return; + if (!map || !map.isStyleLoaded()) return; const accent = getAccent(); - // clear old + + // Remove old markers markersRef.current.forEach((m) => m.remove()); markersRef.current = []; - points.forEach((p) => { - const el = document.createElement("div"); - el.className = - "w-3 h-3 rounded-full shadow " + - (isDark() ? "ring-2 ring-black/60" : "ring-2 ring-white/85"); - el.style.background = accent; - - // Add proper accessibility attributes - el.setAttribute("role", "button"); - el.setAttribute("aria-label", p.name); - el.setAttribute("tabindex", "0"); - - // Add keyboard support for accessibility - el.addEventListener("keydown", (e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - el.click(); // Trigger the popup - } + // Remove old layers and sources if they exist + if (map.getLayer("clusters")) map.removeLayer("clusters"); + if (map.getLayer("cluster-count")) map.removeLayer("cluster-count"); + if (map.getLayer("unclustered-point-visual")) map.removeLayer("unclustered-point-visual"); + if (map.getLayer("unclustered-point")) map.removeLayer("unclustered-point"); + if (map.getSource("beaches")) map.removeSource("beaches"); + + // Create GeoJSON from points + const geojson: GeoJSON.FeatureCollection = { + type: "FeatureCollection", + features: points.map((p) => ({ + type: "Feature", + geometry: { + type: "Point", + coordinates: [p.lon, p.lat], + }, + properties: { + id: p.id, + name: p.name, + }, + })), + }; + + // Add source with clustering enabled + map.addSource("beaches", { + type: "geojson", + data: geojson, + cluster: true, + clusterMaxZoom: 14, // Max zoom to cluster points on + clusterRadius: 50, // Radius of each cluster when clustering points + }); + + // Add cluster circle layer + map.addLayer({ + id: "clusters", + type: "circle", + source: "beaches", + filter: ["has", "point_count"], + paint: { + "circle-color": accent, + "circle-radius": [ + "step", + ["get", "point_count"], + 15, // radius for clusters with < 10 points + 10, 20, // radius for clusters with 10-99 points + 30, 25, // radius for clusters with 100+ points + ], + "circle-stroke-width": 2, + "circle-stroke-color": isDark() ? "rgba(0, 0, 0, 0.6)" : "rgba(255, 255, 255, 0.85)", + }, + }); + + // Add cluster count label + map.addLayer({ + id: "cluster-count", + type: "symbol", + source: "beaches", + filter: ["has", "point_count"], + layout: { + "text-field": "{point_count_abbreviated}", + "text-font": ["Noto Sans Regular"], + "text-size": 12, + }, + paint: { + "text-color": isDark() ? "#ffffff" : "#000000", + }, + }); + + // Add individual point layer (unclustered) + // Using 22px radius (44px diameter) for proper touch targets + map.addLayer({ + id: "unclustered-point", + type: "circle", + source: "beaches", + filter: ["!", ["has", "point_count"]], + paint: { + "circle-color": accent, + "circle-radius": 22, // 44px diameter for proper touch target (WCAG compliance) + "circle-stroke-width": 2, + "circle-stroke-color": isDark() ? "rgba(0, 0, 0, 0.6)" : "rgba(255, 255, 255, 0.85)", + "circle-opacity": 0.2, // Make it mostly transparent + "circle-stroke-opacity": 1, + }, + }); + + // Add visual marker layer on top (smaller, fully visible) + map.addLayer({ + id: "unclustered-point-visual", + type: "circle", + source: "beaches", + filter: ["!", ["has", "point_count"]], + paint: { + "circle-color": accent, + "circle-radius": 8, // Visual size (16px diameter) + "circle-stroke-width": 2, + "circle-stroke-color": isDark() ? "rgba(0, 0, 0, 0.6)" : "rgba(255, 255, 255, 0.85)", + }, + }); + + // Click handler for clusters - zoom in + map.on("click", "clusters", (e) => { + const features = map.queryRenderedFeatures(e.point, { + layers: ["clusters"], }); + if (!features.length) return; + + const clusterId = features[0].properties?.cluster_id; + const source = map.getSource("beaches") as maplibregl.GeoJSONSource; + + source.getClusterExpansionZoom(clusterId, (err, zoom) => { + if (err || !features[0].geometry || features[0].geometry.type !== "Point") return; + + map.easeTo({ + center: features[0].geometry.coordinates as [number, number], + zoom: zoom ?? map.getZoom() + 2, + }); + }); + }); + + // Click handler for individual points - show popup + map.on("click", "unclustered-point", (e) => { + if (!e.features?.length || !e.features[0].geometry || e.features[0].geometry.type !== "Point") return; + + const coordinates = e.features[0].geometry.coordinates.slice() as [number, number]; + const { name, id } = e.features[0].properties as { name: string; id: string }; - const marker = new maplibregl.Marker({ element: el }) - .setLngLat([p.lon, p.lat]) - .setPopup( - new maplibregl.Popup({ closeButton: false }).setHTML( - `${p.name}` - ) + // Ensure that if the map is zoomed out such that multiple + // copies of the feature are visible, the popup appears + // over the copy being pointed to. + while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) { + coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360; + } + + new maplibregl.Popup({ closeButton: false }) + .setLngLat(coordinates) + .setHTML( + `${name}` ) .addTo(map); + }); - markersRef.current.push(marker); + // Change cursor on hover + map.on("mouseenter", "clusters", () => { + map.getCanvas().style.cursor = "pointer"; + }); + map.on("mouseleave", "clusters", () => { + map.getCanvas().style.cursor = ""; }); + map.on("mouseenter", "unclustered-point", () => { + map.getCanvas().style.cursor = "pointer"; + }); + map.on("mouseleave", "unclustered-point", () => { + map.getCanvas().style.cursor = ""; + }); + }, [points]); // Fit to focus (center + radius) without flashing; allow closer zoom for small radii From e8811a6c5069d590b78fb6e3eae4274e909ea0f6 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:55:36 +0100 Subject: [PATCH 196/215] fix: increase max zoom level, adjust clustering settings, add smart cluster click handler, update focus zoom limits --- frontend/src/components/MapView.tsx | 50 +++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 6b988208c1..8a1d5cd5ec 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -82,7 +82,7 @@ export default function MapView({ center: [15, 62], zoom: 4.3, minZoom: 3, - maxZoom: 14, + maxZoom: 18, // Increased to allow more detailed zoom dragRotate: false, pitchWithRotate: false, attributionControl: { compact: true }, @@ -184,8 +184,8 @@ export default function MapView({ type: "geojson", data: geojson, cluster: true, - clusterMaxZoom: 14, // Max zoom to cluster points on - clusterRadius: 50, // Radius of each cluster when clustering points + clusterMaxZoom: 17, // Max zoom to cluster points on (increased for better separation) + clusterRadius: 40, // Radius of each cluster (reduced for tighter clustering) }); // Add cluster circle layer @@ -255,23 +255,45 @@ export default function MapView({ }, }); - // Click handler for clusters - zoom in + // Click handler for clusters - zoom in or show list if can't expand map.on("click", "clusters", (e) => { const features = map.queryRenderedFeatures(e.point, { layers: ["clusters"], }); - if (!features.length) return; + if (!features.length || !features[0].geometry || features[0].geometry.type !== "Point") return; const clusterId = features[0].properties?.cluster_id; + const pointCount = features[0].properties?.point_count; + const coordinates = features[0].geometry.coordinates.slice() as [number, number]; const source = map.getSource("beaches") as maplibregl.GeoJSONSource; source.getClusterExpansionZoom(clusterId, (err, zoom) => { - if (err || !features[0].geometry || features[0].geometry.type !== "Point") return; - - map.easeTo({ - center: features[0].geometry.coordinates as [number, number], - zoom: zoom ?? map.getZoom() + 2, - }); + if (err) return; + + // If cluster can't expand further (at max zoom), show popup with all beaches + if (zoom >= map.getMaxZoom() || zoom === map.getZoom()) { + source.getClusterLeaves(clusterId, pointCount || 100, 0, (error, leaves) => { + if (error || !leaves) return; + + const beachList = leaves + .map((leaf: any) => { + const props = leaf.properties; + return `${props.name}`; + }) + .join(""); + + new maplibregl.Popup({ closeButton: true, maxWidth: "300px" }) + .setLngLat(coordinates) + .setHTML(`
            ${beachList}
            `) + .addTo(map); + }); + } else { + // Otherwise, zoom in + map.easeTo({ + center: coordinates, + zoom: zoom ?? map.getZoom() + 2, + }); + } }); }); @@ -324,13 +346,13 @@ export default function MapView({ const r = focus.radiusKm; // 🔧 TIGHTEN the fit for small radii - // - Shrink the radius a bit so bounds aren’t too wide + // - Shrink the radius a bit so bounds aren't too wide // - Reduce padding so more of the view is the actual area of interest // - Let the map zoom in a bit more - const isSmall = r <= 5; // the 5 km “nearby” case + const isSmall = r <= 5; // the 5 km "nearby" case const effective = isSmall ? r * 0.65 : r; // <— tighten bounds const padding = isSmall ? 12 : 24; // less padding on small areas - const maxZoom = isSmall ? 16 : 12; // allow closer zoom on small areas + const maxZoom = isSmall ? 17 : 14; // allow closer zoom on small areas (updated for new max) const b = circleBounds(focus.center, effective); map.fitBounds(b, { padding, maxZoom }); From 7fb04447287afca784a2de3c06c2a4ec7288dc90 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:00:26 +0100 Subject: [PATCH 197/215] fix: contrast improvements for markers --- frontend/src/components/MapView.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index 8a1d5cd5ec..ea61911e38 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -217,10 +217,12 @@ export default function MapView({ layout: { "text-field": "{point_count_abbreviated}", "text-font": ["Noto Sans Regular"], - "text-size": 12, + "text-size": 13, }, paint: { - "text-color": isDark() ? "#ffffff" : "#000000", + "text-color": "#ffffff", // White text for good contrast on accent color in both themes + "text-halo-color": "rgba(0, 0, 0, 0.3)", // Subtle dark halo for extra contrast + "text-halo-width": 1, }, }); From 1ea4889aea76a35a16932826e2323de0cff3f588 Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:04:39 +0100 Subject: [PATCH 198/215] fix: dark mode beach marker popup contrast --- frontend/src/components/MapView.tsx | 38 ++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/MapView.tsx b/frontend/src/components/MapView.tsx index ea61911e38..79bf806356 100644 --- a/frontend/src/components/MapView.tsx +++ b/frontend/src/components/MapView.tsx @@ -39,6 +39,14 @@ function getAccent() { function styleForTheme() { return isDark() ? STYLE_DARK : STYLE_LIGHT; } +function getPopupStyles() { + const dark = isDark(); + return { + background: dark ? "#1f2937" : "#ffffff", // gray-800 : white + color: dark ? "#f9fafb" : "#111827", // gray-50 : gray-900 + borderColor: dark ? "#374151" : "#e5e7eb", // gray-700 : gray-200 + }; +} // compute bounds from center + radius (km) function circleBounds( @@ -277,17 +285,28 @@ export default function MapView({ source.getClusterLeaves(clusterId, pointCount || 100, 0, (error, leaves) => { if (error || !leaves) return; + const styles = getPopupStyles(); const beachList = leaves .map((leaf: any) => { const props = leaf.properties; - return `${props.name}`; + return `${props.name}`; }) .join(""); - new maplibregl.Popup({ closeButton: true, maxWidth: "300px" }) + const popup = new maplibregl.Popup({ closeButton: true, maxWidth: "300px" }) .setLngLat(coordinates) .setHTML(`
            ${beachList}
            `) .addTo(map); + + // Apply dark mode styles to popup container + const popupEl = popup.getElement(); + if (popupEl) { + const content = popupEl.querySelector(".maplibregl-popup-content"); + if (content) { + (content as HTMLElement).style.backgroundColor = styles.background; + (content as HTMLElement).style.color = styles.color; + } + } }); } else { // Otherwise, zoom in @@ -313,12 +332,23 @@ export default function MapView({ coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360; } - new maplibregl.Popup({ closeButton: false }) + const styles = getPopupStyles(); + const popup = new maplibregl.Popup({ closeButton: false }) .setLngLat(coordinates) .setHTML( - `${name}` + `${name}` ) .addTo(map); + + // Apply dark mode styles to popup container + const popupEl = popup.getElement(); + if (popupEl) { + const content = popupEl.querySelector(".maplibregl-popup-content"); + if (content) { + (content as HTMLElement).style.backgroundColor = styles.background; + (content as HTMLElement).style.color = styles.color; + } + } }); // Change cursor on hover From c4ccd0345e40e917345abfc46b7628142fe401df Mon Sep 17 00:00:00 2001 From: Talo Vargas <166659393+govargas@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:07:07 +0100 Subject: [PATCH 199/215] fix: language switcher layout fix --- frontend/src/components/LanguageSwitcher.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/LanguageSwitcher.tsx b/frontend/src/components/LanguageSwitcher.tsx index dd4284ce98..eebf50079d 100644 --- a/frontend/src/components/LanguageSwitcher.tsx +++ b/frontend/src/components/LanguageSwitcher.tsx @@ -10,11 +10,11 @@ export default function LanguageSwitcher() { } return ( -
            +
            @@ -167,13 +167,13 @@ export default function Header({ languageSwitcher }: HeaderProps) { className="p-2 rounded-full hover:bg-surface-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/50 text-ink" onClick={() => setUserOpen((v) => !v)} > -