diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..84023d9fbe5519c9b714306b80f8243a77a42c0c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,131 @@ +# WHY GitHub Actions CI: catches broken imports, TypeScript errors, and test +# failures before they reach HuggingFace Spaces. Runs on every push to main. +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Detect C sources + id: detect-c + run: | + if find . -name "*.c" -o -name "*.h" | grep -q .; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + + - name: Install lcov + run: sudo apt-get update && sudo apt-get install -y lcov + + - name: Install clang-format (if needed) + if: steps.detect-c.outputs.present == 'true' + run: sudo apt-get install -y clang-format + + - name: Set up Python 3.12 + id: setup-python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.txt', 'requirements-dev.txt') }} + restore-keys: | + ${{ runner.os }}-pip-${{ steps.setup-python.outputs.python-version }}- + ${{ runner.os }}-pip- + + - name: Install Python dependencies + run: pip install -r requirements.txt -r requirements-dev.txt + + - name: Python lint (ruff) + run: ruff check app/ tests/ + + - name: C style (clang-format) + if: steps.detect-c.outputs.present == 'true' + run: | + mapfile -d '' files < <(find . -name "*.c" -o -name "*.h" -print0) + clang-format --version + printf '%s\0' "${files[@]}" | xargs -0 clang-format --dry-run --Werror + + - name: Check all Python modules import cleanly + run: | + python -c "from app.model import ToxicityClassifier" + python -c "from app.database import get_recent_posts, save_post, seed_if_empty" + python -c "from app.graph import build_cooccurrence_graph" + python -c "from app.ingestion import ALGOSPEAK_QUERIES" + python -c "from app.main import app" + + - name: Run tests with coverage + env: + BLUESKY_HANDLE: "test@test.com" + BLUESKY_PASSWORD: "testpassword" + PYTHONPATH: . + run: | + python -m pytest tests/ -v --tb=short --cov=app --cov-report=xml + + # ── Frontend build verification ────────────────────────────────────────── + # WHY build React in CI: catches TypeScript errors and missing imports + # before they cause a silent failure during Docker build on HuggingFace. + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install pnpm + run: npm install -g pnpm + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ~/.local/share/pnpm/store + key: ${{ runner.os }}-pnpm-${{ hashFiles('frontend/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm- + + - name: Install frontend dependencies + working-directory: frontend + run: pnpm install --frozen-lockfile + + - name: Build frontend (verify no TypeScript errors) + working-directory: frontend + run: pnpm build + + - name: Collect lcov coverage + id: lcov + run: | + if find . -name "*.gcda" -o -name "*.gcno" | grep -q .; then + lcov --capture --directory . --output-file coverage.info --ignore-errors unused --no-external + echo "generated=true" >> "$GITHUB_OUTPUT" + else + echo "No gcda/gcno files found; skipping lcov capture." + echo "generated=false" >> "$GITHUB_OUTPUT" + fi + + - name: Upload Python coverage to Codecov + uses: codecov/codecov-action@v4 + with: + files: ./coverage.xml + token: ${{ secrets.CODECOV_TOKEN }} + flags: python + fail_ci_if_error: false + + - name: Upload C coverage to Codecov + if: steps.lcov.outputs.generated == 'true' + uses: codecov/codecov-action@v4 + with: + files: ./coverage.info + token: ${{ secrets.CODECOV_TOKEN }} + flags: c + fail_ci_if_error: false diff --git a/.github/workflows/hf-deploy.yml b/.github/workflows/hf-deploy.yml new file mode 100644 index 0000000000000000000000000000000000000000..761a962cfffcca217070faf683830d95f9edaa79 --- /dev/null +++ b/.github/workflows/hf-deploy.yml @@ -0,0 +1,53 @@ +name: Deploy to HuggingFace Spaces + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install pnpm + run: npm install -g pnpm + + - name: Build React frontend + working-directory: frontend + run: | + pnpm install --frozen-lockfile + pnpm build + + - name: Push to HuggingFace Space + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + run: | + git config --global user.email "deploy@github-actions.com" + git config --global user.name "GitHub Actions" + + git checkout --orphan hf-deploy + + git rm -rf assets/ 2>/dev/null || true + git rm -r --cached frontend/src/ 2>/dev/null || true + git rm -r --cached frontend/node_modules/ 2>/dev/null || true + git rm -r --cached frontend/public/ 2>/dev/null || true + + cp README_HF.md README.md + git rm --cached README_HF.md 2>/dev/null || true + rm README_HF.md + + git add -f frontend/dist/ + git add . + + git commit -m "Deploy to HuggingFace Spaces" + git remote add space https://odeliyach:$HF_TOKEN@huggingface.co/spaces/odeliyach/Algoscope + git push space hf-deploy:main --force diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2249e0ab2f7b868385d4b494a0f4dff13d21e65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Credentials — NEVER commit +.env +.env.local +.env.*.local + +# Database — contains scraped user data +algoscope.db +*.db + +# Python +__pycache__/ +*.py[cod] +*.pyo +venv/ +.venv/ +*.egg-info/ +dist/ +build/ + +# HuggingFace model cache +.cache/ + +# Pyvis auto-generated output — not application code +*.html +test_graph.html + +# React build output — generated by `pnpm build`, not source code +frontend/dist/ +frontend/node_modules/ + +# OS +.DS_Store +Thumbs.db + +# IDE +.vscode/ +.idea/ +*.swp diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7b1cbed4c8fdabb8b8147980fe75c43b6422f22f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY app/ ./app/ +COPY frontend/dist/ ./frontend/dist/ + +EXPOSE 7860 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--log-level", "debug"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..00b9a13e594fbabe203ced36576087c89b7a6243 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Odeliya Charitonova + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..305f0481e66a1d2d4e7e80dd127e7a9588746117 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +.PHONY: install run-api run-dashboard test lint clean + +install: + pip install -r requirements.txt + +run-api: + uvicorn app.main:app --reload --port 8000 + +run-dashboard: + streamlit run dashboard.py + +test: + python -m pytest tests/ -v --tb=short + +lint: + ruff check app/ dashboard.py tests/ + +clean: + find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true + rm -f algoscope.db diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..10c7b6f65d1142ad39146d7f157d9889118a87b3 --- /dev/null +++ b/README.md @@ -0,0 +1,205 @@ +--- +title: AlgoScope +emoji: 🔍 +colorFrom: red +colorTo: yellow +sdk: docker +pinned: false +--- + +
+ +# 🔍 AlgoScope + +**Real-time algospeak & toxicity detection on Bluesky** + +[![Build](https://img.shields.io/github/actions/workflow/status/odeliyach/Algoscope/ci.yml?branch=main&label=Build)](https://github.com/odeliyach/Algoscope/actions/workflows/ci.yml) +[![Coverage](https://img.shields.io/codecov/c/github/odeliyach/Algoscope?branch=main&label=Coverage)](https://codecov.io/gh/odeliyach/Algoscope) +[![Linting/Style](https://img.shields.io/github/actions/workflow/status/odeliyach/Algoscope/ci.yml?branch=main&label=Linting%2FStyle)](https://github.com/odeliyach/Algoscope/actions/workflows/ci.yml) +[![Python 3.12](https://img.shields.io/badge/Python-3.12-blue?logo=python)](https://python.org) +[![HuggingFace Model](https://img.shields.io/badge/Model-AlgoShield-orange?logo=huggingface)](https://huggingface.co/odeliyach/AlgoShield-Algospeak-Detection) +[![HuggingFace Space](https://img.shields.io/badge/Live%20Demo-Spaces-yellow?logo=huggingface)](https://huggingface.co/spaces/odeliyach/algoscope) +[![Streamlit](https://img.shields.io/badge/Dashboard-Streamlit-red?logo=streamlit)](https://streamlit.io) +[![License: MIT](https://img.shields.io/badge/License-MIT-green)](LICENSE) + +*Odeliya Charitonova · Tel Aviv University, School of CS & AI · 2026* + +
+ +--- + +## What is AlgoScope? + +Algospeak is the evolving coded language people use to evade content moderation — "unalive" instead of suicide, "seggs" instead of sex, "le dollar bean" instead of lesbian. Standard toxicity APIs score these near zero because they look benign to classifiers trained on explicit language. + +AlgoScope is a live dashboard that catches them anyway. It ingests posts from the Bluesky social network in real time, classifies each one with a fine-tuned DistilBERT model trained specifically on algospeak, and visualizes toxicity patterns, co-occurrence networks, and trend spikes in an interactive dashboard. + +> **Why this matters:** Algospeak evasion is an active research problem in content moderation. This project turns published NLP research into a live, clickable product. + +--- + +## Live Demo + +| Resource | Link | +|----------|------| +| 🖥️ Live dashboard | [huggingface.co/spaces/odeliyach/algoscope](https://huggingface.co/spaces/odeliyach/algoscope) | +| 🤗 Fine-tuned model | [odeliyach/AlgoShield-Algospeak-Detection](https://huggingface.co/odeliyach/AlgoShield-Algospeak-Detection) | +| 💻 GitHub | [github.com/odeliyach/Algoscope](https://github.com/odeliyach/Algoscope) | + +--- + +--- + +## Features + +- **🚨 Spike alerts** — red banner when a tracked term exceeds 80% toxic in the last hour +- **📊 Toxicity over time** — hourly line chart with color-coded data points (green/orange/red by toxicity level) +- **🕸️ Co-occurrence graph** — interactive word graph built with NetworkX + Pyvis; nodes colored by toxicity rate +- **⚖️ Term comparison** — side-by-side toxicity profiles for any two tracked terms +- **📥 Export** — download all analyzed posts as CSV or JSON +- **🎛️ Threshold slider** — tune precision/recall tradeoff at inference time without retraining + +--- + +## Architecture + +``` +┌─────────────────┐ AT Protocol ┌───────────────────┐ +│ Bluesky API │ ───────────────▶ │ ingestion.py │ +└─────────────────┘ │ dedup + preproc │ + └─────────┬─────────┘ + │ + ┌─────────▼─────────┐ + │ model.py │ + │ DistilBERT │ + │ singleton + batch│ + └─────────┬─────────┘ + │ + ┌────────────────────▼──────────────────────┐ + │ database.py │ + │ SQLite · URI-keyed deduplication │ + └────────────────────┬──────────────────────┘ + │ + ┌────────────────────────────────▼────────────────────────────┐ + │ dashboard.py │ + │ Streamlit · Plotly · NetworkX · Pyvis (4 tabs) │ + └─────────────────────────────────────────────────────────────┘ +``` + +**Stack:** Python 3.12 · FastAPI · Streamlit · SQLite · NetworkX · Pyvis · Plotly · HuggingFace Transformers · AT Protocol (Bluesky) + +--- + +## Model — AlgoShield + +The classifier powering AlgoScope is **AlgoShield**, a DistilBERT model fine-tuned on the [MADOC dataset](https://arxiv.org/abs/2306.01976) (Multimodal Algospeak Detection and Offensive Content). It was trained and evaluated separately — full training code, dataset preprocessing, and evaluation notebooks are in the [AlgoShield repository](https://huggingface.co/odeliyach/AlgoShield-Algospeak-Detection). + +| Metric | Baseline DistilBERT | AlgoShield (fine-tuned) | +|--------|---------------------|------------------------| +| Precision | 70.3% | 61.2% | +| Recall | 33.2% | **73.2% (+40 pts)** | +| F1 | 49.0% | **66.7% (+17.7 pts)** | + +The +40-point recall improvement comes at the cost of ~9 points of precision — a deliberate tradeoff. In content moderation, a false negative (missing a toxic post) causes real harm; a false positive just means a human reviews something innocent. The threshold slider in AlgoScope lets operators tune this tradeoff at deployment time without retraining. + +> Want to understand how AlgoShield was built? See the [model card and training details →](https://huggingface.co/odeliyach/AlgoShield-Algospeak-Detection) + +--- + +## Key Engineering Decisions + +**Train/serve parity** — The same `preprocess_text()` function used during AlgoShield's training is applied at inference time in AlgoScope. Without this, the model sees out-of-distribution input on every prediction — a production ML bug called train/serve skew. + +**Threshold separation** — The model outputs a raw confidence score; a threshold slider converts it to a binary label. This separates the ML model from business policy — the same pattern used in Gmail spam and YouTube moderation. One model, multiple thresholds tuned per context. + +**Graph construction order** — The co-occurrence graph filters to the 1-hop neighborhood of algospeak seed words *before* frequency ranking. The naive approach (top-30 globally, then filter) always returns generic English function words ("get", "like", "know") — useless for the project's purpose. + +**Physics disabled** — Pyvis force-directed layout is O(n²) per animation frame. With 30+ nodes it froze the browser for 2+ minutes. A fixed `randomSeed` layout loads instantly with reproducible positions. + +**SQLite with clean abstraction** — All persistence is isolated in `database.py`. No other file imports `sqlite3` directly. Replacing SQLite with PostgreSQL or Cassandra requires changing only that one file. + +--- + +## Running Locally + +**Requirements:** Python 3.12, a Bluesky account + +```bash +git clone https://github.com/odeliyach/Algoscope +cd Algoscope +python -m venv venv +venv\Scripts\activate # Windows +# source venv/bin/activate # Mac/Linux +pip install -r requirements.txt +``` + +Or with Make: +```bash +make install +make run-dashboard # in one terminal +make run-api # in another +``` + +Create `.env` in the project root: +```env +BLUESKY_HANDLE=yourhandle.bsky.social +BLUESKY_PASSWORD=yourpassword +``` + +--- + +## Project Structure + +``` +Algoscope/ +├── app/ +│ ├── main.py # FastAPI endpoints (/health, /predict) +│ ├── model.py # ToxicityClassifier — singleton load, batch inference +│ ├── ingestion.py # Bluesky AT Protocol client + preprocessing +│ ├── database.py # SQLite persistence — isolated for easy swap +│ └── graph.py # NetworkX co-occurrence graph + Pyvis HTML export +├── assets/ +│ ├── overview.png # Dashboard overview screenshot +│ ├── graph.png # Co-occurrence graph screenshot +│ └── term_comparison.png # Term comparison screenshot +├── tests/ +│ └── test_core.py # Preprocessing parity, DB round-trip, stopwords +├── dashboard.py # Streamlit dashboard — 4 tabs +├── Makefile # install / run / test / lint shortcuts +├── requirements.txt # Runtime dependencies +├── pyproject.toml # Project metadata + tooling config +├── Dockerfile # python:3.12-slim, non-root user +├── .github/workflows/ +│ └── ci.yml # Import checks + syntax + pytest on every push +└── .env # Credentials — not committed +``` + +--- + +## Deployment (HuggingFace Spaces) + +1. Push this repo to GitHub (verify `.env` and `algoscope.db` are in `.gitignore`) +2. Go to [huggingface.co](https://huggingface.co) → New Space → Streamlit → connect this GitHub repo +3. In Space Settings → Secrets, add `BLUESKY_HANDLE` and `BLUESKY_PASSWORD` +4. The Space auto-deploys on every push to `main` + +--- + +## Limitations & Future Work + +- **Bluesky-only** — the ingestion layer is modular; adding Reddit or Mastodon requires only a new adapter in `ingestion.py` +- **Fetch-on-click** — a background ingestion loop would keep data flowing continuously without user interaction +- **Static model** — algospeak evolves; periodic retraining or drift detection would maintain coverage over time +- **SQLite single-writer** — replacing with PostgreSQL or Cassandra enables concurrent multi-worker ingestion + +--- + +## License + +MIT — see [LICENSE](LICENSE) + +--- + +
+AlgoScope · Tel Aviv University, School of CS & AI · Odeliya Charitonova · 2026 +
diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/app/__init__.py @@ -0,0 +1 @@ + diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000000000000000000000000000000000000..25f1153b8ac6ffdb916b8afe3b98cd0831402787 --- /dev/null +++ b/app/database.py @@ -0,0 +1,164 @@ +""" +SQLite database for storing post classification results. + +ARCHITECTURE NOTE (interview talking point): +All persistence is isolated in this file. No other module imports sqlite3 +directly. This means swapping SQLite for PostgreSQL or any other store +requires changing only this one file — the rest of the codebase is +completely unaware of how data is stored. +""" + +import logging +import os +import sqlite3 +from typing import Any + +logger = logging.getLogger(__name__) + +_temp_dir = os.environ.get("TMPDIR") +if not _temp_dir: + _temp_dir = os.environ.get("TEMP") or os.environ.get("TMP") or "/tmp" +DB_PATH = os.environ.get( + "ALGOSCOPE_DB_PATH", + os.path.join(_temp_dir, "algoscope.db"), +) + +_db_initialized = False + + +def _get_connection() -> sqlite3.Connection: + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +def init_db() -> None: + """Create tables if they don't exist. Safe to call multiple times.""" + with _get_connection() as conn: + conn.execute( + """ + CREATE TABLE IF NOT EXISTS posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + text TEXT NOT NULL, + label TEXT NOT NULL, + score REAL NOT NULL, + platform TEXT NOT NULL, + query_term TEXT NOT NULL DEFAULT '', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """ + ) + try: + conn.execute("ALTER TABLE posts ADD COLUMN query_term TEXT NOT NULL DEFAULT ''") + except sqlite3.OperationalError: + pass + conn.commit() + + +def _ensure_init() -> None: + """Initialize DB once per process, not on every call.""" + global _db_initialized + if not _db_initialized: + init_db() + _db_initialized = True + + +def save_post( + text: str, + label: str, + score: float, + platform: str, + query_term: str = "", +) -> None: + """Insert a classified post into the posts table.""" + _ensure_init() + with _get_connection() as conn: + conn.execute( + "INSERT INTO posts (text, label, score, platform, query_term) VALUES (?, ?, ?, ?, ?)", + (text, label, score, platform, query_term), + ) + conn.commit() + + +def get_recent_posts(limit: int = 100) -> list[dict[str, Any]]: + """Return the most recent posts as a list of dicts, newest first.""" + _ensure_init() + with _get_connection() as conn: + cursor = conn.execute( + """ + SELECT id, text, label, score, platform, query_term, created_at + FROM posts + ORDER BY created_at DESC + LIMIT ? + """, + (limit,), + ) + rows = cursor.fetchall() + return [dict(row) for row in rows] + + +def get_post_count() -> int: + """Return total number of posts in the DB.""" + _ensure_init() + with _get_connection() as conn: + return conn.execute("SELECT COUNT(*) FROM posts").fetchone()[0] + + +def seed_if_empty() -> None: + """ + If the DB is empty (cold start or HF ephemeral filesystem wipe), fetch + a small batch of real posts from Bluesky and classify them so the + dashboard has data immediately without requiring the user to click FETCH. + + WHY this is safe now (it was disabled before): + Previously this ran at module import time, triggering a model download + before uvicorn bound to port 7860, killing the container with no logs. + Now it is called from lifespan() AFTER the server is up and AFTER the + classifier has loaded. A failure here is non-fatal. + + WHY 4 queries at limit=32 (not all queries at full limit): + Seeding is best-effort background work. ~30 posts is enough to populate + all dashboard widgets. Seeding all queries would add 10-30s to cold + start time, unacceptable for a free-tier Space that restarts often. + """ + _ensure_init() + count = get_post_count() + if count > 0: + logger.info("seed_if_empty: DB has %d posts, skipping seed", count) + return + + logger.info("seed_if_empty: DB is empty, seeding from Bluesky...") + try: + from app.ingestion import ALGOSPEAK_QUERIES, fetch_posts + from app.model import ToxicityClassifier + + classifier = ToxicityClassifier() + if classifier._pipeline is None: + logger.warning("seed_if_empty: classifier not ready, skipping seed") + return + + seed_queries = ALGOSPEAK_QUERIES[:4] + posts = fetch_posts(query=seed_queries[0], limit=32, queries=seed_queries) + if not posts: + logger.warning("seed_if_empty: no posts returned from Bluesky") + return + + texts = [t for t, _ in posts] + timestamps = [ts for _, ts in posts] + predictions = classifier.predict_batch(texts) + + for text, ts, pred in zip(texts, timestamps, predictions): + score = float(pred.get("score", 0.0) or 0.0) + label = "toxic" if score >= 0.70 else "non-toxic" + matched = next( + (q for q in seed_queries if q and q.lower() in text.lower()), + seed_queries[0], + ) + save_post(text=text, label=label, score=score, platform="bluesky", query_term=matched) + + logger.info("seed_if_empty: seeded %d posts", len(texts)) + except Exception as exc: + # WHY catch-all: Bluesky credentials may not be set, the network may + # be unavailable, or the model may not have loaded. The app must start + # regardless - the user can always click FETCH manually. + logger.warning("seed_if_empty: failed (non-fatal): %s", exc) diff --git a/app/graph.py b/app/graph.py new file mode 100644 index 0000000000000000000000000000000000000000..a778828ab689bc211328fe85bd7308bec3b5d9ec --- /dev/null +++ b/app/graph.py @@ -0,0 +1,211 @@ +""" +Graph utilities for exploring algospeak co-occurrence patterns. + +Co-occurrence graphs are a classic NLP exploratory tool: by connecting words +that frequently appear together, we can surface clusters of related slang +or emergent euphemisms that would be hard to spot from raw text alone. +""" + +from __future__ import annotations + +import re +from itertools import combinations +from typing import Dict, List + +import networkx as nx +from pyvis.network import Network + +from app.database import get_recent_posts + +# All words to exclude from the graph. Centralizing here makes it easy to tune. +STOPWORDS = { + # English function words + "the", "and", "or", "but", "if", "then", "else", "when", "where", + "what", "which", "who", "whom", "this", "that", "these", "those", + "a", "an", "of", "in", "on", "for", "to", "from", "by", "with", + "at", "as", "is", "am", "are", "was", "were", "be", "been", "being", + "it", "its", "he", "she", "they", "them", "we", "us", "you", "your", + "yours", "i", "me", "my", "mine", "our", "ours", "their", "theirs", + "do", "does", "did", "doing", "done", "have", "has", "had", + "will", "would", "can", "could", "should", "must", "may", "might", + "just", "like", "so", "very", "too", "not", "no", "yes", + "there", "here", "than", "then", "also", "even", "more", "most", + "get", "got", "go", "going", "say", "said", "out", "now", "day", + "because", "some", "people", "love", "social", "really", "while", + "think", "know", "want", "see", "make", "take", "come", "look", + "good", "new", "first", "last", "long", "great", "little", "own", + "right", "big", "high", "small", "large", "next", "early", "old", + "well", "still", "way", "every", "never", "always", "much", "need", + "feel", "put", "keep", "let", "ask", "seem", "show", "try", "call", + "back", "other", "free", "real", "best", "true", "about", "after", + "again", "dont", "isnt", "cant", "wont", "didnt", "doesnt", "youre", + "theyre", "whats", "thats", "dont", "thing", "things", "time", + # Spanish function words (common on Bluesky) + "de", "que", "con", "como", "para", "una", "uno", "los", "las", + "por", "del", "sus", "pero", "todo", "esta", "este", "son", "hay", + "nos", "han", "fue", "ser", "ver", "vez", "sin", "sobre", "entre", + "cuando", "bien", "solo", "puede", "tiene", "desde", "hasta", + # Web / file tokens + "jpg", "jpeg", "png", "gif", "webp", "www", "http", "https", + "com", "org", "net", "html", "php", "amp", "via", "bit", +} + + +def _tokenize(text: str) -> List[str]: + """ + Lightweight tokenizer for short social posts. + Drops stopwords, short tokens, and tokens with digits. + """ + text = text.lower() + raw_tokens = re.split(r"\W+", text) + tokens = [] + for tok in raw_tokens: + if not tok or len(tok) <= 2: + continue + if any(ch.isdigit() for ch in tok): + continue + if tok in STOPWORDS: + continue + tokens.append(tok) + return tokens + + +def build_cooccurrence_graph(min_cooccurrence: int = 2) -> nx.Graph: + """ + Build a word co-occurrence graph from all posts in the database. + + WHY co-occurrence graphs for algospeak: slang evolves in clusters. + 'unalive' tends to appear with 'suicide', 'depression', 'mental'. + Mapping these clusters reveals semantic neighborhoods of evasive language + that a simple keyword list would miss. + """ + posts = get_recent_posts(limit=10_000_000) + + G = nx.Graph() + word_counts: Dict[str, int] = {} + toxic_word_counts: Dict[str, int] = {} + + for row in posts: + text = (row.get("text") or "").strip() + label = (row.get("label") or "non-toxic").lower() + if not text: + continue + tokens = _tokenize(text) + if not tokens: + continue + + # Use a set so repeated words in one post don't inflate edge weights + unique_words = set(tokens) + + for w in unique_words: + word_counts[w] = word_counts.get(w, 0) + 1 + if label == "toxic": + toxic_word_counts[w] = toxic_word_counts.get(w, 0) + 1 + + for w1, w2 in combinations(sorted(unique_words), 2): + if G.has_edge(w1, w2): + G[w1][w2]["weight"] += 1 + else: + G.add_edge(w1, w2, weight=1) + + # Remove weak edges and isolated nodes + G.remove_edges_from([ + (u, v) for u, v, d in G.edges(data=True) + if d.get("weight", 0) < min_cooccurrence + ]) + G.remove_nodes_from(list(nx.isolates(G))) + + # Attach node metadata for visualization + for word, count in word_counts.items(): + if word not in G: + continue + G.nodes[word]["count"] = count + G.nodes[word]["toxic_count"] = toxic_word_counts.get(word, 0) + + # STEP 1: Filter to algospeak neighborhood FIRST. + # WHY order matters: filtering before top-30 ensures we get the most + # frequent *algospeak-related* words, not the most frequent generic words. + from app.ingestion import ALGOSPEAK_QUERIES + seed_words = {w for q in ALGOSPEAK_QUERIES for w in q.lower().split()} + + relevant = set() + for seed in seed_words: + if seed in G: + relevant.add(seed) + relevant.update(G.neighbors(seed)) + + if relevant: + G = G.subgraph(relevant).copy() + + # STEP 2: Take top 30 by frequency from the algospeak neighborhood + if G.number_of_nodes() > 30: + top_nodes = sorted( + G.nodes(data=True), + key=lambda x: x[1].get("count", 0), + reverse=True, + )[:30] + G = G.subgraph({n[0] for n in top_nodes}).copy() + + return G + + +def graph_to_pyvis(graph: nx.Graph, toxic_only: bool = False) -> str: + """ + Convert a NetworkX graph into interactive Pyvis HTML. + Physics disabled for instant rendering (animation caused 2min load times). + """ + net = Network(height="600px", width="100%", directed=False, notebook=False) + net.set_options('''{ + "physics": {"enabled": false}, + "configure": {"enabled": false}, + "layout": {"randomSeed": 42} + }''') + + included_nodes = set() + for node, data in graph.nodes(data=True): + count = int(data.get("count", 1) or 1) + toxic_count = int(data.get("toxic_count", 0) or 0) + toxic_ratio = toxic_count / count if count else 0.0 + + if toxic_only and toxic_count == 0: + continue + + # 3-color system: red=mostly toxic, orange=mixed, green=mostly benign + # More informative than binary because it shows usage context gradient + if toxic_ratio > 0.7: + color = "#ff4b4b" + elif toxic_ratio >= 0.4: + color = "#ff9f43" + else: + color = "#2ecc71" + + net.add_node(node, label=node, color=color, value=count) + included_nodes.add(node) + + for u, v, data in graph.edges(data=True): + if u not in included_nodes or v not in included_nodes: + continue + net.add_edge(u, v, value=int(data.get("weight", 1) or 1)) + + # Replace local file reference that breaks in Streamlit's sandboxed iframe + html = net.generate_html() + + import json # noqa: F401 + center_script = """ + + """ + html = html.replace("", center_script + "") + + html = html.replace( + '', + '' + ) + return html \ No newline at end of file diff --git a/app/ingestion.py b/app/ingestion.py new file mode 100644 index 0000000000000000000000000000000000000000..51daa3752f7527a513ed454f303b2d8ea9f1fe4c --- /dev/null +++ b/app/ingestion.py @@ -0,0 +1,221 @@ +""" +Bluesky post ingestion via the atproto library. +""" + +import os +import re +from concurrent.futures import ThreadPoolExecutor, TimeoutError +from typing import Any, Iterable, Optional, Set +import logging + +logger = logging.getLogger(__name__) + +# Using multiple queries lets us cover different algospeak variants that +# users employ to evade simple keyword filters. Centralizing them here makes +# it easy to tune the "vocabulary" without changing the rest of the pipeline. +ALGOSPEAK_QUERIES: list[str] = [ + "unalive", + "le dollar bean", + "seggs", + "cornhole", + "spicy eggplant", + "cope harder", + "ratio", + "touch grass", + "based", + "sus", +] + +# Cache the atproto client so we only pay the authentication cost once. +# In practice this can save ~10 seconds per fetch because login requires +# multiple network round trips, while the resulting session stays valid +# for many minutes to hours. +_client = None + + +def get_client(): + """Return a logged-in atproto Client, cached at module level.""" + global _client + if _client is not None: + return _client + + from atproto import Client + + handle = os.environ.get("BLUESKY_HANDLE") + password = os.environ.get("BLUESKY_PASSWORD") + if not handle or not password: + raise RuntimeError("BLUESKY_HANDLE or BLUESKY_PASSWORD not set") + + logger.info("Authenticating with Bluesky as %s", handle) + client = Client() + client.login(handle, password) + _client = client + logger.info("Bluesky authentication successful") + return client + + +def preprocess_text(text: str) -> str: + """ + Normalize text to match the training-time preprocessing. + + A very common ML bug is to train with one preprocessing pipeline and + serve with another. Keeping train and inference transforms aligned is + critical; otherwise, the model is effectively seeing out-of-distribution + data at serving time even if it "looks" similar to humans. + + Args: + text: Raw post text from Bluesky. + + Returns: + Cleaned text string, or empty string if the post should be discarded. + """ + if not text: + return "" + + # Remove URLs so we don't overfit to specific domains not in training data. + text = re.sub(r"https?://\S+", " ", text) + + # Drop non-ASCII characters (including most emojis) to mimic training preprocessing. + text = text.encode("ascii", errors="ignore").decode("ascii") + + # Collapse repeated whitespace and trim. + text = re.sub(r"\s+", " ", text).strip() + + # Strip hashtags before word analysis — hashtags are metadata, not content. + text_no_hashtags = re.sub(r"#\S+", "", text).strip() + + # Filter posts with no real linguistic content (filenames, hashtag spam). + # WHY: These posts add noise to the model and co-occurrence graph + # without contributing meaningful signal about algospeak patterns. + NON_WORDS = {"jpg", "png", "gif", "com", "www", "http", "https", "the", "and"} + real_words = [ + w for w in re.findall(r"[a-zA-Z]{3,}", text_no_hashtags) + if w.lower() not in NON_WORDS + ] + if len(real_words) < 3: + return "" + + # Enforce minimum length consistent with the training filter from the paper. + if len(text) < 10: + return "" + + return text + + +def _dedupe_texts(texts: Iterable[tuple[str, str | None]]) -> list[tuple[str, str | None]]: + """ + Deduplicate posts while preserving order. + + In real moderation systems, the same content can appear multiple times + (reposts, quote-posts, different queries). Deduplication avoids wasting + model budget on identical texts and keeps metrics from being biased by + repeated copies of the same post. + + Args: + texts: Iterable of text strings (may contain duplicates). + + Returns: + List of unique strings in original order. + """ + seen: Set[str] = set() + result: list[tuple[str, str | None]] = [] + for text, ts in texts: + if text in seen: + continue + seen.add(text) + result.append((text, ts)) + return result + + +def fetch_posts( + query: str, + limit: int = 50, + queries: Optional[list[str]] = None, +) -> list[tuple[str, str | None]]: + """ + Search Bluesky for posts and return their text content. + + If `queries` is provided, we search for each query term independently + and merge the results. This fan-out pattern covers different algospeak + variants without relying on a single brittle keyword. + + Args: + query: Primary search term (used if queries is None). + limit: Maximum number of posts to return across all queries. + queries: Optional list of terms to fan out across. + + Returns: + Deduplicated list of preprocessed post texts. + Returns empty list on any error (credentials, network, API). + """ + all_texts: list[str] = [] + + def _worker() -> None: + """ + Perform the actual API calls in a worker thread. + + WHY a thread + timeout: if Bluesky's API hangs, the request thread + would block indefinitely without a timeout, degrading UX and + potentially exhausting the worker pool in a multi-user deployment. + """ + try: + client = get_client() + query_list = queries if queries is not None else [query] + logger.info("Fetching posts for %d queries (limit=%d)", len(query_list), limit) + + for q in query_list: + # Divide the total limit evenly across all queries so that the + # sum of per-query fetches equals the requested limit. + # WHY: previously every query fetched min(limit, 10) posts, + # so 10 queries × 10 posts = up to 100 posts regardless of + # what the user requested. Now 25 limit ÷ 10 queries = 2-3 + # posts each, giving ~25 total before dedup — matching the UI. + per_query_limit = max(1, limit // len(query_list)) if queries is not None else limit + params: dict[str, Any] = { + "q": q, + "limit": min(max(1, per_query_limit), 100), + } + response = client.app.bsky.feed.search_posts(params=params) + + if not response or not getattr(response, "posts", None): + logger.warning("No results for query: %r", q) + continue + + for post in response.posts: + record = getattr(post, "record", None) + if record is None: + continue + raw_text = getattr(post.record, "text", None) + if raw_text is None or not isinstance(raw_text, str): + continue + cleaned = preprocess_text(raw_text) + if not cleaned: + continue + post_ts = getattr(post.record, "created_at", None) + all_texts.append((cleaned, post_ts)) + + logger.info("Fetched %d posts before deduplication", len(all_texts)) + + except Exception as exc: + # WHY reset client: if auth expired the next fetch should + # re-authenticate from scratch, not retry with a stale session. + logger.error("Fetch failed: %s — resetting client", exc, exc_info=True) + global _client + _client = None + + try: + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(_worker) + try: + future.result(timeout=30) + except TimeoutError: + logger.warning("Bluesky fetch timed out after 30s") + future.cancel() + + deduped = _dedupe_texts(all_texts) + logger.info("Returning %d posts after deduplication", len(deduped)) + return deduped + + except Exception as exc: + logger.error("Unexpected error in fetch_posts: %s", exc, exc_info=True) + return [] \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000000000000000000000000000000000000..97d436a120c07187f63f5e243522b1d389aea51a --- /dev/null +++ b/app/main.py @@ -0,0 +1,297 @@ +""" +FastAPI backend for AlgoScope toxicity detection. + +ARCHITECTURE NOTE (interview talking point): +This file is the boundary between the ML layer and the outside world. +The React frontend talks exclusively to these endpoints — it has no direct +access to the database, the model, or the Bluesky client. That separation +means you can swap any layer (swap SQLite for Postgres, swap the model, +swap the frontend framework) without touching the others. +""" + +import logging +import os +import threading +import time +from contextlib import asynccontextmanager +from typing import Any + +from fastapi import FastAPI, Query +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import FileResponse +from fastapi.staticfiles import StaticFiles +from pydantic import BaseModel +from dotenv import load_dotenv + +from app.database import get_post_count, get_recent_posts, save_post, seed_if_empty +from app.graph import build_cooccurrence_graph +from app.ingestion import ALGOSPEAK_QUERIES, fetch_posts +from app.model import ToxicityClassifier + +logger = logging.getLogger(__name__) + +# Load local environment variables for Bluesky credentials in development. +load_dotenv() + +# WHY None here: initializing ToxicityClassifier() at module scope triggers +# a 250MB model download before uvicorn binds to port 7860. HuggingFace Spaces +# sees no response on the port and kills the container with no logs. +# We initialize inside lifespan() instead, after the server is already up. +classifier: ToxicityClassifier | None = None + + +def _background_init() -> None: + """ + Load the model and seed the DB in a background thread. + + WHY background thread and not lifespan: + HuggingFace Spaces kills containers that don't respond on port 7860 + within ~30 seconds. Loading a 250MB model + fetching Bluesky posts + easily exceeds that. Running both in a daemon thread lets uvicorn bind + immediately; the model and seed data become available a few seconds + later. Requests that arrive before the model is ready return default + predictions (non-toxic / 0.0) which is acceptable for a brief window. + """ + global classifier + try: + classifier = ToxicityClassifier() + classifier._load_model() + logger.info("ToxicityClassifier ready") + except Exception as exc: + logger.warning("Model load failed — predictions will return defaults: %s", exc) + classifier = None + try: + seed_if_empty() + logger.info("DB seed check complete") + except Exception as exc: + logger.warning("Seed skipped (likely missing credentials): %s", exc) + + +@asynccontextmanager +async def lifespan(app: FastAPI): + # WHY daemon=True: daemon threads are killed automatically when the main + # process exits, so we don't block graceful shutdown waiting for a slow + # model download or Bluesky fetch. + t = threading.Thread(target=_background_init, daemon=True) + t.start() + logger.info("AlgoScope API starting up — model loading in background") + yield + logger.info("AlgoScope API shutting down") + + +app = FastAPI( + title="AlgoScope API", + description="Real-time algospeak and toxicity detection for Bluesky posts.", + version="0.2.0", + lifespan=lifespan, +) + +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + + +class PredictRequest(BaseModel): + text: str + + +class PredictResponse(BaseModel): + label: str + score: float + + +class FetchRequest(BaseModel): + queries: list[str] = ALGOSPEAK_QUERIES + limit: int = 25 + threshold: float = 0.70 + + +class PostOut(BaseModel): + id: int + text: str + label: str + score: float + platform: str + created_at: str + query_term: str = "" + + +@app.get("/health") +def health() -> dict[str, str]: + """Liveness check — used by HuggingFace Spaces and load balancers.""" + return {"status": "ok"} + + +@app.post("/predict", response_model=PredictResponse) +def predict(request: PredictRequest) -> dict[str, str | float]: + """Classify a single text as toxic or non-toxic.""" + if classifier is None: + return {"label": "non-toxic", "score": 0.0} + logger.info("Predicting for text (len=%d)", len(request.text)) + result = classifier.predict(request.text) + logger.info("Result: label=%s score=%.3f", result["label"], result["score"]) + return {"label": result["label"], "score": result["score"]} + + +@app.get("/posts") +def get_posts( + limit: int = Query(default=100, ge=1, le=10000), +) -> dict[str, Any]: + """Return recent posts from the database for the React frontend.""" + rows = get_recent_posts(limit=limit) + for row in rows: + if "query_term" not in row: + row["query_term"] = "" + if not row.get("created_at"): + row["created_at"] = "" + # WHY get_post_count() instead of len(rows): + # rows is capped at `limit`, so len(rows) always equals min(limit, n_posts). + # The frontend calls GET /posts?limit=1 to get the true total for the + # "Posts Analyzed" counter — returning len(rows)=1 there was causing + # the counter to reset to 1 after every fetch. + return {"posts": rows, "total": get_post_count()} + + +@app.post("/fetch-and-analyze") +def fetch_and_analyze(request: FetchRequest) -> dict[str, Any]: + """ + Fetch posts from Bluesky, run batch inference, save to DB, return results. + """ + if classifier is None: + return {"posts": [], "fetch_time": 0.0, "infer_time": 0.0, "count": 0, + "message": "Model not loaded yet, please try again in a moment."} + + logger.info( + "fetch-and-analyze: queries=%s limit=%d threshold=%.2f", + request.queries, request.limit, request.threshold, + ) + + t0 = time.time() + posts_text = fetch_posts( + query=request.queries[0] if request.queries else "unalive", + limit=request.limit, + queries=request.queries or None, + ) + fetch_time = time.time() - t0 + + if not posts_text: + return { + "posts": [], + "fetch_time": fetch_time, + "infer_time": 0.0, + "count": 0, + "message": "No posts fetched. Check Bluesky credentials or try again.", + } + + texts_only = [text for text, _ts in posts_text] + timestamps = [ts for _text, ts in posts_text] + + t1 = time.time() + predictions = classifier.predict_batch(texts_only) + infer_time = time.time() - t1 + + batch_ts_iso = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime()) + result_posts: list[dict[str, Any]] = [] + + for i, (text, post_ts, pred) in enumerate(zip(texts_only, timestamps, predictions)): + score = float(pred.get("score", 0.0) or 0.0) + label = "toxic" if score >= request.threshold else "non-toxic" + matched_term = next( + (t for t in request.queries if t and t.lower() in text.lower()), + request.queries[0] if request.queries else "", + ) + save_post(text=text, label=label, score=score, platform="bluesky", query_term=matched_term) + result_posts.append({ + "id": int(time.time() * 1000) + i, + "text": text, + "label": label, + "score": score, + "platform": "bluesky", + "created_at": post_ts or batch_ts_iso, + "query_term": matched_term, + }) + + logger.info( + "fetch-and-analyze: %d posts, fetch=%.2fs infer=%.2fs", + len(result_posts), fetch_time, infer_time, + ) + + return { + "posts": result_posts, + "fetch_time": fetch_time, + "infer_time": infer_time, + "count": len(result_posts), + } + + +@app.get("/graph-data") +def graph_data( + min_cooccurrence: int = Query(default=2, ge=1, le=20), + toxic_only: bool = Query(default=False), +) -> dict[str, Any]: + """Return co-occurrence graph as JSON nodes + edges.""" + graph = build_cooccurrence_graph(min_cooccurrence=min_cooccurrence) + + nodes = [] + for node, data in graph.nodes(data=True): + count = int(data.get("count", 1) or 1) + toxic_count = int(data.get("toxic_count", 0) or 0) + toxic_ratio = toxic_count / count if count else 0.0 + if toxic_only and toxic_count == 0: + continue + nodes.append({ + "id": node, + "count": count, + "toxic_count": toxic_count, + "toxic_ratio": round(toxic_ratio, 3), + }) + + included = {n["id"] for n in nodes} + edges = [ + {"source": u, "target": v, "weight": int(data.get("weight", 1) or 1)} + for u, v, data in graph.edges(data=True) + if u in included and v in included + ] + + return {"nodes": nodes, "edges": edges, "node_count": len(nodes), "edge_count": len(edges)} + + +@app.get("/stats") +def stats() -> dict[str, Any]: + """Aggregate statistics for the Overview tab metric cards.""" + rows = get_recent_posts(limit=100_000) + total = len(rows) + toxic = sum(1 for r in rows if (r.get("label") or "").lower() == "toxic") + term_counts: dict[str, int] = {} + for row in rows: + term = row.get("query_term") or "" + if term: + term_counts[term] = term_counts.get(term, 0) + 1 + return { + "total_posts": total, + "toxic_posts": toxic, + "toxic_rate": round(toxic / total * 100, 2) if total else 0.0, + "term_counts": term_counts, + } + + +# ── Static file serving (React build) ───────────────────────────────────────── +_FRONTEND_DIST = os.path.join(os.path.dirname(__file__), "..", "frontend", "dist") + +if os.path.exists(_FRONTEND_DIST): + _assets_dir = os.path.join(_FRONTEND_DIST, "assets") + if os.path.exists(_assets_dir): + app.mount("/assets", StaticFiles(directory=_assets_dir), name="assets") + + @app.get("/", response_class=FileResponse, include_in_schema=False) + def serve_frontend_root(): + return FileResponse(os.path.join(_FRONTEND_DIST, "index.html")) + + @app.get("/{full_path:path}", response_class=FileResponse, include_in_schema=False) + def serve_frontend_spa(full_path: str): + """Catch-all for React Router — prevents 404 on page refresh.""" + return FileResponse(os.path.join(_FRONTEND_DIST, "index.html")) diff --git a/app/model.py b/app/model.py new file mode 100644 index 0000000000000000000000000000000000000000..1f4db0ce53130d04dbee55390223672ad861cbb8 --- /dev/null +++ b/app/model.py @@ -0,0 +1,103 @@ +""" +Toxicity classifier using the AlgoShield-Algospeak-Detection HuggingFace model. +""" + +from typing import Any +import logging + +logger = logging.getLogger(__name__) + + +class ToxicityClassifier: + """ + Wrapper around the fine-tuned DistilBERT toxicity model. + + WHY lazy loading: calling _load_model() inside __init__ triggers a 250MB + model download at import time, before uvicorn binds to port 7860. + HuggingFace Spaces sees no response and kills the container with no logs. + Instead, main.py calls _load_model() explicitly inside lifespan(), after + the server is already up and logging. + """ + + def __init__(self) -> None: + self._pipeline = None + + def _load_model(self) -> None: + """Load the HuggingFace pipeline. Called once by lifespan() in main.py.""" + if self._pipeline is not None: + return + + try: + import torch # noqa: F401 + from transformers import pipeline + + logger.info("Loading AlgoShield-Algospeak-Detection model...") + self._pipeline = pipeline( + "text-classification", + model="odeliyach/AlgoShield-Algospeak-Detection", + ) + logger.info("Model loaded successfully") + except Exception as exc: + logger.error("Failed to load model: %s", exc, exc_info=True) + self._pipeline = None + + def predict(self, text: str) -> dict[str, Any]: + """Classify a single text as toxic or non-toxic.""" + default: dict[str, Any] = {"label": "non-toxic", "score": 0.0} + + if not text or not text.strip(): + return default + + try: + if self._pipeline is None: + logger.error("Pipeline is None — model was not loaded correctly") + return default + + results = self._pipeline(text, truncation=True, max_length=512) + if not results: + return default + + raw = results[0] + raw_label = str(raw.get("label", "")).lower() + raw_score = float(raw.get("score", 0.0)) + + if "toxic" in raw_label or raw_label in ("1", "label_1", "positive"): + label = "toxic" + else: + label = "non-toxic" + + return {"label": label, "score": raw_score} + + except Exception as exc: + logger.error("predict() failed: %s", exc, exc_info=True) + return default + + def predict_batch(self, texts: list[str]) -> list[dict[str, Any]]: + """Classify a list of texts in a single forward pass.""" + if not texts: + return [] + + cleaned = [t for t in texts if isinstance(t, str) and t.strip()] + if not cleaned or self._pipeline is None: + return [] + + try: + outputs = self._pipeline(cleaned, truncation=True, max_length=512) + except Exception as exc: + logger.error("predict_batch() failed: %s", exc, exc_info=True) + return [] + + results: list[dict[str, Any]] = [] + for raw in outputs: + raw_label = str(raw.get("label", "")).lower() + raw_score = float(raw.get("score", 0.0)) + + if "toxic" in raw_label or raw_label in ("1", "label_1", "positive"): + label = "toxic" + else: + label = "non-toxic" + + results.append({"label": label, "score": raw_score}) + + logger.info("predict_batch: classified %d texts", len(results)) + return results diff --git a/dashboard.py b/dashboard.py new file mode 100644 index 0000000000000000000000000000000000000000..870535590f5db476e5f41a8ef57c46f606a8114b --- /dev/null +++ b/dashboard.py @@ -0,0 +1,953 @@ +""" +AlgoScope Streamlit dashboard for live algospeak/toxicity detection. +""" + +from __future__ import annotations + +import time +from collections import defaultdict +from datetime import datetime, timedelta +from html import escape + +from dotenv import load_dotenv +import plotly.graph_objects as go +import streamlit as st + +from app.database import get_recent_posts, save_post +from app.graph import build_cooccurrence_graph, graph_to_pyvis +from app.ingestion import ALGOSPEAK_QUERIES, fetch_posts +from app.model import ToxicityClassifier + +load_dotenv() + +# Singleton classifier — loads once, survives all reruns. +# DistilBERT takes ~2s to load; without this every button click reloads it. +classifier = ToxicityClassifier() + + +def _parse_dt(ts_str) -> datetime | None: + """ + Safely parse a timestamp string to datetime. + WHY: SQLite stores timestamps as plain text with no timezone info. + Without explicit normalization, timezone-naive comparisons silently + give wrong results — a common subtle data bug in production. + """ + try: + s = str(ts_str).replace("Z", "").replace(" ", "T") + return datetime.fromisoformat(s) + except Exception: + return None + + +def main() -> None: + st.set_page_config( + page_title="AlgoScope — Live Algospeak Detection", + layout="wide", + initial_sidebar_state="expanded", # WHY: prevents sidebar from starting closed + ) + + st.markdown( + """ + + """, + unsafe_allow_html=True, + ) + + # ── Header ───────────────────────────────────────────────────────────────── + # WHY st.components.v1.html: st.markdown strips onclick for security. + # components.html renders inside an iframe, so JS runs freely and can + # reach window.parent.document to click Streamlit's native sidebar button. + import streamlit.components.v1 as components + components.html(""" + + + """, height=50, scrolling=False) + + st.markdown( + """ +
+
+ +
+
AlgoScope
+
Real-time algospeak & toxicity intelligence on Bluesky
+
+
+
+
+ + LIVE +
+
+ by Odeliya Charitonova
+ + github.com/odeliyach/Algoscope + +
+
+
+ """, + unsafe_allow_html=True, + ) + + # ── Session state init ───────────────────────────────────────────────────── + # WHY session_state: Streamlit reruns the entire script on every interaction. + # Anything that must survive between clicks lives here. + defaults = { + "analyzed_results": [], + "just_fetched": False, + "graph_html": None, + "custom_terms": [], + "selected_queries": list(ALGOSPEAK_QUERIES[:3]), + "fetch_stats": None, + "auto_refresh_ran": False, + "auto_trigger_fetch": False, + "fetch_message": None, + "last_min_cooc": 3, + "sidebar_open": True, + } + for k, v in defaults.items(): + if k not in st.session_state: + st.session_state[k] = v + + # ── Sidebar ──────────────────────────────────────────────────────────────── + with st.sidebar: + # ── Tracked terms display (styled pills like the mockup) ─────────────── + st.markdown('', unsafe_allow_html=True) + + # Build toxic-ratio per term from DB history for the sidebar display + history_for_terms = get_recent_posts(limit=1000) + term_toxic_ratio: dict[str, float] = {} + for term in ALGOSPEAK_QUERIES: + term_posts = [r for r in history_for_terms if term in (r.get("text") or "").lower()] + if term_posts: + toxic_n = sum(1 for r in term_posts if (r.get("label") or "").lower() == "toxic") + term_toxic_ratio[term] = toxic_n / len(term_posts) + else: + term_toxic_ratio[term] = 0.0 + + selected_queries = st.session_state.selected_queries + base_options = list(ALGOSPEAK_QUERIES) + extra_options = [t for t in st.session_state.custom_terms if t not in base_options] + all_options = base_options + extra_options + + # Show styled term rows for selected terms + term_rows_html = [] + for term in selected_queries: + ratio = term_toxic_ratio.get(term, 0.0) + pct = int(ratio * 100) + if ratio > 0.7: + dot_color = "#ff4b4b" + elif ratio >= 0.4: + dot_color = "#ff8c42" + else: + dot_color = "#2ecc71" + term_rows_html.append(f""" +
+
+
+ {escape(term)} +
+ {pct}% +
""") + if term_rows_html: + st.markdown("".join(term_rows_html), unsafe_allow_html=True) + + st.markdown('', unsafe_allow_html=True) + # WHY multiselect over checkboxes: supports the full vocabulary list + # and lets analysts add/remove terms without page reload. + selected_queries = st.multiselect( + "Select terms", + options=all_options, + default=st.session_state.selected_queries, + label_visibility="collapsed", + ) + st.session_state.selected_queries = selected_queries + + # Custom term input — full width input + button below + new_term = st.text_input("Add custom term", key="custom_term_input", label_visibility="collapsed", + placeholder="Add custom term...") + add_clicked = st.button("+ Add term", key="add_custom_term", use_container_width=True) + if add_clicked and new_term.strip(): + term = new_term.strip().lower() + if term not in st.session_state.custom_terms and term not in base_options: + st.session_state.custom_terms.append(term) + if term not in st.session_state.selected_queries: + st.session_state.selected_queries.append(term) + selected_queries = st.session_state.selected_queries + + if st.session_state.custom_terms: + pills = "".join( + f"{escape(t)}" + for t in st.session_state.custom_terms + ) + st.markdown(pills, unsafe_allow_html=True) + + # ── Threshold ────────────────────────────────────────────────────────── + st.markdown('', unsafe_allow_html=True) + # WHY threshold slider: separates ML policy from model. Operators tune + # sensitivity without retraining — standard MLOps pattern. + threshold = st.slider("Toxicity threshold", 0.0, 1.0, 0.7, 0.05, label_visibility="collapsed") + + # ── Sampling ─────────────────────────────────────────────────────────── + st.markdown('', unsafe_allow_html=True) + limit = st.slider("Number of posts", 5, 100, 25, label_visibility="collapsed") + + # ── Fetch ────────────────────────────────────────────────────────────── + st.markdown('', unsafe_allow_html=True) + auto_refresh = st.checkbox("Auto-refresh (60s)", value=False) + fetch_button = st.button("Fetch & Analyze", type="primary", use_container_width=True) + + # Auto-refresh countdown + if auto_refresh and not st.session_state.auto_refresh_ran and not fetch_button: + ph = st.empty() + for remaining in range(60, 0, -1): + ph.caption(f"Next fetch in: {remaining}s") + time.sleep(1) + st.session_state.auto_trigger_fetch = True + st.session_state.auto_refresh_ran = True + st.rerun() # WHY st.rerun: experimental_rerun removed in Streamlit 1.34 + if not auto_refresh: + st.session_state.auto_refresh_ran = False + + # ── Fetch stats ──────────────────────────────────────────────────────── + if st.session_state.fetch_stats: + s = st.session_state.fetch_stats + st.caption(f"Fetched {s['n']} posts in {s['fetch']:.1f}s · Inference: {s['infer']:.1f}s") + + min_cooccurrence = st.session_state.last_min_cooc + + # ── Trigger handling ─────────────────────────────────────────────────────── + if st.session_state.auto_trigger_fetch: + fetch_button = True + st.session_state.auto_trigger_fetch = False + + # ── Fetch & Analyze ──────────────────────────────────────────────────────── + if fetch_button: + st.session_state.just_fetched = True + st.session_state.analyzed_results = [] + + with st.spinner("Fetching posts from Bluesky..."): + t0 = time.time() + effective_query = selected_queries[0] if selected_queries else "toxic" + posts = fetch_posts(query=effective_query, limit=limit, queries=selected_queries or None) + fetch_time = time.time() - t0 + + if posts: + t1 = time.time() + # WHY predict_batch: one forward pass for all posts — ~50x faster + # than looping predict() on CPU (measured: 0.36s vs ~18s for 50 posts). + predictions = classifier.predict_batch(posts) + infer_time = time.time() - t1 + + # WHY store created_at and query_term in session_state: + # trend alerts and export both need these fields. Without created_at, + # all datetime comparisons return None and alerts never fire. + batch_ts = datetime.utcnow().isoformat() + for text, pred in zip(posts, predictions): + score = float(pred.get("score", 0.0) or 0.0) + label = "toxic" if score >= threshold else "non-toxic" + # Identify which query term matched this post + matched_term = next( + (t for t in (selected_queries or []) if t and t in text.lower()), + selected_queries[0] if selected_queries else "" + ) + save_post(text=text, label=label, score=score, platform="bluesky") + st.session_state.analyzed_results.append({ + "text": text, + "label": label, + "score": score, + "query_term": matched_term, + "created_at": batch_ts, + }) + + st.session_state.fetch_stats = {"n": len(posts), "fetch": fetch_time, "infer": infer_time} + st.session_state.fetch_message = ("success", f"Done! Analyzed {len(posts)} posts.") + else: + st.session_state.fetch_stats = {"n": 0, "fetch": fetch_time, "infer": 0.0} + st.session_state.fetch_message = ("warning", "No posts fetched. Try again or check your connection.") + else: + st.session_state.just_fetched = False + + + # ── Load data ────────────────────────────────────────────────────────────── + # WHY limit=10000: using 1000 capped the "Posts analyzed" metric + # at 1000 even when the DB had more. High limit shows the real total. + history = get_recent_posts(limit=10000) + batch_rows = st.session_state.analyzed_results + vocab = set(st.session_state.selected_queries or []) | set(st.session_state.custom_terms or []) + + # ── Tabs ─────────────────────────────────────────────────────────────────── + overview_tab, graph_tab, compare_tab, export_tab = st.tabs([" Overview ", " Co-occurrence Graph ", " Term Comparison ", " Export "]) + + with overview_tab: + + # Show fetch message inside the tab (not above tabs) + if st.session_state.get("fetch_message"): + msg_type, msg_text = st.session_state.fetch_message + if msg_type == "success": + st.success(msg_text) + else: + st.warning(msg_text) + st.session_state.fetch_message = None + + # ── Trend alerts ─────────────────────────────────────────────────────── + # WHY: simple spike detection — if a term's toxicity rate in the last + # hour exceeds 80%, show a red banner. In production you'd use z-scores + # or CUSUM control charts; this is the lightweight portfolio version. + now_utc = datetime.utcnow() + one_hour_ago = now_utc - timedelta(hours=1) + alerts = [] + all_terms_alert = list(set(st.session_state.selected_queries or []) | set(st.session_state.custom_terms or [])) + for term in all_terms_alert: + recent_term = [r for r in history + if term in (r.get("text") or "").lower() + and _parse_dt(r.get("created_at")) + and _parse_dt(r.get("created_at")) >= one_hour_ago] + if len(recent_term) >= 3: + t_rate = sum(1 for r in recent_term if (r.get("label") or "").lower() == "toxic") / len(recent_term) + if t_rate >= 0.80: + alerts.append((term, t_rate, len(recent_term))) + for term, t_rate, count in alerts: + st.markdown(f"""
+ 🚨 + Spike detected: + {term} — {t_rate*100:.0f}% + toxic in last hour ({count} posts)
""", + unsafe_allow_html=True) + + # ── Metrics ──────────────────────────────────────────────────────────── + total_posts = len(history) + toxic_posts = sum(1 for r in history if (r.get("label") or "").lower() == "toxic") + toxic_rate = (toxic_posts / total_posts * 100.0) if total_posts else 0.0 + avg_score = ( + sum(float(r.get("score", 0) or 0) for r in batch_rows) / len(batch_rows) + if batch_rows else 0.0 + ) + + term_counts: dict[str, int] = defaultdict(int) + for row in batch_rows: + txt = (row.get("text") or "").lower() + for t in vocab: + if t and t in txt: + term_counts[t] += 1 + top_term = max(term_counts, key=term_counts.get) if term_counts else "—" + + now = datetime.utcnow() + yesterday = now - timedelta(hours=24) + two_days_ago = now - timedelta(hours=48) + + posts_today = sum( + 1 for r in history + if _parse_dt(r.get("created_at")) and _parse_dt(r["created_at"]) > yesterday + ) + posts_yesterday_n = sum( + 1 for r in history + if _parse_dt(r.get("created_at")) + and two_days_ago < _parse_dt(r["created_at"]) <= yesterday + ) + delta_sign = "+" if posts_today >= posts_yesterday_n else "" + delta_text = f"{delta_sign}{posts_today - posts_yesterday_n} vs yesterday" + + # WHY compare batch vs prior history instead of today vs yesterday: + # All posts in the DB were fetched today, so a day-based comparison + # always yields delta=0. Comparing the current batch toxic rate against + # everything fetched before it gives a real, meaningful signal — + # "is this fetch more toxic than usual?" + batch_toxic = sum(1 for r in batch_rows if (r.get("label") or "").lower() == "toxic") + batch_rate = (batch_toxic / len(batch_rows) * 100.0) if batch_rows else None + + prior_rows = [r for r in history if r not in batch_rows] + prior_toxic = sum(1 for r in prior_rows if (r.get("label") or "").lower() == "toxic") + prior_rate = (prior_toxic / len(prior_rows) * 100.0) if prior_rows else None + + if batch_rate is not None and prior_rate is not None: + rate_delta = batch_rate - prior_rate + delta_color = "#ff4b4b" if rate_delta > 0 else "#2ecc71" + delta_arrow = "↑" if rate_delta > 0 else "↓" + rate_delta_str = f"{delta_arrow} {abs(rate_delta):.1f}% vs prior" + elif batch_rate is not None: + rate_delta_str = f"batch: {batch_rate:.1f}%" + delta_color = "#ff4b4b" if batch_rate >= 70 else "#ff8c42" if batch_rate >= 40 else "#2ecc71" + else: + rate_delta_str = "fetch to see delta" + delta_color = "#5a6080" + + c1, c2, c3, c4 = st.columns(4) + with c1: + st.markdown(f"""
+
Posts analyzed
+
{total_posts}
+
+{posts_today} today · {delta_text}
+
""", unsafe_allow_html=True) + with c2: + st.markdown(f"""
+
Toxic rate
+
{toxic_rate:.1f}%
+
{rate_delta_str}
+
""", unsafe_allow_html=True) + with c3: + st.markdown(f"""
+
Avg score (last batch)
+
{avg_score:.3f}
+
Mean toxicity from last fetch
+
""", unsafe_allow_html=True) + with c4: + st.markdown(f"""
+
Top term
+
{escape(str(top_term))}
+
Most frequent in last batch
+
""", unsafe_allow_html=True) + + st.markdown("
", unsafe_allow_html=True) + + # ── Toxicity over time + Score distribution ──────────────────────────── + ts_col, hist_col = st.columns([3, 1]) + + with ts_col: + st.markdown("#### Toxicity over time") + buckets: dict = defaultdict(lambda: {"sum": 0.0, "count": 0}) + for row in history: + dt = _parse_dt(row.get("created_at")) + if not dt: + continue + hour = dt.replace(minute=0, second=0, microsecond=0) + score = float(row.get("score", 0) or 0) + buckets[hour]["sum"] += score + buckets[hour]["count"] += 1 + + if buckets: + # WHY sort by datetime key not string label: hours like "01:00" + # sort alphabetically before "03:00" so without proper datetime + # sorting the line jumps backwards across midnight, creating a + # visual zigzag. Sorting by the actual datetime object fixes this. + # WHY sort by datetime key: ensures chronological order across + # midnight. Using datetime objects (not strings) as the x-axis + # means Plotly treats it as a real time axis — no more zigzag + # when data spans midnight (e.g. 22:00 → 01:00 → 14:00). + # WHY fixed 24 buckets: previous approach only showed hours + # with data, causing uneven spacing and duplicate labels when + # data spanned midnight. A fixed 0-23 hour axis is always + # uniform — empty hours show as gaps in the line (connectgaps=False). + hour_avgs = {} + for dt_key, val in buckets.items(): + h = dt_key.hour + # If same hour appears on multiple days, keep the most recent + if h not in hour_avgs or dt_key > max( + k for k in buckets if k.hour == h + ): + hour_avgs[h] = val["sum"] / val["count"] + + x_labels = [f"{h:02d}:00" for h in range(24)] + y_vals = [hour_avgs.get(h, None) for h in range(24)] + point_colors = [ + "#2ecc71" if v is not None and v < 0.4 + else "#ff8c42" if v is not None and v < 0.7 + else "#ff4b4b" if v is not None + else "rgba(0,0,0,0)" + for v in y_vals + ] + + fig = go.Figure() + fig.add_trace(go.Scatter( + x=x_labels, + y=y_vals, + mode="lines+markers", + connectgaps=True, + line=dict(color="#ff4b4b", width=2, shape="spline"), + fill="tozeroy", + fillcolor="rgba(255,75,75,0.07)", + marker=dict( + color=point_colors, + size=8, + line=dict(color="#0a0d14", width=1.5), + ), + hovertemplate="%{x}
Avg score: %{y:.3f}", + showlegend=False, + )) + fig.update_layout( + showlegend=False, + margin=dict(l=10, r=10, t=10, b=10), + paper_bgcolor="#0a0d14", + plot_bgcolor="#0a0d14", + height=220, + ) + fig.update_xaxes( + showgrid=False, color="#5a6080", tickfont_size=10, + tickmode="array", + tickvals=[f"{h:02d}:00" for h in range(0, 24, 3)], + ticktext=[f"{h:02d}:00" for h in range(0, 24, 3)], + ) + fig.update_yaxes(range=[0, 1], showgrid=True, + gridcolor="#1e2540", color="#5a6080", tickfont_size=10) + st.plotly_chart(fig, use_container_width=True) + else: + st.info("No time series data yet. Fetch posts first.") + + with hist_col: + st.markdown("#### Score distribution") + scores = [float(r.get("score", 0) or 0) for r in batch_rows] + if scores: + bin_labels = ["0-0.2", "0.2-0.4", "0.4-0.6", "0.6-0.8", "0.8-1.0"] + bin_styles = [ + ("rgba(46,204,113,0.15)", "#2ecc71"), + ("rgba(163,230,53,0.15)", "#a3e635"), + ("rgba(255,235,59,0.15)", "#ffeb3b"), + ("rgba(255,140,66,0.15)", "#ff8c42"), + ("rgba(255,75,75,0.18)", "#ff4b4b"), + ] + bins = [0.0, 0.2, 0.4, 0.6, 0.8, 1.01] + counts = [0] * 5 + for s in scores: + for i in range(5): + if bins[i] <= s < bins[i + 1]: + counts[i] += 1 + break + fig2 = go.Figure() + for label, count, (fill, line) in zip(bin_labels, counts, bin_styles): + fig2.add_trace(go.Bar( + x=[label], y=[count], + marker_color=fill, + marker_line_color=line, + marker_line_width=1.5, + hovertemplate=f"{label}: {count} posts", + showlegend=False, + )) + fig2.update_layout( + showlegend=False, + margin=dict(l=5, r=5, t=10, b=10), + paper_bgcolor="#0a0d14", + plot_bgcolor="#0a0d14", + height=220, bargap=0.2, + ) + fig2.update_xaxes(showgrid=False, color="#5a6080", tickfont_size=9) + fig2.update_yaxes(showgrid=True, gridcolor="#1e2540", color="#5a6080", tickfont_size=9) + st.plotly_chart(fig2, use_container_width=True) + else: + st.info("Fetch posts to see score distribution.") + + # ── Activity heatmap ─────────────────────────────────────────────────── + st.markdown("#### Activity heatmap") + # WHY heatmap: reveals temporal patterns in toxic speech — e.g. spikes + # on weekends or late night — that raw numbers can't show. + # GitHub-style contribution grid is immediately readable to developers. + hm: dict = defaultdict(lambda: {"sum": 0.0, "count": 0}) + for row in history: + dt = _parse_dt(row.get("created_at")) + if not dt: + continue + hm[(dt.weekday(), dt.hour)]["sum"] += float(row.get("score", 0) or 0) + hm[(dt.weekday(), dt.hour)]["count"] += 1 + + if hm: + max_avg = max( + (v["sum"] / v["count"] for v in hm.values() if v["count"]), default=1.0 + ) or 1.0 + + days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] + rows_html = ["
"] + + # Hour labels row + hour_labels = "
" + hour_labels += "
" + for h in range(24): + label = str(h) if h % 6 == 0 else "" + hour_labels += f"
{label}
" + hour_labels += "
" + rows_html.append(hour_labels) + + for d_idx, day in enumerate(days): + row_html = "
" + row_html += f"
{day}
" + for h in range(24): + cell = hm.get((d_idx, h)) + if cell and cell["count"]: + intensity = (cell["sum"] / cell["count"]) / max_avg + r_val = int(30 + (255 - 30) * intensity) + g_val = int(37 + (75 - 37) * intensity) + b_val = int(64 + (75 - 64) * intensity) + bg = f"rgb({r_val},{g_val},{b_val})" + title = f"{day} {h:02d}:00 — avg {cell['sum']/cell['count']:.2f}" + else: + bg = "#1a1d2e" + title = f"{day} {h:02d}:00 — no data" + row_html += f"
" + row_html += "
" + rows_html.append(row_html) + + rows_html.append("
") + rows_html.append(""" +
+ Low +
+ High toxicity +
""") + + st.markdown("".join(rows_html), unsafe_allow_html=True) + else: + st.info("Not enough data for heatmap yet.") + + st.markdown("
", unsafe_allow_html=True) + + # ── Recent posts ─────────────────────────────────────────────────────── + st.markdown("#### Recent posts (last batch)") + if batch_rows: + cards_html = ["
"] + for row in batch_rows[:20]: + text = (row.get("text") or "").strip() + score = float(row.get("score", 0) or 0) + sc = "score-high" if score >= 0.7 else ("score-mid" if score >= 0.4 else "score-low") + # WHY prefer stored query_term over vocab search: + # query_term is set at fetch time so it's always accurate. + # vocab search is a fallback for older rows that predate this field. + matched = row.get("query_term") or next((t for t in vocab if t and t in text.lower()), "—") + cards_html.append(f"""
+
{score:.3f}
+
{escape(text[:110])}
+
{escape(matched)}
+
""") + cards_html.append("
") + st.markdown("".join(cards_html), unsafe_allow_html=True) + else: + st.info("Click 'Fetch & Analyze' to see recent posts.") + + with graph_tab: + # Graph controls live inside the tab. + # WHY: we use a session_state trigger flag so the button click stores + # intent in session_state. On the NEXT rerun (which re-renders the tab + # we are already on), the flag is read and graph is built. This avoids + # the tab-reset issue caused by st.button triggering a full rerun. + info_col, ctrl_col = st.columns([3, 1]) + with info_col: + st.markdown("""
+
How to read this graph
+
+ Words that frequently appear together in algospeak posts are connected. + Node size = frequency  |  + red >70% toxic   + orange 40-70% mixed   + green <40% benign +
+
""", unsafe_allow_html=True) + with ctrl_col: + min_cooccurrence = st.slider("Min co-occurrences", 2, 10, + st.session_state.last_min_cooc, key="graph_cooc_slider") + _ = st.checkbox("Toxic posts only", value=False, key="toxic_only_graph") + if st.button("Build Graph", use_container_width=True, key="build_graph_btn"): + st.session_state.build_graph_trigger = True + st.session_state.build_graph_cooc = int(min_cooccurrence) + + if st.session_state.get("build_graph_trigger"): + st.session_state.build_graph_trigger = False + cooc = st.session_state.get("build_graph_cooc", 3) + with st.spinner("Building co-occurrence graph..."): + graph = build_cooccurrence_graph(min_cooccurrence=cooc) + st.session_state.last_min_cooc = cooc + if graph.number_of_nodes() == 0: + st.session_state.graph_html = None + st.warning("Not enough data. Fetch more posts first.") + else: + st.session_state.graph_html = graph_to_pyvis( + graph, toxic_only=st.session_state.get("toxic_only_graph", False) + ) + + if st.session_state.graph_html: + st.components.v1.html(st.session_state.graph_html, height=620, scrolling=True) + else: + st.info("Adjust settings above and click 'Build Graph' to visualize word co-occurrences.") + + # ── Term comparison tab ──────────────────────────────────────────────────── + with compare_tab: + st.markdown("#### Compare two terms side by side") + st.markdown("
Select two algospeak terms to compare their toxicity profiles from all stored posts.
", unsafe_allow_html=True) + + all_opts = list(ALGOSPEAK_QUERIES) + [t for t in st.session_state.custom_terms if t not in ALGOSPEAK_QUERIES] + col_a, col_b = st.columns(2) + with col_a: + term_a = st.selectbox("Term A", all_opts, index=0, key="compare_a") + with col_b: + term_b = st.selectbox("Term B", all_opts, index=min(1, len(all_opts)-1), key="compare_b") + + if term_a == term_b: + st.warning("Select two different terms to compare.") + else: + # WHY: filter history per term, then compute the same metrics used in + # Overview so the numbers are directly comparable side by side. + def term_stats(term): + posts = [r for r in history if term in (r.get("text") or "").lower()] + if not posts: + return None + scores = [float(r.get("score", 0) or 0) for r in posts] + toxic_n = sum(1 for r in posts if (r.get("label") or "").lower() == "toxic") + return { + "count": len(posts), + "toxic_rate": toxic_n / len(posts) * 100, + "avg_score": sum(scores) / len(scores), + "max_score": max(scores), + "posts": posts, + } + + stats_a = term_stats(term_a) + stats_b = term_stats(term_b) + + if not stats_a or not stats_b: + st.info("Not enough data for one or both terms. Fetch more posts first.") + else: + # Metric cards + ca, cb = st.columns(2) + for col, term, stats in [(ca, term_a, stats_a), (cb, term_b, stats_b)]: + color = "#ff4b4b" if stats["toxic_rate"] >= 70 else ("#ff8c42" if stats["toxic_rate"] >= 40 else "#2ecc71") + with col: + st.markdown(f"""
+
"{term}"
+
+
Posts
+
{stats["count"]}
+
Toxic rate
+
{stats["toxic_rate"]:.1f}%
+
Avg score
+
{stats["avg_score"]:.3f}
+
Max score
+
{stats["max_score"]:.3f}
+
""", unsafe_allow_html=True) + + # Side by side score distribution + st.markdown("#### Score distribution comparison") + bins = [0.0, 0.2, 0.4, 0.6, 0.8, 1.01] + bin_labels = ["0-0.2", "0.2-0.4", "0.4-0.6", "0.6-0.8", "0.8-1.0"] + + def bin_scores(posts): + counts = [0] * 5 + for r in posts: + s = float(r.get("score", 0) or 0) + for i in range(5): + if bins[i] <= s < bins[i+1]: + counts[i] += 1 + break + return counts + + counts_a = bin_scores(stats_a["posts"]) + counts_b = bin_scores(stats_b["posts"]) + + fig_cmp = go.Figure() + fig_cmp.add_trace(go.Bar(name=term_a, x=bin_labels, y=counts_a, + marker_color="rgba(166,176,255,0.2)", marker_line_color="#a6b0ff", marker_line_width=1.5)) + fig_cmp.add_trace(go.Bar(name=term_b, x=bin_labels, y=counts_b, + marker_color="rgba(255,140,66,0.2)", marker_line_color="#ff8c42", marker_line_width=1.5)) + fig_cmp.update_layout( + barmode="group", paper_bgcolor="#0a0d14", plot_bgcolor="#0a0d14", + legend=dict(font=dict(color="#e8eaf0")), + margin=dict(l=10, r=10, t=10, b=10), height=260, + ) + fig_cmp.update_xaxes(showgrid=False, color="#5a6080") + fig_cmp.update_yaxes(showgrid=True, gridcolor="#1e2540", color="#5a6080") + st.plotly_chart(fig_cmp, use_container_width=True) + + # ── Export tab ───────────────────────────────────────────────────────────── + with export_tab: + st.markdown("#### Export data") + st.markdown("
Download posts from the current session or the full database history.
", unsafe_allow_html=True) + + export_source = st.radio("Data source", ["Last batch (current fetch)", "Full history (all stored posts)"], + horizontal=True, key="export_source") + export_rows = batch_rows if export_source.startswith("Last") else history + + if not export_rows: + st.info("No data to export yet. Fetch posts first.") + else: + import csv + import io + import json as _json + + # WHY io.StringIO: lets us build the CSV entirely in memory without + # writing to disk — important for a stateless server environment like + # HuggingFace Spaces where you don't control the filesystem. + csv_buf = io.StringIO() + writer = csv.DictWriter(csv_buf, fieldnames=["text", "label", "score", "query_term", "created_at"]) + writer.writeheader() + for r in export_rows: + writer.writerow({ + "text": (r.get("text") or "").replace("\n", " "), + "label": r.get("label", ""), + "score": f'{float(r.get("score", 0) or 0):.4f}', + "query_term": r.get("query_term", ""), + "created_at": r.get("created_at", ""), + }) + + json_buf = _json.dumps(export_rows, ensure_ascii=False, indent=2, default=str) + + st.markdown(f"
{len(export_rows)} posts ready to export
", unsafe_allow_html=True) + + dl_csv, dl_json = st.columns(2) + with dl_csv: + st.download_button( + label="Download CSV", + data=csv_buf.getvalue(), + file_name="algoscope_export.csv", + mime="text/csv", + use_container_width=True, + ) + with dl_json: + st.download_button( + label="Download JSON", + data=json_buf, + file_name="algoscope_export.json", + mime="application/json", + use_container_width=True, + ) + + # Preview table + st.markdown("#### Preview (first 10 rows)") + preview_html = ["
", + "", + "", + "", + "", + ""] + for r in export_rows[:10]: + score = float(r.get("score", 0) or 0) + sc = "score-high" if score >= 0.7 else ("score-mid" if score >= 0.4 else "score-low") + preview_html.append(f""" + + + + + """) + preview_html.append("
ScoreLabelTermText
{score:.3f}{escape(r.get("label",""))}{escape(r.get("query_term",""))}{escape((r.get("text","") or "")[:80])}
") + st.markdown("".join(preview_html), unsafe_allow_html=True) + + +if __name__ == "__main__": + main() diff --git a/frontend/.env.development b/frontend/.env.development new file mode 100644 index 0000000000000000000000000000000000000000..82c4bd3fcfd89cacdd640d6aa8c019e98ba87ff8 --- /dev/null +++ b/frontend/.env.development @@ -0,0 +1,4 @@ +# WHY empty: in production, the frontend is served by the same FastAPI server +# on port 7860. Empty VITE_API_URL means fetch() uses relative URLs (/posts, +# /fetch-and-analyze etc.) which automatically go to the correct host. +VITE_API_URL= diff --git a/frontend/.env.production b/frontend/.env.production new file mode 100644 index 0000000000000000000000000000000000000000..9515bec60238e78a7a11d1c9b685af1b94b20c4f --- /dev/null +++ b/frontend/.env.production @@ -0,0 +1,4 @@ +# WHY empty: in production, React is served by the same FastAPI process. +# All fetch() calls use a relative URL (e.g. /posts), which resolves to +# the same origin — no CORS, no separate server address needed. +VITE_API_URL= diff --git a/frontend/dist/assets/index-CZMDWpNf.js b/frontend/dist/assets/index-CZMDWpNf.js new file mode 100644 index 0000000000000000000000000000000000000000..c6be7a97e199a891663259f42c52062d19431bf2 --- /dev/null +++ b/frontend/dist/assets/index-CZMDWpNf.js @@ -0,0 +1,111 @@ +(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))a(u);new MutationObserver(u=>{for(const d of u)if(d.type==="childList")for(const f of d.addedNodes)f.tagName==="LINK"&&f.rel==="modulepreload"&&a(f)}).observe(document,{childList:!0,subtree:!0});function o(u){const d={};return u.integrity&&(d.integrity=u.integrity),u.referrerPolicy&&(d.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?d.credentials="include":u.crossOrigin==="anonymous"?d.credentials="omit":d.credentials="same-origin",d}function a(u){if(u.ep)return;u.ep=!0;const d=o(u);fetch(u.href,d)}})();var fl={exports:{}},ci={},dl={exports:{}},de={};/** + * @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 Ld;function Zg(){if(Ld)return de;Ld=1;var n=Symbol.for("react.element"),r=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),u=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),f=Symbol.for("react.context"),h=Symbol.for("react.forward_ref"),g=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),m=Symbol.for("react.lazy"),x=Symbol.iterator;function w(P){return P===null||typeof P!="object"?null:(P=x&&P[x]||P["@@iterator"],typeof P=="function"?P:null)}var k={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},E=Object.assign,j={};function D(P,O,fe){this.props=P,this.context=O,this.refs=j,this.updater=fe||k}D.prototype.isReactComponent={},D.prototype.setState=function(P,O){if(typeof P!="object"&&typeof P!="function"&&P!=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,P,O,"setState")},D.prototype.forceUpdate=function(P){this.updater.enqueueForceUpdate(this,P,"forceUpdate")};function _(){}_.prototype=D.prototype;function b(P,O,fe){this.props=P,this.context=O,this.refs=j,this.updater=fe||k}var z=b.prototype=new _;z.constructor=b,E(z,D.prototype),z.isPureReactComponent=!0;var F=Array.isArray,M=Object.prototype.hasOwnProperty,H={current:null},I={key:!0,ref:!0,__self:!0,__source:!0};function N(P,O,fe){var pe,ye={},ve=null,Ce=null;if(O!=null)for(pe in O.ref!==void 0&&(Ce=O.ref),O.key!==void 0&&(ve=""+O.key),O)M.call(O,pe)&&!I.hasOwnProperty(pe)&&(ye[pe]=O[pe]);var Se=arguments.length-2;if(Se===1)ye.children=fe;else if(1>>1,O=U[P];if(0>>1;Pu(ye,X))veu(Ce,ye)?(U[P]=Ce,U[ve]=X,P=ve):(U[P]=ye,U[pe]=X,P=pe);else if(veu(Ce,X))U[P]=Ce,U[ve]=X,P=ve;else break e}}return Z}function u(U,Z){var X=U.sortIndex-Z.sortIndex;return X!==0?X:U.id-Z.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;n.unstable_now=function(){return d.now()}}else{var f=Date,h=f.now();n.unstable_now=function(){return f.now()-h}}var g=[],y=[],m=1,x=null,w=3,k=!1,E=!1,j=!1,D=typeof setTimeout=="function"?setTimeout:null,_=typeof clearTimeout=="function"?clearTimeout:null,b=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function z(U){for(var Z=o(y);Z!==null;){if(Z.callback===null)a(y);else if(Z.startTime<=U)a(y),Z.sortIndex=Z.expirationTime,r(g,Z);else break;Z=o(y)}}function F(U){if(j=!1,z(U),!E)if(o(g)!==null)E=!0,he(M);else{var Z=o(y);Z!==null&&le(F,Z.startTime-U)}}function M(U,Z){E=!1,j&&(j=!1,_(N),N=-1),k=!0;var X=w;try{for(z(Z),x=o(g);x!==null&&(!(x.expirationTime>Z)||U&&!se());){var P=x.callback;if(typeof P=="function"){x.callback=null,w=x.priorityLevel;var O=P(x.expirationTime<=Z);Z=n.unstable_now(),typeof O=="function"?x.callback=O:x===o(g)&&a(g),z(Z)}else a(g);x=o(g)}if(x!==null)var fe=!0;else{var pe=o(y);pe!==null&&le(F,pe.startTime-Z),fe=!1}return fe}finally{x=null,w=X,k=!1}}var H=!1,I=null,N=-1,Y=5,G=-1;function se(){return!(n.unstable_now()-GU||125P?(U.sortIndex=X,r(y,U),o(g)===null&&U===o(y)&&(j?(_(N),N=-1):j=!0,le(F,X-P))):(U.sortIndex=O,r(g,U),E||k||(E=!0,he(M))),U},n.unstable_shouldYield=se,n.unstable_wrapCallback=function(U){var Z=w;return function(){var X=w;w=Z;try{return U.apply(this,arguments)}finally{w=X}}}})(ml)),ml}var zd;function ny(){return zd||(zd=1,pl.exports=ty()),pl.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 Fd;function ry(){if(Fd)return lt;Fd=1;var n=au(),r=ny();function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,i=1;i"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),g=Object.prototype.hasOwnProperty,y=/^[: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]*$/,m={},x={};function w(e){return g.call(x,e)?!0:g.call(m,e)?!1:y.test(e)?x[e]=!0:(m[e]=!0,!1)}function k(e,t,i,s){if(i!==null&&i.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return s?!1:i!==null?!i.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function E(e,t,i,s){if(t===null||typeof t>"u"||k(e,t,i,s))return!0;if(s)return!1;if(i!==null)switch(i.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function j(e,t,i,s,l,c,p){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=s,this.attributeNamespace=l,this.mustUseProperty=i,this.propertyName=e,this.type=t,this.sanitizeURL=c,this.removeEmptyString=p}var D={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){D[e]=new j(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];D[t]=new j(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){D[e]=new j(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){D[e]=new j(e,2,!1,e,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(e){D[e]=new j(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){D[e]=new j(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){D[e]=new j(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){D[e]=new j(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){D[e]=new j(e,5,!1,e.toLowerCase(),null,!1,!1)});var _=/[\-:]([a-z])/g;function b(e){return e[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(e){var t=e.replace(_,b);D[t]=new j(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(_,b);D[t]=new j(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(_,b);D[t]=new j(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){D[e]=new j(e,1,!1,e.toLowerCase(),null,!1,!1)}),D.xlinkHref=new j("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){D[e]=new j(e,1,!1,e.toLowerCase(),null,!0,!0)});function z(e,t,i,s){var l=D.hasOwnProperty(t)?D[t]:null;(l!==null?l.type!==0:s||!(2S||l[p]!==c[S]){var T=` +`+l[p].replace(" at new "," at ");return e.displayName&&T.includes("")&&(T=T.replace("",e.displayName)),T}while(1<=p&&0<=S);break}}}finally{fe=!1,Error.prepareStackTrace=i}return(e=e?e.displayName||e.name:"")?O(e):""}function ye(e){switch(e.tag){case 5:return O(e.type);case 16:return O("Lazy");case 13:return O("Suspense");case 19:return O("SuspenseList");case 0:case 2:case 15:return e=pe(e.type,!1),e;case 11:return e=pe(e.type.render,!1),e;case 1:return e=pe(e.type,!0),e;default:return""}}function ve(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case I:return"Fragment";case H:return"Portal";case Y:return"Profiler";case N:return"StrictMode";case ge:return"Suspense";case we:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case se:return(e.displayName||"Context")+".Consumer";case G:return(e._context.displayName||"Context")+".Provider";case ue:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case ce:return t=e.displayName||null,t!==null?t:ve(e.type)||"Memo";case he:t=e._payload,e=e._init;try{return ve(e(t))}catch{}}return null}function Ce(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ve(t);case 8:return t===N?"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 t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function Se(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function Me(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function ut(e){var t=Me(e)?"checked":"value",i=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),s=""+e[t];if(!e.hasOwnProperty(t)&&typeof i<"u"&&typeof i.get=="function"&&typeof i.set=="function"){var l=i.get,c=i.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return l.call(this)},set:function(p){s=""+p,c.call(this,p)}}),Object.defineProperty(e,t,{enumerable:i.enumerable}),{getValue:function(){return s},setValue:function(p){s=""+p},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Mi(e){e._valueTracker||(e._valueTracker=ut(e))}function zu(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var i=t.getValue(),s="";return e&&(s=Me(e)?e.checked?"true":"false":e.value),e=s,e!==i?(t.setValue(e),!0):!1}function Ai(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function vs(e,t){var i=t.checked;return X({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:i??e._wrapperState.initialChecked})}function Fu(e,t){var i=t.defaultValue==null?"":t.defaultValue,s=t.checked!=null?t.checked:t.defaultChecked;i=Se(t.value!=null?t.value:i),e._wrapperState={initialChecked:s,initialValue:i,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function Nu(e,t){t=t.checked,t!=null&&z(e,"checked",t,!1)}function xs(e,t){Nu(e,t);var i=Se(t.value),s=t.type;if(i!=null)s==="number"?(i===0&&e.value===""||e.value!=i)&&(e.value=""+i):e.value!==""+i&&(e.value=""+i);else if(s==="submit"||s==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?ws(e,t.type,i):t.hasOwnProperty("defaultValue")&&ws(e,t.type,Se(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function Ou(e,t,i){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var s=t.type;if(!(s!=="submit"&&s!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,i||t===e.value||(e.value=t),e.defaultValue=t}i=e.name,i!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,i!==""&&(e.name=i)}function ws(e,t,i){(t!=="number"||Ai(e.ownerDocument)!==e)&&(i==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+i&&(e.defaultValue=""+i))}var Er=Array.isArray;function Un(e,t,i,s){if(e=e.options,t){t={};for(var l=0;l"+t.valueOf().toString()+"",t=Di.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function Pr(e,t){if(t){var i=e.firstChild;if(i&&i===e.lastChild&&i.nodeType===3){i.nodeValue=t;return}}e.textContent=t}var jr={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},n0=["Webkit","ms","Moz","O"];Object.keys(jr).forEach(function(e){n0.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),jr[t]=jr[e]})});function Ku(e,t,i){return t==null||typeof t=="boolean"||t===""?"":i||typeof t!="number"||t===0||jr.hasOwnProperty(e)&&jr[e]?(""+t).trim():t+"px"}function Gu(e,t){e=e.style;for(var i in t)if(t.hasOwnProperty(i)){var s=i.indexOf("--")===0,l=Ku(i,t[i],s);i==="float"&&(i="cssFloat"),s?e.setProperty(i,l):e[i]=l}}var r0=X({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 Ts(e,t){if(t){if(r0[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(o(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(o(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(o(61))}if(t.style!=null&&typeof t.style!="object")throw Error(o(62))}}function Cs(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){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 Es=null;function Ps(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var js=null,$n=null,Hn=null;function Yu(e){if(e=Xr(e)){if(typeof js!="function")throw Error(o(280));var t=e.stateNode;t&&(t=eo(t),js(e.stateNode,e.type,t))}}function Xu(e){$n?Hn?Hn.push(e):Hn=[e]:$n=e}function Qu(){if($n){var e=$n,t=Hn;if(Hn=$n=null,Yu(e),t)for(e=0;e>>=0,e===0?32:31-(p0(e)/m0|0)|0}var Ii=64,zi=4194304;function Dr(e){switch(e&-e){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 e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function Fi(e,t){var i=e.pendingLanes;if(i===0)return 0;var s=0,l=e.suspendedLanes,c=e.pingedLanes,p=i&268435455;if(p!==0){var S=p&~l;S!==0?s=Dr(S):(c&=p,c!==0&&(s=Dr(c)))}else p=i&~l,p!==0?s=Dr(p):c!==0&&(s=Dr(c));if(s===0)return 0;if(t!==0&&t!==s&&(t&l)===0&&(l=s&-s,c=t&-t,l>=c||l===16&&(c&4194240)!==0))return t;if((s&4)!==0&&(s|=i&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=s;0i;i++)t.push(e);return t}function Lr(e,t,i){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Tt(t),e[t]=i}function x0(e,t){var i=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var s=e.eventTimes;for(e=e.expirationTimes;0=Or),Cc=" ",Ec=!1;function Pc(e,t){switch(e){case"keyup":return G0.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function jc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Yn=!1;function X0(e,t){switch(e){case"compositionend":return jc(t);case"keypress":return t.which!==32?null:(Ec=!0,Cc);case"textInput":return e=t.data,e===Cc&&Ec?null:e;default:return null}}function Q0(e,t){if(Yn)return e==="compositionend"||!Hs&&Pc(e,t)?(e=vc(),Ui=Ns=tn=null,Yn=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:i,offset:t-e};e=s}e:{for(;i;){if(i.nextSibling){i=i.nextSibling;break e}i=i.parentNode}i=void 0}i=bc(i)}}function Ic(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Ic(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function zc(){for(var e=window,t=Ai();t instanceof e.HTMLIFrameElement;){try{var i=typeof t.contentWindow.location.href=="string"}catch{i=!1}if(i)e=t.contentWindow;else break;t=Ai(e.document)}return t}function Ys(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function og(e){var t=zc(),i=e.focusedElem,s=e.selectionRange;if(t!==i&&i&&i.ownerDocument&&Ic(i.ownerDocument.documentElement,i)){if(s!==null&&Ys(i)){if(t=s.start,e=s.end,e===void 0&&(e=t),"selectionStart"in i)i.selectionStart=t,i.selectionEnd=Math.min(e,i.value.length);else if(e=(t=i.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var l=i.textContent.length,c=Math.min(s.start,l);s=s.end===void 0?c:Math.min(s.end,l),!e.extend&&c>s&&(l=s,s=c,c=l),l=_c(i,c);var p=_c(i,s);l&&p&&(e.rangeCount!==1||e.anchorNode!==l.node||e.anchorOffset!==l.offset||e.focusNode!==p.node||e.focusOffset!==p.offset)&&(t=t.createRange(),t.setStart(l.node,l.offset),e.removeAllRanges(),c>s?(e.addRange(t),e.extend(p.node,p.offset)):(t.setEnd(p.node,p.offset),e.addRange(t)))}}for(t=[],e=i;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof i.focus=="function"&&i.focus(),i=0;i=document.documentMode,Xn=null,Xs=null,$r=null,Qs=!1;function Fc(e,t,i){var s=i.window===i?i.document:i.nodeType===9?i:i.ownerDocument;Qs||Xn==null||Xn!==Ai(s)||(s=Xn,"selectionStart"in s&&Ys(s)?s={start:s.selectionStart,end:s.selectionEnd}:(s=(s.ownerDocument&&s.ownerDocument.defaultView||window).getSelection(),s={anchorNode:s.anchorNode,anchorOffset:s.anchorOffset,focusNode:s.focusNode,focusOffset:s.focusOffset}),$r&&Ur($r,s)||($r=s,s=qi(Xs,"onSelect"),0er||(e.current=la[er],la[er]=null,er--)}function Ee(e,t){er++,la[er]=e.current,e.current=t}var sn={},Xe=on(sn),rt=on(!1),Pn=sn;function tr(e,t){var i=e.type.contextTypes;if(!i)return sn;var s=e.stateNode;if(s&&s.__reactInternalMemoizedUnmaskedChildContext===t)return s.__reactInternalMemoizedMaskedChildContext;var l={},c;for(c in i)l[c]=t[c];return s&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=l),l}function it(e){return e=e.childContextTypes,e!=null}function to(){je(rt),je(Xe)}function Jc(e,t,i){if(Xe.current!==sn)throw Error(o(168));Ee(Xe,t),Ee(rt,i)}function ef(e,t,i){var s=e.stateNode;if(t=t.childContextTypes,typeof s.getChildContext!="function")return i;s=s.getChildContext();for(var l in s)if(!(l in t))throw Error(o(108,Ce(e)||"Unknown",l));return X({},i,s)}function no(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||sn,Pn=Xe.current,Ee(Xe,e),Ee(rt,rt.current),!0}function tf(e,t,i){var s=e.stateNode;if(!s)throw Error(o(169));i?(e=ef(e,t,Pn),s.__reactInternalMemoizedMergedChildContext=e,je(rt),je(Xe),Ee(Xe,e)):je(rt),Ee(rt,i)}var $t=null,ro=!1,ua=!1;function nf(e){$t===null?$t=[e]:$t.push(e)}function yg(e){ro=!0,nf(e)}function an(){if(!ua&&$t!==null){ua=!0;var e=0,t=ke;try{var i=$t;for(ke=1;e>=p,l-=p,Ht=1<<32-Tt(t)+l|i<ae?($e=ie,ie=null):$e=ie.sibling;var xe=B(R,ie,A[ae],K);if(xe===null){ie===null&&(ie=$e);break}e&&ie&&xe.alternate===null&&t(R,ie),C=c(xe,C,ae),re===null?ne=xe:re.sibling=xe,re=xe,ie=$e}if(ae===A.length)return i(R,ie),Ae&&Rn(R,ae),ne;if(ie===null){for(;aeae?($e=ie,ie=null):$e=ie.sibling;var gn=B(R,ie,xe.value,K);if(gn===null){ie===null&&(ie=$e);break}e&&ie&&gn.alternate===null&&t(R,ie),C=c(gn,C,ae),re===null?ne=gn:re.sibling=gn,re=gn,ie=$e}if(xe.done)return i(R,ie),Ae&&Rn(R,ae),ne;if(ie===null){for(;!xe.done;ae++,xe=A.next())xe=$(R,xe.value,K),xe!==null&&(C=c(xe,C,ae),re===null?ne=xe:re.sibling=xe,re=xe);return Ae&&Rn(R,ae),ne}for(ie=s(R,ie);!xe.done;ae++,xe=A.next())xe=Q(ie,R,ae,xe.value,K),xe!==null&&(e&&xe.alternate!==null&&ie.delete(xe.key===null?ae:xe.key),C=c(xe,C,ae),re===null?ne=xe:re.sibling=xe,re=xe);return e&&ie.forEach(function(qg){return t(R,qg)}),Ae&&Rn(R,ae),ne}function Ie(R,C,A,K){if(typeof A=="object"&&A!==null&&A.type===I&&A.key===null&&(A=A.props.children),typeof A=="object"&&A!==null){switch(A.$$typeof){case M:e:{for(var ne=A.key,re=C;re!==null;){if(re.key===ne){if(ne=A.type,ne===I){if(re.tag===7){i(R,re.sibling),C=l(re,A.props.children),C.return=R,R=C;break e}}else if(re.elementType===ne||typeof ne=="object"&&ne!==null&&ne.$$typeof===he&&uf(ne)===re.type){i(R,re.sibling),C=l(re,A.props),C.ref=Qr(R,re,A),C.return=R,R=C;break e}i(R,re);break}else t(R,re);re=re.sibling}A.type===I?(C=In(A.props.children,R.mode,K,A.key),C.return=R,R=C):(K=Lo(A.type,A.key,A.props,null,R.mode,K),K.ref=Qr(R,C,A),K.return=R,R=K)}return p(R);case H:e:{for(re=A.key;C!==null;){if(C.key===re)if(C.tag===4&&C.stateNode.containerInfo===A.containerInfo&&C.stateNode.implementation===A.implementation){i(R,C.sibling),C=l(C,A.children||[]),C.return=R,R=C;break e}else{i(R,C);break}else t(R,C);C=C.sibling}C=sl(A,R.mode,K),C.return=R,R=C}return p(R);case he:return re=A._init,Ie(R,C,re(A._payload),K)}if(Er(A))return J(R,C,A,K);if(Z(A))return te(R,C,A,K);ao(R,A)}return typeof A=="string"&&A!==""||typeof A=="number"?(A=""+A,C!==null&&C.tag===6?(i(R,C.sibling),C=l(C,A),C.return=R,R=C):(i(R,C),C=ol(A,R.mode,K),C.return=R,R=C),p(R)):i(R,C)}return Ie}var or=cf(!0),ff=cf(!1),lo=on(null),uo=null,sr=null,ma=null;function ga(){ma=sr=uo=null}function ya(e){var t=lo.current;je(lo),e._currentValue=t}function va(e,t,i){for(;e!==null;){var s=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,s!==null&&(s.childLanes|=t)):s!==null&&(s.childLanes&t)!==t&&(s.childLanes|=t),e===i)break;e=e.return}}function ar(e,t){uo=e,ma=sr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(ot=!0),e.firstContext=null)}function yt(e){var t=e._currentValue;if(ma!==e)if(e={context:e,memoizedValue:t,next:null},sr===null){if(uo===null)throw Error(o(308));sr=e,uo.dependencies={lanes:0,firstContext:e}}else sr=sr.next=e;return t}var Mn=null;function xa(e){Mn===null?Mn=[e]:Mn.push(e)}function df(e,t,i,s){var l=t.interleaved;return l===null?(i.next=i,xa(t)):(i.next=l.next,l.next=i),t.interleaved=i,Gt(e,s)}function Gt(e,t){e.lanes|=t;var i=e.alternate;for(i!==null&&(i.lanes|=t),i=e,e=e.return;e!==null;)e.childLanes|=t,i=e.alternate,i!==null&&(i.childLanes|=t),i=e,e=e.return;return i.tag===3?i.stateNode:null}var ln=!1;function wa(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function hf(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Yt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function un(e,t,i){var s=e.updateQueue;if(s===null)return null;if(s=s.shared,(me&2)!==0){var l=s.pending;return l===null?t.next=t:(t.next=l.next,l.next=t),s.pending=t,Gt(e,i)}return l=s.interleaved,l===null?(t.next=t,xa(s)):(t.next=l.next,l.next=t),s.interleaved=t,Gt(e,i)}function co(e,t,i){if(t=t.updateQueue,t!==null&&(t=t.shared,(i&4194240)!==0)){var s=t.lanes;s&=e.pendingLanes,i|=s,t.lanes=i,bs(e,i)}}function pf(e,t){var i=e.updateQueue,s=e.alternate;if(s!==null&&(s=s.updateQueue,i===s)){var l=null,c=null;if(i=i.firstBaseUpdate,i!==null){do{var p={eventTime:i.eventTime,lane:i.lane,tag:i.tag,payload:i.payload,callback:i.callback,next:null};c===null?l=c=p:c=c.next=p,i=i.next}while(i!==null);c===null?l=c=t:c=c.next=t}else l=c=t;i={baseState:s.baseState,firstBaseUpdate:l,lastBaseUpdate:c,shared:s.shared,effects:s.effects},e.updateQueue=i;return}e=i.lastBaseUpdate,e===null?i.firstBaseUpdate=t:e.next=t,i.lastBaseUpdate=t}function fo(e,t,i,s){var l=e.updateQueue;ln=!1;var c=l.firstBaseUpdate,p=l.lastBaseUpdate,S=l.shared.pending;if(S!==null){l.shared.pending=null;var T=S,L=T.next;T.next=null,p===null?c=L:p.next=L,p=T;var W=e.alternate;W!==null&&(W=W.updateQueue,S=W.lastBaseUpdate,S!==p&&(S===null?W.firstBaseUpdate=L:S.next=L,W.lastBaseUpdate=T))}if(c!==null){var $=l.baseState;p=0,W=L=T=null,S=c;do{var B=S.lane,Q=S.eventTime;if((s&B)===B){W!==null&&(W=W.next={eventTime:Q,lane:0,tag:S.tag,payload:S.payload,callback:S.callback,next:null});e:{var J=e,te=S;switch(B=t,Q=i,te.tag){case 1:if(J=te.payload,typeof J=="function"){$=J.call(Q,$,B);break e}$=J;break e;case 3:J.flags=J.flags&-65537|128;case 0:if(J=te.payload,B=typeof J=="function"?J.call(Q,$,B):J,B==null)break e;$=X({},$,B);break e;case 2:ln=!0}}S.callback!==null&&S.lane!==0&&(e.flags|=64,B=l.effects,B===null?l.effects=[S]:B.push(S))}else Q={eventTime:Q,lane:B,tag:S.tag,payload:S.payload,callback:S.callback,next:null},W===null?(L=W=Q,T=$):W=W.next=Q,p|=B;if(S=S.next,S===null){if(S=l.shared.pending,S===null)break;B=S,S=B.next,B.next=null,l.lastBaseUpdate=B,l.shared.pending=null}}while(!0);if(W===null&&(T=$),l.baseState=T,l.firstBaseUpdate=L,l.lastBaseUpdate=W,t=l.shared.interleaved,t!==null){l=t;do p|=l.lane,l=l.next;while(l!==t)}else c===null&&(l.shared.lanes=0);Ln|=p,e.lanes=p,e.memoizedState=$}}function mf(e,t,i){if(e=t.effects,t.effects=null,e!==null)for(t=0;ti?i:4,e(!0);var s=Ea.transition;Ea.transition={};try{e(!1),t()}finally{ke=i,Ea.transition=s}}function bf(){return vt().memoizedState}function Sg(e,t,i){var s=hn(e);if(i={lane:s,action:i,hasEagerState:!1,eagerState:null,next:null},_f(e))If(t,i);else if(i=df(e,t,i,s),i!==null){var l=et();Mt(i,e,s,l),zf(i,t,s)}}function kg(e,t,i){var s=hn(e),l={lane:s,action:i,hasEagerState:!1,eagerState:null,next:null};if(_f(e))If(t,l);else{var c=e.alternate;if(e.lanes===0&&(c===null||c.lanes===0)&&(c=t.lastRenderedReducer,c!==null))try{var p=t.lastRenderedState,S=c(p,i);if(l.hasEagerState=!0,l.eagerState=S,Ct(S,p)){var T=t.interleaved;T===null?(l.next=l,xa(t)):(l.next=T.next,T.next=l),t.interleaved=l;return}}catch{}finally{}i=df(e,t,l,s),i!==null&&(l=et(),Mt(i,e,s,l),zf(i,t,s))}}function _f(e){var t=e.alternate;return e===Le||t!==null&&t===Le}function If(e,t){ei=mo=!0;var i=e.pending;i===null?t.next=t:(t.next=i.next,i.next=t),e.pending=t}function zf(e,t,i){if((i&4194240)!==0){var s=t.lanes;s&=e.pendingLanes,i|=s,t.lanes=i,bs(e,i)}}var vo={readContext:yt,useCallback:Qe,useContext:Qe,useEffect:Qe,useImperativeHandle:Qe,useInsertionEffect:Qe,useLayoutEffect:Qe,useMemo:Qe,useReducer:Qe,useRef:Qe,useState:Qe,useDebugValue:Qe,useDeferredValue:Qe,useTransition:Qe,useMutableSource:Qe,useSyncExternalStore:Qe,useId:Qe,unstable_isNewReconciler:!1},Tg={readContext:yt,useCallback:function(e,t){return It().memoizedState=[e,t===void 0?null:t],e},useContext:yt,useEffect:Pf,useImperativeHandle:function(e,t,i){return i=i!=null?i.concat([e]):null,go(4194308,4,Mf.bind(null,t,e),i)},useLayoutEffect:function(e,t){return go(4194308,4,e,t)},useInsertionEffect:function(e,t){return go(4,2,e,t)},useMemo:function(e,t){var i=It();return t=t===void 0?null:t,e=e(),i.memoizedState=[e,t],e},useReducer:function(e,t,i){var s=It();return t=i!==void 0?i(t):t,s.memoizedState=s.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},s.queue=e,e=e.dispatch=Sg.bind(null,Le,e),[s.memoizedState,e]},useRef:function(e){var t=It();return e={current:e},t.memoizedState=e},useState:Cf,useDebugValue:La,useDeferredValue:function(e){return It().memoizedState=e},useTransition:function(){var e=Cf(!1),t=e[0];return e=wg.bind(null,e[1]),It().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,i){var s=Le,l=It();if(Ae){if(i===void 0)throw Error(o(407));i=i()}else{if(i=t(),Ue===null)throw Error(o(349));(Dn&30)!==0||xf(s,t,i)}l.memoizedState=i;var c={value:i,getSnapshot:t};return l.queue=c,Pf(Sf.bind(null,s,c,e),[e]),s.flags|=2048,ri(9,wf.bind(null,s,c,i,t),void 0,null),i},useId:function(){var e=It(),t=Ue.identifierPrefix;if(Ae){var i=Kt,s=Ht;i=(s&~(1<<32-Tt(s)-1)).toString(32)+i,t=":"+t+"R"+i,i=ti++,0<\/script>",e=e.removeChild(e.firstChild)):typeof s.is=="string"?e=p.createElement(i,{is:s.is}):(e=p.createElement(i),i==="select"&&(p=e,s.multiple?p.multiple=!0:s.size&&(p.size=s.size))):e=p.createElementNS(e,i),e[bt]=t,e[Yr]=s,rd(e,t,!1,!1),t.stateNode=e;e:{switch(p=Cs(i,s),i){case"dialog":Pe("cancel",e),Pe("close",e),l=s;break;case"iframe":case"object":case"embed":Pe("load",e),l=s;break;case"video":case"audio":for(l=0;ldr&&(t.flags|=128,s=!0,ii(c,!1),t.lanes=4194304)}else{if(!s)if(e=ho(p),e!==null){if(t.flags|=128,s=!0,i=e.updateQueue,i!==null&&(t.updateQueue=i,t.flags|=4),ii(c,!0),c.tail===null&&c.tailMode==="hidden"&&!p.alternate&&!Ae)return qe(t),null}else 2*_e()-c.renderingStartTime>dr&&i!==1073741824&&(t.flags|=128,s=!0,ii(c,!1),t.lanes=4194304);c.isBackwards?(p.sibling=t.child,t.child=p):(i=c.last,i!==null?i.sibling=p:t.child=p,c.last=p)}return c.tail!==null?(t=c.tail,c.rendering=t,c.tail=t.sibling,c.renderingStartTime=_e(),t.sibling=null,i=De.current,Ee(De,s?i&1|2:i&1),t):(qe(t),null);case 22:case 23:return nl(),s=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==s&&(t.flags|=8192),s&&(t.mode&1)!==0?(ht&1073741824)!==0&&(qe(t),t.subtreeFlags&6&&(t.flags|=8192)):qe(t),null;case 24:return null;case 25:return null}throw Error(o(156,t.tag))}function Dg(e,t){switch(fa(t),t.tag){case 1:return it(t.type)&&to(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return lr(),je(rt),je(Xe),Ca(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return ka(t),null;case 13:if(je(De),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(o(340));ir()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return je(De),null;case 4:return lr(),null;case 10:return ya(t.type._context),null;case 22:case 23:return nl(),null;case 24:return null;default:return null}}var ko=!1,Ze=!1,Lg=typeof WeakSet=="function"?WeakSet:Set,q=null;function cr(e,t){var i=e.ref;if(i!==null)if(typeof i=="function")try{i(null)}catch(s){Ve(e,t,s)}else i.current=null}function $a(e,t,i){try{i()}catch(s){Ve(e,t,s)}}var sd=!1;function Vg(e,t){if(na=Bi,e=zc(),Ys(e)){if("selectionStart"in e)var i={start:e.selectionStart,end:e.selectionEnd};else e:{i=(i=e.ownerDocument)&&i.defaultView||window;var s=i.getSelection&&i.getSelection();if(s&&s.rangeCount!==0){i=s.anchorNode;var l=s.anchorOffset,c=s.focusNode;s=s.focusOffset;try{i.nodeType,c.nodeType}catch{i=null;break e}var p=0,S=-1,T=-1,L=0,W=0,$=e,B=null;t:for(;;){for(var Q;$!==i||l!==0&&$.nodeType!==3||(S=p+l),$!==c||s!==0&&$.nodeType!==3||(T=p+s),$.nodeType===3&&(p+=$.nodeValue.length),(Q=$.firstChild)!==null;)B=$,$=Q;for(;;){if($===e)break t;if(B===i&&++L===l&&(S=p),B===c&&++W===s&&(T=p),(Q=$.nextSibling)!==null)break;$=B,B=$.parentNode}$=Q}i=S===-1||T===-1?null:{start:S,end:T}}else i=null}i=i||{start:0,end:0}}else i=null;for(ra={focusedElem:e,selectionRange:i},Bi=!1,q=t;q!==null;)if(t=q,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,q=e;else for(;q!==null;){t=q;try{var J=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(J!==null){var te=J.memoizedProps,Ie=J.memoizedState,R=t.stateNode,C=R.getSnapshotBeforeUpdate(t.elementType===t.type?te:Pt(t.type,te),Ie);R.__reactInternalSnapshotBeforeUpdate=C}break;case 3:var A=t.stateNode.containerInfo;A.nodeType===1?A.textContent="":A.nodeType===9&&A.documentElement&&A.removeChild(A.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(o(163))}}catch(K){Ve(t,t.return,K)}if(e=t.sibling,e!==null){e.return=t.return,q=e;break}q=t.return}return J=sd,sd=!1,J}function oi(e,t,i){var s=t.updateQueue;if(s=s!==null?s.lastEffect:null,s!==null){var l=s=s.next;do{if((l.tag&e)===e){var c=l.destroy;l.destroy=void 0,c!==void 0&&$a(t,i,c)}l=l.next}while(l!==s)}}function To(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var i=t=t.next;do{if((i.tag&e)===e){var s=i.create;i.destroy=s()}i=i.next}while(i!==t)}}function Ha(e){var t=e.ref;if(t!==null){var i=e.stateNode;switch(e.tag){case 5:e=i;break;default:e=i}typeof t=="function"?t(e):t.current=e}}function ad(e){var t=e.alternate;t!==null&&(e.alternate=null,ad(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[bt],delete t[Yr],delete t[aa],delete t[mg],delete t[gg])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function ld(e){return e.tag===5||e.tag===3||e.tag===4}function ud(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||ld(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ka(e,t,i){var s=e.tag;if(s===5||s===6)e=e.stateNode,t?i.nodeType===8?i.parentNode.insertBefore(e,t):i.insertBefore(e,t):(i.nodeType===8?(t=i.parentNode,t.insertBefore(e,i)):(t=i,t.appendChild(e)),i=i._reactRootContainer,i!=null||t.onclick!==null||(t.onclick=Ji));else if(s!==4&&(e=e.child,e!==null))for(Ka(e,t,i),e=e.sibling;e!==null;)Ka(e,t,i),e=e.sibling}function Ga(e,t,i){var s=e.tag;if(s===5||s===6)e=e.stateNode,t?i.insertBefore(e,t):i.appendChild(e);else if(s!==4&&(e=e.child,e!==null))for(Ga(e,t,i),e=e.sibling;e!==null;)Ga(e,t,i),e=e.sibling}var He=null,jt=!1;function cn(e,t,i){for(i=i.child;i!==null;)cd(e,t,i),i=i.sibling}function cd(e,t,i){if(Vt&&typeof Vt.onCommitFiberUnmount=="function")try{Vt.onCommitFiberUnmount(_i,i)}catch{}switch(i.tag){case 5:Ze||cr(i,t);case 6:var s=He,l=jt;He=null,cn(e,t,i),He=s,jt=l,He!==null&&(jt?(e=He,i=i.stateNode,e.nodeType===8?e.parentNode.removeChild(i):e.removeChild(i)):He.removeChild(i.stateNode));break;case 18:He!==null&&(jt?(e=He,i=i.stateNode,e.nodeType===8?sa(e.parentNode,i):e.nodeType===1&&sa(e,i),zr(e)):sa(He,i.stateNode));break;case 4:s=He,l=jt,He=i.stateNode.containerInfo,jt=!0,cn(e,t,i),He=s,jt=l;break;case 0:case 11:case 14:case 15:if(!Ze&&(s=i.updateQueue,s!==null&&(s=s.lastEffect,s!==null))){l=s=s.next;do{var c=l,p=c.destroy;c=c.tag,p!==void 0&&((c&2)!==0||(c&4)!==0)&&$a(i,t,p),l=l.next}while(l!==s)}cn(e,t,i);break;case 1:if(!Ze&&(cr(i,t),s=i.stateNode,typeof s.componentWillUnmount=="function"))try{s.props=i.memoizedProps,s.state=i.memoizedState,s.componentWillUnmount()}catch(S){Ve(i,t,S)}cn(e,t,i);break;case 21:cn(e,t,i);break;case 22:i.mode&1?(Ze=(s=Ze)||i.memoizedState!==null,cn(e,t,i),Ze=s):cn(e,t,i);break;default:cn(e,t,i)}}function fd(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var i=e.stateNode;i===null&&(i=e.stateNode=new Lg),t.forEach(function(s){var l=Wg.bind(null,e,s);i.has(s)||(i.add(s),s.then(l,l))})}}function Rt(e,t){var i=t.deletions;if(i!==null)for(var s=0;sl&&(l=p),s&=~c}if(s=l,s=_e()-s,s=(120>s?120:480>s?480:1080>s?1080:1920>s?1920:3e3>s?3e3:4320>s?4320:1960*_g(s/1960))-s,10e?16:e,dn===null)var s=!1;else{if(e=dn,dn=null,Ro=0,(me&6)!==0)throw Error(o(331));var l=me;for(me|=4,q=e.current;q!==null;){var c=q,p=c.child;if((q.flags&16)!==0){var S=c.deletions;if(S!==null){for(var T=0;T_e()-Qa?bn(e,0):Xa|=i),at(e,t)}function Cd(e,t){t===0&&((e.mode&1)===0?t=1:(t=zi,zi<<=1,(zi&130023424)===0&&(zi=4194304)));var i=et();e=Gt(e,t),e!==null&&(Lr(e,t,i),at(e,i))}function Bg(e){var t=e.memoizedState,i=0;t!==null&&(i=t.retryLane),Cd(e,i)}function Wg(e,t){var i=0;switch(e.tag){case 13:var s=e.stateNode,l=e.memoizedState;l!==null&&(i=l.retryLane);break;case 19:s=e.stateNode;break;default:throw Error(o(314))}s!==null&&s.delete(t),Cd(e,i)}var Ed;Ed=function(e,t,i){if(e!==null)if(e.memoizedProps!==t.pendingProps||rt.current)ot=!0;else{if((e.lanes&i)===0&&(t.flags&128)===0)return ot=!1,Mg(e,t,i);ot=(e.flags&131072)!==0}else ot=!1,Ae&&(t.flags&1048576)!==0&&rf(t,oo,t.index);switch(t.lanes=0,t.tag){case 2:var s=t.type;So(e,t),e=t.pendingProps;var l=tr(t,Xe.current);ar(t,i),l=ja(null,t,s,e,l,i);var c=Ra();return t.flags|=1,typeof l=="object"&&l!==null&&typeof l.render=="function"&&l.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,it(s)?(c=!0,no(t)):c=!1,t.memoizedState=l.state!==null&&l.state!==void 0?l.state:null,wa(t),l.updater=xo,t.stateNode=l,l._reactInternals=t,ba(t,s,e,i),t=Fa(null,t,s,!0,c,i)):(t.tag=0,Ae&&c&&ca(t),Je(null,t,l,i),t=t.child),t;case 16:s=t.elementType;e:{switch(So(e,t),e=t.pendingProps,l=s._init,s=l(s._payload),t.type=s,l=t.tag=$g(s),e=Pt(s,e),l){case 0:t=za(null,t,s,e,i);break e;case 1:t=qf(null,t,s,e,i);break e;case 11:t=Kf(null,t,s,e,i);break e;case 14:t=Gf(null,t,s,Pt(s.type,e),i);break e}throw Error(o(306,s,""))}return t;case 0:return s=t.type,l=t.pendingProps,l=t.elementType===s?l:Pt(s,l),za(e,t,s,l,i);case 1:return s=t.type,l=t.pendingProps,l=t.elementType===s?l:Pt(s,l),qf(e,t,s,l,i);case 3:e:{if(Zf(t),e===null)throw Error(o(387));s=t.pendingProps,c=t.memoizedState,l=c.element,hf(e,t),fo(t,s,null,i);var p=t.memoizedState;if(s=p.element,c.isDehydrated)if(c={element:s,isDehydrated:!1,cache:p.cache,pendingSuspenseBoundaries:p.pendingSuspenseBoundaries,transitions:p.transitions},t.updateQueue.baseState=c,t.memoizedState=c,t.flags&256){l=ur(Error(o(423)),t),t=Jf(e,t,s,i,l);break e}else if(s!==l){l=ur(Error(o(424)),t),t=Jf(e,t,s,i,l);break e}else for(dt=rn(t.stateNode.containerInfo.firstChild),ft=t,Ae=!0,Et=null,i=ff(t,null,s,i),t.child=i;i;)i.flags=i.flags&-3|4096,i=i.sibling;else{if(ir(),s===l){t=Xt(e,t,i);break e}Je(e,t,s,i)}t=t.child}return t;case 5:return gf(t),e===null&&ha(t),s=t.type,l=t.pendingProps,c=e!==null?e.memoizedProps:null,p=l.children,ia(s,l)?p=null:c!==null&&ia(s,c)&&(t.flags|=32),Qf(e,t),Je(e,t,p,i),t.child;case 6:return e===null&&ha(t),null;case 13:return ed(e,t,i);case 4:return Sa(t,t.stateNode.containerInfo),s=t.pendingProps,e===null?t.child=or(t,null,s,i):Je(e,t,s,i),t.child;case 11:return s=t.type,l=t.pendingProps,l=t.elementType===s?l:Pt(s,l),Kf(e,t,s,l,i);case 7:return Je(e,t,t.pendingProps,i),t.child;case 8:return Je(e,t,t.pendingProps.children,i),t.child;case 12:return Je(e,t,t.pendingProps.children,i),t.child;case 10:e:{if(s=t.type._context,l=t.pendingProps,c=t.memoizedProps,p=l.value,Ee(lo,s._currentValue),s._currentValue=p,c!==null)if(Ct(c.value,p)){if(c.children===l.children&&!rt.current){t=Xt(e,t,i);break e}}else for(c=t.child,c!==null&&(c.return=t);c!==null;){var S=c.dependencies;if(S!==null){p=c.child;for(var T=S.firstContext;T!==null;){if(T.context===s){if(c.tag===1){T=Yt(-1,i&-i),T.tag=2;var L=c.updateQueue;if(L!==null){L=L.shared;var W=L.pending;W===null?T.next=T:(T.next=W.next,W.next=T),L.pending=T}}c.lanes|=i,T=c.alternate,T!==null&&(T.lanes|=i),va(c.return,i,t),S.lanes|=i;break}T=T.next}}else if(c.tag===10)p=c.type===t.type?null:c.child;else if(c.tag===18){if(p=c.return,p===null)throw Error(o(341));p.lanes|=i,S=p.alternate,S!==null&&(S.lanes|=i),va(p,i,t),p=c.sibling}else p=c.child;if(p!==null)p.return=c;else for(p=c;p!==null;){if(p===t){p=null;break}if(c=p.sibling,c!==null){c.return=p.return,p=c;break}p=p.return}c=p}Je(e,t,l.children,i),t=t.child}return t;case 9:return l=t.type,s=t.pendingProps.children,ar(t,i),l=yt(l),s=s(l),t.flags|=1,Je(e,t,s,i),t.child;case 14:return s=t.type,l=Pt(s,t.pendingProps),l=Pt(s.type,l),Gf(e,t,s,l,i);case 15:return Yf(e,t,t.type,t.pendingProps,i);case 17:return s=t.type,l=t.pendingProps,l=t.elementType===s?l:Pt(s,l),So(e,t),t.tag=1,it(s)?(e=!0,no(t)):e=!1,ar(t,i),Nf(t,s,l),ba(t,s,l,i),Fa(null,t,s,!0,e,i);case 19:return nd(e,t,i);case 22:return Xf(e,t,i)}throw Error(o(156,t.tag))};function Pd(e,t){return ic(e,t)}function Ug(e,t,i,s){this.tag=e,this.key=i,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=s,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function wt(e,t,i,s){return new Ug(e,t,i,s)}function il(e){return e=e.prototype,!(!e||!e.isReactComponent)}function $g(e){if(typeof e=="function")return il(e)?1:0;if(e!=null){if(e=e.$$typeof,e===ue)return 11;if(e===ce)return 14}return 2}function mn(e,t){var i=e.alternate;return i===null?(i=wt(e.tag,t,e.key,e.mode),i.elementType=e.elementType,i.type=e.type,i.stateNode=e.stateNode,i.alternate=e,e.alternate=i):(i.pendingProps=t,i.type=e.type,i.flags=0,i.subtreeFlags=0,i.deletions=null),i.flags=e.flags&14680064,i.childLanes=e.childLanes,i.lanes=e.lanes,i.child=e.child,i.memoizedProps=e.memoizedProps,i.memoizedState=e.memoizedState,i.updateQueue=e.updateQueue,t=e.dependencies,i.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},i.sibling=e.sibling,i.index=e.index,i.ref=e.ref,i}function Lo(e,t,i,s,l,c){var p=2;if(s=e,typeof e=="function")il(e)&&(p=1);else if(typeof e=="string")p=5;else e:switch(e){case I:return In(i.children,l,c,t);case N:p=8,l|=8;break;case Y:return e=wt(12,i,t,l|2),e.elementType=Y,e.lanes=c,e;case ge:return e=wt(13,i,t,l),e.elementType=ge,e.lanes=c,e;case we:return e=wt(19,i,t,l),e.elementType=we,e.lanes=c,e;case le:return Vo(i,l,c,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case G:p=10;break e;case se:p=9;break e;case ue:p=11;break e;case ce:p=14;break e;case he:p=16,s=null;break e}throw Error(o(130,e==null?e:typeof e,""))}return t=wt(p,i,t,l),t.elementType=e,t.type=s,t.lanes=c,t}function In(e,t,i,s){return e=wt(7,e,s,t),e.lanes=i,e}function Vo(e,t,i,s){return e=wt(22,e,s,t),e.elementType=le,e.lanes=i,e.stateNode={isHidden:!1},e}function ol(e,t,i){return e=wt(6,e,null,t),e.lanes=i,e}function sl(e,t,i){return t=wt(4,e.children!==null?e.children:[],e.key,t),t.lanes=i,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Hg(e,t,i,s,l){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Vs(0),this.expirationTimes=Vs(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Vs(0),this.identifierPrefix=s,this.onRecoverableError=l,this.mutableSourceEagerHydrationData=null}function al(e,t,i,s,l,c,p,S,T){return e=new Hg(e,t,i,S,T),t===1?(t=1,c===!0&&(t|=8)):t=0,c=wt(3,null,null,t),e.current=c,c.stateNode=e,c.memoizedState={element:s,isDehydrated:i,cache:null,transitions:null,pendingSuspenseBoundaries:null},wa(c),e}function Kg(e,t,i){var s=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(n)}catch(r){console.error(r)}}return n(),hl.exports=ry(),hl.exports}var Od;function oy(){if(Od)return Oo;Od=1;var n=iy();return Oo.createRoot=n.createRoot,Oo.hydrateRoot=n.hydrateRoot,Oo}var sy=oy(),V=au();const lu=V.createContext({});function uu(n){const r=V.useRef(null);return r.current===null&&(r.current=n()),r.current}const ay=typeof window<"u",mp=ay?V.useLayoutEffect:V.useEffect,ds=V.createContext(null);function cu(n,r){n.indexOf(r)===-1&&n.push(r)}function rs(n,r){const o=n.indexOf(r);o>-1&&n.splice(o,1)}const Wt=(n,r,o)=>o>r?r:o{};const Sn={},gp=n=>/^-?(?:\d+(?:\.\d+)?|\.\d+)$/u.test(n);function yp(n){return typeof n=="object"&&n!==null}const vp=n=>/^0[^.\s]+$/u.test(n);function xp(n){let r;return()=>(r===void 0&&(r=n()),r)}const kt=n=>n,ly=(n,r)=>o=>r(n(o)),Ci=(...n)=>n.reduce(ly),yi=(n,r,o)=>{const a=r-n;return a===0?1:(o-n)/a};class du{constructor(){this.subscriptions=[]}add(r){return cu(this.subscriptions,r),()=>rs(this.subscriptions,r)}notify(r,o,a){const u=this.subscriptions.length;if(u)if(u===1)this.subscriptions[0](r,o,a);else for(let d=0;dn*1e3,St=n=>n/1e3;function wp(n,r){return r?n*(1e3/r):0}const Sp=(n,r,o)=>(((1-3*o+3*r)*n+(3*o-6*r))*n+3*r)*n,uy=1e-7,cy=12;function fy(n,r,o,a,u){let d,f,h=0;do f=r+(o-r)/2,d=Sp(f,a,u)-n,d>0?o=f:r=f;while(Math.abs(d)>uy&&++hfy(d,0,1,n,o);return d=>d===0||d===1?d:Sp(u(d),r,a)}const kp=n=>r=>r<=.5?n(2*r)/2:(2-n(2*(1-r)))/2,Tp=n=>r=>1-n(1-r),Cp=Ei(.33,1.53,.69,.99),hu=Tp(Cp),Ep=kp(hu),Pp=n=>n>=1?1:(n*=2)<1?.5*hu(n):.5*(2-Math.pow(2,-10*(n-1))),pu=n=>1-Math.sin(Math.acos(n)),jp=Tp(pu),Rp=kp(pu),dy=Ei(.42,0,1,1),hy=Ei(0,0,.58,1),Mp=Ei(.42,0,.58,1),py=n=>Array.isArray(n)&&typeof n[0]!="number",Ap=n=>Array.isArray(n)&&typeof n[0]=="number",my={linear:kt,easeIn:dy,easeInOut:Mp,easeOut:hy,circIn:pu,circInOut:Rp,circOut:jp,backIn:hu,backInOut:Ep,backOut:Cp,anticipate:Pp},gy=n=>typeof n=="string",Bd=n=>{if(Ap(n)){fu(n.length===4);const[r,o,a,u]=n;return Ei(r,o,a,u)}else if(gy(n))return my[n];return n},Bo=["setup","read","resolveKeyframes","preUpdate","update","preRender","render","postRender"];function yy(n,r){let o=new Set,a=new Set,u=!1,d=!1;const f=new WeakSet;let h={delta:0,timestamp:0,isProcessing:!1};function g(m){f.has(m)&&(y.schedule(m),n()),m(h)}const y={schedule:(m,x=!1,w=!1)=>{const E=w&&u?o:a;return x&&f.add(m),E.add(m),m},cancel:m=>{a.delete(m),f.delete(m)},process:m=>{if(h=m,u){d=!0;return}u=!0;const x=o;o=a,a=x,o.forEach(g),o.clear(),u=!1,d&&(d=!1,y.process(m))}};return y}const vy=40;function Dp(n,r){let o=!1,a=!0;const u={delta:0,timestamp:0,isProcessing:!1},d=()=>o=!0,f=Bo.reduce((z,F)=>(z[F]=yy(d),z),{}),{setup:h,read:g,resolveKeyframes:y,preUpdate:m,update:x,preRender:w,render:k,postRender:E}=f,j=()=>{const z=Sn.useManualTiming,F=z?u.timestamp:performance.now();o=!1,z||(u.delta=a?1e3/60:Math.max(Math.min(F-u.timestamp,vy),1)),u.timestamp=F,u.isProcessing=!0,h.process(u),g.process(u),y.process(u),m.process(u),x.process(u),w.process(u),k.process(u),E.process(u),u.isProcessing=!1,o&&r&&(a=!1,n(j))},D=()=>{o=!0,a=!0,u.isProcessing||n(j)};return{schedule:Bo.reduce((z,F)=>{const M=f[F];return z[F]=(H,I=!1,N=!1)=>(o||D(),M.schedule(H,I,N)),z},{}),cancel:z=>{for(let F=0;F(Xo===void 0&&tt.set(Ge.isProcessing||Sn.useManualTiming?Ge.timestamp:performance.now()),Xo),set:n=>{Xo=n,queueMicrotask(xy)}},Lp=n=>r=>typeof r=="string"&&r.startsWith(n),Vp=Lp("--"),wy=Lp("var(--"),mu=n=>wy(n)?Sy.test(n.split("/*")[0].trim()):!1,Sy=/var\(--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)$/iu;function Wd(n){return typeof n!="string"?!1:n.split("/*")[0].includes("var(--")}const kr={test:n=>typeof n=="number",parse:parseFloat,transform:n=>n},vi={...kr,transform:n=>Wt(0,1,n)},Wo={...kr,default:1},hi=n=>Math.round(n*1e5)/1e5,gu=/-?(?:\d+(?:\.\d+)?|\.\d+)/gu;function ky(n){return n==null}const Ty=/^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))$/iu,yu=(n,r)=>o=>!!(typeof o=="string"&&Ty.test(o)&&o.startsWith(n)||r&&!ky(o)&&Object.prototype.hasOwnProperty.call(o,r)),bp=(n,r,o)=>a=>{if(typeof a!="string")return a;const[u,d,f,h]=a.match(gu);return{[n]:parseFloat(u),[r]:parseFloat(d),[o]:parseFloat(f),alpha:h!==void 0?parseFloat(h):1}},Cy=n=>Wt(0,255,n),yl={...kr,transform:n=>Math.round(Cy(n))},Nn={test:yu("rgb","red"),parse:bp("red","green","blue"),transform:({red:n,green:r,blue:o,alpha:a=1})=>"rgba("+yl.transform(n)+", "+yl.transform(r)+", "+yl.transform(o)+", "+hi(vi.transform(a))+")"};function Ey(n){let r="",o="",a="",u="";return n.length>5?(r=n.substring(1,3),o=n.substring(3,5),a=n.substring(5,7),u=n.substring(7,9)):(r=n.substring(1,2),o=n.substring(2,3),a=n.substring(3,4),u=n.substring(4,5),r+=r,o+=o,a+=a,u+=u),{red:parseInt(r,16),green:parseInt(o,16),blue:parseInt(a,16),alpha:u?parseInt(u,16)/255:1}}const zl={test:yu("#"),parse:Ey,transform:Nn.transform},Pi=n=>({test:r=>typeof r=="string"&&r.endsWith(n)&&r.split(" ").length===1,parse:parseFloat,transform:r=>`${r}${n}`}),vn=Pi("deg"),Bt=Pi("%"),ee=Pi("px"),Py=Pi("vh"),jy=Pi("vw"),Ud={...Bt,parse:n=>Bt.parse(n)/100,transform:n=>Bt.transform(n*100)},gr={test:yu("hsl","hue"),parse:bp("hue","saturation","lightness"),transform:({hue:n,saturation:r,lightness:o,alpha:a=1})=>"hsla("+Math.round(n)+", "+Bt.transform(hi(r))+", "+Bt.transform(hi(o))+", "+hi(vi.transform(a))+")"},Fe={test:n=>Nn.test(n)||zl.test(n)||gr.test(n),parse:n=>Nn.test(n)?Nn.parse(n):gr.test(n)?gr.parse(n):zl.parse(n),transform:n=>typeof n=="string"?n:n.hasOwnProperty("red")?Nn.transform(n):gr.transform(n),getAnimatableNone:n=>{const r=Fe.parse(n);return r.alpha=0,Fe.transform(r)}},Ry=/(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\))/giu;function My(n){var r,o;return isNaN(n)&&typeof n=="string"&&(((r=n.match(gu))==null?void 0:r.length)||0)+(((o=n.match(Ry))==null?void 0:o.length)||0)>0}const _p="number",Ip="color",Ay="var",Dy="var(",$d="${}",Ly=/var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)/giu;function xr(n){const r=n.toString(),o=[],a={color:[],number:[],var:[]},u=[];let d=0;const h=r.replace(Ly,g=>(Fe.test(g)?(a.color.push(d),u.push(Ip),o.push(Fe.parse(g))):g.startsWith(Dy)?(a.var.push(d),u.push(Ay),o.push(g)):(a.number.push(d),u.push(_p),o.push(parseFloat(g))),++d,$d)).split($d);return{values:o,split:h,indexes:a,types:u}}function Vy(n){return xr(n).values}function zp({split:n,types:r}){const o=n.length;return a=>{let u="";for(let d=0;dtypeof n=="number"?0:Fe.test(n)?Fe.getAnimatableNone(n):n,Iy=(n,r)=>typeof n=="number"?r!=null&&r.trim().endsWith("/")?n:0:_y(n);function zy(n){const r=xr(n);return zp(r)(r.values.map((a,u)=>Iy(a,r.split[u])))}const Lt={test:My,parse:Vy,createTransformer:by,getAnimatableNone:zy};function vl(n,r,o){return o<0&&(o+=1),o>1&&(o-=1),o<1/6?n+(r-n)*6*o:o<1/2?r:o<2/3?n+(r-n)*(2/3-o)*6:n}function Fy({hue:n,saturation:r,lightness:o,alpha:a}){n/=360,r/=100,o/=100;let u=0,d=0,f=0;if(!r)u=d=f=o;else{const h=o<.5?o*(1+r):o+r-o*r,g=2*o-h;u=vl(g,h,n+1/3),d=vl(g,h,n),f=vl(g,h,n-1/3)}return{red:Math.round(u*255),green:Math.round(d*255),blue:Math.round(f*255),alpha:a}}function is(n,r){return o=>o>0?r:n}const Re=(n,r,o)=>n+(r-n)*o,xl=(n,r,o)=>{const a=n*n,u=o*(r*r-a)+a;return u<0?0:Math.sqrt(u)},Ny=[zl,Nn,gr],Oy=n=>Ny.find(r=>r.test(n));function Hd(n){const r=Oy(n);if(!r)return!1;let o=r.parse(n);return r===gr&&(o=Fy(o)),o}const Kd=(n,r)=>{const o=Hd(n),a=Hd(r);if(!o||!a)return is(n,r);const u={...o};return d=>(u.red=xl(o.red,a.red,d),u.green=xl(o.green,a.green,d),u.blue=xl(o.blue,a.blue,d),u.alpha=Re(o.alpha,a.alpha,d),Nn.transform(u))},Fl=new Set(["none","hidden"]);function By(n,r){return Fl.has(n)?o=>o<=0?n:r:o=>o>=1?r:n}function Wy(n,r){return o=>Re(n,r,o)}function vu(n){return typeof n=="number"?Wy:typeof n=="string"?mu(n)?is:Fe.test(n)?Kd:Hy:Array.isArray(n)?Fp:typeof n=="object"?Fe.test(n)?Kd:Uy:is}function Fp(n,r){const o=[...n],a=o.length,u=n.map((d,f)=>vu(d)(d,r[f]));return d=>{for(let f=0;f{for(const d in a)o[d]=a[d](u);return o}}function $y(n,r){const o=[],a={color:0,var:0,number:0};for(let u=0;u{const o=Lt.createTransformer(r),a=xr(n),u=xr(r);return a.indexes.var.length===u.indexes.var.length&&a.indexes.color.length===u.indexes.color.length&&a.indexes.number.length>=u.indexes.number.length?Fl.has(n)&&!u.values.length||Fl.has(r)&&!a.values.length?By(n,r):Ci(Fp($y(a,u),u.values),o):is(n,r)};function Np(n,r,o){return typeof n=="number"&&typeof r=="number"&&typeof o=="number"?Re(n,r,o):vu(n)(n,r)}const Ky=n=>{const r=({timestamp:o})=>n(o);return{start:(o=!0)=>Te.update(r,o),stop:()=>kn(r),now:()=>Ge.isProcessing?Ge.timestamp:tt.now()}},Op=(n,r,o=10)=>{let a="";const u=Math.max(Math.round(r/o),2);for(let d=0;d=os?1/0:r}function Gy(n,r=100,o){const a=o({...n,keyframes:[0,r]}),u=Math.min(xu(a),os);return{type:"keyframes",ease:d=>a.next(u*d).value/r,duration:St(u)}}const be={stiffness:100,damping:10,mass:1,velocity:0,duration:800,bounce:.3,visualDuration:.3,restSpeed:{granular:.01,default:2},restDelta:{granular:.005,default:.5},minDuration:.01,maxDuration:10,minDamping:.05,maxDamping:1};function Nl(n,r){return n*Math.sqrt(1-r*r)}const Yy=12;function Xy(n,r,o){let a=o;for(let u=1;u{const m=y*f,x=m*n,w=m-o,k=Nl(y,f),E=Math.exp(-x);return wl-w/k*E},d=y=>{const x=y*f*n,w=x*o+o,k=Math.pow(f,2)*Math.pow(y,2)*n,E=Math.exp(-x),j=Nl(Math.pow(y,2),f);return(-u(y)+wl>0?-1:1)*((w-k)*E)/j}):(u=y=>{const m=Math.exp(-y*n),x=(y-o)*n+1;return-wl+m*x},d=y=>{const m=Math.exp(-y*n),x=(o-y)*(n*n);return m*x});const h=5/n,g=Xy(u,d,h);if(n=pt(n),isNaN(g))return{stiffness:be.stiffness,damping:be.damping,duration:n};{const y=Math.pow(g,2)*a;return{stiffness:y,damping:f*2*Math.sqrt(a*y),duration:n}}}const qy=["duration","bounce"],Zy=["stiffness","damping","mass"];function Gd(n,r){return r.some(o=>n[o]!==void 0)}function Jy(n){let r={velocity:be.velocity,stiffness:be.stiffness,damping:be.damping,mass:be.mass,isResolvedFromDuration:!1,...n};if(!Gd(n,Zy)&&Gd(n,qy))if(r.velocity=0,n.visualDuration){const o=n.visualDuration,a=2*Math.PI/(o*1.2),u=a*a,d=2*Wt(.05,1,1-(n.bounce||0))*Math.sqrt(u);r={...r,mass:be.mass,stiffness:u,damping:d}}else{const o=Qy({...n,velocity:0});r={...r,...o,mass:be.mass},r.isResolvedFromDuration=!0}return r}function ss(n=be.visualDuration,r=be.bounce){const o=typeof n!="object"?{visualDuration:n,keyframes:[0,1],bounce:r}:n;let{restSpeed:a,restDelta:u}=o;const d=o.keyframes[0],f=o.keyframes[o.keyframes.length-1],h={done:!1,value:d},{stiffness:g,damping:y,mass:m,duration:x,velocity:w,isResolvedFromDuration:k}=Jy({...o,velocity:-St(o.velocity||0)}),E=w||0,j=y/(2*Math.sqrt(g*m)),D=f-d,_=St(Math.sqrt(g/m)),b=Math.abs(D)<5;a||(a=b?be.restSpeed.granular:be.restSpeed.default),u||(u=b?be.restDelta.granular:be.restDelta.default);let z,F,M,H,I,N;if(j<1)M=Nl(_,j),H=(E+j*_*D)/M,z=G=>{const se=Math.exp(-j*_*G);return f-se*(H*Math.sin(M*G)+D*Math.cos(M*G))},I=j*_*H+D*M,N=j*_*D-H*M,F=G=>Math.exp(-j*_*G)*(I*Math.sin(M*G)+N*Math.cos(M*G));else if(j===1){z=se=>f-Math.exp(-_*se)*(D+(E+_*D)*se);const G=E+_*D;F=se=>Math.exp(-_*se)*(_*G*se-E)}else{const G=_*Math.sqrt(j*j-1);z=we=>{const ce=Math.exp(-j*_*we),he=Math.min(G*we,300);return f-ce*((E+j*_*D)*Math.sinh(he)+G*D*Math.cosh(he))/G};const se=(E+j*_*D)/G,ue=j*_*se-D*G,ge=j*_*D-se*G;F=we=>{const ce=Math.exp(-j*_*we),he=Math.min(G*we,300);return ce*(ue*Math.sinh(he)+ge*Math.cosh(he))}}const Y={calculatedDuration:k&&x||null,velocity:G=>pt(F(G)),next:G=>{if(!k&&j<1){const ue=Math.exp(-j*_*G),ge=Math.sin(M*G),we=Math.cos(M*G),ce=f-ue*(H*ge+D*we),he=pt(ue*(I*ge+N*we));return h.done=Math.abs(he)<=a&&Math.abs(f-ce)<=u,h.value=h.done?f:ce,h}const se=z(G);if(k)h.done=G>=x;else{const ue=pt(F(G));h.done=Math.abs(ue)<=a&&Math.abs(f-se)<=u}return h.value=h.done?f:se,h},toString:()=>{const G=Math.min(xu(Y),os),se=Op(ue=>Y.next(G*ue).value,G,30);return G+"ms "+se},toTransition:()=>{}};return Y}ss.applyToOptions=n=>{const r=Gy(n,100,ss);return n.ease=r.ease,n.duration=pt(r.duration),n.type="keyframes",n};const ev=5;function Bp(n,r,o){const a=Math.max(r-ev,0);return wp(o-n(a),r-a)}function Ol({keyframes:n,velocity:r=0,power:o=.8,timeConstant:a=325,bounceDamping:u=10,bounceStiffness:d=500,modifyTarget:f,min:h,max:g,restDelta:y=.5,restSpeed:m}){const x=n[0],w={done:!1,value:x},k=N=>h!==void 0&&Ng,E=N=>h===void 0?g:g===void 0||Math.abs(h-N)-j*Math.exp(-N/a),z=N=>_+b(N),F=N=>{const Y=b(N),G=z(N);w.done=Math.abs(Y)<=y,w.value=w.done?_:G};let M,H;const I=N=>{k(w.value)&&(M=N,H=ss({keyframes:[w.value,E(w.value)],velocity:Bp(z,N,w.value),damping:u,stiffness:d,restDelta:y,restSpeed:m}))};return I(0),{calculatedDuration:null,next:N=>{let Y=!1;return!H&&M===void 0&&(Y=!0,F(N),I(N)),M!==void 0&&N>=M?H.next(N-M):(!Y&&F(N),w)}}}function tv(n,r,o){const a=[],u=o||Sn.mix||Np,d=n.length-1;for(let f=0;fr[0];if(d===2&&r[0]===r[1])return()=>r[1];const f=n[0]===n[1];n[0]>n[d-1]&&(n=[...n].reverse(),r=[...r].reverse());const h=tv(r,a,u),g=h.length,y=m=>{if(f&&m1)for(;xy(Wt(n[0],n[d-1],m)):y}function rv(n,r){const o=n[n.length-1];for(let a=1;a<=r;a++){const u=yi(0,r,a);n.push(Re(o,1,u))}}function iv(n){const r=[0];return rv(r,n.length-1),r}function ov(n,r){return n.map(o=>o*r)}function sv(n,r){return n.map(()=>r||Mp).splice(0,n.length-1)}function pi({duration:n=300,keyframes:r,times:o,ease:a="easeInOut"}){const u=py(a)?a.map(Bd):Bd(a),d={done:!1,value:r[0]},f=ov(o&&o.length===r.length?o:iv(r),n),h=nv(f,r,{ease:Array.isArray(u)?u:sv(r,u)});return{calculatedDuration:n,next:g=>(d.value=h(g),d.done=g>=n,d)}}const av=n=>n!==null;function hs(n,{repeat:r,repeatType:o="loop"},a,u=1){const d=n.filter(av),h=u<0||r&&o!=="loop"&&r%2===1?0:d.length-1;return!h||a===void 0?d[h]:a}const lv={decay:Ol,inertia:Ol,tween:pi,keyframes:pi,spring:ss};function Wp(n){typeof n.type=="string"&&(n.type=lv[n.type])}class wu{constructor(){this.updateFinished()}get finished(){return this._finished}updateFinished(){this._finished=new Promise(r=>{this.resolve=r})}notifyFinished(){this.resolve()}then(r,o){return this.finished.then(r,o)}}const uv=n=>n/100;class as extends wu{constructor(r){super(),this.state="idle",this.startTime=null,this.isStopped=!1,this.currentTime=0,this.holdTime=null,this.playbackSpeed=1,this.delayState={done:!1,value:void 0},this.stop=()=>{var a,u;const{motionValue:o}=this.options;o&&o.updatedAt!==tt.now()&&this.tick(tt.now()),this.isStopped=!0,this.state!=="idle"&&(this.teardown(),(u=(a=this.options).onStop)==null||u.call(a))},this.options=r,this.initAnimation(),this.play(),r.autoplay===!1&&this.pause()}initAnimation(){const{options:r}=this;Wp(r);const{type:o=pi,repeat:a=0,repeatDelay:u=0,repeatType:d,velocity:f=0}=r;let{keyframes:h}=r;const g=o||pi;g!==pi&&typeof h[0]!="number"&&(this.mixKeyframes=Ci(uv,Np(h[0],h[1])),h=[0,100]);const y=g({...r,keyframes:h});d==="mirror"&&(this.mirroredGenerator=g({...r,keyframes:[...h].reverse(),velocity:-f})),y.calculatedDuration===null&&(y.calculatedDuration=xu(y));const{calculatedDuration:m}=y;this.calculatedDuration=m,this.resolvedDuration=m+u,this.totalDuration=this.resolvedDuration*(a+1)-u,this.generator=y}updateTime(r){const o=Math.round(r-this.startTime)*this.playbackSpeed;this.holdTime!==null?this.currentTime=this.holdTime:this.currentTime=o}tick(r,o=!1){const{generator:a,totalDuration:u,mixKeyframes:d,mirroredGenerator:f,resolvedDuration:h,calculatedDuration:g}=this;if(this.startTime===null)return a.next(0);const{delay:y=0,keyframes:m,repeat:x,repeatType:w,repeatDelay:k,type:E,onUpdate:j,finalKeyframe:D}=this.options;this.speed>0?this.startTime=Math.min(this.startTime,r):this.speed<0&&(this.startTime=Math.min(r-u/this.speed,this.startTime)),o?this.currentTime=r:this.updateTime(r);const _=this.currentTime-y*(this.playbackSpeed>=0?1:-1),b=this.playbackSpeed>=0?_<0:_>u;this.currentTime=Math.max(_,0),this.state==="finished"&&this.holdTime===null&&(this.currentTime=u);let z=this.currentTime,F=a;if(x){const N=Math.min(this.currentTime,u)/h;let Y=Math.floor(N),G=N%1;!G&&N>=1&&(G=1),G===1&&Y--,Y=Math.min(Y,x+1),!!(Y%2)&&(w==="reverse"?(G=1-G,k&&(G-=k/h)):w==="mirror"&&(F=f)),z=Wt(0,1,G)*h}let M;b?(this.delayState.value=m[0],M=this.delayState):M=F.next(z),d&&!b&&(M.value=d(M.value));let{done:H}=M;!b&&g!==null&&(H=this.playbackSpeed>=0?this.currentTime>=u:this.currentTime<=0);const I=this.holdTime===null&&(this.state==="finished"||this.state==="running"&&H);return I&&E!==Ol&&(M.value=hs(m,this.options,D,this.speed)),j&&j(M.value),I&&this.finish(),M}then(r,o){return this.finished.then(r,o)}get duration(){return St(this.calculatedDuration)}get iterationDuration(){const{delay:r=0}=this.options||{};return this.duration+St(r)}get time(){return St(this.currentTime)}set time(r){r=pt(r),this.currentTime=r,this.startTime===null||this.holdTime!==null||this.playbackSpeed===0?this.holdTime=r:this.driver&&(this.startTime=this.driver.now()-r/this.playbackSpeed),this.driver?this.driver.start(!1):(this.startTime=0,this.state="paused",this.holdTime=r,this.tick(r))}getGeneratorVelocity(){const r=this.currentTime;if(r<=0)return this.options.velocity||0;if(this.generator.velocity)return this.generator.velocity(r);const o=this.generator.next(r).value;return Bp(a=>this.generator.next(a).value,r,o)}get speed(){return this.playbackSpeed}set speed(r){const o=this.playbackSpeed!==r;o&&this.driver&&this.updateTime(tt.now()),this.playbackSpeed=r,o&&this.driver&&(this.time=St(this.currentTime))}play(){var u,d;if(this.isStopped)return;const{driver:r=Ky,startTime:o}=this.options;this.driver||(this.driver=r(f=>this.tick(f))),(d=(u=this.options).onPlay)==null||d.call(u);const a=this.driver.now();this.state==="finished"?(this.updateFinished(),this.startTime=a):this.holdTime!==null?this.startTime=a-this.holdTime:this.startTime||(this.startTime=o??a),this.state==="finished"&&this.speed<0&&(this.startTime+=this.calculatedDuration),this.holdTime=null,this.state="running",this.driver.start()}pause(){this.state="paused",this.updateTime(tt.now()),this.holdTime=this.currentTime}complete(){this.state!=="running"&&this.play(),this.state="finished",this.holdTime=null}finish(){var r,o;this.notifyFinished(),this.teardown(),this.state="finished",(o=(r=this.options).onComplete)==null||o.call(r)}cancel(){var r,o;this.holdTime=null,this.startTime=0,this.tick(0),this.teardown(),(o=(r=this.options).onCancel)==null||o.call(r)}teardown(){this.state="idle",this.stopDriver(),this.startTime=this.holdTime=null}stopDriver(){this.driver&&(this.driver.stop(),this.driver=void 0)}sample(r){return this.startTime=0,this.tick(r,!0)}attachTimeline(r){var o;return this.options.allowFlatten&&(this.options.type="keyframes",this.options.ease="linear",this.initAnimation()),(o=this.driver)==null||o.stop(),r.observe(this)}}function cv(n){for(let r=1;rn*180/Math.PI,Bl=n=>{const r=On(Math.atan2(n[1],n[0]));return Wl(r)},fv={x:4,y:5,translateX:4,translateY:5,scaleX:0,scaleY:3,scale:n=>(Math.abs(n[0])+Math.abs(n[3]))/2,rotate:Bl,rotateZ:Bl,skewX:n=>On(Math.atan(n[1])),skewY:n=>On(Math.atan(n[2])),skew:n=>(Math.abs(n[1])+Math.abs(n[2]))/2},Wl=n=>(n=n%360,n<0&&(n+=360),n),Yd=Bl,Xd=n=>Math.sqrt(n[0]*n[0]+n[1]*n[1]),Qd=n=>Math.sqrt(n[4]*n[4]+n[5]*n[5]),dv={x:12,y:13,z:14,translateX:12,translateY:13,translateZ:14,scaleX:Xd,scaleY:Qd,scale:n=>(Xd(n)+Qd(n))/2,rotateX:n=>Wl(On(Math.atan2(n[6],n[5]))),rotateY:n=>Wl(On(Math.atan2(-n[2],n[0]))),rotateZ:Yd,rotate:Yd,skewX:n=>On(Math.atan(n[4])),skewY:n=>On(Math.atan(n[1])),skew:n=>(Math.abs(n[1])+Math.abs(n[4]))/2};function Ul(n){return n.includes("scale")?1:0}function $l(n,r){if(!n||n==="none")return Ul(r);const o=n.match(/^matrix3d\(([-\d.e\s,]+)\)$/u);let a,u;if(o)a=dv,u=o;else{const h=n.match(/^matrix\(([-\d.e\s,]+)\)$/u);a=fv,u=h}if(!u)return Ul(r);const d=a[r],f=u[1].split(",").map(pv);return typeof d=="function"?d(f):f[d]}const hv=(n,r)=>{const{transform:o="none"}=getComputedStyle(n);return $l(o,r)};function pv(n){return parseFloat(n.trim())}const Tr=["transformPerspective","x","y","z","translateX","translateY","translateZ","scale","scaleX","scaleY","rotate","rotateX","rotateY","rotateZ","skew","skewX","skewY"],Cr=new Set(Tr),qd=n=>n===kr||n===ee,mv=new Set(["x","y","z"]),gv=Tr.filter(n=>!mv.has(n));function yv(n){const r=[];return gv.forEach(o=>{const a=n.getValue(o);a!==void 0&&(r.push([o,a.get()]),a.set(o.startsWith("scale")?1:0))}),r}const wn={width:({x:n},{paddingLeft:r="0",paddingRight:o="0",boxSizing:a})=>{const u=n.max-n.min;return a==="border-box"?u:u-parseFloat(r)-parseFloat(o)},height:({y:n},{paddingTop:r="0",paddingBottom:o="0",boxSizing:a})=>{const u=n.max-n.min;return a==="border-box"?u:u-parseFloat(r)-parseFloat(o)},top:(n,{top:r})=>parseFloat(r),left:(n,{left:r})=>parseFloat(r),bottom:({y:n},{top:r})=>parseFloat(r)+(n.max-n.min),right:({x:n},{left:r})=>parseFloat(r)+(n.max-n.min),x:(n,{transform:r})=>$l(r,"x"),y:(n,{transform:r})=>$l(r,"y")};wn.translateX=wn.x;wn.translateY=wn.y;const Bn=new Set;let Hl=!1,Kl=!1,Gl=!1;function Up(){if(Kl){const n=Array.from(Bn).filter(a=>a.needsMeasurement),r=new Set(n.map(a=>a.element)),o=new Map;r.forEach(a=>{const u=yv(a);u.length&&(o.set(a,u),a.render())}),n.forEach(a=>a.measureInitialState()),r.forEach(a=>{a.render();const u=o.get(a);u&&u.forEach(([d,f])=>{var h;(h=a.getValue(d))==null||h.set(f)})}),n.forEach(a=>a.measureEndState()),n.forEach(a=>{a.suspendedScrollY!==void 0&&window.scrollTo(0,a.suspendedScrollY)})}Kl=!1,Hl=!1,Bn.forEach(n=>n.complete(Gl)),Bn.clear()}function $p(){Bn.forEach(n=>{n.readKeyframes(),n.needsMeasurement&&(Kl=!0)})}function vv(){Gl=!0,$p(),Up(),Gl=!1}class Su{constructor(r,o,a,u,d,f=!1){this.state="pending",this.isAsync=!1,this.needsMeasurement=!1,this.unresolvedKeyframes=[...r],this.onComplete=o,this.name=a,this.motionValue=u,this.element=d,this.isAsync=f}scheduleResolve(){this.state="scheduled",this.isAsync?(Bn.add(this),Hl||(Hl=!0,Te.read($p),Te.resolveKeyframes(Up))):(this.readKeyframes(),this.complete())}readKeyframes(){const{unresolvedKeyframes:r,name:o,element:a,motionValue:u}=this;if(r[0]===null){const d=u==null?void 0:u.get(),f=r[r.length-1];if(d!==void 0)r[0]=d;else if(a&&o){const h=a.readValue(o,f);h!=null&&(r[0]=h)}r[0]===void 0&&(r[0]=f),u&&d===void 0&&u.set(r[0])}cv(r)}setFinalKeyframe(){}measureInitialState(){}renderEndStyles(){}measureEndState(){}complete(r=!1){this.state="complete",this.onComplete(this.unresolvedKeyframes,this.finalKeyframe,r),Bn.delete(this)}cancel(){this.state==="scheduled"&&(Bn.delete(this),this.state="pending")}resume(){this.state==="pending"&&this.scheduleResolve()}}const xv=n=>n.startsWith("--");function Hp(n,r,o){xv(r)?n.style.setProperty(r,o):n.style[r]=o}const wv={};function Kp(n,r){const o=xp(n);return()=>wv[r]??o()}const Sv=Kp(()=>window.ScrollTimeline!==void 0,"scrollTimeline"),Gp=Kp(()=>{try{document.createElement("div").animate({opacity:0},{easing:"linear(0, 1)"})}catch{return!1}return!0},"linearEasing"),di=([n,r,o,a])=>`cubic-bezier(${n}, ${r}, ${o}, ${a})`,Zd={linear:"linear",ease:"ease",easeIn:"ease-in",easeOut:"ease-out",easeInOut:"ease-in-out",circIn:di([0,.65,.55,1]),circOut:di([.55,0,1,.45]),backIn:di([.31,.01,.66,-.59]),backOut:di([.33,1.53,.69,.99])};function Yp(n,r){if(n)return typeof n=="function"?Gp()?Op(n,r):"ease-out":Ap(n)?di(n):Array.isArray(n)?n.map(o=>Yp(o,r)||Zd.easeOut):Zd[n]}function kv(n,r,o,{delay:a=0,duration:u=300,repeat:d=0,repeatType:f="loop",ease:h="easeOut",times:g}={},y=void 0){const m={[r]:o};g&&(m.offset=g);const x=Yp(h,u);Array.isArray(x)&&(m.easing=x);const w={delay:a,duration:u,easing:Array.isArray(x)?"linear":x,fill:"both",iterations:d+1,direction:f==="reverse"?"alternate":"normal"};return y&&(w.pseudoElement=y),n.animate(m,w)}function Xp(n){return typeof n=="function"&&"applyToOptions"in n}function Tv({type:n,...r}){return Xp(n)&&Gp()?n.applyToOptions(r):(r.duration??(r.duration=300),r.ease??(r.ease="easeOut"),r)}class Qp extends wu{constructor(r){if(super(),this.finishedTime=null,this.isStopped=!1,this.manualStartTime=null,!r)return;const{element:o,name:a,keyframes:u,pseudoElement:d,allowFlatten:f=!1,finalKeyframe:h,onComplete:g}=r;this.isPseudoElement=!!d,this.allowFlatten=f,this.options=r,fu(typeof r.type!="string");const y=Tv(r);this.animation=kv(o,a,u,y,d),y.autoplay===!1&&this.animation.pause(),this.animation.onfinish=()=>{if(this.finishedTime=this.time,!d){const m=hs(u,this.options,h,this.speed);this.updateMotionValue&&this.updateMotionValue(m),Hp(o,a,m),this.animation.cancel()}g==null||g(),this.notifyFinished()}}play(){this.isStopped||(this.manualStartTime=null,this.animation.play(),this.state==="finished"&&this.updateFinished())}pause(){this.animation.pause()}complete(){var r,o;(o=(r=this.animation).finish)==null||o.call(r)}cancel(){try{this.animation.cancel()}catch{}}stop(){if(this.isStopped)return;this.isStopped=!0;const{state:r}=this;r==="idle"||r==="finished"||(this.updateMotionValue?this.updateMotionValue():this.commitStyles(),this.isPseudoElement||this.cancel())}commitStyles(){var o,a,u;const r=(o=this.options)==null?void 0:o.element;!this.isPseudoElement&&(r!=null&&r.isConnected)&&((u=(a=this.animation).commitStyles)==null||u.call(a))}get duration(){var o,a;const r=((a=(o=this.animation.effect)==null?void 0:o.getComputedTiming)==null?void 0:a.call(o).duration)||0;return St(Number(r))}get iterationDuration(){const{delay:r=0}=this.options||{};return this.duration+St(r)}get time(){return St(Number(this.animation.currentTime)||0)}set time(r){const o=this.finishedTime!==null;this.manualStartTime=null,this.finishedTime=null,this.animation.currentTime=pt(r),o&&this.animation.pause()}get speed(){return this.animation.playbackRate}set speed(r){r<0&&(this.finishedTime=null),this.animation.playbackRate=r}get state(){return this.finishedTime!==null?"finished":this.animation.playState}get startTime(){return this.manualStartTime??Number(this.animation.startTime)}set startTime(r){this.manualStartTime=this.animation.startTime=r}attachTimeline({timeline:r,rangeStart:o,rangeEnd:a,observe:u}){var d;return this.allowFlatten&&((d=this.animation.effect)==null||d.updateTiming({easing:"linear"})),this.animation.onfinish=null,r&&Sv()?(this.animation.timeline=r,o&&(this.animation.rangeStart=o),a&&(this.animation.rangeEnd=a),kt):u(this)}}const qp={anticipate:Pp,backInOut:Ep,circInOut:Rp};function Cv(n){return n in qp}function Ev(n){typeof n.ease=="string"&&Cv(n.ease)&&(n.ease=qp[n.ease])}const Sl=10;class Pv extends Qp{constructor(r){Ev(r),Wp(r),super(r),r.startTime!==void 0&&r.autoplay!==!1&&(this.startTime=r.startTime),this.options=r}updateMotionValue(r){const{motionValue:o,onUpdate:a,onComplete:u,element:d,...f}=this.options;if(!o)return;if(r!==void 0){o.set(r);return}const h=new as({...f,autoplay:!1}),g=Math.max(Sl,tt.now()-this.startTime),y=Wt(0,Sl,g-Sl),m=h.sample(g).value,{name:x}=this.options;d&&x&&Hp(d,x,m),o.setWithVelocity(h.sample(Math.max(0,g-y)).value,m,y),h.stop()}}const Jd=(n,r)=>r==="zIndex"?!1:!!(typeof n=="number"||Array.isArray(n)||typeof n=="string"&&(Lt.test(n)||n==="0")&&!n.startsWith("url("));function jv(n){const r=n[0];if(n.length===1)return!0;for(let o=0;oObject.hasOwnProperty.call(Element.prototype,"animate"));function Vv(n){var x;const{motionValue:r,name:o,repeatDelay:a,repeatType:u,damping:d,type:f,keyframes:h}=n;if(!(((x=r==null?void 0:r.owner)==null?void 0:x.current)instanceof HTMLElement))return!1;const{onUpdate:y,transformTemplate:m}=r.owner.getProps();return Lv()&&o&&(Zp.has(o)||Dv.has(o)&&Av(h))&&(o!=="transform"||!m)&&!y&&!a&&u!=="mirror"&&d!==0&&f!=="inertia"}const bv=40;class _v extends wu{constructor({autoplay:r=!0,delay:o=0,type:a="keyframes",repeat:u=0,repeatDelay:d=0,repeatType:f="loop",keyframes:h,name:g,motionValue:y,element:m,...x}){var E;super(),this.stop=()=>{var j,D;this._animation&&(this._animation.stop(),(j=this.stopTimeline)==null||j.call(this)),(D=this.keyframeResolver)==null||D.cancel()},this.createdAt=tt.now();const w={autoplay:r,delay:o,type:a,repeat:u,repeatDelay:d,repeatType:f,name:g,motionValue:y,element:m,...x},k=(m==null?void 0:m.KeyframeResolver)||Su;this.keyframeResolver=new k(h,(j,D,_)=>this.onKeyframesResolved(j,D,w,!_),g,y,m),(E=this.keyframeResolver)==null||E.scheduleResolve()}onKeyframesResolved(r,o,a,u){var _,b;this.keyframeResolver=void 0;const{name:d,type:f,velocity:h,delay:g,isHandoff:y,onUpdate:m}=a;this.resolvedAt=tt.now();let x=!0;Rv(r,d,f,h)||(x=!1,(Sn.instantAnimations||!g)&&(m==null||m(hs(r,a,o))),r[0]=r[r.length-1],Yl(a),a.repeat=0);const k={startTime:u?this.resolvedAt?this.resolvedAt-this.createdAt>bv?this.resolvedAt:this.createdAt:this.createdAt:void 0,finalKeyframe:o,...a,keyframes:r},E=x&&!y&&Vv(k),j=(b=(_=k.motionValue)==null?void 0:_.owner)==null?void 0:b.current;let D;if(E)try{D=new Pv({...k,element:j})}catch{D=new as(k)}else D=new as(k);D.finished.then(()=>{this.notifyFinished()}).catch(kt),this.pendingTimeline&&(this.stopTimeline=D.attachTimeline(this.pendingTimeline),this.pendingTimeline=void 0),this._animation=D}get finished(){return this._animation?this.animation.finished:this._finished}then(r,o){return this.finished.finally(r).then(()=>{})}get animation(){var r;return this._animation||((r=this.keyframeResolver)==null||r.resume(),vv()),this._animation}get duration(){return this.animation.duration}get iterationDuration(){return this.animation.iterationDuration}get time(){return this.animation.time}set time(r){this.animation.time=r}get speed(){return this.animation.speed}get state(){return this.animation.state}set speed(r){this.animation.speed=r}get startTime(){return this.animation.startTime}attachTimeline(r){return this._animation?this.stopTimeline=this.animation.attachTimeline(r):this.pendingTimeline=r,()=>this.stop()}play(){this.animation.play()}pause(){this.animation.pause()}complete(){this.animation.complete()}cancel(){var r;this._animation&&this.animation.cancel(),(r=this.keyframeResolver)==null||r.cancel()}}function Jp(n,r,o,a=0,u=1){const d=Array.from(n).sort((y,m)=>y.sortNodePosition(m)).indexOf(r),f=n.size,h=(f-1)*a;return typeof o=="function"?o(d,f):u===1?d*a:h-d*a}const Iv=/^var\(--(?:([\w-]+)|([\w-]+), ?([a-zA-Z\d ()%#.,-]+))\)/u;function zv(n){const r=Iv.exec(n);if(!r)return[,];const[,o,a,u]=r;return[`--${o??a}`,u]}function em(n,r,o=1){const[a,u]=zv(n);if(!a)return;const d=window.getComputedStyle(r).getPropertyValue(a);if(d){const f=d.trim();return gp(f)?parseFloat(f):f}return mu(u)?em(u,r,o+1):u}const Fv={type:"spring",stiffness:500,damping:25,restSpeed:10},Nv=n=>({type:"spring",stiffness:550,damping:n===0?2*Math.sqrt(550):30,restSpeed:10}),Ov={type:"keyframes",duration:.8},Bv={type:"keyframes",ease:[.25,.1,.35,1],duration:.3},Wv=(n,{keyframes:r})=>r.length>2?Ov:Cr.has(n)?n.startsWith("scale")?Nv(r[1]):Fv:Bv;function tm(n,r){if(n!=null&&n.inherit&&r){const{inherit:o,...a}=n;return{...r,...a}}return n}function ku(n,r){const o=(n==null?void 0:n[r])??(n==null?void 0:n.default)??n;return o!==n?tm(o,n):o}const Uv=new Set(["when","delay","delayChildren","staggerChildren","staggerDirection","repeat","repeatType","repeatDelay","from","elapsed"]);function $v(n){for(const r in n)if(!Uv.has(r))return!0;return!1}const Tu=(n,r,o,a={},u,d)=>f=>{const h=ku(a,n)||{},g=h.delay||a.delay||0;let{elapsed:y=0}=a;y=y-pt(g);const m={keyframes:Array.isArray(o)?o:[null,o],ease:"easeOut",velocity:r.getVelocity(),...h,delay:-y,onUpdate:w=>{r.set(w),h.onUpdate&&h.onUpdate(w)},onComplete:()=>{f(),h.onComplete&&h.onComplete()},name:n,motionValue:r,element:d?void 0:u};$v(h)||Object.assign(m,Wv(n,m)),m.duration&&(m.duration=pt(m.duration)),m.repeatDelay&&(m.repeatDelay=pt(m.repeatDelay)),m.from!==void 0&&(m.keyframes[0]=m.from);let x=!1;if((m.type===!1||m.duration===0&&!m.repeatDelay)&&(Yl(m),m.delay===0&&(x=!0)),(Sn.instantAnimations||Sn.skipAnimations||u!=null&&u.shouldSkipAnimations)&&(x=!0,Yl(m),m.delay=0),m.allowFlatten=!h.type&&!h.ease,x&&!d&&r.get()!==void 0){const w=hs(m.keyframes,h);if(w!==void 0){Te.update(()=>{m.onUpdate(w),m.onComplete()});return}}return h.isSync?new as(m):new _v(m)};function eh(n){const r=[{},{}];return n==null||n.values.forEach((o,a)=>{r[0][a]=o.get(),r[1][a]=o.getVelocity()}),r}function Cu(n,r,o,a){if(typeof r=="function"){const[u,d]=eh(a);r=r(o!==void 0?o:n.custom,u,d)}if(typeof r=="string"&&(r=n.variants&&n.variants[r]),typeof r=="function"){const[u,d]=eh(a);r=r(o!==void 0?o:n.custom,u,d)}return r}function Wn(n,r,o){const a=n.getProps();return Cu(a,r,o!==void 0?o:a.custom,n)}const nm=new Set(["width","height","top","left","right","bottom",...Tr]),th=30,Hv=n=>!isNaN(parseFloat(n));class Kv{constructor(r,o={}){this.canTrackVelocity=null,this.events={},this.updateAndNotify=a=>{var d;const u=tt.now();if(this.updatedAt!==u&&this.setPrevFrameValue(),this.prev=this.current,this.setCurrent(a),this.current!==this.prev&&((d=this.events.change)==null||d.notify(this.current),this.dependents))for(const f of this.dependents)f.dirty()},this.hasAnimated=!1,this.setCurrent(r),this.owner=o.owner}setCurrent(r){this.current=r,this.updatedAt=tt.now(),this.canTrackVelocity===null&&r!==void 0&&(this.canTrackVelocity=Hv(this.current))}setPrevFrameValue(r=this.current){this.prevFrameValue=r,this.prevUpdatedAt=this.updatedAt}onChange(r){return this.on("change",r)}on(r,o){this.events[r]||(this.events[r]=new du);const a=this.events[r].add(o);return r==="change"?()=>{a(),Te.read(()=>{this.events.change.getSize()||this.stop()})}:a}clearListeners(){for(const r in this.events)this.events[r].clear()}attach(r,o){this.passiveEffect=r,this.stopPassiveEffect=o}set(r){this.passiveEffect?this.passiveEffect(r,this.updateAndNotify):this.updateAndNotify(r)}setWithVelocity(r,o,a){this.set(o),this.prev=void 0,this.prevFrameValue=r,this.prevUpdatedAt=this.updatedAt-a}jump(r,o=!0){this.updateAndNotify(r),this.prev=r,this.prevUpdatedAt=this.prevFrameValue=void 0,o&&this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}dirty(){var r;(r=this.events.change)==null||r.notify(this.current)}addDependent(r){this.dependents||(this.dependents=new Set),this.dependents.add(r)}removeDependent(r){this.dependents&&this.dependents.delete(r)}get(){return this.current}getPrevious(){return this.prev}getVelocity(){const r=tt.now();if(!this.canTrackVelocity||this.prevFrameValue===void 0||r-this.updatedAt>th)return 0;const o=Math.min(this.updatedAt-this.prevUpdatedAt,th);return wp(parseFloat(this.current)-parseFloat(this.prevFrameValue),o)}start(r){return this.stop(),new Promise(o=>{this.hasAnimated=!0,this.animation=r(o),this.events.animationStart&&this.events.animationStart.notify()}).then(()=>{this.events.animationComplete&&this.events.animationComplete.notify(),this.clearAnimation()})}stop(){this.animation&&(this.animation.stop(),this.events.animationCancel&&this.events.animationCancel.notify()),this.clearAnimation()}isAnimating(){return!!this.animation}clearAnimation(){delete this.animation}destroy(){var r,o;(r=this.dependents)==null||r.clear(),(o=this.events.destroy)==null||o.notify(),this.clearListeners(),this.stop(),this.stopPassiveEffect&&this.stopPassiveEffect()}}function wr(n,r){return new Kv(n,r)}const Xl=n=>Array.isArray(n);function Gv(n,r,o){n.hasValue(r)?n.getValue(r).set(o):n.addValue(r,wr(o))}function Yv(n){return Xl(n)?n[n.length-1]||0:n}function Xv(n,r){const o=Wn(n,r);let{transitionEnd:a={},transition:u={},...d}=o||{};d={...d,...a};for(const f in d){const h=Yv(d[f]);Gv(n,f,h)}}const Ye=n=>!!(n&&n.getVelocity);function Qv(n){return!!(Ye(n)&&n.add)}function Ql(n,r){const o=n.getValue("willChange");if(Qv(o))return o.add(r);if(!o&&Sn.WillChange){const a=new Sn.WillChange("auto");n.addValue("willChange",a),a.add(r)}}function Eu(n){return n.replace(/([A-Z])/g,r=>`-${r.toLowerCase()}`)}const qv="framerAppearId",rm="data-"+Eu(qv);function im(n){return n.props[rm]}function Zv({protectedKeys:n,needsAnimating:r},o){const a=n.hasOwnProperty(o)&&r[o]!==!0;return r[o]=!1,a}function om(n,r,{delay:o=0,transitionOverride:a,type:u}={}){let{transition:d,transitionEnd:f,...h}=r;const g=n.getDefaultTransition();d=d?tm(d,g):g;const y=d==null?void 0:d.reduceMotion;a&&(d=a);const m=[],x=u&&n.animationState&&n.animationState.getState()[u];for(const w in h){const k=n.getValue(w,n.latestValues[w]??null),E=h[w];if(E===void 0||x&&Zv(x,w))continue;const j={delay:o,...ku(d||{},w)},D=k.get();if(D!==void 0&&!k.isAnimating()&&!Array.isArray(E)&&E===D&&!j.velocity){Te.update(()=>k.set(E));continue}let _=!1;if(window.MotionHandoffAnimation){const F=im(n);if(F){const M=window.MotionHandoffAnimation(F,w,Te);M!==null&&(j.startTime=M,_=!0)}}Ql(n,w);const b=y??n.shouldReduceMotion;k.start(Tu(w,k,E,b&&nm.has(w)?{type:!1}:j,n,_));const z=k.animation;z&&m.push(z)}if(f){const w=()=>Te.update(()=>{f&&Xv(n,f)});m.length?Promise.all(m).then(w):w()}return m}function ql(n,r,o={}){var g;const a=Wn(n,r,o.type==="exit"?(g=n.presenceContext)==null?void 0:g.custom:void 0);let{transition:u=n.getDefaultTransition()||{}}=a||{};o.transitionOverride&&(u=o.transitionOverride);const d=a?()=>Promise.all(om(n,a,o)):()=>Promise.resolve(),f=n.variantChildren&&n.variantChildren.size?(y=0)=>{const{delayChildren:m=0,staggerChildren:x,staggerDirection:w}=u;return Jv(n,r,y,m,x,w,o)}:()=>Promise.resolve(),{when:h}=u;if(h){const[y,m]=h==="beforeChildren"?[d,f]:[f,d];return y().then(()=>m())}else return Promise.all([d(),f(o.delay)])}function Jv(n,r,o=0,a=0,u=0,d=1,f){const h=[];for(const g of n.variantChildren)g.notify("AnimationStart",r),h.push(ql(g,r,{...f,delay:o+(typeof a=="function"?0:a)+Jp(n.variantChildren,g,a,u,d)}).then(()=>g.notify("AnimationComplete",r)));return Promise.all(h)}function e1(n,r,o={}){n.notify("AnimationStart",r);let a;if(Array.isArray(r)){const u=r.map(d=>ql(n,d,o));a=Promise.all(u)}else if(typeof r=="string")a=ql(n,r,o);else{const u=typeof r=="function"?Wn(n,r,o.custom):r;a=Promise.all(om(n,u,o))}return a.then(()=>{n.notify("AnimationComplete",r)})}const t1={test:n=>n==="auto",parse:n=>n},sm=n=>r=>r.test(n),am=[kr,ee,Bt,vn,jy,Py,t1],nh=n=>am.find(sm(n));function n1(n){return typeof n=="number"?n===0:n!==null?n==="none"||n==="0"||vp(n):!0}const r1=new Set(["brightness","contrast","saturate","opacity"]);function i1(n){const[r,o]=n.slice(0,-1).split("(");if(r==="drop-shadow")return n;const[a]=o.match(gu)||[];if(!a)return n;const u=o.replace(a,"");let d=r1.has(r)?1:0;return a!==o&&(d*=100),r+"("+d+u+")"}const o1=/\b([a-z-]*)\(.*?\)/gu,Zl={...Lt,getAnimatableNone:n=>{const r=n.match(o1);return r?r.map(i1).join(" "):n}},Jl={...Lt,getAnimatableNone:n=>{const r=Lt.parse(n);return Lt.createTransformer(n)(r.map(a=>typeof a=="number"?0:typeof a=="object"?{...a,alpha:1}:a))}},rh={...kr,transform:Math.round},s1={rotate:vn,rotateX:vn,rotateY:vn,rotateZ:vn,scale:Wo,scaleX:Wo,scaleY:Wo,scaleZ:Wo,skew:vn,skewX:vn,skewY:vn,distance:ee,translateX:ee,translateY:ee,translateZ:ee,x:ee,y:ee,z:ee,perspective:ee,transformPerspective:ee,opacity:vi,originX:Ud,originY:Ud,originZ:ee},Pu={borderWidth:ee,borderTopWidth:ee,borderRightWidth:ee,borderBottomWidth:ee,borderLeftWidth:ee,borderRadius:ee,borderTopLeftRadius:ee,borderTopRightRadius:ee,borderBottomRightRadius:ee,borderBottomLeftRadius:ee,width:ee,maxWidth:ee,height:ee,maxHeight:ee,top:ee,right:ee,bottom:ee,left:ee,inset:ee,insetBlock:ee,insetBlockStart:ee,insetBlockEnd:ee,insetInline:ee,insetInlineStart:ee,insetInlineEnd:ee,padding:ee,paddingTop:ee,paddingRight:ee,paddingBottom:ee,paddingLeft:ee,paddingBlock:ee,paddingBlockStart:ee,paddingBlockEnd:ee,paddingInline:ee,paddingInlineStart:ee,paddingInlineEnd:ee,margin:ee,marginTop:ee,marginRight:ee,marginBottom:ee,marginLeft:ee,marginBlock:ee,marginBlockStart:ee,marginBlockEnd:ee,marginInline:ee,marginInlineStart:ee,marginInlineEnd:ee,fontSize:ee,backgroundPositionX:ee,backgroundPositionY:ee,...s1,zIndex:rh,fillOpacity:vi,strokeOpacity:vi,numOctaves:rh},a1={...Pu,color:Fe,backgroundColor:Fe,outlineColor:Fe,fill:Fe,stroke:Fe,borderColor:Fe,borderTopColor:Fe,borderRightColor:Fe,borderBottomColor:Fe,borderLeftColor:Fe,filter:Zl,WebkitFilter:Zl,mask:Jl,WebkitMask:Jl},lm=n=>a1[n],l1=new Set([Zl,Jl]);function um(n,r){let o=lm(n);return l1.has(o)||(o=Lt),o.getAnimatableNone?o.getAnimatableNone(r):void 0}const u1=new Set(["auto","none","0"]);function c1(n,r,o){let a=0,u;for(;a{r.getValue(g).set(y)}),this.resolveNoneKeyframes()}}function cm(n,r,o){if(n==null)return[];if(n instanceof EventTarget)return[n];if(typeof n=="string"){let a=document;const u=(o==null?void 0:o[n])??a.querySelectorAll(n);return u?Array.from(u):[]}return Array.from(n).filter(a=>a!=null)}const fm=(n,r)=>r&&typeof n=="number"?r.transform(n):n;function Qo(n){return yp(n)&&"offsetHeight"in n&&!("ownerSVGElement"in n)}const{schedule:ju}=Dp(queueMicrotask,!1),Dt={x:!1,y:!1};function dm(){return Dt.x||Dt.y}function d1(n){return n==="x"||n==="y"?Dt[n]?null:(Dt[n]=!0,()=>{Dt[n]=!1}):Dt.x||Dt.y?null:(Dt.x=Dt.y=!0,()=>{Dt.x=Dt.y=!1})}function hm(n,r){const o=cm(n),a=new AbortController,u={passive:!0,...r,signal:a.signal};return[o,u,()=>a.abort()]}function h1(n){return!(n.pointerType==="touch"||dm())}function p1(n,r,o={}){const[a,u,d]=hm(n,o);return a.forEach(f=>{let h=!1,g=!1,y;const m=()=>{f.removeEventListener("pointerleave",E)},x=D=>{y&&(y(D),y=void 0),m()},w=D=>{h=!1,window.removeEventListener("pointerup",w),window.removeEventListener("pointercancel",w),g&&(g=!1,x(D))},k=()=>{h=!0,window.addEventListener("pointerup",w,u),window.addEventListener("pointercancel",w,u)},E=D=>{if(D.pointerType!=="touch"){if(h){g=!0;return}x(D)}},j=D=>{if(!h1(D))return;g=!1;const _=r(f,D);typeof _=="function"&&(y=_,f.addEventListener("pointerleave",E,u))};f.addEventListener("pointerenter",j,u),f.addEventListener("pointerdown",k,u)}),d}const pm=(n,r)=>r?n===r?!0:pm(n,r.parentElement):!1,Ru=n=>n.pointerType==="mouse"?typeof n.button!="number"||n.button<=0:n.isPrimary!==!1,m1=new Set(["BUTTON","INPUT","SELECT","TEXTAREA","A"]);function g1(n){return m1.has(n.tagName)||n.isContentEditable===!0}const y1=new Set(["INPUT","SELECT","TEXTAREA"]);function v1(n){return y1.has(n.tagName)||n.isContentEditable===!0}const qo=new WeakSet;function ih(n){return r=>{r.key==="Enter"&&n(r)}}function kl(n,r){n.dispatchEvent(new PointerEvent("pointer"+r,{isPrimary:!0,bubbles:!0}))}const x1=(n,r)=>{const o=n.currentTarget;if(!o)return;const a=ih(()=>{if(qo.has(o))return;kl(o,"down");const u=ih(()=>{kl(o,"up")}),d=()=>kl(o,"cancel");o.addEventListener("keyup",u,r),o.addEventListener("blur",d,r)});o.addEventListener("keydown",a,r),o.addEventListener("blur",()=>o.removeEventListener("keydown",a),r)};function oh(n){return Ru(n)&&!dm()}const sh=new WeakSet;function w1(n,r,o={}){const[a,u,d]=hm(n,o),f=h=>{const g=h.currentTarget;if(!oh(h)||sh.has(h))return;qo.add(g),o.stopPropagation&&sh.add(h);const y=r(g,h),m=(k,E)=>{window.removeEventListener("pointerup",x),window.removeEventListener("pointercancel",w),qo.has(g)&&qo.delete(g),oh(k)&&typeof y=="function"&&y(k,{success:E})},x=k=>{m(k,g===window||g===document||o.useGlobalTarget||pm(g,k.target))},w=k=>{m(k,!1)};window.addEventListener("pointerup",x,u),window.addEventListener("pointercancel",w,u)};return a.forEach(h=>{(o.useGlobalTarget?window:h).addEventListener("pointerdown",f,u),Qo(h)&&(h.addEventListener("focus",y=>x1(y,u)),!g1(h)&&!h.hasAttribute("tabindex")&&(h.tabIndex=0))}),d}function Mu(n){return yp(n)&&"ownerSVGElement"in n}const Zo=new WeakMap;let xn;const mm=(n,r,o)=>(a,u)=>u&&u[0]?u[0][n+"Size"]:Mu(a)&&"getBBox"in a?a.getBBox()[r]:a[o],S1=mm("inline","width","offsetWidth"),k1=mm("block","height","offsetHeight");function T1({target:n,borderBoxSize:r}){var o;(o=Zo.get(n))==null||o.forEach(a=>{a(n,{get width(){return S1(n,r)},get height(){return k1(n,r)}})})}function C1(n){n.forEach(T1)}function E1(){typeof ResizeObserver>"u"||(xn=new ResizeObserver(C1))}function P1(n,r){xn||E1();const o=cm(n);return o.forEach(a=>{let u=Zo.get(a);u||(u=new Set,Zo.set(a,u)),u.add(r),xn==null||xn.observe(a)}),()=>{o.forEach(a=>{const u=Zo.get(a);u==null||u.delete(r),u!=null&&u.size||xn==null||xn.unobserve(a)})}}const Jo=new Set;let yr;function j1(){yr=()=>{const n={get width(){return window.innerWidth},get height(){return window.innerHeight}};Jo.forEach(r=>r(n))},window.addEventListener("resize",yr)}function R1(n){return Jo.add(n),yr||j1(),()=>{Jo.delete(n),!Jo.size&&typeof yr=="function"&&(window.removeEventListener("resize",yr),yr=void 0)}}function ah(n,r){return typeof n=="function"?R1(n):P1(n,r)}function M1(n){return Mu(n)&&n.tagName==="svg"}const A1=[...am,Fe,Lt],D1=n=>A1.find(sm(n)),lh=()=>({translate:0,scale:1,origin:0,originPoint:0}),vr=()=>({x:lh(),y:lh()}),uh=()=>({min:0,max:0}),Be=()=>({x:uh(),y:uh()}),L1=new WeakMap;function ps(n){return n!==null&&typeof n=="object"&&typeof n.start=="function"}function xi(n){return typeof n=="string"||Array.isArray(n)}const Au=["animate","whileInView","whileFocus","whileHover","whileTap","whileDrag","exit"],Du=["initial",...Au];function ms(n){return ps(n.animate)||Du.some(r=>xi(n[r]))}function gm(n){return!!(ms(n)||n.variants)}function V1(n,r,o){for(const a in r){const u=r[a],d=o[a];if(Ye(u))n.addValue(a,u);else if(Ye(d))n.addValue(a,wr(u,{owner:n}));else if(d!==u)if(n.hasValue(a)){const f=n.getValue(a);f.liveStyle===!0?f.jump(u):f.hasAnimated||f.set(u)}else{const f=n.getStaticValue(a);n.addValue(a,wr(f!==void 0?f:u,{owner:n}))}}for(const a in o)r[a]===void 0&&n.removeValue(a);return r}const eu={current:null},ym={current:!1},b1=typeof window<"u";function _1(){if(ym.current=!0,!!b1)if(window.matchMedia){const n=window.matchMedia("(prefers-reduced-motion)"),r=()=>eu.current=n.matches;n.addEventListener("change",r),r()}else eu.current=!1}const ch=["AnimationStart","AnimationComplete","Update","BeforeLayoutMeasure","LayoutMeasure","LayoutAnimationStart","LayoutAnimationComplete"];let ls={};function vm(n){ls=n}function I1(){return ls}class z1{scrapeMotionValuesFromProps(r,o,a){return{}}constructor({parent:r,props:o,presenceContext:a,reducedMotionConfig:u,skipAnimations:d,blockInitialAnimation:f,visualState:h},g={}){this.current=null,this.children=new Set,this.isVariantNode=!1,this.isControllingVariants=!1,this.shouldReduceMotion=null,this.shouldSkipAnimations=!1,this.values=new Map,this.KeyframeResolver=Su,this.features={},this.valueSubscriptions=new Map,this.prevMotionValues={},this.hasBeenMounted=!1,this.events={},this.propEventSubscriptions={},this.notifyUpdate=()=>this.notify("Update",this.latestValues),this.render=()=>{this.current&&(this.triggerBuild(),this.renderInstance(this.current,this.renderState,this.props.style,this.projection))},this.renderScheduledAt=0,this.scheduleRender=()=>{const k=tt.now();this.renderScheduledAtthis.bindToMotionValue(d,u)),this.reducedMotionConfig==="never"?this.shouldReduceMotion=!1:this.reducedMotionConfig==="always"?this.shouldReduceMotion=!0:(ym.current||_1(),this.shouldReduceMotion=eu.current),this.shouldSkipAnimations=this.skipAnimationsConfig??!1,(a=this.parent)==null||a.addChild(this),this.update(this.props,this.presenceContext),this.hasBeenMounted=!0}unmount(){var r;this.projection&&this.projection.unmount(),kn(this.notifyUpdate),kn(this.render),this.valueSubscriptions.forEach(o=>o()),this.valueSubscriptions.clear(),this.removeFromVariantTree&&this.removeFromVariantTree(),(r=this.parent)==null||r.removeChild(this);for(const o in this.events)this.events[o].clear();for(const o in this.features){const a=this.features[o];a&&(a.unmount(),a.isMounted=!1)}this.current=null}addChild(r){this.children.add(r),this.enteringChildren??(this.enteringChildren=new Set),this.enteringChildren.add(r)}removeChild(r){this.children.delete(r),this.enteringChildren&&this.enteringChildren.delete(r)}bindToMotionValue(r,o){if(this.valueSubscriptions.has(r)&&this.valueSubscriptions.get(r)(),o.accelerate&&Zp.has(r)&&this.current instanceof HTMLElement){const{factory:f,keyframes:h,times:g,ease:y,duration:m}=o.accelerate,x=new Qp({element:this.current,name:r,keyframes:h,times:g,ease:y,duration:pt(m)}),w=f(x);this.valueSubscriptions.set(r,()=>{w(),x.cancel()});return}const a=Cr.has(r);a&&this.onBindTransform&&this.onBindTransform();const u=o.on("change",f=>{this.latestValues[r]=f,this.props.onUpdate&&Te.preRender(this.notifyUpdate),a&&this.projection&&(this.projection.isTransformDirty=!0),this.scheduleRender()});let d;typeof window<"u"&&window.MotionCheckAppearSync&&(d=window.MotionCheckAppearSync(this,r,o)),this.valueSubscriptions.set(r,()=>{u(),d&&d(),o.owner&&o.stop()})}sortNodePosition(r){return!this.current||!this.sortInstanceNodePosition||this.type!==r.type?0:this.sortInstanceNodePosition(this.current,r.current)}updateFeatures(){let r="animation";for(r in ls){const o=ls[r];if(!o)continue;const{isEnabled:a,Feature:u}=o;if(!this.features[r]&&u&&a(this.props)&&(this.features[r]=new u(this)),this.features[r]){const d=this.features[r];d.isMounted?d.update():(d.mount(),d.isMounted=!0)}}}triggerBuild(){this.build(this.renderState,this.latestValues,this.props)}measureViewportBox(){return this.current?this.measureInstanceViewportBox(this.current,this.props):Be()}getStaticValue(r){return this.latestValues[r]}setStaticValue(r,o){this.latestValues[r]=o}update(r,o){(r.transformTemplate||this.props.transformTemplate)&&this.scheduleRender(),this.prevProps=this.props,this.props=r,this.prevPresenceContext=this.presenceContext,this.presenceContext=o;for(let a=0;ao.variantChildren.delete(r)}addValue(r,o){const a=this.values.get(r);o!==a&&(a&&this.removeValue(r),this.bindToMotionValue(r,o),this.values.set(r,o),this.latestValues[r]=o.get())}removeValue(r){this.values.delete(r);const o=this.valueSubscriptions.get(r);o&&(o(),this.valueSubscriptions.delete(r)),delete this.latestValues[r],this.removeValueFromRenderState(r,this.renderState)}hasValue(r){return this.values.has(r)}getValue(r,o){if(this.props.values&&this.props.values[r])return this.props.values[r];let a=this.values.get(r);return a===void 0&&o!==void 0&&(a=wr(o===null?void 0:o,{owner:this}),this.addValue(r,a)),a}readValue(r,o){let a=this.latestValues[r]!==void 0||!this.current?this.latestValues[r]:this.getBaseTargetFromProps(this.props,r)??this.readValueFromInstance(this.current,r,this.options);return a!=null&&(typeof a=="string"&&(gp(a)||vp(a))?a=parseFloat(a):!D1(a)&&Lt.test(o)&&(a=um(r,o)),this.setBaseTarget(r,Ye(a)?a.get():a)),Ye(a)?a.get():a}setBaseTarget(r,o){this.baseTarget[r]=o}getBaseTarget(r){var d;const{initial:o}=this.props;let a;if(typeof o=="string"||typeof o=="object"){const f=Cu(this.props,o,(d=this.presenceContext)==null?void 0:d.custom);f&&(a=f[r])}if(o&&a!==void 0)return a;const u=this.getBaseTargetFromProps(this.props,r);return u!==void 0&&!Ye(u)?u:this.initialValues[r]!==void 0&&a===void 0?void 0:this.baseTarget[r]}on(r,o){return this.events[r]||(this.events[r]=new du),this.events[r].add(o)}notify(r,...o){this.events[r]&&this.events[r].notify(...o)}scheduleRenderMicrotask(){ju.render(this.render)}}class xm extends z1{constructor(){super(...arguments),this.KeyframeResolver=f1}sortInstanceNodePosition(r,o){return r.compareDocumentPosition(o)&2?1:-1}getBaseTargetFromProps(r,o){const a=r.style;return a?a[o]:void 0}removeValueFromRenderState(r,{vars:o,style:a}){delete o[r],delete a[r]}handleChildMotionValue(){this.childSubscription&&(this.childSubscription(),delete this.childSubscription);const{children:r}=this.props;Ye(r)&&(this.childSubscription=r.on("change",o=>{this.current&&(this.current.textContent=`${o}`)}))}}class Tn{constructor(r){this.isMounted=!1,this.node=r}update(){}}function wm({top:n,left:r,right:o,bottom:a}){return{x:{min:r,max:o},y:{min:n,max:a}}}function F1({x:n,y:r}){return{top:r.min,right:n.max,bottom:r.max,left:n.min}}function N1(n,r){if(!r)return n;const o=r({x:n.left,y:n.top}),a=r({x:n.right,y:n.bottom});return{top:o.y,left:o.x,bottom:a.y,right:a.x}}function Tl(n){return n===void 0||n===1}function tu({scale:n,scaleX:r,scaleY:o}){return!Tl(n)||!Tl(r)||!Tl(o)}function Fn(n){return tu(n)||Sm(n)||n.z||n.rotate||n.rotateX||n.rotateY||n.skewX||n.skewY}function Sm(n){return fh(n.x)||fh(n.y)}function fh(n){return n&&n!=="0%"}function us(n,r,o){const a=n-o,u=r*a;return o+u}function dh(n,r,o,a,u){return u!==void 0&&(n=us(n,u,a)),us(n,o,a)+r}function nu(n,r=0,o=1,a,u){n.min=dh(n.min,r,o,a,u),n.max=dh(n.max,r,o,a,u)}function km(n,{x:r,y:o}){nu(n.x,r.translate,r.scale,r.originPoint),nu(n.y,o.translate,o.scale,o.originPoint)}const hh=.999999999999,ph=1.0000000000001;function O1(n,r,o,a=!1){var h;const u=o.length;if(!u)return;r.x=r.y=1;let d,f;for(let g=0;ghh&&(r.x=1),r.yhh&&(r.y=1)}function Nt(n,r){n.min+=r,n.max+=r}function mh(n,r,o,a,u=.5){const d=Re(n.min,n.max,u);nu(n,r,o,d,a)}function gh(n,r){return typeof n=="string"?parseFloat(n)/100*(r.max-r.min):n}function es(n,r,o){const a=o??n;mh(n.x,gh(r.x,a.x),r.scaleX,r.scale,r.originX),mh(n.y,gh(r.y,a.y),r.scaleY,r.scale,r.originY)}function Tm(n,r){return wm(N1(n.getBoundingClientRect(),r))}function B1(n,r,o){const a=Tm(n,o),{scroll:u}=r;return u&&(Nt(a.x,u.offset.x),Nt(a.y,u.offset.y)),a}const W1={x:"translateX",y:"translateY",z:"translateZ",transformPerspective:"perspective"},U1=Tr.length;function $1(n,r,o){let a="",u=!0;for(let d=0;d{if(!r.target)return n;if(typeof n=="string")if(ee.test(n))n=parseFloat(n);else return n;const o=yh(n,r.target.x),a=yh(n,r.target.y);return`${o}% ${a}%`}},H1={correct:(n,{treeScale:r,projectionDelta:o})=>{const a=n,u=Lt.parse(n);if(u.length>5)return a;const d=Lt.createTransformer(n),f=typeof u[0]!="number"?1:0,h=o.x.scale*r.x,g=o.y.scale*r.y;u[0+f]/=h,u[1+f]/=g;const y=Re(h,g,.5);return typeof u[2+f]=="number"&&(u[2+f]/=y),typeof u[3+f]=="number"&&(u[3+f]/=y),d(u)}},ru={borderRadius:{...fi,applyTo:["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"]},borderTopLeftRadius:fi,borderTopRightRadius:fi,borderBottomLeftRadius:fi,borderBottomRightRadius:fi,boxShadow:H1};function Em(n,{layout:r,layoutId:o}){return Cr.has(n)||n.startsWith("origin")||(r||o!==void 0)&&(!!ru[n]||n==="opacity")}function Vu(n,r,o){var f;const a=n.style,u=r==null?void 0:r.style,d={};if(!a)return d;for(const h in a)(Ye(a[h])||u&&Ye(u[h])||Em(h,n)||((f=o==null?void 0:o.getValue(h))==null?void 0:f.liveStyle)!==void 0)&&(d[h]=a[h]);return d}function K1(n){return window.getComputedStyle(n)}class G1 extends xm{constructor(){super(...arguments),this.type="html",this.renderInstance=Cm}readValueFromInstance(r,o){var a;if(Cr.has(o))return(a=this.projection)!=null&&a.isProjecting?Ul(o):hv(r,o);{const u=K1(r),d=(Vp(o)?u.getPropertyValue(o):u[o])||0;return typeof d=="string"?d.trim():d}}measureInstanceViewportBox(r,{transformPagePoint:o}){return Tm(r,o)}build(r,o,a){Lu(r,o,a.transformTemplate)}scrapeMotionValuesFromProps(r,o,a){return Vu(r,o,a)}}const Y1={offset:"stroke-dashoffset",array:"stroke-dasharray"},X1={offset:"strokeDashoffset",array:"strokeDasharray"};function Q1(n,r,o=1,a=0,u=!0){n.pathLength=1;const d=u?Y1:X1;n[d.offset]=`${-a}`,n[d.array]=`${r} ${o}`}const q1=["offsetDistance","offsetPath","offsetRotate","offsetAnchor"];function Pm(n,{attrX:r,attrY:o,attrScale:a,pathLength:u,pathSpacing:d=1,pathOffset:f=0,...h},g,y,m){if(Lu(n,h,y),g){n.style.viewBox&&(n.attrs.viewBox=n.style.viewBox);return}n.attrs=n.style,n.style={};const{attrs:x,style:w}=n;x.transform&&(w.transform=x.transform,delete x.transform),(w.transform||x.transformOrigin)&&(w.transformOrigin=x.transformOrigin??"50% 50%",delete x.transformOrigin),w.transform&&(w.transformBox=(m==null?void 0:m.transformBox)??"fill-box",delete x.transformBox);for(const k of q1)x[k]!==void 0&&(w[k]=x[k],delete x[k]);r!==void 0&&(x.x=r),o!==void 0&&(x.y=o),a!==void 0&&(x.scale=a),u!==void 0&&Q1(x,u,d,f,!1)}const jm=new Set(["baseFrequency","diffuseConstant","kernelMatrix","kernelUnitLength","keySplines","keyTimes","limitingConeAngle","markerHeight","markerWidth","numOctaves","targetX","targetY","surfaceScale","specularConstant","specularExponent","stdDeviation","tableValues","viewBox","gradientTransform","pathLength","startOffset","textLength","lengthAdjust"]),Rm=n=>typeof n=="string"&&n.toLowerCase()==="svg";function Z1(n,r,o,a){Cm(n,r,void 0,a);for(const u in r.attrs)n.setAttribute(jm.has(u)?u:Eu(u),r.attrs[u])}function Mm(n,r,o){const a=Vu(n,r,o);for(const u in n)if(Ye(n[u])||Ye(r[u])){const d=Tr.indexOf(u)!==-1?"attr"+u.charAt(0).toUpperCase()+u.substring(1):u;a[d]=n[u]}return a}class J1 extends xm{constructor(){super(...arguments),this.type="svg",this.isSVGTag=!1,this.measureInstanceViewportBox=Be}getBaseTargetFromProps(r,o){return r[o]}readValueFromInstance(r,o){if(Cr.has(o)){const a=lm(o);return a&&a.default||0}return o=jm.has(o)?o:Eu(o),r.getAttribute(o)}scrapeMotionValuesFromProps(r,o,a){return Mm(r,o,a)}build(r,o,a){Pm(r,o,this.isSVGTag,a.transformTemplate,a.style)}renderInstance(r,o,a,u){Z1(r,o,a,u)}mount(r){this.isSVGTag=Rm(r.tagName),super.mount(r)}}const ex=Du.length;function Am(n){if(!n)return;if(!n.isControllingVariants){const o=n.parent?Am(n.parent)||{}:{};return n.props.initial!==void 0&&(o.initial=n.props.initial),o}const r={};for(let o=0;oPromise.all(r.map(({animation:o,options:a})=>e1(n,o,a)))}function ix(n){let r=rx(n),o=vh(),a=!0,u=!1;const d=y=>(m,x)=>{var k;const w=Wn(n,x,y==="exit"?(k=n.presenceContext)==null?void 0:k.custom:void 0);if(w){const{transition:E,transitionEnd:j,...D}=w;m={...m,...D,...j}}return m};function f(y){r=y(n)}function h(y){const{props:m}=n,x=Am(n.parent)||{},w=[],k=new Set;let E={},j=1/0;for(let _=0;_j&&M,G=!1;const se=Array.isArray(F)?F:[F];let ue=se.reduce(d(b),{});H===!1&&(ue={});const{prevResolvedValues:ge={}}=z,we={...ge,...ue},ce=U=>{Y=!0,k.has(U)&&(G=!0,k.delete(U)),z.needsAnimating[U]=!0;const Z=n.getValue(U);Z&&(Z.liveStyle=!1)};for(const U in we){const Z=ue[U],X=ge[U];if(E.hasOwnProperty(U))continue;let P=!1;Xl(Z)&&Xl(X)?P=!Dm(Z,X):P=Z!==X,P?Z!=null?ce(U):k.add(U):Z!==void 0&&k.has(U)?ce(U):z.protectedKeys[U]=!0}z.prevProp=F,z.prevResolvedValues=ue,z.isActive&&(E={...E,...ue}),(a||u)&&n.blockInitialAnimation&&(Y=!1);const he=I&&N;Y&&(!he||G)&&w.push(...se.map(U=>{const Z={type:b};if(typeof U=="string"&&(a||u)&&!he&&n.manuallyAnimateOnMount&&n.parent){const{parent:X}=n,P=Wn(X,U);if(X.enteringChildren&&P){const{delayChildren:O}=P.transition||{};Z.delay=Jp(X.enteringChildren,n,O)}}return{animation:U,options:Z}}))}if(k.size){const _={};if(typeof m.initial!="boolean"){const b=Wn(n,Array.isArray(m.initial)?m.initial[0]:m.initial);b&&b.transition&&(_.transition=b.transition)}k.forEach(b=>{const z=n.getBaseTarget(b),F=n.getValue(b);F&&(F.liveStyle=!0),_[b]=z??null}),w.push({animation:_})}let D=!!w.length;return a&&(m.initial===!1||m.initial===m.animate)&&!n.manuallyAnimateOnMount&&(D=!1),a=!1,u=!1,D?r(w):Promise.resolve()}function g(y,m){var w;if(o[y].isActive===m)return Promise.resolve();(w=n.variantChildren)==null||w.forEach(k=>{var E;return(E=k.animationState)==null?void 0:E.setActive(y,m)}),o[y].isActive=m;const x=h(y);for(const k in o)o[k].protectedKeys={};return x}return{animateChanges:h,setActive:g,setAnimateFunction:f,getState:()=>o,reset:()=>{o=vh(),u=!0}}}function ox(n,r){return typeof r=="string"?r!==n:Array.isArray(r)?!Dm(r,n):!1}function zn(n=!1){return{isActive:n,protectedKeys:{},needsAnimating:{},prevResolvedValues:{}}}function vh(){return{animate:zn(!0),whileInView:zn(),whileHover:zn(),whileTap:zn(),whileDrag:zn(),whileFocus:zn(),exit:zn()}}function iu(n,r){n.min=r.min,n.max=r.max}function At(n,r){iu(n.x,r.x),iu(n.y,r.y)}function xh(n,r){n.translate=r.translate,n.scale=r.scale,n.originPoint=r.originPoint,n.origin=r.origin}const Lm=1e-4,sx=1-Lm,ax=1+Lm,Vm=.01,lx=0-Vm,ux=0+Vm;function nt(n){return n.max-n.min}function cx(n,r,o){return Math.abs(n-r)<=o}function wh(n,r,o,a=.5){n.origin=a,n.originPoint=Re(r.min,r.max,n.origin),n.scale=nt(o)/nt(r),n.translate=Re(o.min,o.max,n.origin)-n.originPoint,(n.scale>=sx&&n.scale<=ax||isNaN(n.scale))&&(n.scale=1),(n.translate>=lx&&n.translate<=ux||isNaN(n.translate))&&(n.translate=0)}function mi(n,r,o,a){wh(n.x,r.x,o.x,a?a.originX:void 0),wh(n.y,r.y,o.y,a?a.originY:void 0)}function Sh(n,r,o,a=0){const u=a?Re(o.min,o.max,a):o.min;n.min=u+r.min,n.max=n.min+nt(r)}function fx(n,r,o,a){Sh(n.x,r.x,o.x,a==null?void 0:a.x),Sh(n.y,r.y,o.y,a==null?void 0:a.y)}function kh(n,r,o,a=0){const u=a?Re(o.min,o.max,a):o.min;n.min=r.min-u,n.max=n.min+nt(r)}function cs(n,r,o,a){kh(n.x,r.x,o.x,a==null?void 0:a.x),kh(n.y,r.y,o.y,a==null?void 0:a.y)}function Th(n,r,o,a,u){return n-=r,n=us(n,1/o,a),u!==void 0&&(n=us(n,1/u,a)),n}function dx(n,r=0,o=1,a=.5,u,d=n,f=n){if(Bt.test(r)&&(r=parseFloat(r),r=Re(f.min,f.max,r/100)-f.min),typeof r!="number")return;let h=Re(d.min,d.max,a);n===d&&(h-=r),n.min=Th(n.min,r,o,h,u),n.max=Th(n.max,r,o,h,u)}function Ch(n,r,[o,a,u],d,f){dx(n,r[o],r[a],r[u],r.scale,d,f)}const hx=["x","scaleX","originX"],px=["y","scaleY","originY"];function Eh(n,r,o,a){Ch(n.x,r,hx,o?o.x:void 0,a?a.x:void 0),Ch(n.y,r,px,o?o.y:void 0,a?a.y:void 0)}function Ph(n){return n.translate===0&&n.scale===1}function bm(n){return Ph(n.x)&&Ph(n.y)}function jh(n,r){return n.min===r.min&&n.max===r.max}function mx(n,r){return jh(n.x,r.x)&&jh(n.y,r.y)}function Rh(n,r){return Math.round(n.min)===Math.round(r.min)&&Math.round(n.max)===Math.round(r.max)}function _m(n,r){return Rh(n.x,r.x)&&Rh(n.y,r.y)}function Mh(n){return nt(n.x)/nt(n.y)}function Ah(n,r){return n.translate===r.translate&&n.scale===r.scale&&n.originPoint===r.originPoint}function Ft(n){return[n("x"),n("y")]}function gx(n,r,o){let a="";const u=n.x.translate/r.x,d=n.y.translate/r.y,f=(o==null?void 0:o.z)||0;if((u||d||f)&&(a=`translate3d(${u}px, ${d}px, ${f}px) `),(r.x!==1||r.y!==1)&&(a+=`scale(${1/r.x}, ${1/r.y}) `),o){const{transformPerspective:y,rotate:m,rotateX:x,rotateY:w,skewX:k,skewY:E}=o;y&&(a=`perspective(${y}px) ${a}`),m&&(a+=`rotate(${m}deg) `),x&&(a+=`rotateX(${x}deg) `),w&&(a+=`rotateY(${w}deg) `),k&&(a+=`skewX(${k}deg) `),E&&(a+=`skewY(${E}deg) `)}const h=n.x.scale*r.x,g=n.y.scale*r.y;return(h!==1||g!==1)&&(a+=`scale(${h}, ${g})`),a||"none"}const Im=["borderTopLeftRadius","borderTopRightRadius","borderBottomLeftRadius","borderBottomRightRadius"],yx=Im.length,Dh=n=>typeof n=="string"?parseFloat(n):n,Lh=n=>typeof n=="number"||ee.test(n);function vx(n,r,o,a,u,d){u?(n.opacity=Re(0,o.opacity??1,xx(a)),n.opacityExit=Re(r.opacity??1,0,wx(a))):d&&(n.opacity=Re(r.opacity??1,o.opacity??1,a));for(let f=0;far?1:o(yi(n,r,a))}function Sx(n,r,o){const a=Ye(n)?n:wr(n);return a.start(Tu("",a,r,o)),a.animation}function wi(n,r,o,a={passive:!0}){return n.addEventListener(r,o,a),()=>n.removeEventListener(r,o)}const kx=(n,r)=>n.depth-r.depth;class Tx{constructor(){this.children=[],this.isDirty=!1}add(r){cu(this.children,r),this.isDirty=!0}remove(r){rs(this.children,r),this.isDirty=!0}forEach(r){this.isDirty&&this.children.sort(kx),this.isDirty=!1,this.children.forEach(r)}}function Cx(n,r){const o=tt.now(),a=({timestamp:u})=>{const d=u-o;d>=r&&(kn(a),n(d-r))};return Te.setup(a,!0),()=>kn(a)}function ts(n){return Ye(n)?n.get():n}class Ex{constructor(){this.members=[]}add(r){cu(this.members,r);for(let o=this.members.length-1;o>=0;o--){const a=this.members[o];if(a===r||a===this.lead||a===this.prevLead)continue;const u=a.instance;(!u||u.isConnected===!1)&&!a.snapshot&&(rs(this.members,a),a.unmount())}r.scheduleRender()}remove(r){if(rs(this.members,r),r===this.prevLead&&(this.prevLead=void 0),r===this.lead){const o=this.members[this.members.length-1];o&&this.promote(o)}}relegate(r){var o;for(let a=this.members.indexOf(r)-1;a>=0;a--){const u=this.members[a];if(u.isPresent!==!1&&((o=u.instance)==null?void 0:o.isConnected)!==!1)return this.promote(u),!0}return!1}promote(r,o){var u;const a=this.lead;if(r!==a&&(this.prevLead=a,this.lead=r,r.show(),a)){a.updateSnapshot(),r.scheduleRender();const{layoutDependency:d}=a.options,{layoutDependency:f}=r.options;(d===void 0||d!==f)&&(r.resumeFrom=a,o&&(a.preserveOpacity=!0),a.snapshot&&(r.snapshot=a.snapshot,r.snapshot.latestValues=a.animationValues||a.latestValues),(u=r.root)!=null&&u.isUpdating&&(r.isLayoutDirty=!0)),r.options.crossfade===!1&&a.hide()}}exitAnimationComplete(){this.members.forEach(r=>{var o,a,u,d,f;(a=(o=r.options).onExitComplete)==null||a.call(o),(f=(u=r.resumingFrom)==null?void 0:(d=u.options).onExitComplete)==null||f.call(d)})}scheduleRender(){this.members.forEach(r=>r.instance&&r.scheduleRender(!1))}removeLeadSnapshot(){var r;(r=this.lead)!=null&&r.snapshot&&(this.lead.snapshot=void 0)}}const ns={hasAnimatedSinceResize:!0,hasEverUpdated:!1},Cl=["","X","Y","Z"],Px=1e3;let jx=0;function El(n,r,o,a){const{latestValues:u}=r;u[n]&&(o[n]=u[n],r.setStaticValue(n,0),a&&(a[n]=0))}function Fm(n){if(n.hasCheckedOptimisedAppear=!0,n.root===n)return;const{visualElement:r}=n.options;if(!r)return;const o=im(r);if(window.MotionHasOptimisedAnimation(o,"transform")){const{layout:u,layoutId:d}=n.options;window.MotionCancelOptimisedAnimation(o,"transform",Te,!(u||d))}const{parent:a}=n;a&&!a.hasCheckedOptimisedAppear&&Fm(a)}function Nm({attachResizeListener:n,defaultParent:r,measureScroll:o,checkIsScrollRoot:a,resetTransform:u}){return class{constructor(f={},h=r==null?void 0:r()){this.id=jx++,this.animationId=0,this.animationCommitId=0,this.children=new Set,this.options={},this.isTreeAnimating=!1,this.isAnimationBlocked=!1,this.isLayoutDirty=!1,this.isProjectionDirty=!1,this.isSharedProjectionDirty=!1,this.isTransformDirty=!1,this.updateManuallyBlocked=!1,this.updateBlockedByResize=!1,this.isUpdating=!1,this.isSVG=!1,this.needsReset=!1,this.shouldResetTransform=!1,this.hasCheckedOptimisedAppear=!1,this.treeScale={x:1,y:1},this.eventHandlers=new Map,this.hasTreeAnimated=!1,this.layoutVersion=0,this.updateScheduled=!1,this.scheduleUpdate=()=>this.update(),this.projectionUpdateScheduled=!1,this.checkUpdateFailed=()=>{this.isUpdating&&(this.isUpdating=!1,this.clearAllSnapshots())},this.updateProjection=()=>{this.projectionUpdateScheduled=!1,this.nodes.forEach(Ax),this.nodes.forEach(Ix),this.nodes.forEach(zx),this.nodes.forEach(Dx)},this.resolvedRelativeTargetAt=0,this.linkedParentVersion=0,this.hasProjected=!1,this.isVisible=!0,this.animationProgress=0,this.sharedNodes=new Map,this.latestValues=f,this.root=h?h.root||h:this,this.path=h?[...h.path,h]:[],this.parent=h,this.depth=h?h.depth+1:0;for(let g=0;gthis.root.updateBlockedByResize=!1;Te.read(()=>{x=window.innerWidth}),n(f,()=>{const k=window.innerWidth;k!==x&&(x=k,this.root.updateBlockedByResize=!0,m&&m(),m=Cx(w,250),ns.hasAnimatedSinceResize&&(ns.hasAnimatedSinceResize=!1,this.nodes.forEach(Ih)))})}h&&this.root.registerSharedNode(h,this),this.options.animate!==!1&&y&&(h||g)&&this.addEventListener("didUpdate",({delta:m,hasLayoutChanged:x,hasRelativeLayoutChanged:w,layout:k})=>{if(this.isTreeAnimationBlocked()){this.target=void 0,this.relativeTarget=void 0;return}const E=this.options.transition||y.getDefaultTransition()||Wx,{onLayoutAnimationStart:j,onLayoutAnimationComplete:D}=y.getProps(),_=!this.targetLayout||!_m(this.targetLayout,k),b=!x&&w;if(this.options.layoutRoot||this.resumeFrom||b||x&&(_||!this.currentAnimation)){this.resumeFrom&&(this.resumingFrom=this.resumeFrom,this.resumingFrom.resumingFrom=void 0);const z={...ku(E,"layout"),onPlay:j,onComplete:D};(y.shouldReduceMotion||this.options.layoutRoot)&&(z.delay=0,z.type=!1),this.startAnimation(z),this.setAnimationOrigin(m,b)}else x||Ih(this),this.isLead()&&this.options.onExitComplete&&this.options.onExitComplete();this.targetLayout=k})}unmount(){this.options.layoutId&&this.willUpdate(),this.root.nodes.remove(this);const f=this.getStack();f&&f.remove(this),this.parent&&this.parent.children.delete(this),this.instance=void 0,this.eventHandlers.clear(),kn(this.updateProjection)}blockUpdate(){this.updateManuallyBlocked=!0}unblockUpdate(){this.updateManuallyBlocked=!1}isUpdateBlocked(){return this.updateManuallyBlocked||this.updateBlockedByResize}isTreeAnimationBlocked(){return this.isAnimationBlocked||this.parent&&this.parent.isTreeAnimationBlocked()||!1}startUpdate(){this.isUpdateBlocked()||(this.isUpdating=!0,this.nodes&&this.nodes.forEach(Fx),this.animationId++)}getTransformTemplate(){const{visualElement:f}=this.options;return f&&f.getProps().transformTemplate}willUpdate(f=!0){if(this.root.hasTreeAnimated=!0,this.root.isUpdateBlocked()){this.options.onExitComplete&&this.options.onExitComplete();return}if(window.MotionCancelOptimisedAnimation&&!this.hasCheckedOptimisedAppear&&Fm(this),!this.root.isUpdating&&this.root.startUpdate(),this.isLayoutDirty)return;this.isLayoutDirty=!0;for(let m=0;m{this.isLayoutDirty?this.root.didUpdate():this.root.checkUpdateFailed()})}updateSnapshot(){this.snapshot||!this.instance||(this.snapshot=this.measure(),this.snapshot&&!nt(this.snapshot.measuredBox.x)&&!nt(this.snapshot.measuredBox.y)&&(this.snapshot=void 0))}updateLayout(){if(!this.instance||(this.updateScroll(),!(this.options.alwaysMeasureLayout&&this.isLead())&&!this.isLayoutDirty))return;if(this.resumeFrom&&!this.resumeFrom.instance)for(let g=0;g{const M=F/1e3;zh(x.x,f.x,M),zh(x.y,f.y,M),this.setTargetDelta(x),this.relativeTarget&&this.relativeTargetOrigin&&this.layout&&this.relativeParent&&this.relativeParent.layout&&(cs(w,this.layout.layoutBox,this.relativeParent.layout.layoutBox,this.options.layoutAnchor||void 0),Ox(this.relativeTarget,this.relativeTargetOrigin,w,M),z&&mx(this.relativeTarget,z)&&(this.isProjectionDirty=!1),z||(z=Be()),At(z,this.relativeTarget)),j&&(this.animationValues=m,vx(m,y,this.latestValues,M,b,_)),this.root.scheduleUpdateProjection(),this.scheduleRender(),this.animationProgress=M},this.mixTargetDelta(this.options.layoutRoot?1e3:0)}startAnimation(f){var h,g,y;this.notifyListeners("animationStart"),(h=this.currentAnimation)==null||h.stop(),(y=(g=this.resumingFrom)==null?void 0:g.currentAnimation)==null||y.stop(),this.pendingAnimation&&(kn(this.pendingAnimation),this.pendingAnimation=void 0),this.pendingAnimation=Te.update(()=>{ns.hasAnimatedSinceResize=!0,this.motionValue||(this.motionValue=wr(0)),this.motionValue.jump(0,!1),this.currentAnimation=Sx(this.motionValue,[0,1e3],{...f,velocity:0,isSync:!0,onUpdate:m=>{this.mixTargetDelta(m),f.onUpdate&&f.onUpdate(m)},onStop:()=>{},onComplete:()=>{f.onComplete&&f.onComplete(),this.completeAnimation()}}),this.resumingFrom&&(this.resumingFrom.currentAnimation=this.currentAnimation),this.pendingAnimation=void 0})}completeAnimation(){this.resumingFrom&&(this.resumingFrom.currentAnimation=void 0,this.resumingFrom.preserveOpacity=void 0);const f=this.getStack();f&&f.exitAnimationComplete(),this.resumingFrom=this.currentAnimation=this.animationValues=void 0,this.notifyListeners("animationComplete")}finishAnimation(){this.currentAnimation&&(this.mixTargetDelta&&this.mixTargetDelta(Px),this.currentAnimation.stop()),this.completeAnimation()}applyTransformsToTarget(){const f=this.getLead();let{targetWithTransforms:h,target:g,layout:y,latestValues:m}=f;if(!(!h||!g||!y)){if(this!==f&&this.layout&&y&&Om(this.options.animationType,this.layout.layoutBox,y.layoutBox)){g=this.target||Be();const x=nt(this.layout.layoutBox.x);g.x.min=f.target.x.min,g.x.max=g.x.min+x;const w=nt(this.layout.layoutBox.y);g.y.min=f.target.y.min,g.y.max=g.y.min+w}At(h,g),es(h,m),mi(this.projectionDeltaWithTransform,this.layoutCorrected,h,m)}}registerSharedNode(f,h){this.sharedNodes.has(f)||this.sharedNodes.set(f,new Ex),this.sharedNodes.get(f).add(h);const y=h.options.initialPromotionConfig;h.promote({transition:y?y.transition:void 0,preserveFollowOpacity:y&&y.shouldPreserveFollowOpacity?y.shouldPreserveFollowOpacity(h):void 0})}isLead(){const f=this.getStack();return f?f.lead===this:!0}getLead(){var h;const{layoutId:f}=this.options;return f?((h=this.getStack())==null?void 0:h.lead)||this:this}getPrevLead(){var h;const{layoutId:f}=this.options;return f?(h=this.getStack())==null?void 0:h.prevLead:void 0}getStack(){const{layoutId:f}=this.options;if(f)return this.root.sharedNodes.get(f)}promote({needsReset:f,transition:h,preserveFollowOpacity:g}={}){const y=this.getStack();y&&y.promote(this,g),f&&(this.projectionDelta=void 0,this.needsReset=!0),h&&this.setOptions({transition:h})}relegate(){const f=this.getStack();return f?f.relegate(this):!1}resetSkewAndRotation(){const{visualElement:f}=this.options;if(!f)return;let h=!1;const{latestValues:g}=f;if((g.z||g.rotate||g.rotateX||g.rotateY||g.rotateZ||g.skewX||g.skewY)&&(h=!0),!h)return;const y={};g.z&&El("z",f,y,this.animationValues);for(let m=0;m{var h;return(h=f.currentAnimation)==null?void 0:h.stop()}),this.root.nodes.forEach(bh),this.root.sharedNodes.clear()}}}function Rx(n){n.updateLayout()}function Mx(n){var o;const r=((o=n.resumeFrom)==null?void 0:o.snapshot)||n.snapshot;if(n.isLead()&&n.layout&&r&&n.hasListeners("didUpdate")){const{layoutBox:a,measuredBox:u}=n.layout,{animationType:d}=n.options,f=r.source!==n.layout.source;if(d==="size")Ft(x=>{const w=f?r.measuredBox[x]:r.layoutBox[x],k=nt(w);w.min=a[x].min,w.max=w.min+k});else if(d==="x"||d==="y"){const x=d==="x"?"y":"x";iu(f?r.measuredBox[x]:r.layoutBox[x],a[x])}else Om(d,r.layoutBox,a)&&Ft(x=>{const w=f?r.measuredBox[x]:r.layoutBox[x],k=nt(a[x]);w.max=w.min+k,n.relativeTarget&&!n.currentAnimation&&(n.isProjectionDirty=!0,n.relativeTarget[x].max=n.relativeTarget[x].min+k)});const h=vr();mi(h,a,r.layoutBox);const g=vr();f?mi(g,n.applyTransform(u,!0),r.measuredBox):mi(g,a,r.layoutBox);const y=!bm(h);let m=!1;if(!n.resumeFrom){const x=n.getClosestProjectingParent();if(x&&!x.resumeFrom){const{snapshot:w,layout:k}=x;if(w&&k){const E=n.options.layoutAnchor||void 0,j=Be();cs(j,r.layoutBox,w.layoutBox,E);const D=Be();cs(D,a,k.layoutBox,E),_m(j,D)||(m=!0),x.options.layoutRoot&&(n.relativeTarget=D,n.relativeTargetOrigin=j,n.relativeParent=x)}}}n.notifyListeners("didUpdate",{layout:a,snapshot:r,delta:g,layoutDelta:h,hasLayoutChanged:y,hasRelativeLayoutChanged:m})}else if(n.isLead()){const{onExitComplete:a}=n.options;a&&a()}n.options.transition=void 0}function Ax(n){n.parent&&(n.isProjecting()||(n.isProjectionDirty=n.parent.isProjectionDirty),n.isSharedProjectionDirty||(n.isSharedProjectionDirty=!!(n.isProjectionDirty||n.parent.isProjectionDirty||n.parent.isSharedProjectionDirty)),n.isTransformDirty||(n.isTransformDirty=n.parent.isTransformDirty))}function Dx(n){n.isProjectionDirty=n.isSharedProjectionDirty=n.isTransformDirty=!1}function Lx(n){n.clearSnapshot()}function bh(n){n.clearMeasurements()}function Vx(n){n.isLayoutDirty=!0,n.updateLayout()}function _h(n){n.isLayoutDirty=!1}function bx(n){n.isAnimationBlocked&&n.layout&&!n.isLayoutDirty&&(n.snapshot=n.layout,n.isLayoutDirty=!0)}function _x(n){const{visualElement:r}=n.options;r&&r.getProps().onBeforeLayoutMeasure&&r.notify("BeforeLayoutMeasure"),n.resetTransform()}function Ih(n){n.finishAnimation(),n.targetDelta=n.relativeTarget=n.target=void 0,n.isProjectionDirty=!0}function Ix(n){n.resolveTargetDelta()}function zx(n){n.calcProjection()}function Fx(n){n.resetSkewAndRotation()}function Nx(n){n.removeLeadSnapshot()}function zh(n,r,o){n.translate=Re(r.translate,0,o),n.scale=Re(r.scale,1,o),n.origin=r.origin,n.originPoint=r.originPoint}function Fh(n,r,o,a){n.min=Re(r.min,o.min,a),n.max=Re(r.max,o.max,a)}function Ox(n,r,o,a){Fh(n.x,r.x,o.x,a),Fh(n.y,r.y,o.y,a)}function Bx(n){return n.animationValues&&n.animationValues.opacityExit!==void 0}const Wx={duration:.45,ease:[.4,0,.1,1]},Nh=n=>typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().includes(n),Oh=Nh("applewebkit/")&&!Nh("chrome/")?Math.round:kt;function Bh(n){n.min=Oh(n.min),n.max=Oh(n.max)}function Ux(n){Bh(n.x),Bh(n.y)}function Om(n,r,o){return n==="position"||n==="preserve-aspect"&&!cx(Mh(r),Mh(o),.2)}function $x(n){var r;return n!==n.root&&((r=n.scroll)==null?void 0:r.wasRoot)}const Hx=Nm({attachResizeListener:(n,r)=>wi(n,"resize",r),measureScroll:()=>{var n,r;return{x:document.documentElement.scrollLeft||((n=document.body)==null?void 0:n.scrollLeft)||0,y:document.documentElement.scrollTop||((r=document.body)==null?void 0:r.scrollTop)||0}},checkIsScrollRoot:()=>!0}),Pl={current:void 0},Bm=Nm({measureScroll:n=>({x:n.scrollLeft,y:n.scrollTop}),defaultParent:()=>{if(!Pl.current){const n=new Hx({});n.mount(window),n.setOptions({layoutScroll:!0}),Pl.current=n}return Pl.current},resetTransform:(n,r)=>{n.style.transform=r!==void 0?r:"none"},checkIsScrollRoot:n=>window.getComputedStyle(n).position==="fixed"}),bu=V.createContext({transformPagePoint:n=>n,isStatic:!1,reducedMotion:"never"});function Wh(n,r){if(typeof n=="function")return n(r);n!=null&&(n.current=r)}function Kx(...n){return r=>{let o=!1;const a=n.map(u=>{const d=Wh(u,r);return!o&&typeof d=="function"&&(o=!0),d});if(o)return()=>{for(let u=0;u{const{width:k,height:E,top:j,left:D,right:_,bottom:b}=g.current;if(r||d===!1||!h.current||!k||!E)return;const z=o==="left"?`left: ${D}`:`right: ${_}`,F=a==="bottom"?`bottom: ${b}`:`top: ${j}`;h.current.dataset.motionPopId=f;const M=document.createElement("style");y&&(M.nonce=y);const H=u??document.head;return H.appendChild(M),M.sheet&&M.sheet.insertRule(` + [data-motion-pop-id="${f}"] { + position: absolute !important; + width: ${k}px !important; + height: ${E}px !important; + ${z}px !important; + ${F}px !important; + } + `),()=>{var I;(I=h.current)==null||I.removeAttribute("data-motion-pop-id"),H.contains(M)&&H.removeChild(M)}},[r]),v.jsx(Yx,{isPresent:r,childRef:h,sizeRef:g,pop:d,children:d===!1?n:V.cloneElement(n,{ref:x})})}const Qx=({children:n,initial:r,isPresent:o,onExitComplete:a,custom:u,presenceAffectsLayout:d,mode:f,anchorX:h,anchorY:g,root:y})=>{const m=uu(qx),x=V.useId();let w=!0,k=V.useMemo(()=>(w=!1,{id:x,initial:r,isPresent:o,custom:u,onExitComplete:E=>{m.set(E,!0);for(const j of m.values())if(!j)return;a&&a()},register:E=>(m.set(E,!1),()=>m.delete(E))}),[o,m,a]);return d&&w&&(k={...k}),V.useMemo(()=>{m.forEach((E,j)=>m.set(j,!1))},[o]),V.useEffect(()=>{!o&&!m.size&&a&&a()},[o]),n=v.jsx(Xx,{pop:f==="popLayout",isPresent:o,anchorX:h,anchorY:g,root:y,children:n}),v.jsx(ds.Provider,{value:k,children:n})};function qx(){return new Map}function Wm(n=!0){const r=V.useContext(ds);if(r===null)return[!0,null];const{isPresent:o,onExitComplete:a,register:u}=r,d=V.useId();V.useEffect(()=>{if(n)return u(d)},[n]);const f=V.useCallback(()=>n&&a&&a(d),[d,a,n]);return!o&&a?[!1,f]:[!0]}const Uo=n=>n.key||"";function Uh(n){const r=[];return V.Children.forEach(n,o=>{V.isValidElement(o)&&r.push(o)}),r}const Sr=({children:n,custom:r,initial:o=!0,onExitComplete:a,presenceAffectsLayout:u=!0,mode:d="sync",propagate:f=!1,anchorX:h="left",anchorY:g="top",root:y})=>{const[m,x]=Wm(f),w=V.useMemo(()=>Uh(n),[n]),k=f&&!m?[]:w.map(Uo),E=V.useRef(!0),j=V.useRef(w),D=uu(()=>new Map),_=V.useRef(new Set),[b,z]=V.useState(w),[F,M]=V.useState(w);mp(()=>{E.current=!1,j.current=w;for(let N=0;N{const Y=Uo(N),G=f&&!m?!1:w===F||k.includes(Y),se=()=>{if(_.current.has(Y))return;if(D.has(Y))_.current.add(Y),D.set(Y,!0);else return;let ue=!0;D.forEach(ge=>{ge||(ue=!1)}),ue&&(I==null||I(),M(j.current),f&&(x==null||x()),a&&a())};return v.jsx(Qx,{isPresent:G,initial:!E.current||o?void 0:!1,custom:r,presenceAffectsLayout:u,mode:d,root:y,onExitComplete:G?void 0:se,anchorX:h,anchorY:g,children:N},Y)})})},Um=V.createContext({strict:!1}),$h={animation:["animate","variants","whileHover","whileTap","exit","whileInView","whileFocus","whileDrag"],exit:["exit"],drag:["drag","dragControls"],focus:["whileFocus"],hover:["whileHover","onHoverStart","onHoverEnd"],tap:["whileTap","onTap","onTapStart","onTapCancel"],pan:["onPan","onPanStart","onPanSessionStart","onPanEnd"],inView:["whileInView","onViewportEnter","onViewportLeave"],layout:["layout","layoutId"]};let Hh=!1;function Zx(){if(Hh)return;const n={};for(const r in $h)n[r]={isEnabled:o=>$h[r].some(a=>!!o[a])};vm(n),Hh=!0}function $m(){return Zx(),I1()}function Jx(n){const r=$m();for(const o in n)r[o]={...r[o],...n[o]};vm(r)}const ew=new Set(["animate","exit","variants","initial","style","values","variants","transition","transformTemplate","custom","inherit","onBeforeLayoutMeasure","onAnimationStart","onAnimationComplete","onUpdate","onDragStart","onDrag","onDragEnd","onMeasureDragConstraints","onDirectionLock","onDragTransitionEnd","_dragX","_dragY","onHoverStart","onHoverEnd","onViewportEnter","onViewportLeave","globalTapTarget","propagate","ignoreStrict","viewport"]);function fs(n){return n.startsWith("while")||n.startsWith("drag")&&n!=="draggable"||n.startsWith("layout")||n.startsWith("onTap")||n.startsWith("onPan")||n.startsWith("onLayout")||ew.has(n)}let Hm=n=>!fs(n);function tw(n){typeof n=="function"&&(Hm=r=>r.startsWith("on")?!fs(r):n(r))}try{tw(require("@emotion/is-prop-valid").default)}catch{}function nw(n,r,o){const a={};for(const u in n)u==="values"&&typeof n.values=="object"||Ye(n[u])||(Hm(u)||o===!0&&fs(u)||!r&&!fs(u)||n.draggable&&u.startsWith("onDrag"))&&(a[u]=n[u]);return a}const gs=V.createContext({});function rw(n,r){if(ms(n)){const{initial:o,animate:a}=n;return{initial:o===!1||xi(o)?o:void 0,animate:xi(a)?a:void 0}}return n.inherit!==!1?r:{}}function iw(n){const{initial:r,animate:o}=rw(n,V.useContext(gs));return V.useMemo(()=>({initial:r,animate:o}),[Kh(r),Kh(o)])}function Kh(n){return Array.isArray(n)?n.join(" "):n}const _u=()=>({style:{},transform:{},transformOrigin:{},vars:{}});function Km(n,r,o){for(const a in r)!Ye(r[a])&&!Em(a,o)&&(n[a]=r[a])}function ow({transformTemplate:n},r){return V.useMemo(()=>{const o=_u();return Lu(o,r,n),Object.assign({},o.vars,o.style)},[r])}function sw(n,r){const o=n.style||{},a={};return Km(a,o,n),Object.assign(a,ow(n,r)),a}function aw(n,r){const o={},a=sw(n,r);return n.drag&&n.dragListener!==!1&&(o.draggable=!1,a.userSelect=a.WebkitUserSelect=a.WebkitTouchCallout="none",a.touchAction=n.drag===!0?"none":`pan-${n.drag==="x"?"y":"x"}`),n.tabIndex===void 0&&(n.onTap||n.onTapStart||n.whileTap)&&(o.tabIndex=0),o.style=a,o}const Gm=()=>({..._u(),attrs:{}});function lw(n,r,o,a){const u=V.useMemo(()=>{const d=Gm();return Pm(d,r,Rm(a),n.transformTemplate,n.style),{...d.attrs,style:{...d.style}}},[r]);if(n.style){const d={};Km(d,n.style,n),u.style={...d,...u.style}}return u}const uw=["animate","circle","defs","desc","ellipse","g","image","line","filter","marker","mask","metadata","path","pattern","polygon","polyline","rect","stop","switch","symbol","svg","text","tspan","use","view"];function Iu(n){return typeof n!="string"||n.includes("-")?!1:!!(uw.indexOf(n)>-1||/[A-Z]/u.test(n))}function cw(n,r,o,{latestValues:a},u,d=!1,f){const g=(f??Iu(n)?lw:aw)(r,a,u,n),y=nw(r,typeof n=="string",d),m=n!==V.Fragment?{...y,...g,ref:o}:{},{children:x}=r,w=V.useMemo(()=>Ye(x)?x.get():x,[x]);return V.createElement(n,{...m,children:w})}function fw({scrapeMotionValuesFromProps:n,createRenderState:r},o,a,u){return{latestValues:dw(o,a,u,n),renderState:r()}}function dw(n,r,o,a){const u={},d=a(n,{});for(const w in d)u[w]=ts(d[w]);let{initial:f,animate:h}=n;const g=ms(n),y=gm(n);r&&y&&!g&&n.inherit!==!1&&(f===void 0&&(f=r.initial),h===void 0&&(h=r.animate));let m=o?o.initial===!1:!1;m=m||f===!1;const x=m?h:f;if(x&&typeof x!="boolean"&&!ps(x)){const w=Array.isArray(x)?x:[x];for(let k=0;k(r,o)=>{const a=V.useContext(gs),u=V.useContext(ds),d=()=>fw(n,r,a,u);return o?d():uu(d)},hw=Ym({scrapeMotionValuesFromProps:Vu,createRenderState:_u}),pw=Ym({scrapeMotionValuesFromProps:Mm,createRenderState:Gm}),mw=Symbol.for("motionComponentSymbol");function gw(n,r,o){const a=V.useRef(o);V.useInsertionEffect(()=>{a.current=o});const u=V.useRef(null);return V.useCallback(d=>{var h;d&&((h=n.onMount)==null||h.call(n,d));const f=a.current;if(typeof f=="function")if(d){const g=f(d);typeof g=="function"&&(u.current=g)}else u.current?(u.current(),u.current=null):f(d);else f&&(f.current=d);r&&(d?r.mount(d):r.unmount())},[r])}const Xm=V.createContext({});function mr(n){return n&&typeof n=="object"&&Object.prototype.hasOwnProperty.call(n,"current")}function yw(n,r,o,a,u,d){var z,F;const{visualElement:f}=V.useContext(gs),h=V.useContext(Um),g=V.useContext(ds),y=V.useContext(bu),m=y.reducedMotion,x=y.skipAnimations,w=V.useRef(null),k=V.useRef(!1);a=a||h.renderer,!w.current&&a&&(w.current=a(n,{visualState:r,parent:f,props:o,presenceContext:g,blockInitialAnimation:g?g.initial===!1:!1,reducedMotionConfig:m,skipAnimations:x,isSVG:d}),k.current&&w.current&&(w.current.manuallyAnimateOnMount=!0));const E=w.current,j=V.useContext(Xm);E&&!E.projection&&u&&(E.type==="html"||E.type==="svg")&&vw(w.current,o,u,j);const D=V.useRef(!1);V.useInsertionEffect(()=>{E&&D.current&&E.update(o,g)});const _=o[rm],b=V.useRef(!!_&&typeof window<"u"&&!((z=window.MotionHandoffIsComplete)!=null&&z.call(window,_))&&((F=window.MotionHasOptimisedAnimation)==null?void 0:F.call(window,_)));return mp(()=>{k.current=!0,E&&(D.current=!0,window.MotionIsMounted=!0,E.updateFeatures(),E.scheduleRenderMicrotask(),b.current&&E.animationState&&E.animationState.animateChanges())}),V.useEffect(()=>{E&&(!b.current&&E.animationState&&E.animationState.animateChanges(),b.current&&(queueMicrotask(()=>{var M;(M=window.MotionHandoffMarkAsComplete)==null||M.call(window,_)}),b.current=!1),E.enteringChildren=void 0)}),E}function vw(n,r,o,a){const{layoutId:u,layout:d,drag:f,dragConstraints:h,layoutScroll:g,layoutRoot:y,layoutAnchor:m,layoutCrossfade:x}=r;n.projection=new o(n.latestValues,r["data-framer-portal-id"]?void 0:Qm(n.parent)),n.projection.setOptions({layoutId:u,layout:d,alwaysMeasureLayout:!!f||h&&mr(h),visualElement:n,animationType:typeof d=="string"?d:"both",initialPromotionConfig:a,crossfade:x,layoutScroll:g,layoutRoot:y,layoutAnchor:m})}function Qm(n){if(n)return n.options.allowProjection!==!1?n.projection:Qm(n.parent)}function jl(n,{forwardMotionProps:r=!1,type:o}={},a,u){a&&Jx(a);const d=o?o==="svg":Iu(n),f=d?pw:hw;function h(y,m){let x;const w={...V.useContext(bu),...y,layoutId:xw(y)},{isStatic:k}=w,E=iw(y),j=f(y,k);if(!k&&typeof window<"u"){ww();const D=Sw(w);x=D.MeasureLayout,E.visualElement=yw(n,j,w,u,D.ProjectionNode,d)}return v.jsxs(gs.Provider,{value:E,children:[x&&E.visualElement?v.jsx(x,{visualElement:E.visualElement,...w}):null,cw(n,y,gw(j,E.visualElement,m),j,k,r,d)]})}h.displayName=`motion.${typeof n=="string"?n:`create(${n.displayName??n.name??""})`}`;const g=V.forwardRef(h);return g[mw]=n,g}function xw({layoutId:n}){const r=V.useContext(lu).id;return r&&n!==void 0?r+"-"+n:n}function ww(n,r){V.useContext(Um).strict}function Sw(n){const r=$m(),{drag:o,layout:a}=r;if(!o&&!a)return{};const u={...o,...a};return{MeasureLayout:o!=null&&o.isEnabled(n)||a!=null&&a.isEnabled(n)?u.MeasureLayout:void 0,ProjectionNode:u.ProjectionNode}}function kw(n,r){if(typeof Proxy>"u")return jl;const o=new Map,a=(d,f)=>jl(d,f,n,r),u=(d,f)=>a(d,f);return new Proxy(u,{get:(d,f)=>f==="create"?a:(o.has(f)||o.set(f,jl(f,void 0,n,r)),o.get(f))})}const Tw=(n,r)=>r.isSVG??Iu(n)?new J1(r):new G1(r,{allowProjection:n!==V.Fragment});class Cw extends Tn{constructor(r){super(r),r.animationState||(r.animationState=ix(r))}updateAnimationControlsSubscription(){const{animate:r}=this.node.getProps();ps(r)&&(this.unmountControls=r.subscribe(this.node))}mount(){this.updateAnimationControlsSubscription()}update(){const{animate:r}=this.node.getProps(),{animate:o}=this.node.prevProps||{};r!==o&&this.updateAnimationControlsSubscription()}unmount(){var r;this.node.animationState.reset(),(r=this.unmountControls)==null||r.call(this)}}let Ew=0;class Pw extends Tn{constructor(){super(...arguments),this.id=Ew++,this.isExitComplete=!1}update(){var d;if(!this.node.presenceContext)return;const{isPresent:r,onExitComplete:o}=this.node.presenceContext,{isPresent:a}=this.node.prevPresenceContext||{};if(!this.node.animationState||r===a)return;if(r&&a===!1){if(this.isExitComplete){const{initial:f,custom:h}=this.node.getProps();if(typeof f=="string"){const g=Wn(this.node,f,h);if(g){const{transition:y,transitionEnd:m,...x}=g;for(const w in x)(d=this.node.getValue(w))==null||d.jump(x[w])}}this.node.animationState.reset(),this.node.animationState.animateChanges()}else this.node.animationState.setActive("exit",!1);this.isExitComplete=!1;return}const u=this.node.animationState.setActive("exit",!r);o&&!r&&u.then(()=>{this.isExitComplete=!0,o(this.id)})}mount(){const{register:r,onExitComplete:o}=this.node.presenceContext||{};o&&o(this.id),r&&(this.unmount=r(this.id))}unmount(){}}const jw={animation:{Feature:Cw},exit:{Feature:Pw}};function ji(n){return{point:{x:n.pageX,y:n.pageY}}}const Rw=n=>r=>Ru(r)&&n(r,ji(r));function gi(n,r,o,a){return wi(n,r,Rw(o),a)}const qm=({current:n})=>n?n.ownerDocument.defaultView:null,Gh=(n,r)=>Math.abs(n-r);function Mw(n,r){const o=Gh(n.x,r.x),a=Gh(n.y,r.y);return Math.sqrt(o**2+a**2)}const Yh=new Set(["auto","scroll"]);class Zm{constructor(r,o,{transformPagePoint:a,contextWindow:u=window,dragSnapToOrigin:d=!1,distanceThreshold:f=3,element:h}={}){if(this.startEvent=null,this.lastMoveEvent=null,this.lastMoveEventInfo=null,this.lastRawMoveEventInfo=null,this.handlers={},this.contextWindow=window,this.scrollPositions=new Map,this.removeScrollListeners=null,this.onElementScroll=k=>{this.handleScroll(k.target)},this.onWindowScroll=()=>{this.handleScroll(window)},this.updatePoint=()=>{if(!(this.lastMoveEvent&&this.lastMoveEventInfo))return;this.lastRawMoveEventInfo&&(this.lastMoveEventInfo=$o(this.lastRawMoveEventInfo,this.transformPagePoint));const k=Rl(this.lastMoveEventInfo,this.history),E=this.startEvent!==null,j=Mw(k.offset,{x:0,y:0})>=this.distanceThreshold;if(!E&&!j)return;const{point:D}=k,{timestamp:_}=Ge;this.history.push({...D,timestamp:_});const{onStart:b,onMove:z}=this.handlers;E||(b&&b(this.lastMoveEvent,k),this.startEvent=this.lastMoveEvent),z&&z(this.lastMoveEvent,k)},this.handlePointerMove=(k,E)=>{this.lastMoveEvent=k,this.lastRawMoveEventInfo=E,this.lastMoveEventInfo=$o(E,this.transformPagePoint),Te.update(this.updatePoint,!0)},this.handlePointerUp=(k,E)=>{this.end();const{onEnd:j,onSessionEnd:D,resumeAnimation:_}=this.handlers;if((this.dragSnapToOrigin||!this.startEvent)&&_&&_(),!(this.lastMoveEvent&&this.lastMoveEventInfo))return;const b=Rl(k.type==="pointercancel"?this.lastMoveEventInfo:$o(E,this.transformPagePoint),this.history);this.startEvent&&j&&j(k,b),D&&D(k,b)},!Ru(r))return;this.dragSnapToOrigin=d,this.handlers=o,this.transformPagePoint=a,this.distanceThreshold=f,this.contextWindow=u||window;const g=ji(r),y=$o(g,this.transformPagePoint),{point:m}=y,{timestamp:x}=Ge;this.history=[{...m,timestamp:x}];const{onSessionStart:w}=o;w&&w(r,Rl(y,this.history)),this.removeListeners=Ci(gi(this.contextWindow,"pointermove",this.handlePointerMove),gi(this.contextWindow,"pointerup",this.handlePointerUp),gi(this.contextWindow,"pointercancel",this.handlePointerUp)),h&&this.startScrollTracking(h)}startScrollTracking(r){let o=r.parentElement;for(;o;){const a=getComputedStyle(o);(Yh.has(a.overflowX)||Yh.has(a.overflowY))&&this.scrollPositions.set(o,{x:o.scrollLeft,y:o.scrollTop}),o=o.parentElement}this.scrollPositions.set(window,{x:window.scrollX,y:window.scrollY}),window.addEventListener("scroll",this.onElementScroll,{capture:!0}),window.addEventListener("scroll",this.onWindowScroll),this.removeScrollListeners=()=>{window.removeEventListener("scroll",this.onElementScroll,{capture:!0}),window.removeEventListener("scroll",this.onWindowScroll)}}handleScroll(r){const o=this.scrollPositions.get(r);if(!o)return;const a=r===window,u=a?{x:window.scrollX,y:window.scrollY}:{x:r.scrollLeft,y:r.scrollTop},d={x:u.x-o.x,y:u.y-o.y};d.x===0&&d.y===0||(a?this.lastMoveEventInfo&&(this.lastMoveEventInfo.point.x+=d.x,this.lastMoveEventInfo.point.y+=d.y):this.history.length>0&&(this.history[0].x-=d.x,this.history[0].y-=d.y),this.scrollPositions.set(r,u),Te.update(this.updatePoint,!0))}updateHandlers(r){this.handlers=r}end(){this.removeListeners&&this.removeListeners(),this.removeScrollListeners&&this.removeScrollListeners(),this.scrollPositions.clear(),kn(this.updatePoint)}}function $o(n,r){return r?{point:r(n.point)}:n}function Xh(n,r){return{x:n.x-r.x,y:n.y-r.y}}function Rl({point:n},r){return{point:n,delta:Xh(n,Jm(r)),offset:Xh(n,Aw(r)),velocity:Dw(r,.1)}}function Aw(n){return n[0]}function Jm(n){return n[n.length-1]}function Dw(n,r){if(n.length<2)return{x:0,y:0};let o=n.length-1,a=null;const u=Jm(n);for(;o>=0&&(a=n[o],!(u.timestamp-a.timestamp>pt(r)));)o--;if(!a)return{x:0,y:0};a===n[0]&&n.length>2&&u.timestamp-a.timestamp>pt(r)*2&&(a=n[1]);const d=St(u.timestamp-a.timestamp);if(d===0)return{x:0,y:0};const f={x:(u.x-a.x)/d,y:(u.y-a.y)/d};return f.x===1/0&&(f.x=0),f.y===1/0&&(f.y=0),f}function Lw(n,{min:r,max:o},a){return r!==void 0&&no&&(n=a?Re(o,n,a.max):Math.min(n,o)),n}function Qh(n,r,o){return{min:r!==void 0?n.min+r:void 0,max:o!==void 0?n.max+o-(n.max-n.min):void 0}}function Vw(n,{top:r,left:o,bottom:a,right:u}){return{x:Qh(n.x,o,u),y:Qh(n.y,r,a)}}function qh(n,r){let o=r.min-n.min,a=r.max-n.max;return r.max-r.mina?o=yi(r.min,r.max-a,n.min):a>u&&(o=yi(n.min,n.max-u,r.min)),Wt(0,1,o)}function Iw(n,r){const o={};return r.min!==void 0&&(o.min=r.min-n.min),r.max!==void 0&&(o.max=r.max-n.min),o}const ou=.35;function zw(n=ou){return n===!1?n=0:n===!0&&(n=ou),{x:Zh(n,"left","right"),y:Zh(n,"top","bottom")}}function Zh(n,r,o){return{min:Jh(n,r),max:Jh(n,o)}}function Jh(n,r){return typeof n=="number"?n:n[r]||0}const Fw=new WeakMap;class Nw{constructor(r){this.openDragLock=null,this.isDragging=!1,this.currentDirection=null,this.originPoint={x:0,y:0},this.constraints=!1,this.hasMutatedConstraints=!1,this.elastic=Be(),this.latestPointerEvent=null,this.latestPanInfo=null,this.visualElement=r}start(r,{snapToCursor:o=!1,distanceThreshold:a}={}){const{presenceContext:u}=this.visualElement;if(u&&u.isPresent===!1)return;const d=x=>{o&&this.snapToCursor(ji(x).point),this.stopAnimation()},f=(x,w)=>{const{drag:k,dragPropagation:E,onDragStart:j}=this.getProps();if(k&&!E&&(this.openDragLock&&this.openDragLock(),this.openDragLock=d1(k),!this.openDragLock))return;this.latestPointerEvent=x,this.latestPanInfo=w,this.isDragging=!0,this.currentDirection=null,this.resolveConstraints(),this.visualElement.projection&&(this.visualElement.projection.isAnimationBlocked=!0,this.visualElement.projection.target=void 0),Ft(_=>{let b=this.getAxisMotionValue(_).get()||0;if(Bt.test(b)){const{projection:z}=this.visualElement;if(z&&z.layout){const F=z.layout.layoutBox[_];F&&(b=nt(F)*(parseFloat(b)/100))}}this.originPoint[_]=b}),j&&Te.update(()=>j(x,w),!1,!0),Ql(this.visualElement,"transform");const{animationState:D}=this.visualElement;D&&D.setActive("whileDrag",!0)},h=(x,w)=>{this.latestPointerEvent=x,this.latestPanInfo=w;const{dragPropagation:k,dragDirectionLock:E,onDirectionLock:j,onDrag:D}=this.getProps();if(!k&&!this.openDragLock)return;const{offset:_}=w;if(E&&this.currentDirection===null){this.currentDirection=Bw(_),this.currentDirection!==null&&j&&j(this.currentDirection);return}this.updateAxis("x",w.point,_),this.updateAxis("y",w.point,_),this.visualElement.render(),D&&Te.update(()=>D(x,w),!1,!0)},g=(x,w)=>{this.latestPointerEvent=x,this.latestPanInfo=w,this.stop(x,w),this.latestPointerEvent=null,this.latestPanInfo=null},y=()=>{const{dragSnapToOrigin:x}=this.getProps();(x||this.constraints)&&this.startAnimation({x:0,y:0})},{dragSnapToOrigin:m}=this.getProps();this.panSession=new Zm(r,{onSessionStart:d,onStart:f,onMove:h,onSessionEnd:g,resumeAnimation:y},{transformPagePoint:this.visualElement.getTransformPagePoint(),dragSnapToOrigin:m,distanceThreshold:a,contextWindow:qm(this.visualElement),element:this.visualElement.current})}stop(r,o){const a=r||this.latestPointerEvent,u=o||this.latestPanInfo,d=this.isDragging;if(this.cancel(),!d||!u||!a)return;const{velocity:f}=u;this.startAnimation(f);const{onDragEnd:h}=this.getProps();h&&Te.postRender(()=>h(a,u))}cancel(){this.isDragging=!1;const{projection:r,animationState:o}=this.visualElement;r&&(r.isAnimationBlocked=!1),this.endPanSession();const{dragPropagation:a}=this.getProps();!a&&this.openDragLock&&(this.openDragLock(),this.openDragLock=null),o&&o.setActive("whileDrag",!1)}endPanSession(){this.panSession&&this.panSession.end(),this.panSession=void 0}updateAxis(r,o,a){const{drag:u}=this.getProps();if(!a||!Ho(r,u,this.currentDirection))return;const d=this.getAxisMotionValue(r);let f=this.originPoint[r]+a[r];this.constraints&&this.constraints[r]&&(f=Lw(f,this.constraints[r],this.elastic[r])),d.set(f)}resolveConstraints(){var d;const{dragConstraints:r,dragElastic:o}=this.getProps(),a=this.visualElement.projection&&!this.visualElement.projection.layout?this.visualElement.projection.measure(!1):(d=this.visualElement.projection)==null?void 0:d.layout,u=this.constraints;r&&mr(r)?this.constraints||(this.constraints=this.resolveRefConstraints()):r&&a?this.constraints=Vw(a.layoutBox,r):this.constraints=!1,this.elastic=zw(o),u!==this.constraints&&!mr(r)&&a&&this.constraints&&!this.hasMutatedConstraints&&Ft(f=>{this.constraints!==!1&&this.getAxisMotionValue(f)&&(this.constraints[f]=Iw(a.layoutBox[f],this.constraints[f]))})}resolveRefConstraints(){const{dragConstraints:r,onMeasureDragConstraints:o}=this.getProps();if(!r||!mr(r))return!1;const a=r.current,{projection:u}=this.visualElement;if(!u||!u.layout)return!1;const d=B1(a,u.root,this.visualElement.getTransformPagePoint());let f=bw(u.layout.layoutBox,d);if(o){const h=o(F1(f));this.hasMutatedConstraints=!!h,h&&(f=wm(h))}return f}startAnimation(r){const{drag:o,dragMomentum:a,dragElastic:u,dragTransition:d,dragSnapToOrigin:f,onDragTransitionEnd:h}=this.getProps(),g=this.constraints||{},y=Ft(m=>{if(!Ho(m,o,this.currentDirection))return;let x=g&&g[m]||{};(f===!0||f===m)&&(x={min:0,max:0});const w=u?200:1e6,k=u?40:1e7,E={type:"inertia",velocity:a?r[m]:0,bounceStiffness:w,bounceDamping:k,timeConstant:750,restDelta:1,restSpeed:10,...d,...x};return this.startAxisValueAnimation(m,E)});return Promise.all(y).then(h)}startAxisValueAnimation(r,o){const a=this.getAxisMotionValue(r);return Ql(this.visualElement,r),a.start(Tu(r,a,0,o,this.visualElement,!1))}stopAnimation(){Ft(r=>this.getAxisMotionValue(r).stop())}getAxisMotionValue(r){const o=`_drag${r.toUpperCase()}`,a=this.visualElement.getProps(),u=a[o];return u||this.visualElement.getValue(r,(a.initial?a.initial[r]:void 0)||0)}snapToCursor(r){Ft(o=>{const{drag:a}=this.getProps();if(!Ho(o,a,this.currentDirection))return;const{projection:u}=this.visualElement,d=this.getAxisMotionValue(o);if(u&&u.layout){const{min:f,max:h}=u.layout.layoutBox[o],g=d.get()||0;d.set(r[o]-Re(f,h,.5)+g)}})}scalePositionWithinConstraints(){if(!this.visualElement.current)return;const{drag:r,dragConstraints:o}=this.getProps(),{projection:a}=this.visualElement;if(!mr(o)||!a||!this.constraints)return;this.stopAnimation();const u={x:0,y:0};Ft(f=>{const h=this.getAxisMotionValue(f);if(h&&this.constraints!==!1){const g=h.get();u[f]=_w({min:g,max:g},this.constraints[f])}});const{transformTemplate:d}=this.visualElement.getProps();this.visualElement.current.style.transform=d?d({},""):"none",a.root&&a.root.updateScroll(),a.updateLayout(),this.constraints=!1,this.resolveConstraints(),Ft(f=>{if(!Ho(f,r,null))return;const h=this.getAxisMotionValue(f),{min:g,max:y}=this.constraints[f];h.set(Re(g,y,u[f]))}),this.visualElement.render()}addListeners(){if(!this.visualElement.current)return;Fw.set(this.visualElement,this);const r=this.visualElement.current,o=gi(r,"pointerdown",y=>{const{drag:m,dragListener:x=!0}=this.getProps(),w=y.target,k=w!==r&&v1(w);m&&x&&!k&&this.start(y)});let a;const u=()=>{const{dragConstraints:y}=this.getProps();mr(y)&&y.current&&(this.constraints=this.resolveRefConstraints(),a||(a=Ow(r,y.current,()=>this.scalePositionWithinConstraints())))},{projection:d}=this.visualElement,f=d.addEventListener("measure",u);d&&!d.layout&&(d.root&&d.root.updateScroll(),d.updateLayout()),Te.read(u);const h=wi(window,"resize",()=>this.scalePositionWithinConstraints()),g=d.addEventListener("didUpdate",(({delta:y,hasLayoutChanged:m})=>{this.isDragging&&m&&(Ft(x=>{const w=this.getAxisMotionValue(x);w&&(this.originPoint[x]+=y[x].translate,w.set(w.get()+y[x].translate))}),this.visualElement.render())}));return()=>{h(),o(),f(),g&&g(),a&&a()}}getProps(){const r=this.visualElement.getProps(),{drag:o=!1,dragDirectionLock:a=!1,dragPropagation:u=!1,dragConstraints:d=!1,dragElastic:f=ou,dragMomentum:h=!0}=r;return{...r,drag:o,dragDirectionLock:a,dragPropagation:u,dragConstraints:d,dragElastic:f,dragMomentum:h}}}function ep(n){let r=!0;return()=>{if(r){r=!1;return}n()}}function Ow(n,r,o){const a=ah(n,ep(o)),u=ah(r,ep(o));return()=>{a(),u()}}function Ho(n,r,o){return(r===!0||r===n)&&(o===null||o===n)}function Bw(n,r=10){let o=null;return Math.abs(n.y)>r?o="y":Math.abs(n.x)>r&&(o="x"),o}class Ww extends Tn{constructor(r){super(r),this.removeGroupControls=kt,this.removeListeners=kt,this.controls=new Nw(r)}mount(){const{dragControls:r}=this.node.getProps();r&&(this.removeGroupControls=r.subscribe(this.controls)),this.removeListeners=this.controls.addListeners()||kt}update(){const{dragControls:r}=this.node.getProps(),{dragControls:o}=this.node.prevProps||{};r!==o&&(this.removeGroupControls(),r&&(this.removeGroupControls=r.subscribe(this.controls)))}unmount(){this.removeGroupControls(),this.removeListeners(),this.controls.isDragging||this.controls.endPanSession()}}const Ml=n=>(r,o)=>{n&&Te.update(()=>n(r,o),!1,!0)};class Uw extends Tn{constructor(){super(...arguments),this.removePointerDownListener=kt}onPointerDown(r){this.session=new Zm(r,this.createPanHandlers(),{transformPagePoint:this.node.getTransformPagePoint(),contextWindow:qm(this.node)})}createPanHandlers(){const{onPanSessionStart:r,onPanStart:o,onPan:a,onPanEnd:u}=this.node.getProps();return{onSessionStart:Ml(r),onStart:Ml(o),onMove:Ml(a),onEnd:(d,f)=>{delete this.session,u&&Te.postRender(()=>u(d,f))}}}mount(){this.removePointerDownListener=gi(this.node.current,"pointerdown",r=>this.onPointerDown(r))}update(){this.session&&this.session.updateHandlers(this.createPanHandlers())}unmount(){this.removePointerDownListener(),this.session&&this.session.end()}}let Al=!1;class $w extends V.Component{componentDidMount(){const{visualElement:r,layoutGroup:o,switchLayoutGroup:a,layoutId:u}=this.props,{projection:d}=r;d&&(o.group&&o.group.add(d),a&&a.register&&u&&a.register(d),Al&&d.root.didUpdate(),d.addEventListener("animationComplete",()=>{this.safeToRemove()}),d.setOptions({...d.options,layoutDependency:this.props.layoutDependency,onExitComplete:()=>this.safeToRemove()})),ns.hasEverUpdated=!0}getSnapshotBeforeUpdate(r){const{layoutDependency:o,visualElement:a,drag:u,isPresent:d}=this.props,{projection:f}=a;return f&&(f.isPresent=d,r.layoutDependency!==o&&f.setOptions({...f.options,layoutDependency:o}),Al=!0,u||r.layoutDependency!==o||o===void 0||r.isPresent!==d?f.willUpdate():this.safeToRemove(),r.isPresent!==d&&(d?f.promote():f.relegate()||Te.postRender(()=>{const h=f.getStack();(!h||!h.members.length)&&this.safeToRemove()}))),null}componentDidUpdate(){const{visualElement:r,layoutAnchor:o}=this.props,{projection:a}=r;a&&(a.options.layoutAnchor=o,a.root.didUpdate(),ju.postRender(()=>{!a.currentAnimation&&a.isLead()&&this.safeToRemove()}))}componentWillUnmount(){const{visualElement:r,layoutGroup:o,switchLayoutGroup:a}=this.props,{projection:u}=r;Al=!0,u&&(u.scheduleCheckAfterUnmount(),o&&o.group&&o.group.remove(u),a&&a.deregister&&a.deregister(u))}safeToRemove(){const{safeToRemove:r}=this.props;r&&r()}render(){return null}}function e0(n){const[r,o]=Wm(),a=V.useContext(lu);return v.jsx($w,{...n,layoutGroup:a,switchLayoutGroup:V.useContext(Xm),isPresent:r,safeToRemove:o})}const Hw={pan:{Feature:Uw},drag:{Feature:Ww,ProjectionNode:Bm,MeasureLayout:e0}};function tp(n,r,o){const{props:a}=n;n.animationState&&a.whileHover&&n.animationState.setActive("whileHover",o==="Start");const u="onHover"+o,d=a[u];d&&Te.postRender(()=>d(r,ji(r)))}class Kw extends Tn{mount(){const{current:r}=this.node;r&&(this.unmount=p1(r,(o,a)=>(tp(this.node,a,"Start"),u=>tp(this.node,u,"End"))))}unmount(){}}class Gw extends Tn{constructor(){super(...arguments),this.isActive=!1}onFocus(){let r=!1;try{r=this.node.current.matches(":focus-visible")}catch{r=!0}!r||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!0),this.isActive=!0)}onBlur(){!this.isActive||!this.node.animationState||(this.node.animationState.setActive("whileFocus",!1),this.isActive=!1)}mount(){this.unmount=Ci(wi(this.node.current,"focus",()=>this.onFocus()),wi(this.node.current,"blur",()=>this.onBlur()))}unmount(){}}function np(n,r,o){const{props:a}=n;if(n.current instanceof HTMLButtonElement&&n.current.disabled)return;n.animationState&&a.whileTap&&n.animationState.setActive("whileTap",o==="Start");const u="onTap"+(o==="End"?"":o),d=a[u];d&&Te.postRender(()=>d(r,ji(r)))}class Yw extends Tn{mount(){const{current:r}=this.node;if(!r)return;const{globalTapTarget:o,propagate:a}=this.node.props;this.unmount=w1(r,(u,d)=>(np(this.node,d,"Start"),(f,{success:h})=>np(this.node,f,h?"End":"Cancel")),{useGlobalTarget:o,stopPropagation:(a==null?void 0:a.tap)===!1})}unmount(){}}const su=new WeakMap,Dl=new WeakMap,Xw=n=>{const r=su.get(n.target);r&&r(n)},Qw=n=>{n.forEach(Xw)};function qw({root:n,...r}){const o=n||document;Dl.has(o)||Dl.set(o,{});const a=Dl.get(o),u=JSON.stringify(r);return a[u]||(a[u]=new IntersectionObserver(Qw,{root:n,...r})),a[u]}function Zw(n,r,o){const a=qw(r);return su.set(n,o),a.observe(n),()=>{su.delete(n),a.unobserve(n)}}const Jw={some:0,all:1};class eS extends Tn{constructor(){super(...arguments),this.hasEnteredView=!1,this.isInView=!1}startObserver(){var g;(g=this.stopObserver)==null||g.call(this);const{viewport:r={}}=this.node.getProps(),{root:o,margin:a,amount:u="some",once:d}=r,f={root:o?o.current:void 0,rootMargin:a,threshold:typeof u=="number"?u:Jw[u]},h=y=>{const{isIntersecting:m}=y;if(this.isInView===m||(this.isInView=m,d&&!m&&this.hasEnteredView))return;m&&(this.hasEnteredView=!0),this.node.animationState&&this.node.animationState.setActive("whileInView",m);const{onViewportEnter:x,onViewportLeave:w}=this.node.getProps(),k=m?x:w;k&&k(y)};this.stopObserver=Zw(this.node.current,f,h)}mount(){this.startObserver()}update(){if(typeof IntersectionObserver>"u")return;const{props:r,prevProps:o}=this.node;["amount","margin","root"].some(tS(r,o))&&this.startObserver()}unmount(){var r;(r=this.stopObserver)==null||r.call(this),this.hasEnteredView=!1,this.isInView=!1}}function tS({viewport:n={}},{viewport:r={}}={}){return o=>n[o]!==r[o]}const nS={inView:{Feature:eS},tap:{Feature:Yw},focus:{Feature:Gw},hover:{Feature:Kw}},rS={layout:{ProjectionNode:Bm,MeasureLayout:e0}},iS={...jw,...nS,...Hw,...rS},oe=kw(iS,Tw);function oS({onToggleSidebar:n,sidebarOpen:r}){return v.jsxs(oe.div,{initial:{opacity:0,y:-12},animate:{opacity:1,y:0},transition:{duration:.4},style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.6rem 1.4rem 0.7rem",borderBottom:"1px solid #1e2540",background:"#0a0d14",position:"sticky",top:0,zIndex:10},children:[v.jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.9rem"},children:[v.jsx("button",{onClick:n,style:{width:32,height:32,background:"#141826",border:"1px solid #1e2540",borderRadius:7,color:"#e8eaf0",fontSize:16,cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,transition:"border-color 0.2s, color 0.2s"},onMouseEnter:o=>{o.currentTarget.style.borderColor="#ff6b3d",o.currentTarget.style.color="#ff6b3d"},onMouseLeave:o=>{o.currentTarget.style.borderColor="#1e2540",o.currentTarget.style.color="#e8eaf0"},children:"☰"}),v.jsx(oe.div,{whileHover:{scale:1.05},style:{width:36,height:36,borderRadius:9,background:"linear-gradient(135deg, #ff4b4b, #ff8c42)",display:"flex",alignItems:"center",justifyContent:"center",fontWeight:800,color:"#fff",fontSize:"1.15rem",boxShadow:"0 0 18px rgba(255,76,76,0.45)",flexShrink:0},children:"A"}),v.jsxs("div",{children:[v.jsx("div",{style:{fontSize:"1.55rem",fontWeight:800,color:"#fff",lineHeight:1.1},children:"AlgoScope"}),v.jsx("div",{style:{fontSize:"0.78rem",color:"#9aa0c0"},children:"Real-time algospeak & toxicity intelligence on Bluesky"})]})]}),v.jsxs("div",{style:{textAlign:"right"},children:[v.jsxs("div",{style:{display:"inline-flex",alignItems:"center",gap:"0.35rem",padding:"0.2rem 0.65rem",borderRadius:999,background:"rgba(46,204,113,0.08)",border:"1px solid rgba(46,204,113,0.22)",marginBottom:"0.25rem"},children:[v.jsx("span",{style:{width:7,height:7,borderRadius:"50%",background:"#2ecc71",boxShadow:"0 0 6px rgba(46,204,113,0.9)",display:"inline-block",animation:"pulse-dot 1.5s infinite"}}),v.jsx("span",{style:{color:"#2ecc71",fontWeight:700,letterSpacing:1,fontSize:"0.68rem"},children:"LIVE"})]}),v.jsxs("div",{style:{fontSize:"0.73rem",color:"#6f7695"},children:["by Odeliya Charitonova",v.jsx("br",{}),v.jsx("a",{href:"https://github.com/odeliyach/Algoscope",target:"_blank",rel:"noopener noreferrer",style:{color:"#a6b0ff",textDecoration:"none"},children:"github.com/odeliyach/Algoscope"})]})]}),v.jsx("style",{children:` + @keyframes pulse-dot { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.25; } + } + `})]})}/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const sS=n=>n.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),aS=n=>n.replace(/^([A-Z])|[\s-_]+(\w)/g,(r,o,a)=>a?a.toUpperCase():o.toLowerCase()),rp=n=>{const r=aS(n);return r.charAt(0).toUpperCase()+r.slice(1)},t0=(...n)=>n.filter((r,o,a)=>!!r&&r.trim()!==""&&a.indexOf(r)===o).join(" ").trim();/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */var lS={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const uS=V.forwardRef(({color:n="currentColor",size:r=24,strokeWidth:o=2,absoluteStrokeWidth:a,className:u="",children:d,iconNode:f,...h},g)=>V.createElement("svg",{ref:g,...lS,width:r,height:r,stroke:n,strokeWidth:a?Number(o)*24/Number(r):o,className:t0("lucide",u),...h},[...f.map(([y,m])=>V.createElement(y,m)),...Array.isArray(d)?d:[d]]));/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const Ri=(n,r)=>{const o=V.forwardRef(({className:a,...u},d)=>V.createElement(uS,{ref:d,iconNode:r,className:t0(`lucide-${sS(rp(n))}`,`lucide-${n}`,a),...u}));return o.displayName=rp(n),o};/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const cS=[["path",{d:"M21.801 10A10 10 0 1 1 17 3.335",key:"yps3ct"}],["path",{d:"m9 11 3 3L22 4",key:"1pflzl"}]],fS=Ri("circle-check-big",cS);/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const dS=[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]],hS=Ri("download",dS);/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const pS=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 12a1 1 0 0 0-1 1v1a1 1 0 0 1-1 1 1 1 0 0 1 1 1v1a1 1 0 0 0 1 1",key:"1oajmo"}],["path",{d:"M14 18a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1 1 1 0 0 1-1-1v-1a1 1 0 0 0-1-1",key:"mpwhp6"}]],ip=Ri("file-json",pS);/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const mS=[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]],op=Ri("file-text",mS);/** + * @license lucide-react v0.487.0 - ISC + * + * This source code is licensed under the ISC license. + * See the LICENSE file in the root directory of this source tree. + */const gS=[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]],yS=Ri("x",gS),ys="",Ot=["unalive","le dollar bean","seggs","cute","based","cornhole","nsfw","eggplant","spicy","ratio","touch grass","down bad","pretty","why","mean","better","someone","having","harder","top"];async function vS(n=100){const r=await fetch(`${ys}/posts?limit=${n}`);if(!r.ok)throw new Error(`/posts failed: ${r.status}`);return(await r.json()).posts.map(a=>({...a,id:a.id??0,query_term:a.query_term??"",created_at:a.created_at??"",label:a.label==="toxic"?"toxic":"non-toxic"}))}async function xS(){const n=await fetch(`${ys}/posts?limit=1`);if(!n.ok)return-1;const r=await n.json();return typeof r.total=="number"?r.total:-1}async function wS(n,r,o){const a=await fetch(`${ys}/fetch-and-analyze`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({queries:n,limit:r,threshold:o})});if(!a.ok)throw new Error(`/fetch-and-analyze failed: ${a.status}`);const u=await a.json();return{posts:u.posts.map(f=>({...f,id:f.id??0,query_term:f.query_term??"",created_at:f.created_at??"",label:f.label==="toxic"?"toxic":"non-toxic"})),fetchTime:u.fetch_time,inferTime:u.infer_time,count:u.count,message:u.message}}async function SS(n,r){const o=await fetch(`${ys}/graph-data?min_cooccurrence=${n}&toxic_only=${r}`);if(!o.ok)throw new Error(`/graph-data failed: ${o.status}`);const a=await o.json();return{nodes:a.nodes.map(d=>({id:d.id,frequency:d.count,toxicRatio:d.toxic_ratio})),edges:a.edges}}const sp=["I can't believe they used {term} like that, this is getting out of hand","Found another account using {term} to avoid filters, reported it immediately","The {term} discourse is absolutely wild today, why are people like this","Seeing {term} trending again. Not a great sign for the platform tbh","Whoever invented {term} to avoid filters is frankly unhinged","Just saw {term} used unironically in a serious post, internet is something","Moderators need to update their filters, {term} is literally everywhere now","The {term} to english translation is honestly concerning for society","Why does {term} keep showing up in my feed, algorithm is broken again","Teaching my mom what {term} means was the hardest conversation I've had this year","{term} energy today, not gonna lie tbh","Petition to ban {term} from the platform. Who's with me","Using {term} unironically is such a major red flag ngl","The way {term} has evolved over time is fascinating from a linguistics POV","Just another day of seeing {term} everywhere on here smh","Why is {term} suddenly all over my fyp, is this a new trend","Blocked 3 accounts today for using {term} in my replies","{term} popped up again in the discourse, same old same old","Can we talk about how normalized {term} has become? Not ok.","Researchers studying {term} usage patterns should check bluesky tbh"];function kS(){const n=Math.random();return n<.55?.62+Math.random()*.38:n<.75?.4+Math.random()*.3:Math.random()*.38}function Ll(n,r=180){const o=[],a=Date.now(),u=1440*60*1e3;for(let d=0;d=.7?"toxic":"non-toxic",query_term:f,created_at:m})}return o.sort((d,f)=>new Date(f.created_at).getTime()-new Date(d.created_at).getTime())}function Si(n){return n>=.7?"#ff4b4b":n>=.4?"#ff8c42":"#2ecc71"}function pr({children:n}){return v.jsx("div",{style:{fontSize:"0.58rem",textTransform:"uppercase",letterSpacing:"1.4px",color:"#3a4060",marginTop:"1rem",marginBottom:"0.35rem"},children:n})}function TS({open:n,selectedTerms:r,setSelectedTerms:o,threshold:a,setThreshold:u,sampling:d,setSampling:f,autoRefresh:h,setAutoRefresh:g,onFetch:y,posts:m,fetching:x}){const[w,k]=V.useState(""),[E,j]=V.useState([]),D=()=>{const b=w.trim().toLowerCase();b&&(E.includes(b)||j(z=>[...z,b]),r.includes(b)||o([...r,b]),k(""))},_={};for(const b of r){const z=m.filter(F=>F.query_term===b||F.text.toLowerCase().includes(b));if(z.length===0){_[b]=Math.random()*.8;continue}_[b]=z.filter(F=>F.label==="toxic").length/z.length}return v.jsx(Sr,{children:n&&v.jsxs(oe.aside,{initial:{x:-260,opacity:0},animate:{x:0,opacity:1},exit:{x:-260,opacity:0},transition:{type:"spring",stiffness:280,damping:30},style:{width:240,minWidth:240,background:"#0d1120",borderRight:"1px solid #1e2540",padding:"1rem 0.85rem 1.5rem",display:"flex",flexDirection:"column",overflowY:"auto",overflowX:"hidden"},children:[v.jsx(pr,{children:"Tracked terms"}),v.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4,flexShrink:0},children:r.slice(0,6).map(b=>{const z=Math.round((_[b]??0)*100),F=Si(_[b]??0);return v.jsxs(oe.div,{initial:{opacity:0,x:-10},animate:{opacity:1,x:0},style:{display:"flex",alignItems:"center",justifyContent:"space-between",background:"#141826",border:"1px solid #1e2540",borderRadius:8,padding:"5px 9px"},children:[v.jsxs("div",{style:{display:"flex",alignItems:"center",gap:7},children:[v.jsx("div",{style:{width:8,height:8,borderRadius:"50%",background:F,flexShrink:0}}),v.jsx("span",{style:{fontSize:"0.78rem",color:"#c8cce0"},children:b})]}),v.jsxs("span",{style:{fontSize:"0.68rem",color:"#3a4060"},children:[z,"%"]})]},b)})}),v.jsx(pr,{children:"Algospeak terms"}),v.jsx("div",{style:{background:"#141826",border:"1px solid #1e2540",borderRadius:8,padding:"6px",display:"flex",flexWrap:"wrap",gap:4,maxHeight:160,overflowY:"auto",flexShrink:0},children:[...Ot,...E].map(b=>{const z=r.includes(b);return v.jsxs("button",{onClick:()=>{o(z?r.filter(F=>F!==b):[...r,b])},style:{padding:"2px 6px 2px 8px",borderRadius:999,fontSize:"0.68rem",cursor:"pointer",border:"1px solid",transition:"all 0.15s",background:z?"rgba(155,127,212,0.18)":"transparent",borderColor:z?"rgba(155,127,212,0.5)":"#2a3050",color:z?"#c3a6ff":"#5a6080",display:"flex",alignItems:"center",gap:4},children:[b,z&&v.jsx("span",{onClick:F=>{F.stopPropagation(),o(r.filter(M=>M!==b))},style:{display:"inline-flex",alignItems:"center",justifyContent:"center",width:12,height:12,borderRadius:"50%",background:"rgba(155,127,212,0.3)",color:"#c3a6ff",fontSize:"0.6rem",lineHeight:1,flexShrink:0,cursor:"pointer"},children:"✕"})]},b)})}),v.jsx(pr,{children:"Add custom term"}),v.jsx("div",{style:{display:"flex",gap:6,flexShrink:0},children:v.jsx("input",{value:w,onChange:b=>k(b.target.value),onKeyDown:b=>b.key==="Enter"&&D(),placeholder:"Type a term...",style:{flex:1,background:"#141826",border:"1px solid #1e2540",borderRadius:7,padding:"5px 9px",color:"#e8eaf0",fontSize:"0.78rem",outline:"none"}})}),v.jsx("button",{onClick:D,style:{marginTop:6,width:"100%",background:"rgba(155,127,212,0.12)",border:"1px solid rgba(155,127,212,0.3)",borderRadius:7,color:"#c3a6ff",fontSize:"0.78rem",padding:"5px 0",cursor:"pointer",fontWeight:600,flexShrink:0},children:"+ Add term"}),v.jsx(pr,{children:"Threshold"}),v.jsxs("div",{style:{paddingInline:2,flexShrink:0},children:[v.jsx("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:4},children:v.jsx("span",{style:{fontSize:"0.72rem",color:"#ff6b3d",fontWeight:700},children:a.toFixed(2)})}),v.jsx("input",{type:"range",min:0,max:1,step:.05,value:a,onChange:b=>u(parseFloat(b.target.value)),style:{width:"100%",accentColor:"#ff4b4b"}})]}),v.jsx(pr,{children:"Sampling"}),v.jsxs("div",{style:{paddingInline:2,flexShrink:0},children:[v.jsx("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:4},children:v.jsx("span",{style:{fontSize:"0.72rem",color:"#ff6b3d",fontWeight:700},children:d})}),v.jsx("input",{type:"range",min:5,max:100,step:5,value:d,onChange:b=>f(parseInt(b.target.value)),style:{width:"100%",accentColor:"#ff4b4b"}})]}),v.jsx(pr,{children:"Fetch"}),v.jsxs("label",{style:{display:"flex",alignItems:"center",gap:7,cursor:"pointer",marginBottom:10,flexShrink:0},children:[v.jsx("input",{type:"checkbox",checked:h,onChange:b=>g(b.target.checked),style:{accentColor:"#ff4b4b"}}),v.jsx("span",{style:{fontSize:"0.78rem",color:"#8a90ad"},children:"Auto-refresh (60s)"})]}),v.jsx(oe.button,{onClick:y,disabled:x,whileHover:{scale:x?1:1.02},whileTap:{scale:x?1:.97},style:{width:"100%",background:x?"linear-gradient(135deg, #aa3333, #aa5f2a)":"linear-gradient(135deg, #ff4b4b, #ff8c42)",color:"#fff",border:"none",borderRadius:9,padding:"9px 0",fontWeight:700,fontSize:"0.88rem",cursor:x?"not-allowed":"pointer",boxShadow:x?"none":"0 0 16px rgba(255,75,75,0.3)",transition:"box-shadow 0.2s",flexShrink:0},children:x?"Fetching…":"Fetch & Analyze"}),E.length>0&&v.jsx("div",{style:{marginTop:"0.8rem",display:"flex",flexWrap:"wrap",gap:4,flexShrink:0},children:E.map(b=>v.jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:3,padding:"2px 7px",borderRadius:999,background:"rgba(155,127,212,0.1)",border:"1px solid rgba(155,127,212,0.4)",color:"#c3a6ff",fontSize:"0.65rem"},children:[b,v.jsx(yS,{size:10,style:{cursor:"pointer"},onClick:()=>{j(z=>z.filter(F=>F!==b)),o(r.filter(z=>z!==b))}})]},b))})]},"sidebar")})}function CS(n,r=900){const[o,a]=V.useState(0),u=V.useRef(null),d=V.useRef(0),f=V.useRef(0);return V.useEffect(()=>{const h=d.current;u.current=null;const g=y=>{u.current===null&&(u.current=y);const m=Math.min((y-u.current)/r,1),x=1-Math.pow(1-m,3);a(Math.round(h+(n-h)*x)),m<1?f.current=requestAnimationFrame(g):d.current=n};return f.current=requestAnimationFrame(g),()=>cancelAnimationFrame(f.current)},[n,r]),o}function ap(n,r=900,o=1){const[a,u]=V.useState(0),d=V.useRef(null),f=V.useRef(0),h=V.useRef(0);return V.useEffect(()=>{const g=f.current;d.current=null;const y=m=>{d.current===null&&(d.current=m);const x=Math.min((m-d.current)/r,1),w=1-Math.pow(1-x,3);u(g+(n-g)*w),x<1?h.current=requestAnimationFrame(y):f.current=n};return h.current=requestAnimationFrame(y),()=>cancelAnimationFrame(h.current)},[n,r]),a.toFixed(o)}function Ko({label:n,displayValue:r,sub:o,subIcon:a,subColor:u,subBg:d,valueColor:f,delay:h,isAlert:g}){return v.jsxs(oe.div,{initial:{opacity:0,y:24},animate:{opacity:1,y:0},transition:{delay:h,duration:.45,ease:"easeOut"},style:{background:g?"rgba(255,75,75,0.06)":"#0d1120",border:g?"1px solid rgba(255,75,75,0.3)":"1px solid #1e2540",borderRadius:10,padding:"0.8rem 1rem",flex:1,minWidth:0,position:"relative",overflow:"hidden"},children:[g&&v.jsx(oe.div,{animate:{opacity:[.4,1,.4]},transition:{duration:1.5,repeat:1/0},style:{position:"absolute",top:0,left:0,width:"100%",height:2,background:"linear-gradient(90deg, transparent, #ff4b4b, transparent)"}}),v.jsx("div",{style:{fontSize:"0.58rem",textTransform:"uppercase",letterSpacing:"1px",color:"#3a4060",marginBottom:"0.35rem"},children:n}),v.jsx("div",{style:{fontSize:"1.45rem",fontWeight:700,color:f,lineHeight:1.1,fontVariantNumeric:"tabular-nums"},children:r}),v.jsxs(oe.div,{initial:{opacity:0,x:-6},animate:{opacity:1,x:0},transition:{delay:h+.25,duration:.35},style:{marginTop:"0.4rem",display:"inline-flex",alignItems:"center",gap:4,padding:"2px 7px 2px 5px",borderRadius:999,background:d??"rgba(90,96,128,0.12)",border:`1px solid ${u?u+"30":"#2a3050"}`,maxWidth:"100%"},children:[a&&v.jsx("span",{style:{fontSize:"0.65rem",flexShrink:0},children:a}),v.jsx("span",{style:{fontSize:"0.65rem",color:u??"#6a7090",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",fontWeight:u?600:400},children:o})]})]})}function ES({batchToxicRate:n,batchCount:r}){const[o,a]=V.useState(!1);if(V.useEffect(()=>{a(!1)},[Math.floor(n)]),n<38||r===0||o)return null;const u=n>=70?"critical":n>=55?"high":"elevated",d=u==="critical"?"#ff4b4b":u==="high"?"#ff6b3d":"#ff9f43",f=u==="critical"?"rgba(255,75,75,0.08)":u==="high"?"rgba(255,107,61,0.08)":"rgba(255,159,67,0.08)",h=u==="critical"?"rgba(255,75,75,0.35)":u==="high"?"rgba(255,107,61,0.3)":"rgba(255,159,67,0.28)";return v.jsx(Sr,{children:v.jsxs(oe.div,{initial:{opacity:0,y:-12,height:0},animate:{opacity:1,y:0,height:"auto"},exit:{opacity:0,y:-12,height:0},transition:{duration:.4},style:{background:f,border:`1px solid ${h}`,borderRadius:9,padding:"0.7rem 1rem",display:"flex",alignItems:"center",gap:"0.75rem",overflow:"hidden",position:"relative"},children:[v.jsx(oe.div,{animate:{x:["-100%","400%"]},transition:{duration:2.5,repeat:1/0,ease:"linear",repeatDelay:1},style:{position:"absolute",top:0,left:0,width:"30%",height:"100%",background:`linear-gradient(90deg, transparent, ${d}10, transparent)`,pointerEvents:"none"}}),v.jsx(oe.div,{animate:{scale:[1,1.5,1],opacity:[1,.4,1]},transition:{duration:1.1,repeat:1/0},style:{width:10,height:10,borderRadius:"50%",background:d,boxShadow:`0 0 12px ${d}cc`,flexShrink:0}}),v.jsxs("div",{style:{flex:1},children:[v.jsx("span",{style:{color:d,fontWeight:700,fontSize:"0.82rem"},children:"⚠ Toxicity Spike Detected"}),v.jsxs("span",{style:{color:"#9a8060",fontSize:"0.78rem",marginLeft:8},children:[n.toFixed(1),"% of last ",r," posts flagged as toxic"]}),v.jsxs("div",{style:{marginTop:4,display:"flex",alignItems:"center",gap:6},children:[v.jsx("div",{style:{flex:1,height:3,background:"#1a1d2e",borderRadius:2,overflow:"hidden"},children:v.jsx(oe.div,{initial:{width:0},animate:{width:`${Math.min(n,100)}%`},transition:{duration:.8,ease:"easeOut"},style:{height:"100%",background:`linear-gradient(90deg, ${d}88, ${d})`,borderRadius:2}})}),v.jsx("span",{style:{fontSize:"0.65rem",color:d,fontWeight:700,minWidth:30},children:u.toUpperCase()})]})]}),v.jsx("button",{onClick:()=>a(!0),style:{background:"none",border:"none",color:"#5a4040",cursor:"pointer",fontSize:"1rem",padding:"0 4px",flexShrink:0},children:"✕"})]})})}function PS({data:n}){const r=V.useRef(null),[o,a]=V.useState(400);V.useEffect(()=>{if(!r.current)return;const M=new ResizeObserver(H=>{for(const I of H)a(I.contentRect.width)});return M.observe(r.current),a(r.current.clientWidth),()=>M.disconnect()},[]);const u=o,d=170,f={top:12,right:12,bottom:28,left:36},h={w:u-f.left-f.right,h:d-f.top-f.bottom},g=n.map(M=>({...M,value:M.value??0})),y=g.map(M=>M.value),m=0,w=Math.max(...y,.01)-m||.001,[k,E]=V.useState(null),j=M=>f.left+M/(g.length-1)*h.w,D=M=>f.top+h.h-(M-m)/w*h.h,_=g.map((M,H)=>{const I=j(H),N=D(M.value);return`${H===0?"M":"L"} ${I},${N}`}).join(" "),b=`${_} L ${j(g.length-1)},${f.top+h.h} L ${j(0)},${f.top+h.h} Z`,z=[0,.25,.5,.75,1],F=g.filter((M,H)=>H%4===0);return v.jsx("div",{ref:r,style:{position:"relative",width:"100%"},children:v.jsxs("svg",{viewBox:`0 0 ${u} ${d}`,width:u,height:d,style:{display:"block",overflow:"visible",width:"100%"},onMouseLeave:()=>E(null),children:[v.jsxs("defs",{children:[v.jsxs("linearGradient",{id:"areaGrad",x1:"0",y1:"0",x2:"0",y2:"1",children:[v.jsx("stop",{offset:"0%",stopColor:"#ff4b4b",stopOpacity:.25}),v.jsx("stop",{offset:"100%",stopColor:"#ff4b4b",stopOpacity:.02})]}),v.jsxs("filter",{id:"glow",children:[v.jsx("feGaussianBlur",{stdDeviation:"2",result:"coloredBlur"}),v.jsxs("feMerge",{children:[v.jsx("feMergeNode",{in:"coloredBlur"}),v.jsx("feMergeNode",{in:"SourceGraphic"})]})]})]}),z.map(M=>{const H=f.top+h.h-M/1*h.h;return v.jsxs("g",{children:[v.jsx("line",{x1:f.left,y1:H,x2:f.left+h.w,y2:H,stroke:"#1e2540",strokeDasharray:"4 4",strokeOpacity:.7}),v.jsx("text",{x:f.left-6,y:H+4,textAnchor:"end",fill:"#3a4060",fontSize:9,children:M.toFixed(1)})]},`grid-y-${M}`)}),F.map(M=>{const H=g.indexOf(M);return v.jsx("text",{x:j(H),y:d-6,textAnchor:"middle",fill:"#3a4060",fontSize:9,children:M.hour},`xlabel-${M.hour}`)}),v.jsx("path",{d:b,fill:"url(#areaGrad)",strokeWidth:0}),v.jsx("path",{d:_,fill:"none",stroke:"#ff4b4b",strokeWidth:2,strokeLinejoin:"round",strokeLinecap:"round",filter:"url(#glow)"}),n.map((M,H)=>{if(M.value===null||M.value===0)return null;const I=j(H),N=D(M.value),Y=M.value>=.7?"#ff4b4b":M.value>=.4?"#ff8c42":"#2ecc71";return v.jsx("circle",{cx:I,cy:N,r:3.5,fill:Y,stroke:"#0a0d14",strokeWidth:1.5,style:{cursor:"pointer"},onMouseEnter:()=>E({x:I,y:N,hour:M.hour,val:M.value})},`dot-${M.hour}`)}),k&&v.jsxs("g",{children:[v.jsx("line",{x1:k.x,y1:f.top,x2:k.x,y2:f.top+h.h,stroke:"#ff4b4b",strokeWidth:1,strokeDasharray:"3 3",strokeOpacity:.4}),v.jsx("rect",{x:k.x-40,y:k.y-40,width:80,height:32,rx:6,fill:"#141826",stroke:"#2a3050",strokeWidth:1}),v.jsx("text",{x:k.x,y:k.y-26,textAnchor:"middle",fill:"#8a90ad",fontSize:9,children:k.hour}),v.jsx("text",{x:k.x,y:k.y-13,textAnchor:"middle",fill:"#ff6b6b",fontSize:11,fontWeight:"bold",children:k.val.toFixed(3)})]})]})})}function jS({data:n}){const r=Math.max(...n.map(d=>d.count),1),[o,a]=V.useState(null);V.useRef(null);const u=[["#4ade80","#22c55e"],["#a3e635","#84cc16"],["#facc15","#eab308"],["#fb923c","#f97316"],["#f87171","#ef4444"]];return v.jsxs("div",{style:{position:"relative"},children:[v.jsxs("div",{style:{position:"relative",height:160,display:"flex",alignItems:"flex-end",gap:6,padding:"0 2px"},children:[[.25,.5,.75,1].map((d,f)=>v.jsx("div",{style:{position:"absolute",bottom:`${d*100}%`,left:0,right:0,height:1,background:"rgba(30,37,64,0.7)",pointerEvents:"none"}},f)),n.map((d,f)=>{const h=d.count/r*100,g=o===f,[y,m]=u[f];return v.jsxs("div",{style:{flex:1,display:"flex",flexDirection:"column",alignItems:"center",height:"100%",justifyContent:"flex-end",position:"relative"},onMouseEnter:()=>a(f),onMouseLeave:()=>a(null),children:[g&&v.jsxs(oe.div,{initial:{opacity:0,y:4},animate:{opacity:1,y:0},style:{position:"absolute",bottom:`calc(${h}% + 10px)`,left:"50%",transform:"translateX(-50%)",background:"#141826",border:`1px solid ${y}55`,borderRadius:6,padding:"3px 8px",fontSize:10,color:y,whiteSpace:"nowrap",zIndex:10,fontWeight:700,boxShadow:`0 0 8px ${y}33`},children:[d.count," posts"]}),v.jsx(oe.div,{initial:{height:0},animate:{height:`${h}%`},transition:{delay:.3+f*.07,duration:.6,ease:[.34,1.2,.64,1]},style:{width:"100%",background:`linear-gradient(180deg, ${y} 0%, ${m} 100%)`,borderRadius:"5px 5px 0 0",minHeight:d.count>0?4:0,opacity:o===null||g?1:.4,transition:"opacity 0.2s",boxShadow:g?`0 0 16px ${y}80, 0 0 6px ${y}40`:"none",position:"relative"},children:v.jsx("div",{style:{position:"absolute",top:0,left:0,right:0,height:3,background:`linear-gradient(90deg, transparent, ${y}cc, transparent)`,borderRadius:"5px 5px 0 0"}})})]},`score-bar-${d.label}`)})]}),v.jsx("div",{style:{height:1,background:"#1e2540",margin:"0 2px"}}),v.jsx("div",{style:{display:"flex",gap:6,padding:"5px 2px 0",marginTop:2},children:n.map((d,f)=>v.jsx("div",{style:{flex:1,fontSize:8,color:o===f?u[f][0]:"#4a5070",textAlign:"center",transition:"color 0.2s",lineHeight:1.3},children:d.label},f))})]})}const RS=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"];function MS({posts:n,batchPosts:r,selectedTerms:o,justFetched:a,totalAnalyzed:u}){const d=n.length,f=n.filter(M=>M.label==="toxic").length,h=d?f/d*100:0,g=r.filter(M=>M.label==="toxic").length,y=r.length?g/r.length*100:0,m=r.length?r.reduce((M,H)=>M+H.score,0)/r.length:0,x={};for(const M of r)x[M.query_term]=(x[M.query_term]||0)+1;const w=Object.keys(x).sort((M,H)=>x[H]-x[M])[0]||"—",k=CS(u),E=ap(h,900,1),j=ap(m,900,3),D=V.useMemo(()=>{const M={};for(const H of n){const I=new Date(H.created_at).getUTCHours();M[I]||(M[I]={sum:0,count:0}),M[I].sum+=H.score,M[I].count+=1}return Array.from({length:24},(H,I)=>({hour:`${String(I).padStart(2,"0")}:00`,value:M[I]?+(M[I].sum/M[I].count).toFixed(3):null}))},[n]),_=V.useMemo(()=>{const M=[0,0,0,0,0];for(const N of r){const Y=Math.min(4,Math.floor(N.score/.2));M[Y]++}const H=["#2ecc71","#a3e635","#ffeb3b","#ff8c42","#ff4b4b"];return["0-0.2","0.2-0.4","0.4-0.6","0.6-0.8","0.8-1.0"].map((N,Y)=>({label:N,count:M[Y],color:H[Y]}))},[r]),b=V.useMemo(()=>{const M={};for(const H of n){const I=new Date(H.created_at),N=(I.getDay()+6)%7,Y=I.getHours(),G=`${N}-${Y}`;M[G]||(M[G]={sum:0,count:0}),M[G].sum+=H.score,M[G].count+=1}return M},[n]),z=V.useMemo(()=>{let M=0;for(const H of Object.values(b))M=Math.max(M,H.count?H.sum/H.count:0);return M||1},[b]),F=r.slice(0,20);return v.jsxs("div",{style:{padding:"1.2rem 1.4rem",display:"flex",flexDirection:"column",gap:"1.1rem"},children:[v.jsx(ES,{batchToxicRate:y,batchCount:r.length}),v.jsxs("div",{style:{display:"flex",gap:"0.9rem"},children:[v.jsx(Ko,{label:"Posts analyzed",displayValue:String(k),sub:`+${r.length} this batch`,subIcon:"📥",subColor:"#4ade80",subBg:"rgba(74,222,128,0.08)",valueColor:"#fff",delay:0}),v.jsx(Ko,{label:"Toxic rate",displayValue:`${E}%`,sub:h>=38?"↑ High — monitor closely":"✓ Within expected range",subIcon:h>=38?"🔴":void 0,subColor:h>=38?"#ff4b4b":"#2ecc71",subBg:h>=38?"rgba(255,75,75,0.1)":"rgba(46,204,113,0.08)",valueColor:"#ff4b4b",delay:.07,isAlert:h>=38}),v.jsx(Ko,{label:"Avg score (last batch)",displayValue:j,sub:"mean toxicity · last fetch",subIcon:"📊",subColor:"#ff8c42",subBg:"rgba(255,140,66,0.08)",valueColor:"#ff8c42",delay:.14}),v.jsx(Ko,{label:"Top term",displayValue:w,sub:"most frequent · last batch",subIcon:"🏷️",subColor:"#9b7fd4",subBg:"rgba(155,127,212,0.1)",valueColor:"#9b7fd4",delay:.21})]}),v.jsxs("div",{style:{display:"flex",gap:"0.9rem"},children:[v.jsxs(oe.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{delay:.28,duration:.45},style:{flex:3,background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"0.7rem"},children:"Toxicity over time"}),v.jsx(PS,{data:D})]}),v.jsxs(oe.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{delay:.35,duration:.45},style:{flex:1,background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"0.7rem"},children:"Score distribution"}),v.jsx(jS,{data:_})]})]}),v.jsxs(oe.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{delay:.42,duration:.45},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"0.8rem"},children:"Activity heatmap"}),v.jsxs("div",{style:{overflowX:"auto"},children:[v.jsxs("div",{style:{display:"flex",alignItems:"center",gap:2,marginBottom:4},children:[v.jsx("div",{style:{width:36}}),Array.from({length:24},(M,H)=>v.jsx("div",{style:{width:20,fontSize:"0.52rem",color:"#3a4060",textAlign:"center",userSelect:"none"},children:H%6===0?H:""},`hlabel-${H}`))]}),RS.map((M,H)=>v.jsxs("div",{style:{display:"flex",alignItems:"center",gap:2,marginBottom:2},children:[v.jsx("div",{style:{width:36,fontSize:"0.65rem",color:"#5a6080",textAlign:"right",paddingRight:6},children:M}),Array.from({length:24},(I,N)=>{const Y=b[`${H}-${N}`],G=Y?Y.sum/Y.count/z:0,se=Y&&Y.count>0,ue=se?Math.round(30+225*G):26,ge=se?Math.round(37+38*G):29,we=se?Math.round(64+11*G):46;return v.jsx(oe.div,{initial:{opacity:0,scale:.5},animate:{opacity:1,scale:1},transition:{delay:(H*24+N)*.002,duration:.25},title:se?`${M} ${String(N).padStart(2,"0")}:00 — avg ${(Y.sum/Y.count).toFixed(2)}`:`${M} ${String(N).padStart(2,"0")}:00 — no data`,style:{width:20,height:20,borderRadius:3,background:`rgb(${ue},${ge},${we})`,cursor:se?"pointer":"default"}},`cell-${M}-${N}`)})]},`day-${M}`)),v.jsxs("div",{style:{marginTop:8,display:"flex",alignItems:"center",gap:7,fontSize:"0.65rem",color:"#5a6080"},children:[v.jsx("span",{children:"Low"}),v.jsx("div",{style:{width:90,height:7,borderRadius:4,background:"linear-gradient(90deg, #1a1d2e, #ff4b4b)"}}),v.jsx("span",{children:"High toxicity"})]})]})]}),v.jsxs(oe.div,{initial:{opacity:0,y:20},animate:{opacity:1,y:0},transition:{delay:.5,duration:.45},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"0.6rem"},children:"Recent posts (last batch)"}),F.length===0?v.jsx("div",{style:{color:"#5a6080",fontSize:"0.8rem",padding:"1rem 0"},children:'Click "Fetch & Analyze" to see recent posts.'}):v.jsx("div",{style:{maxHeight:300,overflowY:"auto",display:"flex",flexDirection:"column"},children:F.map((M,H)=>{const I=M.score>=.7?"#ff4b4b":M.score>=.4?"#ff9f43":"#2ecc71",N=M.score>=.7?"rgba(255,75,75,0.12)":M.score>=.4?"rgba(255,159,67,0.12)":"rgba(46,204,113,0.12)";return v.jsxs(oe.div,{initial:{opacity:0,x:-10},animate:{opacity:1,x:0},transition:{delay:H*.03},style:{display:"flex",alignItems:"center",gap:"0.6rem",padding:"0.55rem 0.3rem",borderBottom:"1px solid #141826"},children:[v.jsx("div",{style:{minWidth:50,textAlign:"center",fontSize:"0.7rem",fontWeight:700,borderRadius:6,padding:"0.2rem 0.3rem",background:N,color:I,flexShrink:0},children:M.score.toFixed(3)}),v.jsx("div",{style:{flex:1,fontSize:"0.77rem",color:"#c8cce0"},children:M.text.slice(0,110)}),v.jsx("div",{style:{fontSize:"0.65rem",padding:"0.1rem 0.4rem",borderRadius:999,background:"rgba(155,127,212,0.12)",color:"#c3a6ff",border:"1px solid rgba(155,127,212,0.4)",whiteSpace:"nowrap",flexShrink:0},children:M.query_term})]},M.id)})})]})]})}const ki=820,Ti=540,AS=6e3,DS=.018,LS=180,lp=.008,up=.82,Vl=ki/2,bl=Ti/2;class VS extends V.Component{constructor(r){super(r),this.state={hasError:!1}}static getDerivedStateFromError(r){return{hasError:!0,error:r.message}}render(){return this.state.hasError?v.jsxs("div",{style:{margin:"1.5rem",background:"rgba(255,75,75,0.07)",border:"1px solid rgba(255,75,75,0.2)",borderRadius:10,padding:"1.5rem",textAlign:"center"},children:[v.jsx("div",{style:{color:"#ff4b4b",fontSize:"0.9rem",marginBottom:8},children:"⚠ Graph failed to render"}),v.jsx("div",{style:{color:"#5a6080",fontSize:"0.75rem"},children:this.state.error||"Unknown error"}),v.jsx("button",{onClick:()=>this.setState({hasError:!1}),style:{marginTop:12,background:"rgba(255,75,75,0.12)",border:"1px solid rgba(255,75,75,0.3)",borderRadius:7,color:"#ff6b3d",padding:"5px 14px",cursor:"pointer",fontSize:"0.78rem"},children:"Retry"})]}):this.props.children}}function yn(n,r=0){return isFinite(n)&&!isNaN(n)?n:r}function bS(n,r,o){const a=V.useRef([]),[u,d]=V.useState([]),f=V.useRef(0),h=V.useRef(r);h.current=r;const g=V.useRef(!1),y=V.useCallback(()=>{if(!g.current)return;const m=a.current;if(!m.length)return;const x=h.current;try{for(let w=0;wH.id===w.source),E=m.find(H=>H.id===w.target);if(!k||!E)continue;const j=E.x-k.x,D=E.y-k.y,_=Math.sqrt(j*j+D*D)||1,b=LS/(1+Math.min(w.weight,8)*.04),z=(_-b)*DS,F=yn(j/_*z),M=yn(D/_*z);k.vx+=F,k.vy+=M,E.vx-=F,E.vy-=M}for(const w of m){w.vx=yn(w.vx+(Vl-w.x)*lp),w.vy=yn(w.vy+(bl-w.y)*lp),w.vx*=up,w.vy*=up,w.x=yn(w.x+w.vx,Vl),w.y=yn(w.y+w.vy,bl);const k=5+Math.min(Math.log1p(w.frequency??1)*1.8,12);w.x=Math.max(k+40,Math.min(ki-k-40,w.x)),w.y=Math.max(k+20,Math.min(Ti-k-20,w.y))}g.current&&(d(m.map(k=>({...k}))),m.reduce((k,E)=>Math.max(k,Math.abs(E.vx)+Math.abs(E.vy)),0)>.15&&(f.current=requestAnimationFrame(y)))}catch{g.current=!1}},[]);return V.useEffect(()=>(cancelAnimationFrame(f.current),g.current=!0,a.current=n.map((m,x)=>{const w=x/Math.max(n.length,1)*2*Math.PI,k=Math.min(ki,Ti)*.32;return{id:m.id,frequency:m.frequency,toxicRatio:m.toxicRatio,x:Vl+Math.cos(w)*k,y:bl+Math.sin(w)*k,vx:(Math.random()-.5)*1.5,vy:(Math.random()-.5)*1.5}}),d(a.current.map(m=>({...m}))),f.current=requestAnimationFrame(y),()=>{g.current=!1,cancelAnimationFrame(f.current)}),[o,y]),u}function _S(n,r,o){const a=((n??0)+(r??0))/2,u=Math.min(.9,.3+o*.05);return a>=.7?`rgba(255,75,75,${u})`:a>=.4?`rgba(255,140,66,${u})`:`rgba(46,204,113,${u})`}function IS({minCooccurrence:n,toxicOnly:r,setMinCooccurrence:o,setToxicOnly:a}){const[u,d]=V.useState(!1),[f,h]=V.useState(null),[g,y]=V.useState(!1),[m,x]=V.useState(null),[w,k]=V.useState([]),[E,j]=V.useState([]),D=V.useCallback(async()=>{y(!0),x(null);try{const I=await SS(n,r);k(I.nodes),j(I.edges),d(!0)}catch(I){x(I instanceof Error?I.message:"Failed to load graph data")}finally{y(!1)}},[n,r]),_=V.useMemo(()=>w.filter(I=>r&&I.toxicRatio<.7?!1:E.filter(Y=>(Y.source===I.id||Y.target===I.id)&&Y.weight>=n).length>0||n<=2),[w,E,r,n]),b=V.useMemo(()=>{const I=new Set(_.map(N=>N.id));return E.filter(N=>N.weight>=n&&I.has(N.source)&&I.has(N.target))},[_,E,n]),z=V.useMemo(()=>_.map(I=>I.id).sort().join(","),[_]),F=bS(_,b,z),M=V.useMemo(()=>{const I={};for(const N of F)I[N.id]={x:N.x,y:N.y};return I},[F]),H=V.useMemo(()=>{const I={};for(const N of _)I[N.id]=N;return I},[_]);return v.jsxs("div",{style:{padding:"1.2rem 1.4rem"},children:[v.jsxs("div",{style:{display:"flex",gap:"1rem",marginBottom:"1rem"},children:[v.jsxs(oe.div,{initial:{opacity:0,y:16},animate:{opacity:1,y:0},style:{flex:3,background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.75rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.58rem",textTransform:"uppercase",letterSpacing:"1px",color:"#3a4060",marginBottom:6},children:"How to read this graph"}),v.jsxs("div",{style:{fontSize:"0.78rem",color:"#8a90ad"},children:["Words that frequently appear together in algospeak posts are connected. Node size = frequency  | ",v.jsx("span",{style:{color:"#ff4b4b"},children:"red >70% toxic"})," ",v.jsx("span",{style:{color:"#ff9f43"},children:"orange 40-70% mixed"})," ",v.jsx("span",{style:{color:"#2ecc71"},children:"green <40% benign"})]})]}),v.jsxs(oe.div,{initial:{opacity:0,y:16},animate:{opacity:1,y:0},transition:{delay:.05},style:{flex:1,background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.75rem 1rem",display:"flex",flexDirection:"column",gap:8},children:[v.jsxs("div",{children:[v.jsx("div",{style:{fontSize:"0.62rem",color:"#5a6080",marginBottom:3},children:"Min co-occurrences"}),v.jsx("div",{style:{display:"flex",justifyContent:"flex-end",marginBottom:2},children:v.jsx("span",{style:{fontSize:"0.72rem",color:"#ff6b3d",fontWeight:700},children:n})}),v.jsx("input",{type:"range",min:2,max:10,step:1,value:n,onChange:I=>{o(parseInt(I.target.value)),d(!1),k([]),j([])},style:{width:"100%",accentColor:"#ff4b4b"}})]}),v.jsxs("label",{style:{display:"flex",alignItems:"center",gap:6,cursor:"pointer"},children:[v.jsx("input",{type:"checkbox",checked:r,onChange:I=>{a(I.target.checked),d(!1),k([]),j([])},style:{accentColor:"#ff4b4b"}}),v.jsx("span",{style:{fontSize:"0.75rem",color:"#8a90ad"},children:"Toxic posts only"})]}),v.jsx(oe.button,{onClick:D,disabled:g,whileHover:{scale:g?1:1.02},whileTap:{scale:g?1:.97},style:{background:u?"linear-gradient(135deg, #2ecc71, #27ae60)":"linear-gradient(135deg, #ff4b4b, #ff8c42)",color:"#fff",border:"none",borderRadius:8,padding:"7px 0",fontWeight:700,fontSize:"0.82rem",cursor:g?"wait":"pointer",opacity:g?.7:1,boxShadow:u?"0 0 14px rgba(46,204,113,0.25)":"0 0 14px rgba(255,75,75,0.25)"},children:g?"Loading…":u?"✓ Graph Active":"Build Graph"})]})]}),m&&v.jsxs("div",{style:{background:"rgba(255,75,75,0.07)",border:"1px solid rgba(255,75,75,0.2)",borderRadius:10,padding:"1rem",color:"#ff6b3d",fontSize:"0.8rem",marginBottom:"1rem"},children:["⚠ ",m]}),u?v.jsxs(oe.div,{initial:{opacity:0},animate:{opacity:1},transition:{duration:.5},style:{background:"#080c18",border:"1px solid #1e2540",borderRadius:10,overflow:"hidden",position:"relative",maxHeight:"62vh"},children:[v.jsxs("svg",{width:"100%",viewBox:`0 0 ${ki} ${Ti}`,preserveAspectRatio:"xMidYMid meet",style:{display:"block",width:"100%",height:"auto",maxHeight:"62vh"},children:[v.jsx("defs",{children:v.jsx("pattern",{id:"grid",width:"40",height:"40",patternUnits:"userSpaceOnUse",children:v.jsx("path",{d:"M 40 0 L 0 0 0 40",fill:"none",stroke:"#1a1f35",strokeWidth:"0.5"})})}),v.jsx("rect",{width:ki,height:Ti,fill:"url(#grid)",opacity:.5}),b.map(I=>{const N=M[I.source],Y=M[I.target];if(!N||!Y)return null;const G=H[I.source],se=H[I.target];if(!G||!se)return null;const ue=f===I.source||f===I.target;return v.jsx("line",{x1:N.x,y1:N.y,x2:Y.x,y2:Y.y,stroke:_S(G.toxicRatio,se.toxicRatio,I.weight),strokeWidth:.5+Math.min(Math.log1p(I.weight)*.5,2.5),opacity:f?ue?.9:.1:.7,style:{transition:"opacity 0.2s"}},`${I.source}--${I.target}`)}),F.map(I=>{const N=I.frequency??1,Y=I.toxicRatio??.5,G=5+Math.min(Math.log1p(N)*1.8,12),se=Si(Y),ue=f===I.id,ge=!!(f&&!ue),we=ue?G*1.25:G;return!isFinite(I.x)||!isFinite(I.y)?null:v.jsxs("g",{onMouseEnter:()=>h(I.id),onMouseLeave:()=>h(null),style:{cursor:"pointer"},children:[v.jsx("circle",{cx:I.x,cy:I.y,r:we+6,fill:se,opacity:ue?.2:0,style:{transition:"opacity 0.2s"}}),v.jsx("circle",{cx:I.x,cy:I.y,r:we,fill:se,fillOpacity:ge?.2:.85,stroke:se,strokeWidth:ue?2.5:1.5,strokeOpacity:ge?.2:.6,style:{transition:"opacity 0.2s"}}),v.jsx("text",{x:I.x,y:I.y+we+12,textAnchor:"middle",fontSize:ue?11:9.5,fill:ge?"#3a4060":"#c8cce0",fontFamily:"system-ui, sans-serif",style:{userSelect:"none",pointerEvents:"none",transition:"opacity 0.2s"},opacity:ge?.2:1,children:I.id})]},I.id)})]}),f&&(()=>{const I=_.find(G=>G.id===f);if(!I)return null;const N=Si(I.toxicRatio),Y=b.filter(G=>G.source===I.id||G.target===I.id).length;return v.jsxs(oe.div,{initial:{opacity:0,scale:.9},animate:{opacity:1,scale:1},style:{position:"absolute",top:12,right:12,background:"#0d1120",border:`1px solid ${N}44`,borderRadius:9,padding:"0.6rem 0.9rem",minWidth:150,boxShadow:`0 0 20px ${N}22`},children:[v.jsx("div",{style:{fontWeight:700,color:"#e8eaf0",fontSize:"0.88rem",marginBottom:6},children:I.id}),v.jsxs("div",{style:{fontSize:"0.72rem",color:"#8a90ad",marginBottom:2},children:["Frequency: ",v.jsx("span",{style:{color:"#e8eaf0"},children:I.frequency})]}),v.jsxs("div",{style:{fontSize:"0.72rem",color:"#8a90ad",marginBottom:2},children:["Toxic ratio:"," ",v.jsxs("span",{style:{color:N,fontWeight:700},children:[(I.toxicRatio*100).toFixed(0),"%"]})]}),v.jsxs("div",{style:{fontSize:"0.72rem",color:"#8a90ad"},children:["Connections: ",v.jsx("span",{style:{color:"#e8eaf0"},children:Y})]}),v.jsx("div",{style:{marginTop:6,background:"#1a1f35",borderRadius:4,height:4,overflow:"hidden"},children:v.jsx("div",{style:{width:`${I.toxicRatio*100}%`,height:"100%",background:N,borderRadius:4,transition:"width 0.3s"}})})]},f)})()]}):v.jsx(oe.div,{initial:{opacity:0},animate:{opacity:1},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"3rem",textAlign:"center",color:"#5a6080",fontSize:"0.85rem"},children:'Adjust settings above and click "Build Graph" to visualize word co-occurrences.'})]})}function zS({minCooccurrence:n,setMinCooccurrence:r,toxicOnly:o,setToxicOnly:a}){return v.jsx(VS,{children:v.jsx(IS,{minCooccurrence:n,setMinCooccurrence:r,toxicOnly:o,setToxicOnly:a})})}function cp(n,r){const o=n.filter(d=>d.query_term===r||d.text.toLowerCase().includes(r));if(!o.length)return null;const a=o.map(d=>d.score),u=o.filter(d=>d.label==="toxic").length;return{count:o.length,toxicRate:u/o.length*100,avgScore:a.reduce((d,f)=>d+f,0)/a.length,maxScore:Math.max(...a),posts:o}}const _l=["0-0.2","0.2-0.4","0.4-0.6","0.6-0.8","0.8-1.0"];function Go(n){const r=[0,0,0,0,0];for(const o of n){const a=Math.min(4,Math.floor(o.score/.2));r[a]++}return r}function FS({posts:n}){const[r,o]=V.useState(Ot[0]),[a,u]=V.useState(Ot[1]),[d,f]=V.useState(null),h=cp(n,r),g=cp(n,a),y={background:"#141826",border:"1px solid #1e2540",borderRadius:8,padding:"6px 10px",color:"#e8eaf0",fontSize:"0.83rem",width:"100%",cursor:"pointer"};return _l.map((m,x)=>({label:m,a:h?Go(h.posts)[x]:0,b:g?Go(g.posts)[x]:0})),v.jsxs("div",{style:{padding:"1.2rem 1.4rem",display:"flex",flexDirection:"column",gap:"1.1rem"},children:[v.jsx(oe.div,{initial:{opacity:0,y:12},animate:{opacity:1,y:0},style:{color:"#5a6080",fontSize:"0.82rem"},children:"Select two algospeak terms to compare their toxicity profiles from all stored posts."}),v.jsxs("div",{style:{display:"flex",gap:"1rem"},children:[v.jsxs(oe.div,{initial:{opacity:0,y:14},animate:{opacity:1,y:0},transition:{delay:.07},style:{flex:1},children:[v.jsx("div",{style:{fontSize:"0.68rem",color:"#5a6080",marginBottom:5},children:"Term A"}),v.jsx("select",{value:r,onChange:m=>o(m.target.value),style:y,children:Ot.map(m=>v.jsx("option",{value:m,children:m},m))})]}),v.jsxs(oe.div,{initial:{opacity:0,y:14},animate:{opacity:1,y:0},transition:{delay:.12},style:{flex:1},children:[v.jsx("div",{style:{fontSize:"0.68rem",color:"#5a6080",marginBottom:5},children:"Term B"}),v.jsx("select",{value:a,onChange:m=>u(m.target.value),style:y,children:Ot.map(m=>v.jsx("option",{value:m,children:m},m))})]})]}),r===a&&v.jsx("div",{style:{background:"rgba(255,159,67,0.08)",border:"1px solid rgba(255,159,67,0.2)",borderRadius:8,padding:"0.6rem 1rem",color:"#ff9f43",fontSize:"0.8rem"},children:"Select two different terms to compare."}),r!==a&&v.jsxs(v.Fragment,{children:[v.jsx("div",{style:{display:"flex",gap:"1rem"},children:[{term:r,stats:h,color:"#a6b0ff"},{term:a,stats:g,color:"#ff8c42"}].map(({term:m,stats:x,color:w},k)=>v.jsxs(oe.div,{initial:{opacity:0,y:18},animate:{opacity:1,y:0},transition:{delay:k*.08+.15},style:{flex:1,background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsxs("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"0.6rem"},children:["“",m,"”"]}),x?v.jsx("div",{style:{display:"flex",gap:"1.5rem",flexWrap:"wrap"},children:[{label:"Posts",value:String(x.count),valueColor:w},{label:"Toxic rate",value:`${x.toxicRate.toFixed(1)}%`,valueColor:Si(x.toxicRate/100)},{label:"Avg score",value:x.avgScore.toFixed(3),valueColor:Si(x.avgScore)},{label:"Max score",value:x.maxScore.toFixed(3),valueColor:"#ff4b4b"}].map(({label:E,value:j,valueColor:D})=>v.jsxs("div",{children:[v.jsx("div",{style:{fontSize:"0.58rem",textTransform:"uppercase",letterSpacing:"1px",color:"#3a4060",marginBottom:4},children:E}),v.jsx("div",{style:{fontSize:"1.25rem",fontWeight:700,color:D},children:j})]},E))}):v.jsx("div",{style:{color:"#5a6080",fontSize:"0.8rem"},children:"No data — fetch more posts first."})]},m))}),h&&g&&(()=>{const m=Go(h.posts),x=Go(g.posts),w=Math.max(...m,...x,1),k=[.25,.5,.75,1];return v.jsxs(oe.div,{initial:{opacity:0,y:18},animate:{opacity:1,y:0},transition:{delay:.3},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"1rem 1.1rem 0.8rem"},children:[v.jsxs("div",{style:{display:"flex",alignItems:"center",justifyContent:"space-between",marginBottom:"0.6rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0"},children:"Score distribution comparison"}),v.jsx("div",{style:{display:"flex",gap:"1rem"},children:[{label:r,color:"#a6b0ff",glow:"rgba(166,176,255,0.4)"},{label:a,color:"#ff8c42",glow:"rgba(255,140,66,0.4)"}].map(({label:E,color:j,glow:D})=>v.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,fontSize:"0.72rem",color:"#8a90ad"},children:[v.jsx("div",{style:{width:10,height:10,borderRadius:2,background:j,boxShadow:`0 0 6px ${D}`}}),E]},E))})]}),v.jsxs("div",{style:{display:"flex",gap:6},children:[v.jsx("div",{style:{display:"flex",flexDirection:"column",justifyContent:"space-between",alignItems:"flex-end",height:180,paddingBottom:18,paddingTop:2},children:[w,Math.round(w*.75),Math.round(w*.5),Math.round(w*.25),0].map((E,j)=>v.jsx("div",{style:{fontSize:9,color:"#3a4060",lineHeight:1},children:E},j))}),v.jsxs("div",{style:{flex:1,position:"relative"},children:[v.jsxs("div",{style:{position:"absolute",inset:"0 0 18px 0",display:"flex",flexDirection:"column",justifyContent:"space-between",pointerEvents:"none"},children:[k.map((E,j)=>v.jsx("div",{style:{height:1,background:"rgba(30,37,64,0.8)",width:"100%"}},j)),v.jsx("div",{style:{height:1,background:"#1e2540",width:"100%"}})]}),v.jsx("div",{style:{display:"flex",alignItems:"flex-end",gap:"0.5rem",height:162,padding:"0 2px"},children:_l.map((E,j)=>v.jsx("div",{style:{flex:1,display:"flex",flexDirection:"column",alignItems:"center",gap:0,height:"100%"},children:v.jsxs("div",{style:{flex:1,display:"flex",alignItems:"flex-end",gap:2,width:"100%"},children:[v.jsx("div",{style:{flex:1,display:"flex",alignItems:"flex-end",height:"100%",cursor:"pointer"},onMouseEnter:()=>f({binIdx:j,series:"a"}),onMouseLeave:()=>f(null),children:v.jsx(oe.div,{initial:{height:0},animate:{height:`${m[j]/w*100}%`},transition:{delay:.35+j*.04,duration:.55,ease:"easeOut"},style:{width:"100%",background:"linear-gradient(180deg, #c0c8ff 0%, #7080e8 100%)",opacity:d&&d.binIdx===j&&d.series==="a"?1:.82,borderRadius:"3px 3px 0 0",minHeight:m[j]>0?3:0,position:"relative",boxShadow:(d==null?void 0:d.binIdx)===j&&(d==null?void 0:d.series)==="a"?"0 0 12px rgba(166,176,255,0.6)":"none",transition:"box-shadow 0.15s, opacity 0.15s"},children:(d==null?void 0:d.binIdx)===j&&(d==null?void 0:d.series)==="a"&&v.jsxs("div",{style:{position:"absolute",bottom:"calc(100% + 5px)",left:"50%",transform:"translateX(-50%)",background:"#1a2038",border:"1px solid #2e3a5e",borderRadius:5,padding:"4px 8px",fontSize:10,color:"#a6b0ff",whiteSpace:"nowrap",zIndex:10,boxShadow:"0 2px 8px rgba(0,0,0,0.4)"},children:[v.jsx("span",{style:{fontWeight:700},children:r}),": ",m[j]]})})}),v.jsx("div",{style:{flex:1,display:"flex",alignItems:"flex-end",height:"100%",cursor:"pointer"},onMouseEnter:()=>f({binIdx:j,series:"b"}),onMouseLeave:()=>f(null),children:v.jsx(oe.div,{initial:{height:0},animate:{height:`${x[j]/w*100}%`},transition:{delay:.38+j*.04,duration:.55,ease:"easeOut"},style:{width:"100%",background:"linear-gradient(180deg, #ffa96a 0%, #e06820 100%)",opacity:d&&d.binIdx===j&&d.series==="b"?1:.82,borderRadius:"3px 3px 0 0",minHeight:x[j]>0?3:0,position:"relative",boxShadow:(d==null?void 0:d.binIdx)===j&&(d==null?void 0:d.series)==="b"?"0 0 12px rgba(255,140,66,0.6)":"none",transition:"box-shadow 0.15s, opacity 0.15s"},children:(d==null?void 0:d.binIdx)===j&&(d==null?void 0:d.series)==="b"&&v.jsxs("div",{style:{position:"absolute",bottom:"calc(100% + 5px)",left:"50%",transform:"translateX(-50%)",background:"#1a2038",border:"1px solid #2e3a5e",borderRadius:5,padding:"4px 8px",fontSize:10,color:"#ff8c42",whiteSpace:"nowrap",zIndex:10,boxShadow:"0 2px 8px rgba(0,0,0,0.4)"},children:[v.jsx("span",{style:{fontWeight:700},children:a}),": ",x[j]]})})})]})},`bin-group-${j}`))}),v.jsx("div",{style:{height:1,background:"#1e2540"}}),v.jsx("div",{style:{display:"flex",gap:"0.5rem",padding:"4px 2px 0"},children:_l.map((E,j)=>v.jsx("div",{style:{flex:1,fontSize:9,color:"#4a5070",textAlign:"center"},children:E},j))})]})]}),v.jsx("div",{style:{textAlign:"center",fontSize:"0.65rem",color:"#3a4060",marginTop:"0.35rem",letterSpacing:"0.5px"},children:"Toxicity score range"})]})})(),h&&g&&v.jsxs(oe.div,{initial:{opacity:0,y:18},animate:{opacity:1,y:0},transition:{delay:.38},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem"},children:[v.jsx("div",{style:{fontSize:"0.95rem",fontWeight:700,color:"#e8eaf0",marginBottom:"1rem"},children:"Key metrics at a glance"}),[{label:"Toxic rate (%)",a:h.toxicRate,b:g.toxicRate,max:100,colorA:"#a6b0ff",colorB:"#ff8c42"},{label:"Avg score",a:h.avgScore*100,b:g.avgScore*100,max:100,colorA:"#a6b0ff",colorB:"#ff8c42"}].map(({label:m,a:x,b:w,max:k,colorA:E,colorB:j})=>v.jsxs("div",{style:{marginBottom:"0.8rem"},children:[v.jsxs("div",{style:{display:"flex",justifyContent:"space-between",marginBottom:5},children:[v.jsx("span",{style:{fontSize:"0.72rem",color:"#5a6080"},children:m}),v.jsxs("span",{style:{fontSize:"0.72rem",color:"#5a6080"},children:[v.jsxs("span",{style:{color:E},children:[r,": ",x.toFixed(1)]})," vs ",v.jsxs("span",{style:{color:j},children:[a,": ",w.toFixed(1)]})]})]}),v.jsx("div",{style:{display:"flex",flexDirection:"column",gap:4},children:[{pct:x/k*100,color:E},{pct:w/k*100,color:j}].map((D,_)=>v.jsx("div",{style:{background:"#1a1f35",borderRadius:4,height:6,overflow:"hidden"},children:v.jsx(oe.div,{initial:{width:0},animate:{width:`${D.pct}%`},transition:{delay:.4+_*.07,duration:.6,ease:"easeOut"},style:{height:"100%",background:D.color,borderRadius:4}})},_))})]},m))]})]})]})}function fp(n,r){const o=new Blob([JSON.stringify(n,null,2)],{type:"application/json"}),a=URL.createObjectURL(o),u=document.createElement("a");u.href=a,u.download=r,u.click(),URL.revokeObjectURL(a)}function dp(n,r){const o="id,text,score,label,query_term,created_at",a=n.map(h=>[h.id,`"${h.text.replace(/"/g,'""')}"`,h.score,h.label,h.query_term,h.created_at].join(",")),u=new Blob([[o,...a].join(` +`)],{type:"text/csv"}),d=URL.createObjectURL(u),f=document.createElement("a");f.href=d,f.download=r,f.click(),URL.revokeObjectURL(d)}function Yo({icon:n,title:r,description:o,buttonLabel:a,color:u,onClick:d,delay:f}){const[h,g]=V.useState(!1),y=()=>{d(),g(!0),setTimeout(()=>g(!1),2e3)};return v.jsxs(oe.div,{initial:{opacity:0,y:18},animate:{opacity:1,y:0},transition:{delay:f,duration:.45},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"1.1rem 1.2rem",display:"flex",alignItems:"center",gap:"1rem"},children:[v.jsx("div",{style:{width:42,height:42,borderRadius:10,background:`${u}18`,border:`1px solid ${u}44`,display:"flex",alignItems:"center",justifyContent:"center",flexShrink:0,color:u},children:n}),v.jsxs("div",{style:{flex:1,minWidth:0},children:[v.jsx("div",{style:{fontSize:"0.88rem",fontWeight:700,color:"#e8eaf0",marginBottom:3},children:r}),v.jsx("div",{style:{fontSize:"0.73rem",color:"#5a6080"},children:o})]}),v.jsxs(oe.button,{onClick:y,whileHover:{scale:1.04},whileTap:{scale:.96},style:{background:h?"rgba(46,204,113,0.12)":`${u}18`,border:`1px solid ${h?"#2ecc71":u}55`,borderRadius:8,color:h?"#2ecc71":u,padding:"6px 14px",fontSize:"0.78rem",fontWeight:600,cursor:"pointer",display:"flex",alignItems:"center",gap:5,whiteSpace:"nowrap",transition:"all 0.2s"},children:[h?v.jsx(fS,{size:14}):v.jsx(hS,{size:14}),h?"Downloaded!":a]})]})}function NS({posts:n}){const r=n.filter(u=>u.label==="toxic"),o=n.filter(u=>u.label==="non-toxic"),a={exported_at:new Date().toISOString(),total_posts:n.length,toxic_count:r.length,non_toxic_count:o.length,toxic_rate:n.length?(r.length/n.length*100).toFixed(2)+"%":"0%",avg_score:n.length?(n.reduce((u,d)=>u+d.score,0)/n.length).toFixed(4):"0",posts:n};return v.jsxs("div",{style:{padding:"1.2rem 1.4rem",display:"flex",flexDirection:"column",gap:"0.9rem"},children:[v.jsx(oe.div,{initial:{opacity:0,y:10},animate:{opacity:1,y:0},style:{color:"#5a6080",fontSize:"0.82rem"},children:"Export your analyzed data for further research or archiving."}),v.jsx(oe.div,{initial:{opacity:0,y:14},animate:{opacity:1,y:0},transition:{delay:.05},style:{background:"#0d1120",border:"1px solid #1e2540",borderRadius:10,padding:"0.9rem 1rem",display:"flex",gap:"2rem",flexWrap:"wrap"},children:[{label:"Total posts",value:String(n.length),color:"#fff"},{label:"Toxic",value:String(r.length),color:"#ff4b4b"},{label:"Non-toxic",value:String(o.length),color:"#2ecc71"},{label:"Toxic rate",value:n.length?`${(r.length/n.length*100).toFixed(1)}%`:"0%",color:"#ff8c42"}].map(({label:u,value:d,color:f})=>v.jsxs("div",{children:[v.jsx("div",{style:{fontSize:"0.58rem",textTransform:"uppercase",letterSpacing:"1px",color:"#3a4060",marginBottom:4},children:u}),v.jsx("div",{style:{fontSize:"1.3rem",fontWeight:700,color:f},children:d})]},u))}),v.jsx(Yo,{icon:v.jsx(ip,{size:20}),title:"Full dataset (JSON)",description:`Export all ${n.length} analyzed posts with scores, labels, and metadata`,buttonLabel:"Export JSON",color:"#a6b0ff",onClick:()=>fp(a,"algoscope-export.json"),delay:.1}),v.jsx(Yo,{icon:v.jsx(op,{size:20}),title:"All posts (CSV)",description:`${n.length} rows · id, text, score, label, query_term, created_at`,buttonLabel:"Export CSV",color:"#ff8c42",onClick:()=>dp(n,"algoscope-posts.csv"),delay:.18}),v.jsx(Yo,{icon:v.jsx(op,{size:20}),title:"Toxic posts only (CSV)",description:`${r.length} rows · filtered to toxic label (score ≥ threshold)`,buttonLabel:"Export CSV",color:"#ff4b4b",onClick:()=>dp(r,"algoscope-toxic.csv"),delay:.26}),v.jsx(Yo,{icon:v.jsx(ip,{size:20}),title:"Summary statistics (JSON)",description:"Aggregate metrics: counts, rates, avg scores per term",buttonLabel:"Export JSON",color:"#2ecc71",onClick:()=>{const u={},d=[...new Set(n.map(f=>f.query_term))];for(const f of d){const h=n.filter(y=>y.query_term===f),g=h.filter(y=>y.label==="toxic").length;u[f]={count:h.length,toxic_count:g,toxic_rate:`${(g/h.length*100).toFixed(1)}%`,avg_score:(h.reduce((y,m)=>y+m.score,0)/h.length).toFixed(4)}}fp({exported_at:new Date().toISOString(),total:n.length,toxic_rate:`${(r.length/n.length*100).toFixed(1)}%`,per_term:u},"algoscope-summary.json")},delay:.34})]})}const hp="ALGOSCOPE",OS="Real-time algospeak & toxicity intelligence on Bluesky",Il=Array.from({length:14},(n,r)=>({id:r,x:8+r*13.5%92,y:10+r*17%80,r:3+r%4*1.8,color:r%3===0?"#ff4b4b":r%3===1?"#ff8c42":"#a6b0ff",delay:r*.07})),BS=[[0,3],[3,7],[7,11],[1,5],[5,9],[9,12],[2,6],[6,10],[0,4],[4,8],[8,13],[2,7],[1,6],[3,10],[5,12],[4,11]];function WS({onDone:n}){const[r,o]=V.useState(0),[a,u]=V.useState(!1),[d,f]=V.useState(0),[h,g]=V.useState(!1),y=V.useRef(null);return V.useEffect(()=>{let m=0;const x=setInterval(()=>{m++,o(m),m>=hp.length&&clearInterval(x)},80);y.current=setTimeout(()=>u(!0),900);let w=0;const k=setInterval(()=>{w+=1.6,f(Math.min(100,w)),w>=100&&clearInterval(k)},28),E=setTimeout(()=>{g(!0),setTimeout(n,700)},2600);return()=>{clearInterval(x),clearInterval(k),y.current&&clearTimeout(y.current),clearTimeout(E)}},[n]),v.jsx(Sr,{children:!h&&v.jsxs(oe.div,{initial:{opacity:1},exit:{opacity:0,scale:1.04},transition:{duration:.65,ease:"easeInOut"},style:{position:"fixed",inset:0,zIndex:9999,background:"#080b12",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden"},children:[v.jsxs("svg",{style:{position:"absolute",inset:0,width:"100%",height:"100%",opacity:.18},viewBox:"0 0 100 100",preserveAspectRatio:"xMidYMid slice",children:[BS.map(([m,x],w)=>{const k=Il[m],E=Il[x];return v.jsx(oe.line,{x1:k.x,y1:k.y,x2:E.x,y2:E.y,stroke:"#a6b0ff",strokeWidth:"0.3",initial:{pathLength:0,opacity:0},animate:{pathLength:1,opacity:1},transition:{delay:.3+w*.04,duration:.5}},w)}),Il.map(m=>v.jsx(oe.circle,{cx:m.x,cy:m.y,r:m.r,fill:m.color,initial:{scale:0,opacity:0},animate:{scale:1,opacity:.7},transition:{delay:m.delay,duration:.4,type:"spring"}},m.id))]}),v.jsx("div",{style:{position:"absolute",width:520,height:520,borderRadius:"50%",background:"radial-gradient(circle, rgba(255,75,75,0.08) 0%, rgba(10,13,20,0) 70%)",pointerEvents:"none"}}),v.jsx(oe.div,{initial:{scale:0,rotate:-15},animate:{scale:1,rotate:0},transition:{duration:.5,type:"spring",stiffness:200},style:{width:72,height:72,borderRadius:18,background:"linear-gradient(135deg, #ff4b4b, #ff8c42)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:"2.2rem",fontWeight:900,color:"#fff",boxShadow:"0 0 40px rgba(255,75,75,0.5), 0 0 80px rgba(255,75,75,0.2)",marginBottom:"1.5rem"},children:"A"}),v.jsx("div",{style:{display:"flex",gap:3,marginBottom:"1rem"},children:hp.split("").map((m,x)=>v.jsx(oe.span,{initial:{opacity:0,y:20,filter:"blur(8px)"},animate:x{let ce=!1;async function he(){try{const le=await vS(200);if(ce)return;if(le.length>0)j(le),_(le.slice(0,25)),Y(le.length);else{const U=Ll(Ot.slice(0,4),30);j(U),_(U.slice(0,25))}}catch{if(ce)return;const le=Ll(Ot.slice(0,4),30);j(le),_(le.slice(0,25))}}return he(),()=>{ce=!0}},[]);const we=V.useCallback(async()=>{z(!0),M(!1),I(null);try{const{posts:ce,message:he}=await wS(f.length?f:Ot,m,g);if(ce.length===0){I(he??"No posts fetched from Bluesky. Check credentials and try again."),M(!0);return}_(ce),j(U=>{const Z=new Set(ce.map(P=>String(P.id))),X=U.filter(P=>!Z.has(String(P.id)));return[...ce,...X].slice(0,500)});const le=await xS();le>=0&&Y(le),M(!0)}catch(ce){const he=ce instanceof Error?ce.message:"Fetch failed";I(he);const le=Ll(f.length?f:Ot,m);_(le),j(U=>[...le,...U].slice(0,500)),M(!0)}finally{z(!1)}},[f,m,g]);return v.jsxs(v.Fragment,{children:[n&&v.jsx(WS,{onDone:()=>r(!1)}),v.jsxs("div",{style:{width:"100vw",minHeight:"100vh",background:"#0a0d14",display:"flex",flexDirection:"column",overflowY:"auto",overflowX:"hidden",fontFamily:"system-ui, -apple-system, sans-serif",color:"#e8eaf0"},children:[v.jsx(oS,{onToggleSidebar:()=>a(ce=>!ce),sidebarOpen:o}),v.jsxs("div",{style:{flex:1,display:"flex",overflow:"visible",minHeight:0},children:[v.jsx(TS,{open:o,selectedTerms:f,setSelectedTerms:h,threshold:g,setThreshold:y,sampling:m,setSampling:x,autoRefresh:w,setAutoRefresh:k,onFetch:we,posts:E,fetching:b}),v.jsxs("div",{style:{flex:1,display:"flex",flexDirection:"column",overflow:"visible",minHeight:0},children:[v.jsxs("div",{style:{display:"flex",borderBottom:"1px solid #1e2540",background:"#0a0d14",paddingInline:"1.4rem",gap:"0.25rem",flexShrink:0},children:[US.map(ce=>{const he=ce.id===u;return v.jsx("button",{onClick:()=>d(ce.id),style:{background:"transparent",border:"none",borderBottom:he?"2px solid #ff6b3d":"2px solid transparent",color:he?"#ff6b3d":"#5a6080",fontSize:"0.82rem",padding:"0.65rem 0.9rem",cursor:"pointer",fontWeight:he?600:400,transition:"color 0.2s, border-color 0.2s",whiteSpace:"nowrap"},onMouseEnter:le=>{he||(le.currentTarget.style.color="#9aa0c0")},onMouseLeave:le=>{he||(le.currentTarget.style.color="#5a6080")},children:ce.label},ce.id)}),v.jsxs(Sr,{children:[F&&!H&&v.jsxs(oe.div,{initial:{opacity:0,x:20},animate:{opacity:1,x:0},exit:{opacity:0,x:20},transition:{duration:.3},style:{marginLeft:"auto",alignSelf:"center",fontSize:"0.72rem",padding:"4px 10px",borderRadius:999,background:"rgba(46,204,113,0.1)",border:"1px solid rgba(46,204,113,0.25)",color:"#2ecc71"},children:["✓ Done! Analyzed ",D.length," posts"]}),F&&H&&v.jsx(oe.div,{initial:{opacity:0,x:20},animate:{opacity:1,x:0},exit:{opacity:0,x:20},transition:{duration:.3},style:{marginLeft:"auto",alignSelf:"center",fontSize:"0.72rem",padding:"4px 10px",borderRadius:999,background:"rgba(255,75,75,0.1)",border:"1px solid rgba(255,75,75,0.25)",color:"#ff6b3d"},children:"⚠ API unavailable — showing mock data"})]})]}),v.jsx("div",{style:{flex:1,overflow:"visible",minHeight:0},children:v.jsx(Sr,{mode:"wait",children:v.jsxs(oe.div,{initial:{opacity:0,y:8},animate:{opacity:1,y:0},exit:{opacity:0,y:-8},transition:{duration:.25},style:{minHeight:"100%"},children:[u==="overview"&&v.jsx(MS,{posts:E,batchPosts:D,selectedTerms:f,justFetched:F,totalAnalyzed:N}),u==="graph"&&v.jsx(zS,{minCooccurrence:G,setMinCooccurrence:se,toxicOnly:ue,setToxicOnly:ge}),u==="compare"&&v.jsx(FS,{posts:E}),u==="export"&&v.jsx(NS,{posts:E})]},u)})})]})]}),v.jsx("style",{children:` + * { box-sizing: border-box; } + ::-webkit-scrollbar { width: 5px; height: 5px; } + ::-webkit-scrollbar-track { background: #0d1120; } + ::-webkit-scrollbar-thumb { background: #1e2540; border-radius: 3px; } + ::-webkit-scrollbar-thumb:hover { background: #2e3560; } + input[type=range] { appearance: none; height: 4px; border-radius: 2px; background: #1e2540; outline: none; } + input[type=range]::-webkit-slider-thumb { + appearance: none; width: 14px; height: 14px; border-radius: 50%; + background: #ff4b4b; cursor: pointer; border: 2px solid #0a0d14; + } + select option { background: #141826; color: #e8eaf0; } + `})]})]})}sy.createRoot(document.getElementById("root")).render(v.jsx($S,{})); diff --git a/frontend/dist/assets/index-DgSDpXn3.css b/frontend/dist/assets/index-DgSDpXn3.css new file mode 100644 index 0000000000000000000000000000000000000000..4ff6e305640b7a57253c30f779d355678eee1c14 --- /dev/null +++ b/frontend/dist/assets/index-DgSDpXn3.css @@ -0,0 +1 @@ +@media source(none){@layer theme,base,components,utilities;@layer theme{@theme default{ --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; --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-100: oklch(93.6% .032 17.717); --color-red-200: oklch(88.5% .062 18.334); --color-red-300: oklch(80.8% .114 19.571); --color-red-400: oklch(70.4% .191 22.216); --color-red-500: oklch(63.7% .237 25.331); --color-red-600: oklch(57.7% .245 27.325); --color-red-700: oklch(50.5% .213 27.518); --color-red-800: oklch(44.4% .177 26.899); --color-red-900: oklch(39.6% .141 25.723); --color-red-950: oklch(25.8% .092 26.042); --color-orange-50: oklch(98% .016 73.684); --color-orange-100: oklch(95.4% .038 75.164); --color-orange-200: oklch(90.1% .076 70.697); --color-orange-300: oklch(83.7% .128 66.29); --color-orange-400: oklch(75% .183 55.934); --color-orange-500: oklch(70.5% .213 47.604); --color-orange-600: oklch(64.6% .222 41.116); --color-orange-700: oklch(55.3% .195 38.402); --color-orange-800: oklch(47% .157 37.304); --color-orange-900: oklch(40.8% .123 38.172); --color-orange-950: oklch(26.6% .079 36.259); --color-amber-50: oklch(98.7% .022 95.277); --color-amber-100: oklch(96.2% .059 95.617); --color-amber-200: oklch(92.4% .12 95.746); --color-amber-300: oklch(87.9% .169 91.605); --color-amber-400: oklch(82.8% .189 84.429); --color-amber-500: oklch(76.9% .188 70.08); --color-amber-600: oklch(66.6% .179 58.318); --color-amber-700: oklch(55.5% .163 48.998); --color-amber-800: oklch(47.3% .137 46.201); --color-amber-900: oklch(41.4% .112 45.904); --color-amber-950: oklch(27.9% .077 45.635); --color-yellow-50: oklch(98.7% .026 102.212); --color-yellow-100: oklch(97.3% .071 103.193); --color-yellow-200: oklch(94.5% .129 101.54); --color-yellow-300: oklch(90.5% .182 98.111); --color-yellow-400: oklch(85.2% .199 91.936); --color-yellow-500: oklch(79.5% .184 86.047); --color-yellow-600: oklch(68.1% .162 75.834); --color-yellow-700: oklch(55.4% .135 66.442); --color-yellow-800: oklch(47.6% .114 61.907); --color-yellow-900: oklch(42.1% .095 57.708); --color-yellow-950: oklch(28.6% .066 53.813); --color-lime-50: oklch(98.6% .031 120.757); --color-lime-100: oklch(96.7% .067 122.328); --color-lime-200: oklch(93.8% .127 124.321); --color-lime-300: oklch(89.7% .196 126.665); --color-lime-400: oklch(84.1% .238 128.85); --color-lime-500: oklch(76.8% .233 130.85); --color-lime-600: oklch(64.8% .2 131.684); --color-lime-700: oklch(53.2% .157 131.589); --color-lime-800: oklch(45.3% .124 130.933); --color-lime-900: oklch(40.5% .101 131.063); --color-lime-950: oklch(27.4% .072 132.109); --color-green-50: oklch(98.2% .018 155.826); --color-green-100: oklch(96.2% .044 156.743); --color-green-200: oklch(92.5% .084 155.995); --color-green-300: oklch(87.1% .15 154.449); --color-green-400: oklch(79.2% .209 151.711); --color-green-500: oklch(72.3% .219 149.579); --color-green-600: oklch(62.7% .194 149.214); --color-green-700: oklch(52.7% .154 150.069); --color-green-800: oklch(44.8% .119 151.328); --color-green-900: oklch(39.3% .095 152.535); --color-green-950: oklch(26.6% .065 152.934); --color-emerald-50: oklch(97.9% .021 166.113); --color-emerald-100: oklch(95% .052 163.051); --color-emerald-200: oklch(90.5% .093 164.15); --color-emerald-300: oklch(84.5% .143 164.978); --color-emerald-400: oklch(76.5% .177 163.223); --color-emerald-500: oklch(69.6% .17 162.48); --color-emerald-600: oklch(59.6% .145 163.225); --color-emerald-700: oklch(50.8% .118 165.612); --color-emerald-800: oklch(43.2% .095 166.913); --color-emerald-900: oklch(37.8% .077 168.94); --color-emerald-950: oklch(26.2% .051 172.552); --color-teal-50: oklch(98.4% .014 180.72); --color-teal-100: oklch(95.3% .051 180.801); --color-teal-200: oklch(91% .096 180.426); --color-teal-300: oklch(85.5% .138 181.071); --color-teal-400: oklch(77.7% .152 181.912); --color-teal-500: oklch(70.4% .14 182.503); --color-teal-600: oklch(60% .118 184.704); --color-teal-700: oklch(51.1% .096 186.391); --color-teal-800: oklch(43.7% .078 188.216); --color-teal-900: oklch(38.6% .063 188.416); --color-teal-950: oklch(27.7% .046 192.524); --color-cyan-50: oklch(98.4% .019 200.873); --color-cyan-100: oklch(95.6% .045 203.388); --color-cyan-200: oklch(91.7% .08 205.041); --color-cyan-300: oklch(86.5% .127 207.078); --color-cyan-400: oklch(78.9% .154 211.53); --color-cyan-500: oklch(71.5% .143 215.221); --color-cyan-600: oklch(60.9% .126 221.723); --color-cyan-700: oklch(52% .105 223.128); --color-cyan-800: oklch(45% .085 224.283); --color-cyan-900: oklch(39.8% .07 227.392); --color-cyan-950: oklch(30.2% .056 229.695); --color-sky-50: oklch(97.7% .013 236.62); --color-sky-100: oklch(95.1% .026 236.824); --color-sky-200: oklch(90.1% .058 230.902); --color-sky-300: oklch(82.8% .111 230.318); --color-sky-400: oklch(74.6% .16 232.661); --color-sky-500: oklch(68.5% .169 237.323); --color-sky-600: oklch(58.8% .158 241.966); --color-sky-700: oklch(50% .134 242.749); --color-sky-800: oklch(44.3% .11 240.79); --color-sky-900: oklch(39.1% .09 240.876); --color-sky-950: oklch(29.3% .066 243.157); --color-blue-50: oklch(97% .014 254.604); --color-blue-100: oklch(93.2% .032 255.585); --color-blue-200: oklch(88.2% .059 254.128); --color-blue-300: oklch(80.9% .105 251.813); --color-blue-400: oklch(70.7% .165 254.624); --color-blue-500: oklch(62.3% .214 259.815); --color-blue-600: oklch(54.6% .245 262.881); --color-blue-700: oklch(48.8% .243 264.376); --color-blue-800: oklch(42.4% .199 265.638); --color-blue-900: oklch(37.9% .146 265.522); --color-blue-950: oklch(28.2% .091 267.935); --color-indigo-50: oklch(96.2% .018 272.314); --color-indigo-100: oklch(93% .034 272.788); --color-indigo-200: oklch(87% .065 274.039); --color-indigo-300: oklch(78.5% .115 274.713); --color-indigo-400: oklch(67.3% .182 276.935); --color-indigo-500: oklch(58.5% .233 277.117); --color-indigo-600: oklch(51.1% .262 276.966); --color-indigo-700: oklch(45.7% .24 277.023); --color-indigo-800: oklch(39.8% .195 277.366); --color-indigo-900: oklch(35.9% .144 278.697); --color-indigo-950: oklch(25.7% .09 281.288); --color-violet-50: oklch(96.9% .016 293.756); --color-violet-100: oklch(94.3% .029 294.588); --color-violet-200: oklch(89.4% .057 293.283); --color-violet-300: oklch(81.1% .111 293.571); --color-violet-400: oklch(70.2% .183 293.541); --color-violet-500: oklch(60.6% .25 292.717); --color-violet-600: oklch(54.1% .281 293.009); --color-violet-700: oklch(49.1% .27 292.581); --color-violet-800: oklch(43.2% .232 292.759); --color-violet-900: oklch(38% .189 293.745); --color-violet-950: oklch(28.3% .141 291.089); --color-purple-50: oklch(97.7% .014 308.299); --color-purple-100: oklch(94.6% .033 307.174); --color-purple-200: oklch(90.2% .063 306.703); --color-purple-300: oklch(82.7% .119 306.383); --color-purple-400: oklch(71.4% .203 305.504); --color-purple-500: oklch(62.7% .265 303.9); --color-purple-600: oklch(55.8% .288 302.321); --color-purple-700: oklch(49.6% .265 301.924); --color-purple-800: oklch(43.8% .218 303.724); --color-purple-900: oklch(38.1% .176 304.987); --color-purple-950: oklch(29.1% .149 302.717); --color-fuchsia-50: oklch(97.7% .017 320.058); --color-fuchsia-100: oklch(95.2% .037 318.852); --color-fuchsia-200: oklch(90.3% .076 319.62); --color-fuchsia-300: oklch(83.3% .145 321.434); --color-fuchsia-400: oklch(74% .238 322.16); --color-fuchsia-500: oklch(66.7% .295 322.15); --color-fuchsia-600: oklch(59.1% .293 322.896); --color-fuchsia-700: oklch(51.8% .253 323.949); --color-fuchsia-800: oklch(45.2% .211 324.591); --color-fuchsia-900: oklch(40.1% .17 325.612); --color-fuchsia-950: oklch(29.3% .136 325.661); --color-pink-50: oklch(97.1% .014 343.198); --color-pink-100: oklch(94.8% .028 342.258); --color-pink-200: oklch(89.9% .061 343.231); --color-pink-300: oklch(82.3% .12 346.018); --color-pink-400: oklch(71.8% .202 349.761); --color-pink-500: oklch(65.6% .241 354.308); --color-pink-600: oklch(59.2% .249 .584); --color-pink-700: oklch(52.5% .223 3.958); --color-pink-800: oklch(45.9% .187 3.815); --color-pink-900: oklch(40.8% .153 2.432); --color-pink-950: oklch(28.4% .109 3.907); --color-rose-50: oklch(96.9% .015 12.422); --color-rose-100: oklch(94.1% .03 12.58); --color-rose-200: oklch(89.2% .058 10.001); --color-rose-300: oklch(81% .117 11.638); --color-rose-400: oklch(71.2% .194 13.428); --color-rose-500: oklch(64.5% .246 16.439); --color-rose-600: oklch(58.6% .253 17.585); --color-rose-700: oklch(51.4% .222 16.935); --color-rose-800: oklch(45.5% .188 13.697); --color-rose-900: oklch(41% .159 10.272); --color-rose-950: oklch(27.1% .105 12.094); --color-slate-50: oklch(98.4% .003 247.858); --color-slate-100: oklch(96.8% .007 247.896); --color-slate-200: oklch(92.9% .013 255.508); --color-slate-300: oklch(86.9% .022 252.894); --color-slate-400: oklch(70.4% .04 256.788); --color-slate-500: oklch(55.4% .046 257.417); --color-slate-600: oklch(44.6% .043 257.281); --color-slate-700: oklch(37.2% .044 257.287); --color-slate-800: oklch(27.9% .041 260.031); --color-slate-900: oklch(20.8% .042 265.755); --color-slate-950: oklch(12.9% .042 264.695); --color-gray-50: oklch(98.5% .002 247.839); --color-gray-100: oklch(96.7% .003 264.542); --color-gray-200: oklch(92.8% .006 264.531); --color-gray-300: oklch(87.2% .01 258.338); --color-gray-400: oklch(70.7% .022 261.325); --color-gray-500: oklch(55.1% .027 264.364); --color-gray-600: oklch(44.6% .03 256.802); --color-gray-700: oklch(37.3% .034 259.733); --color-gray-800: oklch(27.8% .033 256.848); --color-gray-900: oklch(21% .034 264.665); --color-gray-950: oklch(13% .028 261.692); --color-zinc-50: oklch(98.5% 0 0); --color-zinc-100: oklch(96.7% .001 286.375); --color-zinc-200: oklch(92% .004 286.32); --color-zinc-300: oklch(87.1% .006 286.286); --color-zinc-400: oklch(70.5% .015 286.067); --color-zinc-500: oklch(55.2% .016 285.938); --color-zinc-600: oklch(44.2% .017 285.786); --color-zinc-700: oklch(37% .013 285.805); --color-zinc-800: oklch(27.4% .006 286.033); --color-zinc-900: oklch(21% .006 285.885); --color-zinc-950: oklch(14.1% .005 285.823); --color-neutral-50: oklch(98.5% 0 0); --color-neutral-100: oklch(97% 0 0); --color-neutral-200: oklch(92.2% 0 0); --color-neutral-300: oklch(87% 0 0); --color-neutral-400: oklch(70.8% 0 0); --color-neutral-500: oklch(55.6% 0 0); --color-neutral-600: oklch(43.9% 0 0); --color-neutral-700: oklch(37.1% 0 0); --color-neutral-800: oklch(26.9% 0 0); --color-neutral-900: oklch(20.5% 0 0); --color-neutral-950: oklch(14.5% 0 0); --color-stone-50: oklch(98.5% .001 106.423); --color-stone-100: oklch(97% .001 106.424); --color-stone-200: oklch(92.3% .003 48.717); --color-stone-300: oklch(86.9% .005 56.366); --color-stone-400: oklch(70.9% .01 56.259); --color-stone-500: oklch(55.3% .013 58.071); --color-stone-600: oklch(44.4% .011 73.639); --color-stone-700: oklch(37.4% .01 67.558); --color-stone-800: oklch(26.8% .007 34.298); --color-stone-900: oklch(21.6% .006 56.043); --color-stone-950: oklch(14.7% .004 49.25); --color-black: #000; --color-white: #fff; --spacing: .25rem; --breakpoint-sm: 40rem; --breakpoint-md: 48rem; --breakpoint-lg: 64rem; --breakpoint-xl: 80rem; --breakpoint-2xl: 96rem; --container-3xs: 16rem; --container-2xs: 18rem; --container-xs: 20rem; --container-sm: 24rem; --container-md: 28rem; --container-lg: 32rem; --container-xl: 36rem; --container-2xl: 42rem; --container-3xl: 48rem; --container-4xl: 56rem; --container-5xl: 64rem; --container-6xl: 72rem; --container-7xl: 80rem; --text-xs: .75rem; --text-xs--line-height: calc(1 / .75); --text-sm: .875rem; --text-sm--line-height: calc(1.25 / .875); --text-base: 1rem; --text-base--line-height: 1.5 ; --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); --text-5xl: 3rem; --text-5xl--line-height: 1; --text-6xl: 3.75rem; --text-6xl--line-height: 1; --text-7xl: 4.5rem; --text-7xl--line-height: 1; --text-8xl: 6rem; --text-8xl--line-height: 1; --text-9xl: 8rem; --text-9xl--line-height: 1; --font-weight-thin: 100; --font-weight-extralight: 200; --font-weight-light: 300; --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; --font-weight-extrabold: 800; --font-weight-black: 900; --tracking-tighter: -.05em; --tracking-tight: -.025em; --tracking-normal: 0em; --tracking-wide: .025em; --tracking-wider: .05em; --tracking-widest: .1em; --leading-tight: 1.25; --leading-snug: 1.375; --leading-normal: 1.5; --leading-relaxed: 1.625; --leading-loose: 2; --radius-xs: .125rem; --radius-sm: .25rem; --radius-md: .375rem; --radius-lg: .5rem; --radius-xl: .75rem; --radius-2xl: 1rem; --radius-3xl: 1.5rem; --radius-4xl: 2rem; --shadow-2xs: 0 1px rgb(0 0 0 / .05); --shadow-xs: 0 1px 2px 0 rgb(0 0 0 / .05); --shadow-sm: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1); --shadow-md: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1); --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1); --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1); --shadow-2xl: 0 25px 50px -12px rgb(0 0 0 / .25); --inset-shadow-2xs: inset 0 1px rgb(0 0 0 / .05); --inset-shadow-xs: inset 0 1px 1px rgb(0 0 0 / .05); --inset-shadow-sm: inset 0 2px 4px rgb(0 0 0 / .05); --drop-shadow-xs: 0 1px 1px rgb(0 0 0 / .05); --drop-shadow-sm: 0 1px 2px rgb(0 0 0 / .15); --drop-shadow-md: 0 3px 3px rgb(0 0 0 / .12); --drop-shadow-lg: 0 4px 4px rgb(0 0 0 / .15); --drop-shadow-xl: 0 9px 7px rgb(0 0 0 / .1); --drop-shadow-2xl: 0 25px 25px rgb(0 0 0 / .15); --text-shadow-2xs: 0px 1px 0px rgb(0 0 0 / .15); --text-shadow-xs: 0px 1px 1px rgb(0 0 0 / .2); --text-shadow-sm: 0px 1px 0px rgb(0 0 0 / .075), 0px 1px 1px rgb(0 0 0 / .075), 0px 2px 2px rgb(0 0 0 / .075); --text-shadow-md: 0px 1px 1px rgb(0 0 0 / .1), 0px 1px 2px rgb(0 0 0 / .1), 0px 2px 4px rgb(0 0 0 / .1); --text-shadow-lg: 0px 1px 2px rgb(0 0 0 / .1), 0px 3px 2px rgb(0 0 0 / .1), 0px 4px 8px rgb(0 0 0 / .1); --ease-in: cubic-bezier(.4, 0, 1, 1); --ease-out: cubic-bezier(0, 0, .2, 1); --ease-in-out: cubic-bezier(.4, 0, .2, 1); --animate-spin: spin 1s linear infinite; --animate-ping: ping 1s cubic-bezier(0, 0, .2, 1) infinite; --animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; --animate-bounce: bounce 1s infinite; @keyframes spin { to { transform: rotate(360deg); } } @keyframes ping { 75%, 100% { transform: scale(2); opacity: 0; } } @keyframes pulse { 50% { opacity: .5; } } @keyframes bounce { 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(.8, 0, 1, 1); } 50% { transform: none; animation-timing-function: cubic-bezier(0, 0, .2, 1); } } --blur-xs: 4px; --blur-sm: 8px; --blur-md: 12px; --blur-lg: 16px; --blur-xl: 24px; --blur-2xl: 40px; --blur-3xl: 64px; --perspective-dramatic: 100px; --perspective-near: 300px; --perspective-normal: 500px; --perspective-midrange: 800px; --perspective-distant: 1200px; --aspect-video: 16 / 9; --default-transition-duration: .15s; --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); --default-font-family: --theme(--font-sans, initial); --default-font-feature-settings: --theme( --font-sans--font-feature-settings, initial ); --default-font-variation-settings: --theme( --font-sans--font-variation-settings, initial ); --default-mono-font-family: --theme(--font-mono, initial); --default-mono-font-feature-settings: --theme( --font-mono--font-feature-settings, initial ); --default-mono-font-variation-settings: --theme( --font-mono--font-variation-settings, initial ); }@theme default inline reference{ --blur: 8px; --shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1); --shadow-inner: inset 0 2px 4px 0 rgb(0 0 0 / .05); --drop-shadow: 0 1px 2px rgb(0 0 0 / .1), 0 1px 1px rgb(0 0 0 / .06); --radius: .25rem; --max-width-prose: 65ch; }}@layer base{*,:after,:before,::backdrop,::file-selector-button{box-sizing:border-box;margin:0;padding:0;border:0 solid}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:--theme(--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:--theme(--default-font-feature-settings,normal);font-variation-settings:--theme(--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:--theme(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:--theme(--default-mono-font-feature-settings,normal);font-variation-settings:--theme(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}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{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea,::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;border-radius:0;background-color:transparent;opacity:1}: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: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,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-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]),::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{@tailwind utilities;}}@property --tw-animation-delay{syntax:"*";inherits:false;initial-value:0s}@property --tw-animation-direction{syntax:"*";inherits:false;initial-value:normal}@property --tw-animation-duration{syntax:"*";inherits:false}@property --tw-animation-fill-mode{syntax:"*";inherits:false;initial-value:none}@property --tw-animation-iteration-count{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-enter-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-enter-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-blur{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-opacity{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-rotate{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-scale{syntax:"*";inherits:false;initial-value:1}@property --tw-exit-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-exit-translate-y{syntax:"*";inherits:false;initial-value:0}@theme inline{--animation-delay-0: 0s; --animation-delay-75: 75ms; --animation-delay-100: .1s; --animation-delay-150: .15s; --animation-delay-200: .2s; --animation-delay-300: .3s; --animation-delay-500: .5s; --animation-delay-700: .7s; --animation-delay-1000: 1s; --animation-repeat-0: 0; --animation-repeat-1: 1; --animation-repeat-infinite: infinite; --animation-direction-normal: normal; --animation-direction-reverse: reverse; --animation-direction-alternate: alternate; --animation-direction-alternate-reverse: alternate-reverse; --animation-fill-mode-none: none; --animation-fill-mode-forwards: forwards; --animation-fill-mode-backwards: backwards; --animation-fill-mode-both: both; --percentage-0: 0; --percentage-5: .05; --percentage-10: .1; --percentage-15: .15; --percentage-20: .2; --percentage-25: .25; --percentage-30: .3; --percentage-35: .35; --percentage-40: .4; --percentage-45: .45; --percentage-50: .5; --percentage-55: .55; --percentage-60: .6; --percentage-65: .65; --percentage-70: .7; --percentage-75: .75; --percentage-80: .8; --percentage-85: .85; --percentage-90: .9; --percentage-95: .95; --percentage-100: 1; --percentage-translate-full: 1; --animate-in: enter var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); --animate-out: exit var(--tw-animation-duration,var(--tw-duration,.15s))var(--tw-ease,ease)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); @keyframes enter { from { opacity: var(--tw-enter-opacity,1); transform: translate3d(var(--tw-enter-translate-x,0),var(--tw-enter-translate-y,0),0)scale3d(var(--tw-enter-scale,1),var(--tw-enter-scale,1),var(--tw-enter-scale,1))rotate(var(--tw-enter-rotate,0)); filter: blur(var(--tw-enter-blur,0)); }}@keyframes exit { to { opacity: var(--tw-exit-opacity,1); transform: translate3d(var(--tw-exit-translate-x,0),var(--tw-exit-translate-y,0),0)scale3d(var(--tw-exit-scale,1),var(--tw-exit-scale,1),var(--tw-exit-scale,1))rotate(var(--tw-exit-rotate,0)); filter: blur(var(--tw-exit-blur,0)); }}--animate-accordion-down: accordion-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); --animate-accordion-up: accordion-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); --animate-collapsible-down: collapsible-down var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); --animate-collapsible-up: collapsible-up var(--tw-animation-duration,var(--tw-duration,.2s))var(--tw-ease,ease-out)var(--tw-animation-delay,0s)var(--tw-animation-iteration-count,1)var(--tw-animation-direction,normal)var(--tw-animation-fill-mode,none); @keyframes accordion-down { from { height: 0; }to { height: var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto))))); }}@keyframes accordion-up { from { height: var(--radix-accordion-content-height,var(--bits-accordion-content-height,var(--reka-accordion-content-height,var(--kb-accordion-content-height,var(--ngp-accordion-content-height,auto))))); }to { height: 0; }}@keyframes collapsible-down { from { height: 0; }to { height: var(--radix-collapsible-content-height,var(--bits-collapsible-content-height,var(--reka-collapsible-content-height,var(--kb-collapsible-content-height,auto)))); }}@keyframes collapsible-up { from { height: var(--radix-collapsible-content-height,var(--bits-collapsible-content-height,var(--reka-collapsible-content-height,var(--kb-collapsible-content-height,auto)))); }to { height: 0; }}--animate-caret-blink: caret-blink 1.25s ease-out infinite; @keyframes caret-blink { 0%,70%,100% { opacity: 1; }20%,50% { opacity: 0; }}}@utility animation-duration-*{--tw-animation-duration: calc(--value(number)*1ms) ; --tw-animation-duration: --value(--animation-duration-*,[duration],"initial",[*]); animation-duration: calc(--value(number)*1ms) ; animation-duration: --value(--animation-duration-*,[duration],"initial",[*]);}@utility delay-*{animation-delay: calc(--value(number)*1ms) ; animation-delay: --value(--animation-delay-*,[duration],"initial",[*]); --tw-animation-delay: calc(--value(number)*1ms) ; --tw-animation-delay: --value(--animation-delay-*,[duration],"initial",[*]);}@utility repeat-*{animation-iteration-count: --value(--animation-repeat-*,number,"initial",[*]); --tw-animation-iteration-count: --value(--animation-repeat-*,number,"initial",[*]);}@utility direction-*{animation-direction: --value(--animation-direction-*,"initial",[*]); --tw-animation-direction: --value(--animation-direction-*,"initial",[*]);}@utility fill-mode-*{animation-fill-mode: --value(--animation-fill-mode-*,"initial",[*]); --tw-animation-fill-mode: --value(--animation-fill-mode-*,"initial",[*]);}@utility running{animation-play-state: running;}@utility paused{animation-play-state: paused;}@utility play-state-*{animation-play-state: --value("initial",[*]);}@utility blur-in{--tw-enter-blur: 20px;}@utility blur-in-*{--tw-enter-blur: calc(--value(number)*1px) ; --tw-enter-blur: --value(--blur-*,[*]);}@utility blur-out{--tw-exit-blur: 20px;}@utility blur-out-*{--tw-exit-blur: calc(--value(number)*1px) ; --tw-exit-blur: --value(--blur-*,[*]);}@utility fade-in{--tw-enter-opacity: 0;}@utility fade-in-*{--tw-enter-opacity: calc(--value(number)*.01) ; --tw-enter-opacity: --value(--percentage-*,[*]);}@utility fade-out{--tw-exit-opacity: 0;}@utility fade-out-*{--tw-exit-opacity: calc(--value(number)*.01) ; --tw-exit-opacity: --value(--percentage-*,[*]);}@utility zoom-in{--tw-enter-scale: 0;}@utility zoom-in-*{--tw-enter-scale: calc(--value(number)*1%) ; --tw-enter-scale: --value(ratio) ; --tw-enter-scale: --value(--percentage-*,[*]);}@utility -zoom-in-*{--tw-enter-scale: calc(--value(number)*-1%) ; --tw-enter-scale: calc(--value(ratio)*-1) ; --tw-enter-scale: --value(--percentage-*,[*]);}@utility zoom-out{--tw-exit-scale: 0;}@utility zoom-out-*{--tw-exit-scale: calc(--value(number)*1%) ; --tw-exit-scale: --value(ratio) ; --tw-exit-scale: --value(--percentage-*,[*]);}@utility -zoom-out-*{--tw-exit-scale: calc(--value(number)*-1%) ; --tw-exit-scale: calc(--value(ratio)*-1) ; --tw-exit-scale: --value(--percentage-*,[*]);}@utility spin-in{--tw-enter-rotate: 30deg;}@utility spin-in-*{--tw-enter-rotate: calc(--value(number)*1deg) ; --tw-enter-rotate: calc(--value(ratio)*360deg) ; --tw-enter-rotate: --value(--rotate-*,[*]);}@utility -spin-in{--tw-enter-rotate: -30deg;}@utility -spin-in-*{--tw-enter-rotate: calc(--value(number)*-1deg) ; --tw-enter-rotate: calc(--value(ratio)*-360deg) ; --tw-enter-rotate: --value(--rotate-*,[*]);}@utility spin-out{--tw-exit-rotate: 30deg;}@utility spin-out-*{--tw-exit-rotate: calc(--value(number)*1deg) ; --tw-exit-rotate: calc(--value(ratio)*360deg) ; --tw-exit-rotate: --value(--rotate-*,[*]);}@utility -spin-out{--tw-exit-rotate: -30deg;}@utility -spin-out-*{--tw-exit-rotate: calc(--value(number)*-1deg) ; --tw-exit-rotate: calc(--value(ratio)*-360deg) ; --tw-exit-rotate: --value(--rotate-*,[*]);}@utility slide-in-from-top{--tw-enter-translate-y: -100%;}@utility slide-in-from-top-*{--tw-enter-translate-y: --spacing(--value(integer)*-1); --tw-enter-translate-y: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-enter-translate-y: calc(--value(ratio)*-100%) ; --tw-enter-translate-y: calc(--value(--translate-*,[percentage],[length])*-1) ;}@utility slide-in-from-bottom{--tw-enter-translate-y: 100%;}@utility slide-in-from-bottom-*{--tw-enter-translate-y: --spacing(--value(integer)); --tw-enter-translate-y: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-enter-translate-y: calc(--value(ratio)*100%) ; --tw-enter-translate-y: --value(--translate-*,[percentage],[length]);}@utility slide-in-from-left{--tw-enter-translate-x: -100%;}@utility slide-in-from-left-*{--tw-enter-translate-x: --spacing(--value(integer)*-1); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-enter-translate-x: calc(--value(ratio)*-100%) ; --tw-enter-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ;}@utility slide-in-from-right{--tw-enter-translate-x: 100%;}@utility slide-in-from-right-*{--tw-enter-translate-x: --spacing(--value(integer)); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-enter-translate-x: calc(--value(ratio)*100%) ; --tw-enter-translate-x: --value(--translate-*,[percentage],[length]);}@utility slide-in-from-start{&:dir(ltr){ --tw-enter-translate-x: -100%; }&:dir(rtl){ --tw-enter-translate-x: 100%; }}@utility slide-in-from-start-*{&:where(:dir(ltr),[dir="ltr"],[dir="ltr"]*){ --tw-enter-translate-x: --spacing(--value(integer)*-1); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-enter-translate-x: calc(--value(ratio)*-100%) ; --tw-enter-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ; }&:where(:dir(rtl),[dir="rtl"],[dir="rtl"]*){ --tw-enter-translate-x: --spacing(--value(integer)); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-enter-translate-x: calc(--value(ratio)*100%) ; --tw-enter-translate-x: --value(--translate-*,[percentage],[length]); }}@utility slide-in-from-end{&:dir(ltr){ --tw-enter-translate-x: 100%; }&:dir(rtl){ --tw-enter-translate-x: -100%; }}@utility slide-in-from-end-*{&:where(:dir(ltr),[dir="ltr"],[dir="ltr"]*){ --tw-enter-translate-x: --spacing(--value(integer)); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-enter-translate-x: calc(--value(ratio)*100%) ; --tw-enter-translate-x: --value(--translate-*,[percentage],[length]); }&:where(:dir(rtl),[dir="rtl"],[dir="rtl"]*){ --tw-enter-translate-x: --spacing(--value(integer)*-1); --tw-enter-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-enter-translate-x: calc(--value(ratio)*-100%) ; --tw-enter-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ; }}@utility slide-out-to-top{--tw-exit-translate-y: -100%;}@utility slide-out-to-top-*{--tw-exit-translate-y: --spacing(--value(integer)*-1); --tw-exit-translate-y: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-exit-translate-y: calc(--value(ratio)*-100%) ; --tw-exit-translate-y: calc(--value(--translate-*,[percentage],[length])*-1) ;}@utility slide-out-to-bottom{--tw-exit-translate-y: 100%;}@utility slide-out-to-bottom-*{--tw-exit-translate-y: --spacing(--value(integer)); --tw-exit-translate-y: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-exit-translate-y: calc(--value(ratio)*100%) ; --tw-exit-translate-y: --value(--translate-*,[percentage],[length]);}@utility slide-out-to-left{--tw-exit-translate-x: -100%;}@utility slide-out-to-left-*{--tw-exit-translate-x: --spacing(--value(integer)*-1); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-exit-translate-x: calc(--value(ratio)*-100%) ; --tw-exit-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ;}@utility slide-out-to-right{--tw-exit-translate-x: 100%;}@utility slide-out-to-right-*{--tw-exit-translate-x: --spacing(--value(integer)); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-exit-translate-x: calc(--value(ratio)*100%) ; --tw-exit-translate-x: --value(--translate-*,[percentage],[length]);}@utility slide-out-to-start{&:dir(ltr){ --tw-exit-translate-x: -100%; }&:dir(rtl){ --tw-exit-translate-x: 100%; }}@utility slide-out-to-start-*{&:where(:dir(ltr),[dir="ltr"],[dir="ltr"]*){ --tw-exit-translate-x: --spacing(--value(integer)*-1); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-exit-translate-x: calc(--value(ratio)*-100%) ; --tw-exit-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ; }&:where(:dir(rtl),[dir="rtl"],[dir="rtl"]*){ --tw-exit-translate-x: --spacing(--value(integer)); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-exit-translate-x: calc(--value(ratio)*100%) ; --tw-exit-translate-x: --value(--translate-*,[percentage],[length]); }}@utility slide-out-to-end{&:dir(ltr){ --tw-exit-translate-x: 100%; }&:dir(rtl){ --tw-exit-translate-x: -100%; }}@utility slide-out-to-end-*{&:where(:dir(ltr),[dir="ltr"],[dir="ltr"]*){ --tw-exit-translate-x: --spacing(--value(integer)); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*100%) ; --tw-exit-translate-x: calc(--value(ratio)*100%) ; --tw-exit-translate-x: --value(--translate-*,[percentage],[length]); }&:where(:dir(rtl),[dir="rtl"],[dir="rtl"]*){ --tw-exit-translate-x: --spacing(--value(integer)*-1); --tw-exit-translate-x: calc(--value(--percentage-*,--percentage-translate-*)*-100%) ; --tw-exit-translate-x: calc(--value(ratio)*-100%) ; --tw-exit-translate-x: calc(--value(--translate-*,[percentage],[length])*-1) ; }}@source "../**/*.{js,ts,jsx,tsx}";@custom-variant dark (&:is(.dark *));:root{--font-size: 16px;--background: #ffffff;--foreground: oklch(.145 0 0);--card: #ffffff;--card-foreground: oklch(.145 0 0);--popover: oklch(1 0 0);--popover-foreground: oklch(.145 0 0);--primary: #030213;--primary-foreground: oklch(1 0 0);--secondary: oklch(.95 .0058 264.53);--secondary-foreground: #030213;--muted: #ececf0;--muted-foreground: #717182;--accent: #e9ebef;--accent-foreground: #030213;--destructive: #d4183d;--destructive-foreground: #ffffff;--border: rgba(0, 0, 0, .1);--input: transparent;--input-background: #f3f3f5;--switch-background: #cbced4;--font-weight-medium: 500;--font-weight-normal: 400;--ring: oklch(.708 0 0);--chart-1: oklch(.646 .222 41.116);--chart-2: oklch(.6 .118 184.704);--chart-3: oklch(.398 .07 227.392);--chart-4: oklch(.828 .189 84.429);--chart-5: oklch(.769 .188 70.08);--radius: .625rem;--sidebar: oklch(.985 0 0);--sidebar-foreground: oklch(.145 0 0);--sidebar-primary: #030213;--sidebar-primary-foreground: oklch(.985 0 0);--sidebar-accent: oklch(.97 0 0);--sidebar-accent-foreground: oklch(.205 0 0);--sidebar-border: oklch(.922 0 0);--sidebar-ring: oklch(.708 0 0)}.dark{--background: oklch(.145 0 0);--foreground: oklch(.985 0 0);--card: oklch(.145 0 0);--card-foreground: oklch(.985 0 0);--popover: oklch(.145 0 0);--popover-foreground: oklch(.985 0 0);--primary: oklch(.985 0 0);--primary-foreground: oklch(.205 0 0);--secondary: oklch(.269 0 0);--secondary-foreground: oklch(.985 0 0);--muted: oklch(.269 0 0);--muted-foreground: oklch(.708 0 0);--accent: oklch(.269 0 0);--accent-foreground: oklch(.985 0 0);--destructive: oklch(.396 .141 25.723);--destructive-foreground: oklch(.637 .237 25.331);--border: oklch(.269 0 0);--input: oklch(.269 0 0);--ring: oklch(.439 0 0);--font-weight-medium: 500;--font-weight-normal: 400;--chart-1: oklch(.488 .243 264.376);--chart-2: oklch(.696 .17 162.48);--chart-3: oklch(.769 .188 70.08);--chart-4: oklch(.627 .265 303.9);--chart-5: oklch(.645 .246 16.439);--sidebar: oklch(.205 0 0);--sidebar-foreground: oklch(.985 0 0);--sidebar-primary: oklch(.488 .243 264.376);--sidebar-primary-foreground: oklch(.985 0 0);--sidebar-accent: oklch(.269 0 0);--sidebar-accent-foreground: oklch(.985 0 0);--sidebar-border: oklch(.269 0 0);--sidebar-ring: oklch(.439 0 0)}@theme inline{ --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-secondary: var(--secondary); --color-secondary-foreground: var(--secondary-foreground); --color-muted: var(--muted); --color-muted-foreground: var(--muted-foreground); --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); --color-destructive-foreground: var(--destructive-foreground); --color-border: var(--border); --color-input: var(--input); --color-input-background: var(--input-background); --color-switch-background: var(--switch-background); --color-ring: var(--ring); --color-chart-1: var(--chart-1); --color-chart-2: var(--chart-2); --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); --radius-xl: calc(var(--radius) + 4px); --color-sidebar: var(--sidebar); --color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); --color-sidebar-accent: var(--sidebar-accent); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); }@layer base{*{@apply border-border outline-ring/50;}body{@apply bg-background text-foreground;}html{font-size:var(--font-size)}h1{font-size:var(--text-2xl);font-weight:var(--font-weight-medium);line-height:1.5}h2{font-size:var(--text-xl);font-weight:var(--font-weight-medium);line-height:1.5}h3{font-size:var(--text-lg);font-weight:var(--font-weight-medium);line-height:1.5}h4,label,button{font-size:var(--text-base);font-weight:var(--font-weight-medium);line-height:1.5}input{font-size:var(--text-base);font-weight:var(--font-weight-normal);line-height:1.5}} diff --git a/frontend/dist/index.html b/frontend/dist/index.html new file mode 100644 index 0000000000000000000000000000000000000000..19474fc08bc489ea945a40d8cfb012ecdb667999 --- /dev/null +++ b/frontend/dist/index.html @@ -0,0 +1,16 @@ + + + + + + + שיפור עיצוב עם אנימציה + + + + + +
+ + + \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000000000000000000000000000000000000..349ccf9de9cee070e72de35fc96b34745bf6fc35 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,15 @@ + + + + + + + שיפור עיצוב עם אנימציה + + + +
+ + + + \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000000000000000000000000000000000000..49ddba603f9ef02861b72948bb1a1e75976ad3d9 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,81 @@ +{ + "name": "@figma/my-make-file", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "dependencies": { + "react": "18.3.1", + "react-dom": "18.3.1", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/icons-material": "7.3.5", + "@mui/material": "7.3.5", + "@popperjs/core": "2.11.8", + "@radix-ui/react-accordion": "1.2.3", + "@radix-ui/react-alert-dialog": "1.1.6", + "@radix-ui/react-aspect-ratio": "1.1.2", + "@radix-ui/react-avatar": "1.1.3", + "@radix-ui/react-checkbox": "1.1.4", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-context-menu": "2.2.6", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-dropdown-menu": "2.1.6", + "@radix-ui/react-hover-card": "1.1.6", + "@radix-ui/react-label": "2.1.2", + "@radix-ui/react-menubar": "1.1.6", + "@radix-ui/react-navigation-menu": "1.2.5", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-progress": "1.1.2", + "@radix-ui/react-radio-group": "1.2.3", + "@radix-ui/react-scroll-area": "1.2.3", + "@radix-ui/react-select": "2.1.6", + "@radix-ui/react-separator": "1.1.2", + "@radix-ui/react-slider": "1.2.3", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-switch": "1.1.3", + "@radix-ui/react-tabs": "1.1.3", + "@radix-ui/react-toggle-group": "1.1.2", + "@radix-ui/react-toggle": "1.1.2", + "@radix-ui/react-tooltip": "1.1.8", + "canvas-confetti": "1.9.4", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "cmdk": "1.1.1", + "date-fns": "3.6.0", + "embla-carousel-react": "8.6.0", + "input-otp": "1.4.2", + "lucide-react": "0.487.0", + "motion": "12.23.24", + "next-themes": "0.4.6", + "react-day-picker": "8.10.1", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", + "react-hook-form": "7.55.0", + "react-popper": "2.3.0", + "react-resizable-panels": "2.1.7", + "react-responsive-masonry": "2.7.1", + "react-router": "7.13.0", + "react-slick": "0.31.0", + "recharts": "2.15.2", + "sonner": "2.0.3", + "tailwind-merge": "3.2.0", + "tw-animate-css": "1.3.8", + "vaul": "1.1.2" + }, + "devDependencies": { + "@tailwindcss/vite": "4.1.12", + "@vitejs/plugin-react": "4.7.0", + "tailwindcss": "4.1.12", + "vite": "6.3.5" + }, + "pnpm": { + "overrides": { + "vite": "6.3.5" + }, + "onlyBuiltDependencies": ["@tailwindcss/oxide", "esbuild"] + } +} diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c6899ddb06842be346595abb23ee9b7d3b1f1db5 --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,4284 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + vite: 6.3.5 + +importers: + + .: + dependencies: + '@emotion/react': + specifier: 11.14.0 + version: 11.14.0(@types/react@19.2.14)(react@18.3.1) + '@emotion/styled': + specifier: 11.14.1 + version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + '@mui/icons-material': + specifier: 7.3.5 + version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + '@mui/material': + specifier: 7.3.5 + version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@popperjs/core': + specifier: 2.11.8 + version: 2.11.8 + '@radix-ui/react-accordion': + specifier: 1.2.3 + version: 1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-alert-dialog': + specifier: 1.1.6 + version: 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-aspect-ratio': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-avatar': + specifier: 1.1.3 + version: 1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-checkbox': + specifier: 1.1.4 + version: 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collapsible': + specifier: 1.1.3 + version: 1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-context-menu': + specifier: 2.2.6 + version: 2.2.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': + specifier: 1.1.6 + version: 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': + specifier: 2.1.6 + version: 2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-hover-card': + specifier: 1.1.6 + version: 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-label': + specifier: 2.1.2 + version: 2.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-menubar': + specifier: 1.1.6 + version: 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-navigation-menu': + specifier: 1.2.5 + version: 1.2.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': + specifier: 1.1.6 + version: 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-progress': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-radio-group': + specifier: 1.2.3 + version: 1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-scroll-area': + specifier: 1.2.3 + version: 1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-select': + specifier: 2.1.6 + version: 2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-separator': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slider': + specifier: 1.2.3 + version: 1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-switch': + specifier: 1.1.3 + version: 1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tabs': + specifier: 1.1.3 + version: 1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-toggle': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-toggle-group': + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': + specifier: 1.1.8 + version: 1.1.8(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + canvas-confetti: + specifier: 1.9.4 + version: 1.9.4 + class-variance-authority: + specifier: 0.7.1 + version: 0.7.1 + clsx: + specifier: 2.1.1 + version: 2.1.1 + cmdk: + specifier: 1.1.1 + version: 1.1.1(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + date-fns: + specifier: 3.6.0 + version: 3.6.0 + embla-carousel-react: + specifier: 8.6.0 + version: 8.6.0(react@18.3.1) + input-otp: + specifier: 1.4.2 + version: 1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + lucide-react: + specifier: 0.487.0 + version: 0.487.0(react@18.3.1) + motion: + specifier: 12.23.24 + version: 12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes: + specifier: 0.4.6 + version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: + specifier: 18.3.1 + version: 18.3.1 + react-day-picker: + specifier: 8.10.1 + version: 8.10.1(date-fns@3.6.0)(react@18.3.1) + react-dnd: + specifier: 16.0.1 + version: 16.0.1(@types/react@19.2.14)(react@18.3.1) + react-dnd-html5-backend: + specifier: 16.0.1 + version: 16.0.1 + react-dom: + specifier: 18.3.1 + version: 18.3.1(react@18.3.1) + react-hook-form: + specifier: 7.55.0 + version: 7.55.0(react@18.3.1) + react-popper: + specifier: 2.3.0 + version: 2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-resizable-panels: + specifier: 2.1.7 + version: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-responsive-masonry: + specifier: 2.7.1 + version: 2.7.1 + react-router: + specifier: 7.13.0 + version: 7.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-slick: + specifier: 0.31.0 + version: 0.31.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts: + specifier: 2.15.2 + version: 2.15.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + sonner: + specifier: 2.0.3 + version: 2.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: 3.2.0 + version: 3.2.0 + tw-animate-css: + specifier: 1.3.8 + version: 1.3.8 + vaul: + specifier: 1.1.2 + version: 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + devDependencies: + '@tailwindcss/vite': + specifier: 4.1.12 + version: 4.1.12(vite@6.3.5(jiti@2.6.1)(lightningcss@1.30.1)) + '@vitejs/plugin-react': + specifier: 4.7.0 + version: 4.7.0(vite@6.3.5(jiti@2.6.1)(lightningcss@1.30.1)) + tailwindcss: + specifier: 4.1.12 + version: 4.1.12 + vite: + specifier: 6.3.5 + version: 6.3.5(jiti@2.6.1)(lightningcss@1.30.1) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.14.1': + resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mui/core-downloads-tracker@7.3.9': + resolution: {integrity: sha512-MOkOCTfbMJwLshlBCKJ59V2F/uaLYfmKnN76kksj6jlGUVdI25A9Hzs08m+zjBRdLv+sK7Rqdsefe8X7h/6PCw==} + + '@mui/icons-material@7.3.5': + resolution: {integrity: sha512-LciL1GLMZ+VlzyHAALSVAR22t8IST4LCXmljcUSx2NOutgO2XnxdIp8ilFbeNf9wpo0iUFbAuoQcB7h+HHIf3A==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^7.3.5 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@7.3.5': + resolution: {integrity: sha512-8VVxFmp1GIm9PpmnQoCoYo0UWHoOrdA57tDL62vkpzEgvb/d71Wsbv4FRg7r1Gyx7PuSo0tflH34cdl/NvfHNQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^7.3.5 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@7.3.9': + resolution: {integrity: sha512-ErIyRQvsiQEq7Yvcvfw9UDHngaqjMy9P3JDPnRAaKG5qhpl2C4tX/W1S4zJvpu+feihmZJStjIyvnv6KDbIrlw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@7.3.9': + resolution: {integrity: sha512-JqujWt5bX4okjUPGpVof/7pvgClqh7HvIbsIBIOOlCh2u3wG/Bwp4+E1bc1dXSwkrkp9WUAoNdI5HEC+5HKvMw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@7.3.9': + resolution: {integrity: sha512-aL1q9am8XpRrSabv9qWf5RHhJICJql34wnrc1nz0MuOglPRYF/liN+c8VqZdTvUn9qg+ZjRVbKf4sJVFfIDtmg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.4.12': + resolution: {integrity: sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@7.3.9': + resolution: {integrity: sha512-U6SdZaGbfb65fqTsH3V5oJdFj9uYwyLE2WVuNvmbggTSDBb8QHrFsqY8BN3taK9t3yJ8/BPHD/kNvLNyjwM7Yw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + + '@radix-ui/primitive@1.1.1': + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + + '@radix-ui/react-accordion@1.2.3': + resolution: {integrity: sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-alert-dialog@1.1.6': + resolution: {integrity: sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.2': + resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-aspect-ratio@1.1.2': + resolution: {integrity: sha512-TaJxYoCpxJ7vfEkv2PTNox/6zzmpKXT6ewvCuf2tTOIVN45/Jahhlld29Yw4pciOXS2Xq91/rSGEdmEnUWZCqA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-avatar@1.1.3': + resolution: {integrity: sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.1.4': + resolution: {integrity: sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.3': + resolution: {integrity: sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.2': + resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.2.6': + resolution: {integrity: sha512-aUP99QZ3VU84NPsHeaFt4cQUNgJqFsLLOt/RbbWXszZ6MP0DpDyjkFZORr4RpAEx3sUBk+Kc8h13yGtC5Qw8dg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.6': + resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.5': + resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.6': + resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.1': + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.2': + resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-hover-card@1.1.6': + resolution: {integrity: sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.2': + resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menu@2.1.6': + resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menubar@1.1.6': + resolution: {integrity: sha512-FHq7+3DlXwh/7FOM4i0G4bC4vPjiq89VEEvNF4VMLchGnaUuUbE5uKXMUCjdKaOghEEMeiKa5XCa2Pk4kteWmg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-navigation-menu@1.2.5': + resolution: {integrity: sha512-myMHHQUZ3ZLTi8W381/Vu43Ia0NqakkQZ2vzynMmTUtQQ9kNkjzhOwkZC9TAM5R07OZUVIQyHC06f/9JZJpvvA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.6': + resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.2': + resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.4': + resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.2': + resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.2': + resolution: {integrity: sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-radio-group@1.2.3': + resolution: {integrity: sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.2': + resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.3': + resolution: {integrity: sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.1.6': + resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.2': + resolution: {integrity: sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slider@1.2.3': + resolution: {integrity: sha512-nNrLAWLjGESnhqBqcCNW4w2nn7LxudyMzeB6VgdyAnFLC6kfQgnAjSL2v6UkQTnDctJBlxrmxfplWS4iYjdUTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.2': + resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.1.3': + resolution: {integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.3': + resolution: {integrity: sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.2': + resolution: {integrity: sha512-JBm6s6aVG/nwuY5eadhU2zDi/IwYS0sDM5ZWb4nymv/hn3hZdkw+gENn0LP4iY1yCd7+bgJaCwueMYJIU3vk4A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.2': + resolution: {integrity: sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.1.8': + resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.0': + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.2': + resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.0': + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + + '@react-dnd/asap@5.0.2': + resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==} + + '@react-dnd/invariant@4.0.2': + resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==} + + '@react-dnd/shallowequal@4.0.2': + resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.60.0': + resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.60.0': + resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.60.0': + resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.60.0': + resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.60.0': + resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.60.0': + resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.0': + resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.0': + resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.0': + resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.0': + resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.0': + resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.0': + resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.0': + resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.0': + resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.0': + resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.0': + resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.60.0': + resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.60.0': + resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.60.0': + resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.60.0': + resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.60.0': + resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.1.12': + resolution: {integrity: sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==} + + '@tailwindcss/oxide-android-arm64@4.1.12': + resolution: {integrity: sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + resolution: {integrity: sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.12': + resolution: {integrity: sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + resolution: {integrity: sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + resolution: {integrity: sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + resolution: {integrity: sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + resolution: {integrity: sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + resolution: {integrity: sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + resolution: {integrity: sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + resolution: {integrity: sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + resolution: {integrity: sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + resolution: {integrity: sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.12': + resolution: {integrity: sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.12': + resolution: {integrity: sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==} + peerDependencies: + vite: 6.3.5 + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: 6.3.5 + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + baseline-browser-mapping@2.10.11: + resolution: {integrity: sha512-DAKrHphkJyiGuau/cFieRYhcTFeK/lBuD++C7cZ6KZHbMhBrisoi+EvhQ5RZrIfV5qwsW8kgQ07JIC+MDJRAhg==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001781: + resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} + + canvas-confetti@1.9.4: + resolution: {integrity: sha512-yxQbJkAVrFXWNbTUjPqjF7G+g6pDotOUHGbkZq2NELZUMDpiJ85rIEazVb8GTaAptNW2miJAXbs1BtioA251Pw==} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cmdk@1.1.1: + resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + dnd-core@16.0.1: + resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + electron-to-chromium@1.5.327: + resolution: {integrity: sha512-hLxLdIJDf8zIzKoH2TPCs+Botc+wUmj9sp4jVMwklY/sKleM8xxxOExRX3Gxj73nCXmJe3anhG7SvsDDPDvmuQ==} + + embla-carousel-react@8.6.0: + resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + embla-carousel-reactive-utils@8.6.0: + resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} + peerDependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + framer-motion@12.38.0: + resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + input-otp@1.4.2: + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lightningcss-darwin-arm64@1.30.1: + resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.1: + resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.1: + resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.1: + resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.1: + resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.30.1: + resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.30.1: + resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.30.1: + resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.30.1: + resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.1: + resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.1: + resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} + engines: {node: '>= 12.0.0'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@0.487.0: + resolution: {integrity: sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + motion-dom@12.38.0: + resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} + + motion-utils@12.36.0: + resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + + motion@12.23.24: + resolution: {integrity: sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + + node-releases@2.0.36: + resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + engines: {node: ^10 || ^12 || >=14} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + react-day-picker@8.10.1: + resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==} + peerDependencies: + date-fns: ^2.28.0 || ^3.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + react-dnd-html5-backend@16.0.1: + resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} + + react-dnd@16.0.1: + resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==} + peerDependencies: + '@types/hoist-non-react-statics': '>= 3.3.1' + '@types/node': '>= 12' + '@types/react': '>= 16' + react: '>= 16.14' + peerDependenciesMeta: + '@types/hoist-non-react-statics': + optional: true + '@types/node': + optional: true + '@types/react': + optional: true + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-hook-form@7.55.0: + resolution: {integrity: sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + + react-popper@2.3.0: + resolution: {integrity: sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==} + peerDependencies: + '@popperjs/core': ^2.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-resizable-panels@2.1.7: + resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==} + peerDependencies: + react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + react-responsive-masonry@2.7.1: + resolution: {integrity: sha512-Q+u+nOH87PzjqGFd2PgTcmLpHPZnCmUPREHYoNBc8dwJv6fi51p9U6hqwG8g/T8MN86HrFjrU+uQU6yvETU7cA==} + + react-router@7.13.0: + resolution: {integrity: sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-slick@0.31.0: + resolution: {integrity: sha512-zo6VLT8wuSBJffg/TFPbzrw2dEnfZ/cUKmYsKByh3AgatRv29m2LoFbq5vRMa3R3A4wp4d8gwbJKO2fWZFaI3g==} + peerDependencies: + react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-smooth@4.0.4: + resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.15.2: + resolution: {integrity: sha512-xv9lVztv3ingk7V3Jf05wfAZbM9Q2umJzu5t/cfnAK7LUslNrGT7LPBr74G+ok8kSCeFMaePmWMg0rcYOnczTw==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.60.0: + resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + sonner@2.0.3: + resolution: {integrity: sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwind-merge@3.2.0: + resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} + + tailwindcss@4.1.12: + resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} + + tapable@2.3.2: + resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + engines: {node: '>=6'} + + tar@7.5.13: + resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + engines: {node: '>=18'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.3.8: + resolution: {integrity: sha512-Qrk3PZ7l7wUcGYhwZloqfkWCmaXZAoqjkdbIDvzfGshwGtexa/DAs9koXxIkrpEasyevandomzCBAV1Yyop5rw==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + 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 + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6 + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/babel-plugin': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@emotion/utils': 1.4.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.11': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.3 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mui/core-downloads-tracker@7.3.9': {} + + '@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/core-downloads-tracker': 7.3.9 + '@mui/system': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + '@mui/types': 7.4.12(@types/react@19.2.14) + '@mui/utils': 7.3.9(@types/react@19.2.14)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.12(@types/react@19.2.14) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 19.2.4 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + '@types/react': 19.2.14 + + '@mui/private-theming@7.3.9(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/utils': 7.3.9(@types/react@19.2.14)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@mui/styled-engine@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + + '@mui/system@7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/private-theming': 7.3.9(@types/react@19.2.14)(react@18.3.1) + '@mui/styled-engine': 7.3.9(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.4.12(@types/react@19.2.14) + '@mui/utils': 7.3.9(@types/react@19.2.14)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@19.2.14)(react@18.3.1) + '@types/react': 19.2.14 + + '@mui/types@7.4.12(@types/react@19.2.14)': + dependencies: + '@babel/runtime': 7.29.2 + optionalDependencies: + '@types/react': 19.2.14 + + '@mui/utils@7.3.9(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.2 + '@mui/types': 7.4.12(@types/react@19.2.14) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + + '@popperjs/core@2.11.8': {} + + '@radix-ui/number@1.1.0': {} + + '@radix-ui/primitive@1.1.1': {} + + '@radix-ui/react-accordion@1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collapsible': 1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-alert-dialog@1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-arrow@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-aspect-ratio@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-avatar@1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-checkbox@1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-collapsible@1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-collection@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-compose-refs@1.1.1(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-context-menu@2.2.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-menu': 2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-context@1.1.1(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dialog@1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-direction@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dismissable-layer@1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-dropdown-menu@2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-menu': 2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-guards@1.1.1(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-focus-scope@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-hover-card@1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popper': 1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-id@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-label@2.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-menu@2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-popper': 1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-menubar@1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-menu': 2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-navigation-menu@1.2.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-popover@1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-popper': 1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-popper@1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-portal@1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-presence@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-primitive@2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-primitive@2.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-progress@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-radio-group@1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-roving-focus@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-scroll-area@1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-select@2.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-popper': 1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-separator@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slider@1.2.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slot@1.1.2(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-switch@1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-tabs@1.1.3(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-toggle-group@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-toggle': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-toggle@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-tooltip@1.1.8(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-popper': 1.2.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-previous@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-rect@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-use-size@1.1.0(@types/react@19.2.14)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.2.14)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/react-visually-hidden@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + '@radix-ui/rect@1.1.0': {} + + '@react-dnd/asap@5.0.2': {} + + '@react-dnd/invariant@4.0.2': {} + + '@react-dnd/shallowequal@4.0.2': {} + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.60.0': + optional: true + + '@rollup/rollup-android-arm64@4.60.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.60.0': + optional: true + + '@rollup/rollup-darwin-x64@4.60.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.60.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.60.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.60.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.60.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.60.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.60.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.60.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.60.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.60.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.60.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.60.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.60.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.60.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.60.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.0': + optional: true + + '@tailwindcss/node@4.1.12': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.1 + jiti: 2.6.1 + lightningcss: 1.30.1 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.12 + + '@tailwindcss/oxide-android-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.12': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.12': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.12': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.12': + optional: true + + '@tailwindcss/oxide@4.1.12': + dependencies: + detect-libc: 2.1.2 + tar: 7.5.13 + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-arm64': 4.1.12 + '@tailwindcss/oxide-darwin-x64': 4.1.12 + '@tailwindcss/oxide-freebsd-x64': 4.1.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.12 + '@tailwindcss/oxide-linux-x64-musl': 4.1.12 + '@tailwindcss/oxide-wasm32-wasi': 4.1.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.12 + + '@tailwindcss/vite@4.1.12(vite@6.3.5(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@tailwindcss/node': 4.1.12 + '@tailwindcss/oxide': 4.1.12 + tailwindcss: 4.1.12 + vite: 6.3.5(jiti@2.6.1)(lightningcss@1.30.1) + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/d3-array@3.2.2': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.15': {} + + '@types/react-transition-group@4.4.12(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@vitejs/plugin-react@4.7.0(vite@6.3.5(jiti@2.6.1)(lightningcss@1.30.1))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.3.5(jiti@2.6.1)(lightningcss@1.30.1) + transitivePeerDependencies: + - supports-color + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.29.2 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + + baseline-browser-mapping@2.10.11: {} + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.11 + caniuse-lite: 1.0.30001781 + electron-to-chromium: 1.5.327 + node-releases: 2.0.36 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001781: {} + + canvas-confetti@1.9.4: {} + + chownr@3.0.0: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + classnames@2.5.1: {} + + clsx@2.1.1: {} + + cmdk@1.1.1(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.4(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + convert-source-map@1.9.0: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + + csstype@3.2.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + date-fns@3.6.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js-light@2.5.1: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + dnd-core@16.0.1: + dependencies: + '@react-dnd/asap': 5.0.2 + '@react-dnd/invariant': 4.0.2 + redux: 4.2.1 + + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.29.2 + csstype: 3.2.3 + + electron-to-chromium@1.5.327: {} + + embla-carousel-react@8.6.0(react@18.3.1): + dependencies: + embla-carousel: 8.6.0 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) + react: 18.3.1 + + embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: {} + + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.2 + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eventemitter3@4.0.7: {} + + fast-deep-equal@3.1.3: {} + + fast-equals@5.4.0: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + find-root@1.1.0: {} + + framer-motion@12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + motion-dom: 12.38.0 + motion-utils: 12.36.0 + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-nonce@1.0.1: {} + + graceful-fs@4.2.11: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + input-otp@1.4.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + internmap@2.0.3: {} + + is-arrayish@0.2.1: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + jsesc@3.1.0: {} + + json-parse-even-better-errors@2.3.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + lightningcss-darwin-arm64@1.30.1: + optional: true + + lightningcss-darwin-x64@1.30.1: + optional: true + + lightningcss-freebsd-x64@1.30.1: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.1: + optional: true + + lightningcss-linux-arm64-gnu@1.30.1: + optional: true + + lightningcss-linux-arm64-musl@1.30.1: + optional: true + + lightningcss-linux-x64-gnu@1.30.1: + optional: true + + lightningcss-linux-x64-musl@1.30.1: + optional: true + + lightningcss-win32-arm64-msvc@1.30.1: + optional: true + + lightningcss-win32-x64-msvc@1.30.1: + optional: true + + lightningcss@1.30.1: + dependencies: + detect-libc: 2.1.2 + 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 + + lines-and-columns@1.2.4: {} + + lodash.debounce@4.0.8: {} + + lodash@4.17.23: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@0.487.0(react@18.3.1): + dependencies: + react: 18.3.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + minipass@7.1.3: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.3 + + motion-dom@12.38.0: + dependencies: + motion-utils: 12.36.0 + + motion-utils@12.36.0: {} + + motion@12.23.24(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + framer-motion: 12.38.0(@emotion/is-prop-valid@1.4.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@emotion/is-prop-valid': 1.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + next-themes@0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + node-releases@2.0.36: {} + + object-assign@4.1.1: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + react-day-picker@8.10.1(date-fns@3.6.0)(react@18.3.1): + dependencies: + date-fns: 3.6.0 + react: 18.3.1 + + react-dnd-html5-backend@16.0.1: + dependencies: + dnd-core: 16.0.1 + + react-dnd@16.0.1(@types/react@19.2.14)(react@18.3.1): + dependencies: + '@react-dnd/invariant': 4.0.2 + '@react-dnd/shallowequal': 4.0.2 + dnd-core: 16.0.1 + fast-deep-equal: 3.1.3 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-fast-compare@3.2.2: {} + + react-hook-form@7.55.0(react@18.3.1): + dependencies: + react: 18.3.1 + + react-is@16.13.1: {} + + react-is@18.3.1: {} + + react-is@19.2.4: {} + + react-popper@2.3.0(@popperjs/core@2.11.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@popperjs/core': 2.11.8 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-fast-compare: 3.2.2 + warning: 4.0.3 + + react-refresh@0.17.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-remove-scroll@2.7.2(@types/react@19.2.14)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@19.2.14)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.14)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@19.2.14)(react@18.3.1) + optionalDependencies: + '@types/react': 19.2.14 + + react-resizable-panels@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react-responsive-masonry@2.7.1: {} + + react-router@7.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + cookie: 1.1.1 + react: 18.3.1 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + + react-slick@0.31.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + classnames: 2.5.1 + json2mq: 0.2.0 + lodash.debounce: 4.0.8 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + resize-observer-polyfill: 1.5.1 + + react-smooth@4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + fast-equals: 5.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-style-singleton@2.2.3(@types/react@19.2.14)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.29.2 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + recharts-scale@0.4.5: + dependencies: + decimal.js-light: 2.5.1 + + recharts@2.15.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.23 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-smooth: 4.0.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + + redux@4.2.1: + dependencies: + '@babel/runtime': 7.29.2 + + resize-observer-polyfill@1.5.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.60.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.60.0 + '@rollup/rollup-android-arm64': 4.60.0 + '@rollup/rollup-darwin-arm64': 4.60.0 + '@rollup/rollup-darwin-x64': 4.60.0 + '@rollup/rollup-freebsd-arm64': 4.60.0 + '@rollup/rollup-freebsd-x64': 4.60.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 + '@rollup/rollup-linux-arm-musleabihf': 4.60.0 + '@rollup/rollup-linux-arm64-gnu': 4.60.0 + '@rollup/rollup-linux-arm64-musl': 4.60.0 + '@rollup/rollup-linux-loong64-gnu': 4.60.0 + '@rollup/rollup-linux-loong64-musl': 4.60.0 + '@rollup/rollup-linux-ppc64-gnu': 4.60.0 + '@rollup/rollup-linux-ppc64-musl': 4.60.0 + '@rollup/rollup-linux-riscv64-gnu': 4.60.0 + '@rollup/rollup-linux-riscv64-musl': 4.60.0 + '@rollup/rollup-linux-s390x-gnu': 4.60.0 + '@rollup/rollup-linux-x64-gnu': 4.60.0 + '@rollup/rollup-linux-x64-musl': 4.60.0 + '@rollup/rollup-openbsd-x64': 4.60.0 + '@rollup/rollup-openharmony-arm64': 4.60.0 + '@rollup/rollup-win32-arm64-msvc': 4.60.0 + '@rollup/rollup-win32-ia32-msvc': 4.60.0 + '@rollup/rollup-win32-x64-gnu': 4.60.0 + '@rollup/rollup-win32-x64-msvc': 4.60.0 + fsevents: 2.3.3 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + set-cookie-parser@2.7.2: {} + + sonner@2.0.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + source-map-js@1.2.1: {} + + source-map@0.5.7: {} + + string-convert@0.2.1: {} + + stylis@4.2.0: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwind-merge@3.2.0: {} + + tailwindcss@4.1.12: {} + + tapable@2.3.2: {} + + tar@7.5.13: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + tiny-invariant@1.3.3: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tslib@2.8.1: {} + + tw-animate-css@1.3.8: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + use-callback-ref@1.3.3(@types/react@19.2.14)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + use-sidecar@1.1.3(@types/react@19.2.14)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.14 + + vaul@1.1.2(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-dialog': 1.1.6(@types/react@19.2.14)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + victory-vendor@36.9.2: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite@6.3.5(jiti@2.6.1)(lightningcss@1.30.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.8 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + yallist@3.1.1: {} + + yallist@5.0.0: {} + + yaml@1.10.3: {} diff --git a/frontend/postcss.config.mjs b/frontend/postcss.config.mjs new file mode 100644 index 0000000000000000000000000000000000000000..531dbecdad58a2ecaad9b45b5eefced747424c4f --- /dev/null +++ b/frontend/postcss.config.mjs @@ -0,0 +1,15 @@ +/** + * PostCSS Configuration + * + * Tailwind CSS v4 (via @tailwindcss/vite) automatically sets up all required + * PostCSS plugins — you do NOT need to include `tailwindcss` or `autoprefixer` here. + * + * This file only exists for adding additional PostCSS plugins, if needed. + * For example: + * + * import postcssNested from 'postcss-nested' + * export default { plugins: [postcssNested()] } + * + * Otherwise, you can leave this file empty. + */ +export default {} diff --git a/frontend/src/app/App.tsx b/frontend/src/app/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4f1babda5015a4eb860fb57c7034ca49941f936d --- /dev/null +++ b/frontend/src/app/App.tsx @@ -0,0 +1,333 @@ +import { useState, useCallback, useEffect } from "react"; +// AlgoScope dashboard — v2 (custom charts, no Recharts) +import { motion, AnimatePresence } from "motion/react"; +import { Header } from "./components/Header"; +import { Sidebar } from "./components/Sidebar"; +import { OverviewTab } from "./components/OverviewTab"; +import { CoOccurrenceGraph } from "./components/CoOccurrenceGraph"; +import { TermComparisonTab } from "./components/TermComparisonTab"; +import { ExportTab } from "./components/ExportTab"; +import { SplashScreen } from "./components/SplashScreen"; +import { + apiFetchPosts, + apiFetchAndAnalyze, + apiFetchTotal, + generateMockPosts, + ALGOSPEAK_TERMS, + Post, +} from "./components/mockData"; + +// WHY empty initial state instead of mock data: +// We fetch real posts from the backend in the useEffect below. Starting with +// an empty array avoids a flash of mock data before real data arrives. +const EMPTY_POSTS: Post[] = []; + +const TABS = [ + { id: "overview", label: "Overview" }, + { id: "graph", label: "Co-occurrence Graph" }, + { id: "compare", label: "Term Comparison" }, + { id: "export", label: "Export" }, +]; + +export default function App() { + const [showSplash, setShowSplash] = useState(true); + const [sidebarOpen, setSidebarOpen] = useState(true); + const [activeTab, setActiveTab] = useState("overview"); + + // Sidebar state + const [selectedTerms, setSelectedTerms] = useState(ALGOSPEAK_TERMS.slice(0, 4)); + const [threshold, setThreshold] = useState(0.70); + const [sampling, setSampling] = useState(25); + const [autoRefresh, setAutoRefresh] = useState(false); + + // Data + const [allPosts, setAllPosts] = useState(EMPTY_POSTS); + const [batchPosts, setBatchPosts] = useState(EMPTY_POSTS); + const [fetching, setFetching] = useState(false); + const [justFetched, setJustFetched] = useState(false); + const [fetchError, setFetchError] = useState(null); + // WHY separate counter instead of allPosts.length: + // allPosts is capped at 500 and deduplicates by id, so it can go DOWN + // when new posts replace old ones. totalAnalyzed is a monotonically + // increasing sum — the true count of posts ever processed this session. + const [totalAnalyzed, setTotalAnalyzed] = useState(0); + + // Graph controls + const [minCooccurrence, setMinCooccurrence] = useState(3); + const [toxicOnly, setToxicOnly] = useState(false); + + // ── Load initial posts from backend on mount ──────────────────────────────── + // WHY useEffect + apiFetchPosts: + // On mount we call GET /posts to populate the dashboard with whatever the + // server already has (either seeded posts from cold start, or posts from + // previous sessions). This replaces the old `generateMockPosts()` call. + // + // WHY fallback to mock data on error: + // If the backend is unavailable (local dev without FastAPI running), we fall + // back to mock data so the UI is still usable. This is dev-only behaviour; + // in production the frontend and backend are served from the same process. + useEffect(() => { + let cancelled = false; + async function loadInitial() { + try { + const posts = await apiFetchPosts(200); + if (cancelled) return; + if (posts.length > 0) { + setAllPosts(posts); + setBatchPosts(posts.slice(0, 25)); + // WHY set totalAnalyzed from DB count on mount: + // The counter starts at 0 but the DB already has posts from previous + // sessions. Without this, the display jumps DOWN from 200 (DB load) + // to 25 (first fetch) because totalAnalyzed || totalPosts picks + // totalPosts on load, then switches to the smaller totalAnalyzed. + setTotalAnalyzed(posts.length); + } else { + // Backend is healthy but DB is empty — show mock data as a placeholder + const mock = generateMockPosts(ALGOSPEAK_TERMS.slice(0, 4), 30); + setAllPosts(mock); + setBatchPosts(mock.slice(0, 25)); + } + } catch { + if (cancelled) return; + // Backend unreachable — fall back to mock data for dev/offline use + const mock = generateMockPosts(ALGOSPEAK_TERMS.slice(0, 4), 30); + setAllPosts(mock); + setBatchPosts(mock.slice(0, 25)); + } + } + loadInitial(); + return () => { cancelled = true; }; + }, []); + + // ── Fetch & Analyze handler ───────────────────────────────────────────────── + // WHY apiFetchAndAnalyze instead of generateMockPosts: + // This calls POST /fetch-and-analyze which fetches real Bluesky posts, + // runs batch DistilBERT inference, saves them to SQLite, and returns results. + const handleFetch = useCallback(async () => { + setFetching(true); + setJustFetched(false); + setFetchError(null); + try { + const { posts: newBatch, message } = await apiFetchAndAnalyze( + selectedTerms.length ? selectedTerms : ALGOSPEAK_TERMS, + sampling, + threshold, + ); + + if (newBatch.length === 0) { + setFetchError(message ?? "No posts fetched from Bluesky. Check credentials and try again."); + setJustFetched(true); + return; + } + + setBatchPosts(newBatch); + setAllPosts(prev => { + const idSet = new Set(newBatch.map(p => String(p.id))); + const filtered = prev.filter(p => !idSet.has(String(p.id))); + return [...newBatch, ...filtered].slice(0, 500); + }); + // WHY fetch total from DB instead of computing locally: + // Any local computation (trulyNew, c => c + n) suffers from stale + // closures inside useCallback — allPosts captured at creation time is + // never the latest value, so the counter stops accumulating after the + // first fetch. The DB is the only source of truth for how many posts + // have ever been analyzed. One extra GET /posts?limit=1 call (tiny) + // gives us the exact real count with zero closure risk. + const dbTotal = await apiFetchTotal(); + if (dbTotal >= 0) setTotalAnalyzed(dbTotal); + setJustFetched(true); + } catch (err) { + const msg = err instanceof Error ? err.message : "Fetch failed"; + setFetchError(msg); + // Fallback: generate mock data so the UI doesn't go blank + const mock = generateMockPosts(selectedTerms.length ? selectedTerms : ALGOSPEAK_TERMS, sampling); + setBatchPosts(mock); + setAllPosts(prev => [...mock, ...prev].slice(0, 500)); + setJustFetched(true); + } finally { + setFetching(false); + } + }, [selectedTerms, sampling, threshold]); + + return ( + <> + {showSplash && setShowSplash(false)} />} +
+ {/* Header */} +
setSidebarOpen(v => !v)} sidebarOpen={sidebarOpen} /> + + {/* Body */} +
+ {/* Sidebar */} + + + {/* Main content */} +
+ {/* Tabs nav */} +
+ {TABS.map(tab => { + const active = tab.id === activeTab; + return ( + + ); + })} + + {/* Status toast — shows fetch success or error */} + + {justFetched && !fetchError && ( + + ✓ Done! Analyzed {batchPosts.length} posts + + )} + {justFetched && fetchError && ( + + ⚠ API unavailable — showing mock data + + )} + +
+ + {/* Tab content */} +
+ + + {activeTab === "overview" && ( + + )} + {activeTab === "graph" && ( + + )} + {activeTab === "compare" && ( + + )} + {activeTab === "export" && ( + + )} + + +
+
+
+ + +
+ + ); +} diff --git a/frontend/src/app/components/CoOccurrenceGraph.tsx b/frontend/src/app/components/CoOccurrenceGraph.tsx new file mode 100644 index 0000000000000000000000000000000000000000..a59dd12129fc7ed7f9792a511bcadb7e815facac --- /dev/null +++ b/frontend/src/app/components/CoOccurrenceGraph.tsx @@ -0,0 +1,609 @@ +import { useEffect, useRef, useState, useCallback, useMemo, Component } from "react"; +import { motion } from "motion/react"; +import { nodeColor, apiGetGraphData, GraphNode, GraphEdge } from "./mockData"; + +// ── Types ────────────────────────────────────────────────────────────────────── +interface SimNode { + id: string; + x: number; + y: number; + vx: number; + vy: number; + frequency: number; + toxicRatio: number; +} + +interface Props { + minCooccurrence: number; + setMinCooccurrence: (v: number) => void; + toxicOnly: boolean; + setToxicOnly: (v: boolean) => void; +} + +// ── Constants ────────────────────────────────────────────────────────────────── +const W = 820; +const H = 540; +// WHY increased REPULSION + reduced SPRING for real data: +// Mock data had ~6 nodes with low edge weights. Real data has 20-30 nodes +// where a hub like "unalive" has 17 edges — the combined spring pull +// overwhelmed repulsion and collapsed the graph into one blob. +// Higher REPULSION (6000) pushes nodes apart more aggressively, +// lower SPRING (0.018) reduces the per-edge pull so a hub with 17 edges +// doesn't dominate. EDGE_LEN increased so nodes have more breathing room. +const REPULSION = 6000; +const SPRING = 0.018; +const EDGE_LEN = 180; +const GRAVITY = 0.008; +const DAMPING = 0.82; +const CENTER_X = W / 2; +const CENTER_Y = H / 2; + +// ── Error Boundary ───────────────────────────────────────────────────────────── +interface EBState { hasError: boolean; error?: string } +class GraphErrorBoundary extends Component<{ children: React.ReactNode }, EBState> { + constructor(props: { children: React.ReactNode }) { + super(props); + this.state = { hasError: false }; + } + static getDerivedStateFromError(err: Error): EBState { + return { hasError: true, error: err.message }; + } + render() { + if (this.state.hasError) { + return ( +
+
+ ⚠ Graph failed to render +
+
+ {this.state.error || "Unknown error"} +
+ +
+ ); + } + return this.props.children; + } +} + +// ── Physics hook ─────────────────────────────────────────────────────────────── +function safeNum(v: number, fallback = 0): number { + return isFinite(v) && !isNaN(v) ? v : fallback; +} + +function useForceSimulation( + nodeConfigs: GraphNode[], + edges: GraphEdge[], + nodeKey: string, +) { + const nodesRef = useRef([]); + const [positions, setPositions] = useState([]); + const rafRef = useRef(0); + const edgesRef = useRef(edges); + edgesRef.current = edges; + const activeRef = useRef(false); + + const run = useCallback(() => { + if (!activeRef.current) return; + const ns = nodesRef.current; + if (!ns.length) return; + const es = edgesRef.current; + + try { + // Repulsion + for (let i = 0; i < ns.length; i++) { + for (let j = i + 1; j < ns.length; j++) { + const dx = (ns[i].x - ns[j].x) || 0.5; + const dy = (ns[i].y - ns[j].y) || 0.5; + const dist2 = Math.max(0.01, dx * dx + dy * dy); + const dist = Math.sqrt(dist2); + const force = REPULSION / dist2; + const fx = safeNum((dx / dist) * force); + const fy = safeNum((dy / dist) * force); + ns[i].vx += fx; + ns[i].vy += fy; + ns[j].vx -= fx; + ns[j].vy -= fy; + } + } + + // Spring edges + for (const e of es) { + const s = ns.find(n => n.id === e.source); + const t = ns.find(n => n.id === e.target); + if (!s || !t) continue; + const dx = t.x - s.x; + const dy = t.y - s.y; + const dist = Math.sqrt(dx * dx + dy * dy) || 1; + // WHY Math.min cap: with real data edge weights can be 50–500+ + // (co-occurrence count across hundreds of posts). Without capping, + // naturalLen collapses to ~3px, pulling all nodes into a single blob. + // Capping at 8 keeps naturalLen in the range 115–140px regardless of + // how large the real-data weights get. + const naturalLen = EDGE_LEN / (1 + Math.min(e.weight, 8) * 0.04); + const force = (dist - naturalLen) * SPRING; + const fx = safeNum((dx / dist) * force); + const fy = safeNum((dy / dist) * force); + s.vx += fx; + s.vy += fy; + t.vx -= fx; + t.vy -= fy; + } + + // Gravity + integrate + for (const n of ns) { + n.vx = safeNum(n.vx + (CENTER_X - n.x) * GRAVITY); + n.vy = safeNum(n.vy + (CENTER_Y - n.y) * GRAVITY); + n.vx *= DAMPING; + n.vy *= DAMPING; + n.x = safeNum(n.x + n.vx, CENTER_X); + n.y = safeNum(n.y + n.vy, CENTER_Y); + // WHY log scale: linear sizing (freq * 0.25) lets high-frequency common + // words (e.g. "yeah", "his") grow to 10x the size of algospeak terms, + // dominating the canvas. Math.log compresses the range so all nodes + // stay visually comparable. Min 8px, max ~32px regardless of frequency. + const r = 5 + Math.min(Math.log1p(n.frequency ?? 1) * 1.8, 12); + n.x = Math.max(r + 40, Math.min(W - r - 40, n.x)); + n.y = Math.max(r + 20, Math.min(H - r - 20, n.y)); + } + + if (activeRef.current) { + setPositions(ns.map(n => ({ ...n }))); + const maxV = ns.reduce((mx, n) => Math.max(mx, Math.abs(n.vx) + Math.abs(n.vy)), 0); + if (maxV > 0.15) { + rafRef.current = requestAnimationFrame(run); + } + } + } catch { + activeRef.current = false; + } + }, []); + + useEffect(() => { + cancelAnimationFrame(rafRef.current); + activeRef.current = true; + + nodesRef.current = nodeConfigs.map((n, i) => { + // WHY circle spread: random init clusters nodes near center, requiring + // hundreds of ticks to separate. Evenly spreading in a circle means + // repulsion forces are balanced from tick 1 — graph settles readable. + const angle = (i / Math.max(nodeConfigs.length, 1)) * 2 * Math.PI; + const spread = Math.min(W, H) * 0.32; + return { + id: n.id, + frequency: n.frequency, + toxicRatio: n.toxicRatio, + x: CENTER_X + Math.cos(angle) * spread, + y: CENTER_Y + Math.sin(angle) * spread, + vx: (Math.random() - 0.5) * 1.5, + vy: (Math.random() - 0.5) * 1.5, + }; + }); + setPositions(nodesRef.current.map(n => ({ ...n }))); + rafRef.current = requestAnimationFrame(run); + + return () => { + activeRef.current = false; + cancelAnimationFrame(rafRef.current); + }; + }, [nodeKey, run]); + + return positions; +} + +// ── Edge colour ─────────────────────────────────────────────────────────────── +function edgeColor(sourceRatio: number, targetRatio: number, weight: number): string { + const avg = ((sourceRatio ?? 0) + (targetRatio ?? 0)) / 2; + const alpha = Math.min(0.9, 0.3 + weight * 0.05); + if (avg >= 0.7) return `rgba(255,75,75,${alpha})`; + if (avg >= 0.4) return `rgba(255,140,66,${alpha})`; + return `rgba(46,204,113,${alpha})`; +} + +// ── Inner graph component ───────────────────────────────────────────────────── +function GraphCanvas({ + minCooccurrence, toxicOnly, setMinCooccurrence, setToxicOnly, +}: { + minCooccurrence: number; + toxicOnly: boolean; + setMinCooccurrence: (v: number) => void; + setToxicOnly: (v: boolean) => void; +}) { + const [built, setBuilt] = useState(false); + const [hoveredNode, setHoveredNode] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // WHY state for nodes/edges instead of hardcoded constants: + // Previously these were GRAPH_NODES / GRAPH_EDGES imported from mockData. + // Now they come from GET /graph-data when the user clicks "Build Graph". + // The physics simulation code is exactly unchanged — it just receives real data. + const [nodes, setNodes] = useState([]); + const [edges, setEdges] = useState([]); + + // ── Fetch graph data from backend ─────────────────────────────────────────── + const handleBuild = useCallback(async () => { + setLoading(true); + setError(null); + try { + const data = await apiGetGraphData(minCooccurrence, toxicOnly); + setNodes(data.nodes); + setEdges(data.edges); + setBuilt(true); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to load graph data"); + } finally { + setLoading(false); + } + }, [minCooccurrence, toxicOnly]); + + const visibleNodes = useMemo(() => { + return nodes.filter(n => { + if (toxicOnly && n.toxicRatio < 0.7) return false; + const edgeCount = edges.filter( + e => (e.source === n.id || e.target === n.id) && e.weight >= minCooccurrence + ).length; + return edgeCount > 0 || minCooccurrence <= 2; + }); + }, [nodes, edges, toxicOnly, minCooccurrence]); + + const visibleEdges = useMemo(() => { + const nodeIds = new Set(visibleNodes.map(n => n.id)); + return edges.filter( + e => e.weight >= minCooccurrence && nodeIds.has(e.source) && nodeIds.has(e.target) + ); + }, [visibleNodes, edges, minCooccurrence]); + + const nodeKey = useMemo( + () => visibleNodes.map(n => n.id).sort().join(","), + [visibleNodes] + ); + + const positions = useForceSimulation(visibleNodes, visibleEdges, nodeKey); + + const posMap = useMemo(() => { + const m: Record = {}; + for (const p of positions) m[p.id] = { x: p.x, y: p.y }; + return m; + }, [positions]); + + const nodeMap = useMemo(() => { + const m: Record = {}; + for (const n of visibleNodes) m[n.id] = n; + return m; + }, [visibleNodes]); + + return ( +
+ {/* Top controls */} +
+ {/* Info card */} + +
+ How to read this graph +
+
+ Words that frequently appear together in algospeak posts are connected. Node size = frequency  |  + red >70% toxic + {" "}orange 40-70% mixed + {" "}green <40% benign +
+
+ + {/* Controls */} + +
+
+ Min co-occurrences +
+
+ {minCooccurrence} +
+ { + setMinCooccurrence(parseInt(e.target.value)); + // Reset graph so user re-clicks Build Graph with new params + setBuilt(false); + setNodes([]); + setEdges([]); + }} + style={{ width: "100%", accentColor: "#ff4b4b" }} + /> +
+ + + {loading ? "Loading…" : built ? "✓ Graph Active" : "Build Graph"} + +
+
+ + {/* Error state */} + {error && ( +
+ ⚠ {error} +
+ )} + + {/* Graph canvas */} + {!built ? ( + + Adjust settings above and click "Build Graph" to visualize word co-occurrences. + + ) : ( + + + {/* Background grid */} + + + + + + + + {/* Edges */} + {visibleEdges.map(e => { + const s = posMap[e.source]; + const t = posMap[e.target]; + if (!s || !t) return null; + const sNode = nodeMap[e.source]; + const tNode = nodeMap[e.target]; + if (!sNode || !tNode) return null; + const isHighlighted = hoveredNode === e.source || hoveredNode === e.target; + return ( + + ); + })} + + {/* Nodes */} + {positions.map(node => { + const freq = node.frequency ?? 1; + const tRatio = node.toxicRatio ?? 0.5; + // WHY log1p: same formula as the physics loop so the rendered + // circle matches the collision radius used for simulation. + const size = 5 + Math.min(Math.log1p(freq) * 1.8, 12); + const color = nodeColor(tRatio); + const isHovered = hoveredNode === node.id; + const isDimmed = !!(hoveredNode && !isHovered); + const r = isHovered ? size * 1.25 : size; + + if (!isFinite(node.x) || !isFinite(node.y)) return null; + + return ( + setHoveredNode(node.id)} + onMouseLeave={() => setHoveredNode(null)} + style={{ cursor: "pointer" }} + > + + + + {node.id} + + + ); + })} + + + {/* Hover tooltip */} + {hoveredNode && (() => { + const n = visibleNodes.find(x => x.id === hoveredNode); + if (!n) return null; + const color = nodeColor(n.toxicRatio); + const connections = visibleEdges.filter( + e => e.source === n.id || e.target === n.id + ).length; + return ( + +
+ {n.id} +
+
+ Frequency: {n.frequency} +
+
+ Toxic ratio:{" "} + + {(n.toxicRatio * 100).toFixed(0)}% + +
+
+ Connections: {connections} +
+
+
+
+ + ); + })()} + + )} +
+ ); +} + +// ── Public component ─────────────────────────────────────────────────────────── +export function CoOccurrenceGraph({ minCooccurrence, setMinCooccurrence, toxicOnly, setToxicOnly }: Props) { + return ( + + + + ); +} diff --git a/frontend/src/app/components/ExportTab.tsx b/frontend/src/app/components/ExportTab.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9990a95373b093eb9f1901b7511be78a2eca800b --- /dev/null +++ b/frontend/src/app/components/ExportTab.tsx @@ -0,0 +1,242 @@ +import { useState } from "react"; +import { motion } from "motion/react"; +import { Download, FileText, FileJson, CheckCircle } from "lucide-react"; +import { Post } from "./mockData"; + +interface Props { + posts: Post[]; +} + +function downloadJSON(data: unknown, filename: string) { + const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); +} + +function downloadCSV(posts: Post[], filename: string) { + const header = "id,text,score,label,query_term,created_at"; + const rows = posts.map(p => + [p.id, `"${p.text.replace(/"/g, '""')}"`, p.score, p.label, p.query_term, p.created_at].join(",") + ); + const blob = new Blob([[header, ...rows].join("\n")], { type: "text/csv" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); +} + +function ExportCard({ + icon, + title, + description, + buttonLabel, + color, + onClick, + delay, +}: { + icon: React.ReactNode; + title: string; + description: string; + buttonLabel: string; + color: string; + onClick: () => void; + delay: number; +}) { + const [clicked, setClicked] = useState(false); + + const handleClick = () => { + onClick(); + setClicked(true); + setTimeout(() => setClicked(false), 2000); + }; + + return ( + +
+ {icon} +
+
+
{title}
+
{description}
+
+ + {clicked ? : } + {clicked ? "Downloaded!" : buttonLabel} + +
+ ); +} + +export function ExportTab({ posts }: Props) { + const toxicPosts = posts.filter(p => p.label === "toxic"); + const nonToxicPosts = posts.filter(p => p.label === "non-toxic"); + + const summaryData = { + exported_at: new Date().toISOString(), + total_posts: posts.length, + toxic_count: toxicPosts.length, + non_toxic_count: nonToxicPosts.length, + toxic_rate: posts.length ? ((toxicPosts.length / posts.length) * 100).toFixed(2) + "%" : "0%", + avg_score: posts.length + ? (posts.reduce((s, p) => s + p.score, 0) / posts.length).toFixed(4) + : "0", + posts, + }; + + return ( +
+ + Export your analyzed data for further research or archiving. + + + {/* Stats summary */} + + {[ + { label: "Total posts", value: String(posts.length), color: "#fff" }, + { label: "Toxic", value: String(toxicPosts.length), color: "#ff4b4b" }, + { label: "Non-toxic", value: String(nonToxicPosts.length), color: "#2ecc71" }, + { + label: "Toxic rate", + value: posts.length ? `${((toxicPosts.length / posts.length) * 100).toFixed(1)}%` : "0%", + color: "#ff8c42", + }, + ].map(({ label, value, color }) => ( +
+
+ {label} +
+
{value}
+
+ ))} +
+ + {/* Export options */} + } + title="Full dataset (JSON)" + description={`Export all ${posts.length} analyzed posts with scores, labels, and metadata`} + buttonLabel="Export JSON" + color="#a6b0ff" + onClick={() => downloadJSON(summaryData, "algoscope-export.json")} + delay={0.1} + /> + + } + title="All posts (CSV)" + description={`${posts.length} rows · id, text, score, label, query_term, created_at`} + buttonLabel="Export CSV" + color="#ff8c42" + onClick={() => downloadCSV(posts, "algoscope-posts.csv")} + delay={0.18} + /> + + } + title="Toxic posts only (CSV)" + description={`${toxicPosts.length} rows · filtered to toxic label (score ≥ threshold)`} + buttonLabel="Export CSV" + color="#ff4b4b" + onClick={() => downloadCSV(toxicPosts, "algoscope-toxic.csv")} + delay={0.26} + /> + + } + title="Summary statistics (JSON)" + description="Aggregate metrics: counts, rates, avg scores per term" + buttonLabel="Export JSON" + color="#2ecc71" + onClick={() => { + const termStats: Record = {}; + const terms = [...new Set(posts.map(p => p.query_term))]; + for (const t of terms) { + const tp = posts.filter(p => p.query_term === t); + const toxicN = tp.filter(p => p.label === "toxic").length; + termStats[t] = { + count: tp.length, + toxic_count: toxicN, + toxic_rate: `${((toxicN / tp.length) * 100).toFixed(1)}%`, + avg_score: (tp.reduce((s, p) => s + p.score, 0) / tp.length).toFixed(4), + }; + } + downloadJSON({ + exported_at: new Date().toISOString(), + total: posts.length, + toxic_rate: `${((toxicPosts.length / posts.length) * 100).toFixed(1)}%`, + per_term: termStats, + }, "algoscope-summary.json"); + }} + delay={0.34} + /> +
+ ); +} diff --git a/frontend/src/app/components/Header.tsx b/frontend/src/app/components/Header.tsx new file mode 100644 index 0000000000000000000000000000000000000000..18778eb796a033827387daf0fc9e4eacf72c1caf --- /dev/null +++ b/frontend/src/app/components/Header.tsx @@ -0,0 +1,140 @@ +import { motion } from "motion/react"; + +interface HeaderProps { + onToggleSidebar: () => void; + sidebarOpen: boolean; +} + +export function Header({ onToggleSidebar, sidebarOpen }: HeaderProps) { + return ( + +
+ {/* Sidebar toggle */} + + + {/* Logo */} + + A + + + {/* Title */} +
+
+ AlgoScope +
+
+ Real-time algospeak & toxicity intelligence on Bluesky +
+
+
+ + {/* Right side */} +
+
+ + + LIVE + +
+
+ by Odeliya Charitonova +
+ + github.com/odeliyach/Algoscope + +
+
+ + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/app/components/OverviewTab.tsx b/frontend/src/app/components/OverviewTab.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0f580a2f888861a8218c452ab8cacfa2c8577cca --- /dev/null +++ b/frontend/src/app/components/OverviewTab.tsx @@ -0,0 +1,687 @@ +import { useMemo, useEffect, useRef, useState } from "react"; +import { motion, AnimatePresence } from "motion/react"; +import { Post } from "./mockData"; + +interface OverviewTabProps { + posts: Post[]; + batchPosts: Post[]; + selectedTerms: string[]; + justFetched: boolean; + totalAnalyzed: number; +} + +// ── Animated counter hook ────────────────────────────────────────────────────── +function useAnimatedNumber(target: number, duration = 900): number { + const [current, setCurrent] = useState(0); + const startRef = useRef(null); + const fromRef = useRef(0); + const rafRef = useRef(0); + + useEffect(() => { + const from = fromRef.current; + startRef.current = null; + const tick = (ts: number) => { + if (startRef.current === null) startRef.current = ts; + const progress = Math.min((ts - startRef.current) / duration, 1); + const ease = 1 - Math.pow(1 - progress, 3); + setCurrent(Math.round(from + (target - from) * ease)); + if (progress < 1) rafRef.current = requestAnimationFrame(tick); + else fromRef.current = target; + }; + rafRef.current = requestAnimationFrame(tick); + return () => cancelAnimationFrame(rafRef.current); + }, [target, duration]); + + return current; +} + +function useAnimatedFloat(target: number, duration = 900, decimals = 1): string { + const [current, setCurrent] = useState(0); + const startRef = useRef(null); + const fromRef = useRef(0); + const rafRef = useRef(0); + + useEffect(() => { + const from = fromRef.current; + startRef.current = null; + const tick = (ts: number) => { + if (startRef.current === null) startRef.current = ts; + const progress = Math.min((ts - startRef.current) / duration, 1); + const ease = 1 - Math.pow(1 - progress, 3); + setCurrent(from + (target - from) * ease); + if (progress < 1) rafRef.current = requestAnimationFrame(tick); + else fromRef.current = target; + }; + rafRef.current = requestAnimationFrame(tick); + return () => cancelAnimationFrame(rafRef.current); + }, [target, duration]); + + return current.toFixed(decimals); +} + +// ── Metric card ──────────────────────────────────────────────────────────────── +function MetricCard({ + label, displayValue, sub, subIcon, subColor, subBg, valueColor, delay, isAlert, +}: { + label: string; + displayValue?: string; + sub: string; + subIcon?: string; + subColor?: string; + subBg?: string; + valueColor: string; + delay: number; + isAlert?: boolean; +}) { + return ( + + {isAlert && ( + + )} +
+ {label} +
+
+ {displayValue} +
+ {/* Styled sub badge */} + + {subIcon && ( + {subIcon} + )} + + {sub} + + +
+ ); +} + +// ── Spike alert banner ───────────────────────────────────────────────────────── +function SpikeAlert({ batchToxicRate, batchCount }: { batchToxicRate: number; batchCount: number }) { + const [dismissed, setDismissed] = useState(false); + useEffect(() => { setDismissed(false); }, [Math.floor(batchToxicRate)]); + if (batchToxicRate < 38 || batchCount === 0 || dismissed) return null; + + const severity = batchToxicRate >= 70 ? "critical" : batchToxicRate >= 55 ? "high" : "elevated"; + const severityColor = severity === "critical" ? "#ff4b4b" : severity === "high" ? "#ff6b3d" : "#ff9f43"; + const severityBg = severity === "critical" ? "rgba(255,75,75,0.08)" : severity === "high" ? "rgba(255,107,61,0.08)" : "rgba(255,159,67,0.08)"; + const severityBorder = severity === "critical" ? "rgba(255,75,75,0.35)" : severity === "high" ? "rgba(255,107,61,0.3)" : "rgba(255,159,67,0.28)"; + + return ( + + + {/* Animated scan line */} + + {/* Pulsing dot */} + +
+ + ⚠ Toxicity Spike Detected + + + {batchToxicRate.toFixed(1)}% of last {batchCount} posts flagged as toxic + +
+
+ +
+ + {severity.toUpperCase()} + +
+
+ +
+
+ ); +} + +// ── Custom SVG area chart (no Recharts) ──────────────────────────────────────── +function CustomAreaChart({ data }: { data: { hour: string; value: number | null }[] }) { + const containerRef = useRef(null); + const [containerWidth, setContainerWidth] = useState(400); + + useEffect(() => { + if (!containerRef.current) return; + const obs = new ResizeObserver(entries => { + for (const e of entries) setContainerWidth(e.contentRect.width); + }); + obs.observe(containerRef.current); + setContainerWidth(containerRef.current.clientWidth); + return () => obs.disconnect(); + }, []); + + const W = containerWidth; + const H = 170; + const PAD = { top: 12, right: 12, bottom: 28, left: 36 }; + const inner = { w: W - PAD.left - PAD.right, h: H - PAD.top - PAD.bottom }; + + // Fill null with 0 so the line is always continuous + const filled = data.map(d => ({ ...d, value: d.value ?? 0 })); + + const vals = filled.map(d => d.value) as number[]; + const minV = 0; + const maxV = Math.max(...vals, 0.01); + const rangeV = maxV - minV || 0.001; + + const [tooltip, setTooltip] = useState<{ x: number; y: number; hour: string; val: number } | null>(null); + + const toX = (i: number) => PAD.left + (i / (filled.length - 1)) * inner.w; + const toY = (v: number) => PAD.top + inner.h - ((v - minV) / rangeV) * inner.h; + + const linePath = filled.map((d, i) => { + const x = toX(i); const y = toY(d.value); + return `${i === 0 ? "M" : "L"} ${x},${y}`; + }).join(" "); + + const areaPath = `${linePath} L ${toX(filled.length - 1)},${PAD.top + inner.h} L ${toX(0)},${PAD.top + inner.h} Z`; + + const yTicks = [0, 0.25, 0.5, 0.75, 1.0]; + const xLabels = filled.filter((_, i) => i % 4 === 0); + + return ( +
+ setTooltip(null)} + > + + + + + + + + + + + + {/* Grid lines */} + {yTicks.map(t => { + const y = PAD.top + inner.h - (t / 1) * inner.h; + return ( + + + {t.toFixed(1)} + + ); + })} + + {/* X labels */} + {xLabels.map(d => { + const i = filled.indexOf(d); + return ( + {d.hour} + ); + })} + + {/* Area fill */} + + + {/* Line */} + + + {/* Interactive dots — only at hours with real data */} + {data.map((d, i) => { + if (d.value === null || d.value === 0) return null; + const x = toX(i); const y = toY(d.value); + const c = d.value >= 0.7 ? "#ff4b4b" : d.value >= 0.4 ? "#ff8c42" : "#2ecc71"; + return ( + setTooltip({ x, y, hour: d.hour, val: d.value! })} + /> + ); + })} + + {/* Tooltip */} + {tooltip && ( + + + + {tooltip.hour} + {tooltip.val.toFixed(3)} + + )} + +
+ ); +} + +// ── Cool Score Distribution Chart ───────────────────────────────────────────── +function CustomScoreBars({ data }: { data: { label: string; count: number; color: string }[] }) { + const maxCount = Math.max(...data.map(d => d.count), 1); + const [hovered, setHovered] = useState(null); + const containerRef = useRef(null); + + const GRADIENTS: [string, string][] = [ + ["#4ade80", "#22c55e"], + ["#a3e635", "#84cc16"], + ["#facc15", "#eab308"], + ["#fb923c", "#f97316"], + ["#f87171", "#ef4444"], + ]; + + return ( +
+ {/* Y grid lines */} +
+ {/* Grid lines overlay */} + {[0.25, 0.5, 0.75, 1].map((t, gi) => ( +
+ ))} + + {data.map((d, i) => { + const heightPct = (d.count / maxCount) * 100; + const isHov = hovered === i; + const [colorTop, colorBot] = GRADIENTS[i]; + return ( +
setHovered(i)} + onMouseLeave={() => setHovered(null)} + > + {/* Tooltip */} + {isHov && ( + + {d.count} posts + + )} + + {/* Bar */} + 0 ? 4 : 0, + opacity: hovered === null || isHov ? 1 : 0.4, + transition: "opacity 0.2s", + boxShadow: isHov ? `0 0 16px ${colorTop}80, 0 0 6px ${colorTop}40` : "none", + position: "relative", + }} + > + {/* Shine on top */} +
+ +
+ ); + })} +
+ + {/* Baseline */} +
+ + {/* X labels */} +
+ {data.map((d, i) => ( +
+ {d.label} +
+ ))} +
+
+ ); +} + +const DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; + +export function OverviewTab({ posts, batchPosts, selectedTerms, justFetched, totalAnalyzed }: OverviewTabProps) { + const totalPosts = posts.length; + const toxicCount = posts.filter(p => p.label === "toxic").length; + const toxicRate = totalPosts ? (toxicCount / totalPosts) * 100 : 0; + + // Batch-specific spike detection + const batchToxicCount = batchPosts.filter(p => p.label === "toxic").length; + const batchToxicRate = batchPosts.length ? (batchToxicCount / batchPosts.length) * 100 : 0; + + const avgScoreRaw = batchPosts.length + ? batchPosts.reduce((s, p) => s + p.score, 0) / batchPosts.length + : 0; + + const termCounts: Record = {}; + for (const p of batchPosts) { + termCounts[p.query_term] = (termCounts[p.query_term] || 0) + 1; + } + const topTerm = Object.keys(termCounts).sort((a, b) => termCounts[b] - termCounts[a])[0] || "—"; + + // WHY totalAnalyzed directly (no || totalPosts fallback): + // The old fallback `totalAnalyzed || totalPosts` caused the counter to show + // totalPosts (200) on load, then jump DOWN to totalAnalyzed (25) after the + // first fetch. Now App.tsx seeds totalAnalyzed from the DB count on mount, + // so the fallback is no longer needed and only caused confusion. + const animatedTotal = useAnimatedNumber(totalAnalyzed); + const animatedToxicRate = useAnimatedFloat(toxicRate, 900, 1); + const animatedAvg = useAnimatedFloat(avgScoreRaw, 900, 3); + + const timeData = useMemo(() => { + const buckets: Record = {}; + for (const p of posts) { + const h = new Date(p.created_at).getUTCHours(); + if (!buckets[h]) buckets[h] = { sum: 0, count: 0 }; + buckets[h].sum += p.score; + buckets[h].count += 1; + } + return Array.from({ length: 24 }, (_, h) => ({ + hour: `${String(h).padStart(2, "0")}:00`, + value: buckets[h] ? +(buckets[h].sum / buckets[h].count).toFixed(3) : null, + })); + }, [posts]); + + const scoreDistData = useMemo(() => { + const bins = [0, 0, 0, 0, 0]; + for (const p of batchPosts) { + const idx = Math.min(4, Math.floor(p.score / 0.2)); + bins[idx]++; + } + const COLORS = ["#2ecc71", "#a3e635", "#ffeb3b", "#ff8c42", "#ff4b4b"]; + const LABELS = ["0-0.2", "0.2-0.4", "0.4-0.6", "0.6-0.8", "0.8-1.0"]; + return LABELS.map((label, i) => ({ label, count: bins[i], color: COLORS[i] })); + }, [batchPosts]); + + const heatmapData = useMemo(() => { + const grid: Record = {}; + for (const p of posts) { + const d = new Date(p.created_at); + const day = (d.getDay() + 6) % 7; + const hour = d.getHours(); + const key = `${day}-${hour}`; + if (!grid[key]) grid[key] = { sum: 0, count: 0 }; + grid[key].sum += p.score; + grid[key].count += 1; + } + return grid; + }, [posts]); + + const maxHeat = useMemo(() => { + let mx = 0; + for (const v of Object.values(heatmapData)) { + mx = Math.max(mx, v.count ? v.sum / v.count : 0); + } + return mx || 1; + }, [heatmapData]); + + const recentPosts = batchPosts.slice(0, 20); + + return ( +
+ + + + {/* Metric cards */} +
+ + = 38 ? "↑ High — monitor closely" : "✓ Within expected range"} + subIcon={toxicRate >= 38 ? "🔴" : undefined} + subColor={toxicRate >= 38 ? "#ff4b4b" : "#2ecc71"} + subBg={toxicRate >= 38 ? "rgba(255,75,75,0.1)" : "rgba(46,204,113,0.08)"} + valueColor="#ff4b4b" + delay={0.07} + isAlert={toxicRate >= 38} + /> + + +
+ + {/* Charts row */} +
+ +
Toxicity over time
+ +
+ + +
Score distribution
+ +
+
+ + {/* Activity heatmap */} + +
Activity heatmap
+
+
+
+ {Array.from({ length: 24 }, (_, h) => ( +
+ {h % 6 === 0 ? h : ""} +
+ ))} +
+ {DAYS.map((day, d) => ( +
+
{day}
+ {Array.from({ length: 24 }, (_, h) => { + const cell = heatmapData[`${d}-${h}`]; + const intensity = cell ? (cell.sum / cell.count) / maxHeat : 0; + const hasData = cell && cell.count > 0; + const r = hasData ? Math.round(30 + (255 - 30) * intensity) : 26; + const g = hasData ? Math.round(37 + (75 - 37) * intensity) : 29; + const b = hasData ? Math.round(64 + (75 - 64) * intensity) : 46; + return ( + + ); + })} +
+ ))} +
+ Low +
+ High toxicity +
+
+ + + {/* Recent posts */} + +
Recent posts (last batch)
+ {recentPosts.length === 0 ? ( +
Click "Fetch & Analyze" to see recent posts.
+ ) : ( +
+ {recentPosts.map((p, i) => { + const sc = p.score >= 0.7 ? "#ff4b4b" : p.score >= 0.4 ? "#ff9f43" : "#2ecc71"; + const scBg = p.score >= 0.7 ? "rgba(255,75,75,0.12)" : p.score >= 0.4 ? "rgba(255,159,67,0.12)" : "rgba(46,204,113,0.12)"; + return ( + +
+ {p.score.toFixed(3)} +
+
{p.text.slice(0, 110)}
+
+ {p.query_term} +
+
+ ); + })} +
+ )} +
+
+ ); +} diff --git a/frontend/src/app/components/Sidebar.tsx b/frontend/src/app/components/Sidebar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c7c2c4a7002b0a262a7c557e47c909495cae6d27 --- /dev/null +++ b/frontend/src/app/components/Sidebar.tsx @@ -0,0 +1,335 @@ +import { useState } from "react"; +import { motion, AnimatePresence } from "motion/react"; +import { X } from "lucide-react"; +import { ALGOSPEAK_TERMS, Post, nodeColor } from "./mockData"; + +interface SidebarProps { + open: boolean; + selectedTerms: string[]; + setSelectedTerms: (terms: string[]) => void; + threshold: number; + setThreshold: (v: number) => void; + sampling: number; + setSampling: (v: number) => void; + autoRefresh: boolean; + setAutoRefresh: (v: boolean) => void; + onFetch: () => void; + posts: Post[]; + fetching: boolean; +} + +function SectionLabel({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +export function Sidebar({ + open, + selectedTerms, + setSelectedTerms, + threshold, + setThreshold, + sampling, + setSampling, + autoRefresh, + setAutoRefresh, + onFetch, + posts, + fetching, +}: SidebarProps) { + const [customInput, setCustomInput] = useState(""); + const [customTerms, setCustomTerms] = useState([]); + + const addCustom = () => { + const t = customInput.trim().toLowerCase(); + if (!t) return; + if (!customTerms.includes(t)) setCustomTerms(prev => [...prev, t]); + if (!selectedTerms.includes(t)) setSelectedTerms([...selectedTerms, t]); + setCustomInput(""); + }; + + // Compute toxic ratio per selected term from posts + const termRatio: Record = {}; + for (const term of selectedTerms) { + const matching = posts.filter(p => p.query_term === term || p.text.toLowerCase().includes(term)); + if (matching.length === 0) { termRatio[term] = Math.random() * 0.8; continue; } + termRatio[term] = matching.filter(p => p.label === "toxic").length / matching.length; + } + + return ( + + {open && ( + + {/* Tracked terms */} + Tracked terms +
+ {selectedTerms.slice(0, 6).map(term => { + const pct = Math.round((termRatio[term] ?? 0) * 100); + const color = nodeColor(termRatio[term] ?? 0); + return ( + +
+
+ {term} +
+ {pct}% + + ); + })} +
+ + {/* Algospeak terms multiselect */} + Algospeak terms +
+ {[...ALGOSPEAK_TERMS, ...customTerms].map(t => { + const selected = selectedTerms.includes(t); + return ( + + ); + })} +
+ + {/* Custom term input */} + Add custom term +
+ setCustomInput(e.target.value)} + onKeyDown={e => e.key === "Enter" && addCustom()} + placeholder="Type a term..." + style={{ + flex: 1, + background: "#141826", + border: "1px solid #1e2540", + borderRadius: 7, + padding: "5px 9px", + color: "#e8eaf0", + fontSize: "0.78rem", + outline: "none", + }} + /> +
+ + + {/* Threshold slider */} + Threshold +
+
+ + {threshold.toFixed(2)} + +
+ setThreshold(parseFloat(e.target.value))} + style={{ width: "100%", accentColor: "#ff4b4b" }} + /> +
+ + {/* Sampling slider */} + Sampling +
+
+ {sampling} +
+ setSampling(parseInt(e.target.value))} + style={{ width: "100%", accentColor: "#ff4b4b" }} + /> +
+ + {/* Fetch section */} + Fetch + + + + {fetching ? "Fetching…" : "Fetch & Analyze"} + + + {/* Custom terms pills at bottom */} + {customTerms.length > 0 && ( +
+ {customTerms.map(t => ( + + {t} + { + setCustomTerms(prev => prev.filter(x => x !== t)); + setSelectedTerms(selectedTerms.filter(x => x !== t)); + }} + /> + + ))} +
+ )} + + )} + + ); +} \ No newline at end of file diff --git a/frontend/src/app/components/SplashScreen.tsx b/frontend/src/app/components/SplashScreen.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4ebf5d6ce57149801a59751e39d865d54d52a186 --- /dev/null +++ b/frontend/src/app/components/SplashScreen.tsx @@ -0,0 +1,279 @@ +import { useEffect, useState, useRef } from "react"; +import { motion, AnimatePresence } from "motion/react"; + +interface SplashScreenProps { + onDone: () => void; +} + +const TITLE = "ALGOSCOPE"; +const SUBTITLE = "Real-time algospeak & toxicity intelligence on Bluesky"; + +// Animated background nodes for the splash +const BG_NODES = Array.from({ length: 14 }, (_, i) => ({ + id: i, + x: 8 + (i * 13.5) % 92, + y: 10 + (i * 17) % 80, + r: 3 + (i % 4) * 1.8, + color: i % 3 === 0 ? "#ff4b4b" : i % 3 === 1 ? "#ff8c42" : "#a6b0ff", + delay: i * 0.07, +})); + +const BG_EDGES = [ + [0, 3], [3, 7], [7, 11], [1, 5], [5, 9], [9, 12], + [2, 6], [6, 10], [0, 4], [4, 8], [8, 13], [2, 7], + [1, 6], [3, 10], [5, 12], [4, 11], +]; + +export function SplashScreen({ onDone }: SplashScreenProps) { + const [lettersDone, setLettersDone] = useState(0); + const [subtitleVisible, setSubtitleVisible] = useState(false); + const [progress, setProgress] = useState(0); + const [exiting, setExiting] = useState(false); + const timerRef = useRef>(null); + + useEffect(() => { + // Letter-by-letter reveal + let i = 0; + const letterInterval = setInterval(() => { + i++; + setLettersDone(i); + if (i >= TITLE.length) clearInterval(letterInterval); + }, 80); + + // Show subtitle after title is done + timerRef.current = setTimeout(() => setSubtitleVisible(true), 900); + + // Progress bar + let prog = 0; + const progInterval = setInterval(() => { + prog += 1.6; + setProgress(Math.min(100, prog)); + if (prog >= 100) clearInterval(progInterval); + }, 28); + + // Exit + const exitTimer = setTimeout(() => { + setExiting(true); + setTimeout(onDone, 700); + }, 2600); + + return () => { + clearInterval(letterInterval); + clearInterval(progInterval); + if (timerRef.current) clearTimeout(timerRef.current); + clearTimeout(exitTimer); + }; + }, [onDone]); + + return ( + + {!exiting && ( + + {/* Animated background graph */} + + {BG_EDGES.map(([a, b], i) => { + const nodeA = BG_NODES[a]; + const nodeB = BG_NODES[b]; + return ( + + ); + })} + {BG_NODES.map(node => ( + + ))} + + + {/* Radial glow */} +
+ + {/* Logo icon */} + + A + + + {/* Title: letter by letter */} +
+ {TITLE.split("").map((char, i) => ( + + {char} + + ))} +
+ + {/* Subtitle */} + + {subtitleVisible && ( + + {SUBTITLE} + + )} + + + {/* Progress bar */} +
+ +
+ + {/* Loading label */} + + {progress < 100 ? "Loading intelligence engine…" : "Ready"} + + + {/* Version badge */} + + by Odeliya Charitonova · TAU CS + + + )} + + ); +} diff --git a/frontend/src/app/components/TermComparisonTab.tsx b/frontend/src/app/components/TermComparisonTab.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ef81d2cd74530e1874d0c71de2780d8181e426e9 --- /dev/null +++ b/frontend/src/app/components/TermComparisonTab.tsx @@ -0,0 +1,364 @@ +import { useState } from "react"; +import { motion } from "motion/react"; +import { Post, ALGOSPEAK_TERMS, nodeColor } from "./mockData"; + +interface Props { + posts: Post[]; +} + +function termStats(posts: Post[], term: string) { + const matched = posts.filter(p => + p.query_term === term || p.text.toLowerCase().includes(term) + ); + if (!matched.length) return null; + const scores = matched.map(p => p.score); + const toxicN = matched.filter(p => p.label === "toxic").length; + return { + count: matched.length, + toxicRate: (toxicN / matched.length) * 100, + avgScore: scores.reduce((a, b) => a + b, 0) / scores.length, + maxScore: Math.max(...scores), + posts: matched, + }; +} + +const BIN_LABELS = ["0-0.2", "0.2-0.4", "0.4-0.6", "0.6-0.8", "0.8-1.0"]; + +function binPosts(posts: Post[]) { + const bins = [0, 0, 0, 0, 0]; + for (const p of posts) { + const idx = Math.min(4, Math.floor(p.score / 0.2)); + bins[idx]++; + } + return bins; +} + +export function TermComparisonTab({ posts }: Props) { + const [termA, setTermA] = useState(ALGOSPEAK_TERMS[0]); + const [termB, setTermB] = useState(ALGOSPEAK_TERMS[1]); + const [hovered, setHovered] = useState<{ binIdx: number; series: "a" | "b" } | null>(null); + + const sA = termStats(posts, termA); + const sB = termStats(posts, termB); + + const selectStyle: React.CSSProperties = { + background: "#141826", + border: "1px solid #1e2540", + borderRadius: 8, + padding: "6px 10px", + color: "#e8eaf0", + fontSize: "0.83rem", + width: "100%", + cursor: "pointer", + }; + + const compData = BIN_LABELS.map((label, i) => ({ + label, + a: sA ? binPosts(sA.posts)[i] : 0, + b: sB ? binPosts(sB.posts)[i] : 0, + })); + + return ( +
+ + Select two algospeak terms to compare their toxicity profiles from all stored posts. + + + {/* Term selectors */} +
+ +
Term A
+ +
+ +
Term B
+ +
+
+ + {termA === termB && ( +
+ Select two different terms to compare. +
+ )} + + {termA !== termB && ( + <> + {/* Stat cards */} +
+ {[ + { term: termA, stats: sA, color: "#a6b0ff" }, + { term: termB, stats: sB, color: "#ff8c42" }, + ].map(({ term, stats, color }, idx) => ( + +
+ “{term}” +
+ {!stats ? ( +
No data — fetch more posts first.
+ ) : ( +
+ {[ + { label: "Posts", value: String(stats.count), valueColor: color }, + { label: "Toxic rate", value: `${stats.toxicRate.toFixed(1)}%`, valueColor: nodeColor(stats.toxicRate / 100) }, + { label: "Avg score", value: stats.avgScore.toFixed(3), valueColor: nodeColor(stats.avgScore) }, + { label: "Max score", value: stats.maxScore.toFixed(3), valueColor: "#ff4b4b" }, + ].map(({ label, value, valueColor }) => ( +
+
+ {label} +
+
+ {value} +
+
+ ))} +
+ )} +
+ ))} +
+ + {/* Comparison chart — custom div-based with enhanced visuals */} + {sA && sB && (() => { + const binsA = binPosts(sA.posts); + const binsB = binPosts(sB.posts); + const maxVal = Math.max(...binsA, ...binsB, 1); + const gridLines = [0.25, 0.5, 0.75, 1.0]; + return ( + +
+
+ Score distribution comparison +
+ {/* Legend */} +
+ {[{ label: termA, color: "#a6b0ff", glow: "rgba(166,176,255,0.4)" }, { label: termB, color: "#ff8c42", glow: "rgba(255,140,66,0.4)" }].map(({ label, color, glow }) => ( +
+
+ {label} +
+ ))} +
+
+ + {/* Chart wrapper with y-axis */} +
+ {/* Y-axis labels */} +
+ {[maxVal, Math.round(maxVal * 0.75), Math.round(maxVal * 0.5), Math.round(maxVal * 0.25), 0].map((v, i) => ( +
{v}
+ ))} +
+ + {/* Chart area */} +
+ {/* Grid lines */} +
+ {gridLines.map((_, i) => ( +
+ ))} +
+
+ + {/* Bars */} +
+ {BIN_LABELS.map((label, i) => ( +
+
+ {/* Bar A */} +
setHovered({ binIdx: i, series: "a" })} + onMouseLeave={() => setHovered(null)} + > + 0 ? 3 : 0, + position: "relative", + boxShadow: hovered?.binIdx === i && hovered?.series === "a" + ? "0 0 12px rgba(166,176,255,0.6)" + : "none", + transition: "box-shadow 0.15s, opacity 0.15s", + }} + > + {hovered?.binIdx === i && hovered?.series === "a" && ( +
+ {termA}: {binsA[i]} +
+ )} +
+
+ {/* Bar B */} +
setHovered({ binIdx: i, series: "b" })} + onMouseLeave={() => setHovered(null)} + > + 0 ? 3 : 0, + position: "relative", + boxShadow: hovered?.binIdx === i && hovered?.series === "b" + ? "0 0 12px rgba(255,140,66,0.6)" + : "none", + transition: "box-shadow 0.15s, opacity 0.15s", + }} + > + {hovered?.binIdx === i && hovered?.series === "b" && ( +
+ {termB}: {binsB[i]} +
+ )} +
+
+
+
+ ))} +
+ + {/* Baseline */} +
+ + {/* X-axis labels */} +
+ {BIN_LABELS.map((label, i) => ( +
{label}
+ ))} +
+
+
+ + {/* X-axis title */} +
+ Toxicity score range +
+ + ); + })()} + + {/* Per-term bar comparisons */} + {sA && sB && ( + +
+ Key metrics at a glance +
+ {[ + { label: "Toxic rate (%)", a: sA.toxicRate, b: sB.toxicRate, max: 100, colorA: "#a6b0ff", colorB: "#ff8c42" }, + { label: "Avg score", a: sA.avgScore * 100, b: sB.avgScore * 100, max: 100, colorA: "#a6b0ff", colorB: "#ff8c42" }, + ].map(({ label, a, b, max, colorA, colorB }) => ( +
+
+ {label} + + {termA}: {a.toFixed(1)} + {" vs "} + {termB}: {b.toFixed(1)} + +
+
+ {[{ pct: a / max * 100, color: colorA }, { pct: b / max * 100, color: colorB }].map((bar, bi) => ( +
+ +
+ ))} +
+
+ ))} +
+ )} + + )} +
+ ); +} \ No newline at end of file diff --git a/frontend/src/app/components/figma/ImageWithFallback.tsx b/frontend/src/app/components/figma/ImageWithFallback.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0e26139bea20f0f26f899190b21161e44510e740 --- /dev/null +++ b/frontend/src/app/components/figma/ImageWithFallback.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react' + +const ERROR_IMG_SRC = + 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg==' + +export function ImageWithFallback(props: React.ImgHTMLAttributes) { + const [didError, setDidError] = useState(false) + + const handleError = () => { + setDidError(true) + } + + const { src, alt, style, className, ...rest } = props + + return didError ? ( +
+
+ Error loading image +
+
+ ) : ( + {alt} + ) +} diff --git a/frontend/src/app/components/mockData.ts b/frontend/src/app/components/mockData.ts new file mode 100644 index 0000000000000000000000000000000000000000..eb6f2dfd25551c27d5dc093935b8e55495909796 --- /dev/null +++ b/frontend/src/app/components/mockData.ts @@ -0,0 +1,198 @@ +// ── API base URL ─────────────────────────────────────────────────────────────── +// WHY import.meta.env: Vite reads environment variables at build time. +// VITE_API_URL is empty string in production (same origin as FastAPI), +// and http://localhost:8000 in development. +// This means the same compiled artifact works in both environments without +// any code change — just a different .env file. +const API_BASE = import.meta.env.VITE_API_URL ?? ""; + +// ── Shared types ─────────────────────────────────────────────────────────────── +export const ALGOSPEAK_TERMS = [ + "unalive", "le dollar bean", "seggs", "cute", "based", + "cornhole", "nsfw", "eggplant", "spicy", "ratio", + "touch grass", "down bad", "pretty", "why", "mean", + "better", "someone", "having", "harder", "top", +]; + +export interface Post { + id: number | string; // DB returns number; mock generator uses string + text: string; + score: number; + label: "toxic" | "non-toxic"; + query_term: string; + created_at: string; +} + +export interface GraphNode { + id: string; + frequency: number; // maps to "count" from the API + toxicRatio: number; // maps to "toxic_ratio" from the API +} + +export interface GraphEdge { + source: string; + target: string; + weight: number; +} + +// ── Real API client functions ────────────────────────────────────────────────── + +/** + * Fetch recent classified posts from the database. + * Called on mount to populate initial dashboard state. + */ +export async function apiFetchPosts(limit = 100): Promise { + const res = await fetch(`${API_BASE}/posts?limit=${limit}`); + if (!res.ok) throw new Error(`/posts failed: ${res.status}`); + const data = await res.json(); + // Normalize: API returns id as number, mock uses string — cast to keep + // downstream components happy with either. + return (data.posts as Post[]).map(p => ({ + ...p, + id: p.id ?? 0, + query_term: p.query_term ?? "", + created_at: p.created_at ?? "", + label: p.label === "toxic" ? "toxic" : "non-toxic", + })); +} + +/** + * Fetch just the total post count from the DB without pulling all rows. + * Used to keep the "Posts analyzed" counter in sync with the real DB after + * every fetch — avoids stale-closure bugs in useCallback. + */ +export async function apiFetchTotal(): Promise { + const res = await fetch(`${API_BASE}/posts?limit=1`); + if (!res.ok) return -1; + const data = await res.json(); + return typeof data.total === "number" ? data.total : -1; +} + +/** + * Trigger a Bluesky fetch + batch inference cycle. + * Returns the new posts plus timing metadata for the UI. + */ +export async function apiFetchAndAnalyze( + queries: string[], + limit: number, + threshold: number, +): Promise<{ posts: Post[]; fetchTime: number; inferTime: number; count: number; message?: string }> { + const res = await fetch(`${API_BASE}/fetch-and-analyze`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ queries, limit, threshold }), + }); + if (!res.ok) throw new Error(`/fetch-and-analyze failed: ${res.status}`); + const data = await res.json(); + const posts = (data.posts as Post[]).map(p => ({ + ...p, + id: p.id ?? 0, + query_term: p.query_term ?? "", + created_at: p.created_at ?? "", + label: p.label === "toxic" ? "toxic" : "non-toxic", + })); + return { + posts, + fetchTime: data.fetch_time, + inferTime: data.infer_time, + count: data.count, + message: data.message, + }; +} + +/** + * Fetch co-occurrence graph data as nodes + edges JSON. + * The React canvas simulation handles layout — the server only provides structure. + */ +export async function apiGetGraphData( + minCooccurrence: number, + toxicOnly: boolean, +): Promise<{ nodes: GraphNode[]; edges: GraphEdge[] }> { + const res = await fetch( + `${API_BASE}/graph-data?min_cooccurrence=${minCooccurrence}&toxic_only=${toxicOnly}` + ); + if (!res.ok) throw new Error(`/graph-data failed: ${res.status}`); + const data = await res.json(); + // Map API field names (count, toxic_ratio) to the names the graph + // simulation already uses (frequency, toxicRatio). + const nodes: GraphNode[] = (data.nodes as Array<{ + id: string; count: number; toxic_ratio: number; + }>).map(n => ({ + id: n.id, + frequency: n.count, + toxicRatio: n.toxic_ratio, + })); + return { nodes, edges: data.edges as GraphEdge[] }; +} + +// ── Mock data (kept for local dev / offline fallback) ───────────────────────── +// These are only used if the API is unreachable. They are never sent to the +// backend — no secrets here. + +const MOCK_TEMPLATES = [ + "I can't believe they used {term} like that, this is getting out of hand", + "Found another account using {term} to avoid filters, reported it immediately", + "The {term} discourse is absolutely wild today, why are people like this", + "Seeing {term} trending again. Not a great sign for the platform tbh", + "Whoever invented {term} to avoid filters is frankly unhinged", + "Just saw {term} used unironically in a serious post, internet is something", + "Moderators need to update their filters, {term} is literally everywhere now", + "The {term} to english translation is honestly concerning for society", + "Why does {term} keep showing up in my feed, algorithm is broken again", + "Teaching my mom what {term} means was the hardest conversation I've had this year", + "{term} energy today, not gonna lie tbh", + "Petition to ban {term} from the platform. Who's with me", + "Using {term} unironically is such a major red flag ngl", + "The way {term} has evolved over time is fascinating from a linguistics POV", + "Just another day of seeing {term} everywhere on here smh", + "Why is {term} suddenly all over my fyp, is this a new trend", + "Blocked 3 accounts today for using {term} in my replies", + "{term} popped up again in the discourse, same old same old", + "Can we talk about how normalized {term} has become? Not ok.", + "Researchers studying {term} usage patterns should check bluesky tbh", +]; + +function randomScore(): number { + const r = Math.random(); + if (r < 0.55) return 0.62 + Math.random() * 0.38; + if (r < 0.75) return 0.40 + Math.random() * 0.30; + return Math.random() * 0.38; +} + +export function generateMockPosts(terms: string[], count = 180): Post[] { + const posts: Post[] = []; + const now = Date.now(); + const dayMs = 24 * 60 * 60 * 1000; + + for (let i = 0; i < count; i++) { + const term = terms[Math.floor(Math.random() * terms.length)]; + const tpl = MOCK_TEMPLATES[Math.floor(Math.random() * MOCK_TEMPLATES.length)]; + const text = tpl.replace("{term}", term); + const score = randomScore(); + const created_at = new Date(now - Math.random() * dayMs).toISOString(); + posts.push({ + id: `mock-${i}`, + text, + score: Math.round(score * 1000) / 1000, + label: score >= 0.7 ? "toxic" : "non-toxic", + query_term: term, + created_at, + }); + } + return posts.sort( + (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + ); +} + +// ── Graph colour helpers (used by CoOccurrenceGraph.tsx) ────────────────────── +export function nodeColor(toxicRatio: number): string { + if (toxicRatio >= 0.7) return "#ff4b4b"; + if (toxicRatio >= 0.4) return "#ff8c42"; + return "#2ecc71"; +} + +export function nodeColorAlpha(toxicRatio: number, alpha = 0.25): string { + if (toxicRatio >= 0.7) return `rgba(255,75,75,${alpha})`; + if (toxicRatio >= 0.4) return `rgba(255,140,66,${alpha})`; + return `rgba(46,204,113,${alpha})`; +} diff --git a/frontend/src/app/components/ui/accordion.tsx b/frontend/src/app/components/ui/accordion.tsx new file mode 100644 index 0000000000000000000000000000000000000000..bd6b1e3351323c2ccca3460dee3fd37b1101eba2 --- /dev/null +++ b/frontend/src/app/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client"; + +import * as React from "react"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDownIcon } from "lucide-react"; + +import { cn } from "./utils"; + +function Accordion({ + ...props +}: React.ComponentProps) { + return ; +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + + ); +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ); +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/frontend/src/app/components/ui/alert-dialog.tsx b/frontend/src/app/components/ui/alert-dialog.tsx new file mode 100644 index 0000000000000000000000000000000000000000..875b8df4de0231047736dcb04c5d81287d88ae42 --- /dev/null +++ b/frontend/src/app/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client"; + +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "./utils"; +import { buttonVariants } from "./button"; + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ); +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/frontend/src/app/components/ui/alert.tsx b/frontend/src/app/components/ui/alert.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9c35976c714d89ae0497f599d3a1fd707dd75fd8 --- /dev/null +++ b/frontend/src/app/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "./utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-card text-card-foreground", + destructive: + "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ); +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { Alert, AlertTitle, AlertDescription }; diff --git a/frontend/src/app/components/ui/aspect-ratio.tsx b/frontend/src/app/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c16d6bcb917ec490aa9c468afcbd5c829086b080 --- /dev/null +++ b/frontend/src/app/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client"; + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return ; +} + +export { AspectRatio }; diff --git a/frontend/src/app/components/ui/avatar.tsx b/frontend/src/app/components/ui/avatar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c9904518548717b6bbfe9bdf1857e483bbe35d78 --- /dev/null +++ b/frontend/src/app/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client"; + +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "./utils"; + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/frontend/src/app/components/ui/badge.tsx b/frontend/src/app/components/ui/badge.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2ccc2c444051b79c7e9fe5ef2e07f7eebd09c8d3 --- /dev/null +++ b/frontend/src/app/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "./utils"; + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/frontend/src/app/components/ui/breadcrumb.tsx b/frontend/src/app/components/ui/breadcrumb.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8f84d7e9fe4dcfdec34819e5af802a80d44e3fdf --- /dev/null +++ b/frontend/src/app/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; + +import { cn } from "./utils"; + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return