diff --git a/web/.gitignore b/web/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/web/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/web/README.md b/web/README.md
new file mode 100644
index 0000000..c9ba6ba
--- /dev/null
+++ b/web/README.md
@@ -0,0 +1,331 @@
+each commit in the `main` on this repo triggers a benchmark execution. each benchmark execution runs few `${agent}:${provider?}/${model}` combinations.
+
+the current combinations:
+```
+opencode:opencode/gpt-5-codex
+opencode:opencode/claude-sonnet-4-5
+opencode:opencode/big-pickle
+opencode:opencode/glm-4.6
+claude-code:claude-sonnet-4-5
+codex:gpt-5-codex
+```
+
+the list is dynamic and new combinations can be added to it.
+
+each of the combinations is measured across a handful of evals, currently 3, short-term goal is 25 and the long-term goal is 100 afaik. each eval consists of a mainstream github repo, a `from` commit that the agent starts with, a `to` commit that it should replicate but it does not know about, and few scores that measure how well did the agent replicate `to` commit.
+
+for example, the `api-signature` measures the combination's performmance in replicating the same architectural patterns that is apparent across the eval and specifically across the `to` commit diff. each score has 3 judges (claude sonnet, gpt-5-codex and kimi k2) that each rate by the binary decision 0 (FAIL) or 1 (PASS) and by aggregating all rates together, we produce a number fluid between 0 to 1. each judge produces a rationale as well. we also have an overall summary of an analysis over the rationales.
+each score has its own description that should be shown to the user seeing it.
+
+the `weight` captures the score's importance in the final score. the list of scores is dynamic as well. each eval might have a different list of scores compared to another eval.
+
+```yaml
+- repo: DataDog/datadog-lambda-python
+ from: 93d4a07fa61a4d4d2feec08e722505a9e0cc8657
+ to: d7763789f262b2da228f8210509e302e6e510d0a
+ prompts: prompts/datadog-lambda-python.yaml
+ issues: []
+ scores:
+ api-signature:
+ weight: 0.2
+ logic-equivalence:
+ weight: 0.3
+ integration-points:
+ weight: 0.2
+ test-coverage:
+ weight: 0.2
+ checks:
+ weight: 0.1
+ args:
+ setup:
+ - python3 -m venv .venv
+ - ./.venv/bin/python -m pip install ".[dev]"
+ commands:
+ - ./.venv/bin/pytest -vv
+ - ./.venv/bin/flake8 datadog_lambda/
+```
+
+any commit between `from` (exclusive) and `to` (inclusive) has a specific prompt (task) that the agent should act on. these prompts might change on a monthly basis by a maintainer so we avoid making the benchmarks too deterministic. the prompts change slightly so this does not make old benchmarks incomparable to the new benchmarks even though that's not the goal of OpenCode-bench because with this benchmark we're trying to compare agents & models to each other, rather than compare them to the old themselves.
+
+```yaml
+generated_at: 2025-11-04T01:45:24.286Z
+prompts:
+ - commit: d7763789f262b2da228f8210509e302e6e510d0a
+ prompt: "Add a metric to track Lambda batch item failures. When Lambda functions return a response containing batch item failures (the batchItemFailures field), emit a count of how many items failed as an enhanced metric. This should only happen when enhanced metrics are enabled and the response structure is valid (response is a dict and batchItemFailures is a list). The metric should be submitted asynchronously with enhanced metrics tags. Integrate this into the wrapper's after-execution hook so it automatically captures the response from any wrapped Lambda handler. Follow existing codebase patterns for function signatures and integration points. Include comprehensive test coverage for various scenarios: responses with multiple failures, empty failure lists, missing batchItemFailures field, None responses, invalid field types, and disabled enhanced metrics. Also add integration tests in the wrapper test suite to verify the metric submission is called with the correct response object."
+```
+
+that's a general flow of how the benchmarking system works under the hood.
+
+---
+
+now let's jump into what the UI might need to show to the user.
+
+since each commit has a benchmark execution with it, we let the user navigate between commit (we might want to show the date of each of those commits, but the commit hash looks cooler) and see the associated run. each of those runs has enough information about each agent and model across each eval.
+
+by default, the last run is shown on the home page as the main information. but that can change through navigating the commit history to show the benchmarks of a 1 month old run for instance.
+
+each run shows a per combination comparison/chart is formed by aggregating the scores of each combination per eval. so the user is able to see more specific information that is specific to a single eval.
+
+we store a per combination (agent:model) analysis summary as well that talks about how that agent behaved in a specific run. there's also a difference analysis summary that is per eval, which talks about how different agents/models behvaed in that eval.
+
+[scatter charts](https://recharts.github.io/en-US/examples/SimpleScatterChart/) are often used to demonstrate the performace of AI models compared to each other.
+
+we can use [radar charts](https://recharts.github.io/en-US/examples/SimpleRadarChart/) to show the scores (e.g. `api-signature`) of a specific agent:model combination.
+
+we can draw a difficulty line based on the 70th percentile to differentiate between top and average agents & models.
+
+across the history of benchmark runs, the difficulty line (vertical) can be a chart as well to demonstrate the volatility of the difficulty run. the more time passes, difficulty decreases since models get better over time, but by adding and adjusting evals, we can keep the difficulty high. that's kinda the point of having a difficulty chart.
+
+### error-analysis
+
+https://www.youtube.com/watch?v=e2i6JbU2R-s
+https://hamel.dev/blog/posts/field-guide/index.html
+
+the community should help us carry out error analysis to improve the task generation capability (e.g. how much do those generated tasks resemble real-world user prompting) and the accuracy of the judges (e.g. catch the strong inconsistency between the judges). they should be able to see each task that led the agent to reproduce a specific diff, compare it to the ideal original diff (extracted from the evals the community gifts us) and see the judges response to that.
+
+this way (hopefully through the ui) they can suggest us improvments, or whether they think a judge output was too strict or lean or even prompt changes to the way judges behave or the way our planner generates tasks.
+
+basically, wherever we have an LLM as judge or a part of our workflow that relies on an LLM, we need a detailed error analysis process so we keep improving these non-deterministic parts of our workflow. most ai benchmarks get outdated or do not represent real-world difficulty because they're not monitored which makes them irrelevant after a small portion of time.
+
+error-analysis can be as simple as like and dislike buttons and a small input box for explanation. that would be ideally shown on each prompt and and its output.
+
+- each judge's prompt and its rating and rationale
+- the task generation prompt and its generated tasks.
+
+the goal is to constantly verify that our automated evaluations (LLM-as-a-judge) align with human judgement.
+
+## Error Analysis Implementation
+
+We have four LLM-powered components that require community error analysis:
+
+### 1. Task Generation (Planner)
+Location: Converts git diffs into agent prompts
+Model: `opencode/claude-sonnet-4-5`
+
+UI Should Show:
+- Original commit diff (truncated for readability)
+- Generated task prompt
+- The actual changes the agent produced
+
+Feedback Collection:
+- Binary: "Does this prompt capture the commit's intent?" [Yes/No]
+- Multiple choice: "This prompt is: [Too vague | Too specific | Just right | Missing context]"
+- Text input: "How would you improve this prompt?"
+
+### 2. Score Judges (15 total: 5 score types × 3 judges)
+Judges: claude-4.5, gpt-5-codex, kimi
+Score Types: api-signature, logic-equivalence, integration-points, test-coverage, checks
+
+UI Should Show:
+- Episode selector: Toggle between Episode 0, 1, 2 (judges evaluate each independently)
+- Reference diff (expected changes)
+- Candidate diff (agent's actual changes for this episode)
+- Judge's system prompt (scoring criteria)
+- All 3 judges side-by-side, always showing:
+ - Judge name and model
+ - Score (0 or 1) for this episode
+ - Full rationale (200-1000 words with code analysis) for this episode
+- Episode variance indicator (shows consistency across episodes)
+
+Feedback Collection:
+- Binary per judge: "Do you agree with this judge's decision?" [Agree/Disagree/Unsure]
+- If disagree: "What did the judge get wrong?" (text input)
+- Rating per judge: "This judge was: [Too strict | Just right | Too lenient]"
+- Flag: "These judges are inconsistent" (when variance is high within an episode)
+- Flag: "This episode is an outlier" (when one episode diverges significantly from the other two)
+
+Design Note:
+- Always display all 3 judges' rationales side-by-side for the selected episode to make inconsistencies obvious. Highlight when variance > 0.15.
+- When episode scores vary significantly (e.g., Episode 0: 0.5, Episode 1: 0.2, Episode 2: 0.3), allow comparing the same judge's rationale across episodes to understand why scores changed.
+
+### 3. Agent Behavior Summarizer
+Location: Summarizes agent's actions across 3 episodes
+Model: `opencode/claude-sonnet-4-5`
+
+UI Should Show:
+- Raw action logs per episode (first 50 actions, expandable)
+- Generated summary (200-500 words)
+- Final scores for context
+
+Feedback Collection:
+- Rating: "Is this summary accurate?" [Very | Mostly | Partially | Not at all]
+- Text input: "What's missing from this summary?"
+- Text input: "Did the summary claim something that didn't happen?"
+
+### 4. Cross-Agent Analysis
+Location: Compares all agent:model combinations on same eval
+Model: `opencode/claude-sonnet-4-5`
+
+UI Should Show:
+- All runs' scores and summaries (the input data)
+- Generated comparative analysis (1000+ words)
+
+Feedback Collection:
+- Rating: "Is this analysis insightful?" [Very | Somewhat | Not really | No]
+- Text input: "What pattern did the analysis miss?"
+- Binary: "Are the recommendations actionable?" [Yes/Somewhat/No]
+
+## Error Analysis Data Structure
+
+Store feedback alongside benchmark results:
+
+```typescript
+interface ErrorAnalysisFeedback {
+ feedbackId: string;
+ timestamp: string;
+ userId?: string; // Optional: track who provided feedback
+
+ // Component identification
+ componentType: "planner" | "judge" | "agent-summary" | "cross-analysis";
+ componentId: string; // e.g., "api-signature:claude-4.5" or "planner:abc123"
+
+ // Structured responses
+ rating?: "agree" | "disagree" | "unsure" | number;
+ category?: string; // Multiple choice responses
+ comment?: string; // Free text
+
+ // Context for aggregation
+ evalRepo: string;
+ benchmarkCommit: string;
+ agentModel?: string; // If applicable to specific run
+}
+```
+
+## Aggregation Dashboard
+
+Create a monitoring dashboard showing:
+- Judge Consistency: % of human agreement per judge per score type
+- High-Variance Issues: Evals where judges disagree + humans flag inconsistencies
+- Planner Quality: % of tasks rated "too vague" or "too specific"
+- Summary Accuracy: % of summaries rated "mostly" or "very" accurate
+- Trending Issues: Patterns in feedback over time
+
+---
+
+## Data Structure Reference
+
+This section documents the exact data fields available for each agent:model:eval combination. Use this as a reference when designing UI components.
+
+### Top-Level Run Information
+Each benchmark run contains:
+- agent: Agent type (e.g., "claude-code", "opencode", "codex")
+- model: Model identifier (e.g., "claude-sonnet-4-5", "gpt-5-codex", "big-pickle")
+- evaluation: Object with:
+ - `repo`: GitHub repository (e.g., "DataDog/datadog-lambda-python")
+ - `from`: Starting commit hash
+ - `to`: Target commit hash the agent attempts to replicate
+
+### Scoring Metrics
+- finalScore: Final weighted score after penalties (0-1 scale, e.g., 0.32469)
+- baseScore: Raw weighted average before penalties (0-1 scale, e.g., 0.36667)
+- variancePenalty: Penalty for inconsistent judge performance (e.g., 0.04198)
+
+### Per-Score Breakdown
+For each score dimension (api-signature, logic-equivalence, integration-points, test-coverage, checks):
+
+- assignment:
+ - `name`: Score type identifier (e.g., "api-signature")
+ - `weight`: Importance weight (e.g., 0.2 = 20%)
+ - `args`: (Optional) Configuration for executable checks:
+ - `setup`: Array of setup commands
+ - `commands`: Array of test commands to run
+
+- averageScore: Mean score across all judges (0-1 scale)
+- normalizedWeight: Weight after normalization (typically same as original)
+- variance: Statistical variance in judge scores (higher = more disagreement among judges)
+
+- judges: Array of individual judge evaluations:
+ - `name`: Judge identifier (e.g., "claude-4.5", "gpt-5-codex", "kimi")
+ - `model`: Full model path (e.g., "opencode/claude-sonnet-4-5")
+ - `score`: Binary rating (0 = FAIL, 1 = PASS)
+ - `rationale`: Full text explanation (typically 200-1000 words with code examples, diffs, and detailed technical analysis)
+
+### Analysis Summaries
+
+Per Agent:Model Summary (stored in benchmark's `summary` field):
+- Multi-paragraph markdown text (typically 200-500 words)
+- Describes agent behavior across episodes
+- Common sections: "Overview", "Approach", "Key Actions", "Observations"
+- Highlights patterns like tool usage, exploration strategies, consistency
+
+Per Eval Cross-Agent Analysis (stored in `analysis-{safe-repo-name}/analysis.txt`):
+- Comprehensive comparison document (1000+ words)
+- Compares all agent:model combinations on the same eval
+- Example sections:
+ - "Executive Summary"
+ - "Systematic Performance Patterns" (tier separation, penalty analysis)
+ - "Implementation Quality Differences"
+ - "Testing Strategy Divergence"
+ - "Agent Behavioral Tendencies"
+- Includes comparison tables and detailed insights
+
+Analysis Metadata (in `analysis-{safe-repo-name}/analysis-info.json`):
+- `eval`: Repository name
+- `safe`: URL-safe repository name
+- `url`: Link to GitHub Actions run that generated the analysis
+
+### Run Metadata (in `metadata.json`)
+Each benchmark run includes:
+- commit: Git commit hash of the benchmark run
+- workflowRun: GitHub Actions workflow details:
+ - `id`: Workflow run ID
+ - `name`: Workflow name (e.g., "Publish and Benchmark Preview Packages")
+ - `status`: "completed", "in_progress", etc.
+ - `conclusion`: "success", "failure", etc.
+ - `createdAt`: ISO timestamp
+- artifacts: Array of generated benchmark/analysis artifacts:
+ - `name`: Artifact identifier (pattern: `benchmark-{agent}-{model}-{safe-repo-name}` or `analysis-{safe-repo-name}`)
+ - `size`: File size in bytes
+ - `createdAt`: ISO timestamp
+ - `expired`: Boolean
+
+### Episode-Specific Data
+Each benchmark run executes 3 episodes (independent attempts at the same task). This helps measure consistency and identify variance in agent behavior.
+
+episodes: Array of 3 episode objects, each containing:
+- finalScore: Episode-specific final score (0-1 scale)
+- baseScore: Episode-specific base score before penalties
+- variancePenalty: Penalty applied for this episode
+- usage: Token usage for this episode:
+ - `input`: Input tokens consumed
+ - `output`: Output tokens generated
+- scores: Full score breakdown identical to the run-level structure:
+ - Each score dimension (api-signature, logic-equivalence, etc.) with:
+ - `assignment`: Score name, weight, args
+ - `averageScore`: Mean judge score for this episode
+ - `variance`: Judge disagreement variance for this episode
+ - `judges`: Array of 3 judge evaluations (name, model, score, rationale)
+
+Key Insight: Episode-level scores can vary significantly. For example:
+- Episode 0: finalScore = 0.500
+- Episode 1: finalScore = 0.211
+- Episode 2: finalScore = 0.322
+
+This variance reveals agent consistency issues and is critical for understanding reliability.
+
+### Aggregate-Level Data
+- usage: Aggregated token usage across all episodes:
+ - `input`: Total input tokens
+ - `output`: Total output tokens
+
+### Important Design Considerations
+
+1. Judge rationales are substantial - Not one-liners; they're detailed technical analyses with code snippets, sometimes 500+ words each. Design for expandable/collapsible detailed views.
+
+2. Data is hierarchical - Run → Eval → Scores → Judges. Navigation should reflect this hierarchy clearly.
+
+3. Two types of summaries exist:
+ - Agent-specific: "How did this agent:model perform on this eval?"
+ - Cross-agent: "How did all agents compare on this specific eval?"
+
+4. Multiple commits = multiple snapshots - Each commit has its own `metadata.json` with different artifact lists. Design for temporal navigation between these snapshots.
+
+5. Scores are multi-dimensional and variable - Each eval can have different score types with different weights. One eval might have 5 scores, another might have 3. Radar charts should handle variable dimensions.
+
+6. Variance is meaningful - High variance indicates judge disagreement. Surface this visually as it signals potential evaluation issues or edge cases.
+
+7. Episodes reveal consistency patterns - Each run has 3 independent episodes with separate scores and judge rationales. Design for:
+ - Episode-by-episode comparison views (show all 3 side-by-side)
+ - Variance visualization across episodes (e.g., bar chart showing episode scores)
+ - Episode-specific judge rationales (judges evaluate each episode independently, so rationales differ per episode)
+ - Identifying which episodes succeeded vs failed and why
diff --git a/web/bun.lock b/web/bun.lock
new file mode 100644
index 0000000..f01267f
--- /dev/null
+++ b/web/bun.lock
@@ -0,0 +1,683 @@
+{
+ "lockfileVersion": 1,
+ "workspaces": {
+ "": {
+ "name": "web",
+ "dependencies": {
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.552.0",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "recharts": "2.15.4",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss-animate": "^1.0.7",
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@tailwindcss/postcss": "^4.1.16",
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.36.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "globals": "^16.4.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.16",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.7",
+ },
+ },
+ },
+ "packages": {
+ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+
+ "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
+
+ "@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
+
+ "@babel/core": ["@babel/core@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw=="],
+
+ "@babel/generator": ["@babel/generator@7.28.5", "", { "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="],
+
+ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
+
+ "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
+
+ "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
+
+ "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="],
+
+ "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
+
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
+
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
+
+ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
+
+ "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="],
+
+ "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
+
+ "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
+
+ "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
+
+ "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
+
+ "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
+
+ "@babel/traverse": ["@babel/traverse@7.28.5", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="],
+
+ "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="],
+
+ "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
+
+ "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
+
+ "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
+
+ "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
+
+ "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
+
+ "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
+
+ "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
+
+ "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
+
+ "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
+
+ "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
+
+ "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
+
+ "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
+
+ "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
+
+ "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
+
+ "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
+
+ "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
+
+ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
+
+ "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
+
+ "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
+
+ "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
+
+ "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
+
+ "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
+
+ "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
+
+ "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
+
+ "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
+
+ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
+
+ "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
+
+ "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
+
+ "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="],
+
+ "@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
+
+ "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
+
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+
+ "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="],
+
+ "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
+
+ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
+
+ "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
+
+ "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
+
+ "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
+
+ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
+
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.43", "", {}, "sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ=="],
+
+ "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
+
+ "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
+
+ "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
+
+ "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
+
+ "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
+
+ "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
+
+ "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
+
+ "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
+
+ "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
+
+ "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
+
+ "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
+
+ "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
+
+ "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
+
+ "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
+
+ "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
+
+ "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
+
+ "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
+
+ "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
+
+ "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
+
+ "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
+
+ "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
+
+ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.16", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.16", "@tailwindcss/oxide-darwin-arm64": "4.1.16", "@tailwindcss/oxide-darwin-x64": "4.1.16", "@tailwindcss/oxide-freebsd-x64": "4.1.16", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.16", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.16", "@tailwindcss/oxide-linux-arm64-musl": "4.1.16", "@tailwindcss/oxide-linux-x64-gnu": "4.1.16", "@tailwindcss/oxide-linux-x64-musl": "4.1.16", "@tailwindcss/oxide-wasm32-wasi": "4.1.16", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.16", "@tailwindcss/oxide-win32-x64-msvc": "4.1.16" } }, "sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.16", "", { "os": "android", "cpu": "arm64" }, "sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16", "", { "os": "linux", "cpu": "arm" }, "sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.16", "", { "os": "linux", "cpu": "x64" }, "sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.16", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.0.7", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, "cpu": "none" }, "sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.16", "", { "os": "win32", "cpu": "x64" }, "sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg=="],
+
+ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.16", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "postcss": "^8.4.41", "tailwindcss": "4.1.16" } }, "sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A=="],
+
+ "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
+
+ "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
+
+ "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
+
+ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
+
+ "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
+
+ "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
+
+ "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
+
+ "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
+
+ "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
+
+ "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
+
+ "@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="],
+
+ "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
+
+ "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+
+ "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
+
+ "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
+
+ "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw=="],
+
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.3", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/type-utils": "8.46.3", "@typescript-eslint/utils": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.3", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw=="],
+
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.3", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg=="],
+
+ "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.3", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.3", "@typescript-eslint/types": "^8.46.3", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ=="],
+
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3" } }, "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg=="],
+
+ "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.3", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA=="],
+
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw=="],
+
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.46.3", "", {}, "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA=="],
+
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.3", "@typescript-eslint/tsconfig-utils": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/visitor-keys": "8.46.3", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA=="],
+
+ "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.3", "@typescript-eslint/types": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g=="],
+
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.3", "", { "dependencies": { "@typescript-eslint/types": "8.46.3", "eslint-visitor-keys": "^4.2.1" } }, "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg=="],
+
+ "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="],
+
+ "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
+
+ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "baseline-browser-mapping": ["baseline-browser-mapping@2.8.25", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA=="],
+
+ "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "browserslist": ["browserslist@4.27.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", "electron-to-chromium": "^1.5.238", "node-releases": "^2.0.26", "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw=="],
+
+ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001753", "", {}, "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw=="],
+
+ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
+
+ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+ "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
+
+ "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
+
+ "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
+
+ "d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
+
+ "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
+
+ "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
+
+ "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
+
+ "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
+
+ "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
+
+ "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
+
+ "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
+
+ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
+
+ "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
+
+ "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
+
+ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
+
+ "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
+
+ "electron-to-chromium": ["electron-to-chromium@1.5.245", "", {}, "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
+
+ "esbuild": ["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" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
+
+ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
+
+ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
+
+ "eslint": ["eslint@9.39.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="],
+
+ "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
+
+ "eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.24", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w=="],
+
+ "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
+
+ "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
+
+ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
+
+ "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
+
+ "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
+
+ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
+
+ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+
+ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
+
+ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+
+ "fast-equals": ["fast-equals@5.3.2", "", {}, "sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ=="],
+
+ "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
+
+ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+
+ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
+
+ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
+
+ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
+
+ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
+
+ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="],
+
+ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
+
+ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
+
+ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+
+ "globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
+
+ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
+
+ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
+
+ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+
+ "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
+
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
+ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
+
+ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
+
+ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+
+ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
+
+ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+
+ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+
+ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+
+ "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="],
+
+ "lightningcss-android-arm64": ["lightningcss-android-arm64@1.30.2", "", { "os": "android", "cpu": "arm64" }, "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.2", "", { "os": "linux", "cpu": "arm" }, "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.2", "", { "os": "linux", "cpu": "x64" }, "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="],
+
+ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+
+ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+
+ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+
+ "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
+
+ "lucide-react": ["lucide-react@0.552.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw=="],
+
+ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
+
+ "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
+
+ "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="],
+
+ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
+
+ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+
+ "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
+
+ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+
+ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
+
+ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
+
+ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+
+ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
+ "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="],
+
+ "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ=="],
+
+ "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
+
+ "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
+
+ "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "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" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
+
+ "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
+
+ "recharts": ["recharts@2.15.4", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "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" } }, "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw=="],
+
+ "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
+
+ "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
+
+ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+
+ "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
+
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
+ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
+
+ "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+
+ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+ "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="],
+
+ "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
+
+ "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="],
+
+ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
+
+ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
+
+ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
+
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
+
+ "typescript-eslint": ["typescript-eslint@8.46.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.3", "@typescript-eslint/parser": "8.46.3", "@typescript-eslint/typescript-estree": "8.46.3", "@typescript-eslint/utils": "8.46.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA=="],
+
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
+
+ "update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
+
+ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+
+ "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
+
+ "vite": ["vite@7.2.0", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-C/Naxf8H0pBx1PA4BdpT+c/5wdqI9ILMdwjSMILw7tVIh3JsxzZqdeTLmmdaoh5MYUEOyBnM9K3o0DzoZ/fe+w=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
+
+ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
+
+ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+
+ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.0.7", "", { "dependencies": { "@emnapi/core": "^1.5.0", "@emnapi/runtime": "^1.5.0", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
+
+ "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
+
+ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
+
+ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+
+ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+ }
+}
diff --git a/web/components.json b/web/components.json
new file mode 100644
index 0000000..1537d50
--- /dev/null
+++ b/web/components.json
@@ -0,0 +1,22 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.js",
+ "css": "src/index.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "iconLibrary": "lucide",
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "registries": {}
+}
diff --git a/web/data/README.md b/web/data/README.md
new file mode 100644
index 0000000..d33f80d
--- /dev/null
+++ b/web/data/README.md
@@ -0,0 +1,50 @@
+# Benchmark Data
+
+This directory contains artifacts fetched from GitHub Actions workflows.
+
+## Current Data
+
+**Commit**: `ea446df3c3284cf6be379486a9807d0c48ef7d78`
+**Workflow Run**: `19057352801` - "Publish and Benchmark Preview Packages"
+**Fetched**: See `metadata.json` for details
+
+## Artifacts Found
+
+### Benchmark Artifacts (12)
+- `benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next`
+- `benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer`
+- `benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python`
+- `benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python`
+- `benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer`
+- `benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python`
+- `benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer`
+- `benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python`
+- `benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer`
+
+### Analysis Artifacts (3)
+- `analysis-AlaminPu1007-algorithm-visualizer`
+- `analysis-prismicio-community-course-fizzi-next`
+- `analysis-DataDog-datadog-lambda-python`
+
+## Downloading Artifacts
+
+GitHub Actions artifacts require authentication. To download the artifacts, run:
+
+```bash
+GITHUB_TOKEN=your_token_here bun scripts/fetch-artifacts.ts
+```
+
+You can create a GitHub Personal Access Token with `actions:read` permission at:
+https://github.com/settings/tokens
+
+## Data Structure
+
+Each benchmark artifact contains:
+- `benchmark.json` - Full evaluation run export with scores, episodes, and usage data
+
+Each analysis artifact contains:
+- `analysis.txt` - Judge analysis text
+- `analysis-info.json` - Metadata with eval info and job URL
diff --git a/web/data/analysis-AlaminPu1007-algorithm-visualizer.zip b/web/data/analysis-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..dd03b4d
Binary files /dev/null and b/web/data/analysis-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json b/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json
new file mode 100644
index 0000000..04bc688
--- /dev/null
+++ b/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "AlaminPu1007/algorithm-visualizer",
+ "safe": "AlaminPu1007-algorithm-visualizer",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051186#step:7:0"
+}
diff --git a/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt b/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt
new file mode 100644
index 0000000..097b69d
--- /dev/null
+++ b/web/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt
@@ -0,0 +1,119 @@
+# Cross-Agent Benchmark Analysis
+
+## Overall Performance Pattern
+
+The benchmark reveals remarkably **consistent performance** across the top three agents, with scores clustering tightly between 0.804-0.827 (final) and 0.813-0.870 (base). This narrow range suggests the task has well-defined success criteria that multiple approaches can satisfy effectively.
+
+### Performance Ranking
+1. **claude-code (claude-sonnet-4-5)**: 0.827 final — Highest base score (0.870), lowest penalty
+2. **opencode (big-pickle)**: 0.827 final — Tied for first, slightly higher penalty (0.043)
+3. **opencode (claude-sonnet-4-5)**: 0.804 final — Higher penalty (0.054) dragged down final score
+4. **codex (gpt-5-codex)**: 0.770 final — Technical failure prevented summary generation
+
+## Key Insights
+
+### 1. **Model Consistency vs. Scope Management**
+
+**Claude-code's advantage**: Achieved the highest base score (0.870) with minimal penalty (0.043) by maintaining **strict consistency** across episodes. The agent followed an identical approach in all three runs, with only Episode 1 showing minor exploratory variation (schema file search).
+
+**OpenCode's trade-off**: Both OpenCode variants achieved identical base scores to claude-code (0.870 for big-pickle, 0.858 for claude-sonnet-4-5) but incurred higher penalties. The big-pickle variant matched claude-code's penalty, while the claude-sonnet-4-5 variant showed **25% higher penalty** (0.054 vs 0.043), suggesting:
+- Episode 2 was "more focused" (documentation only)
+- Episodes 1 & 3 included "comprehensive UI refinements"
+- **Inconsistent scope across episodes** likely triggered the penalty increase
+
+### 2. **Systematic Approach Patterns**
+
+All successful agents followed a similar workflow:
+```
+Explore → Identify files → Update version → Document changes → Verify
+```
+
+**Differentiation emerged in execution details:**
+
+- **Claude-code**: Used `TodoWrite` for explicit task tracking, demonstrating structured project management
+- **OpenCode (big-pickle)**: Emphasized verification with "linting and build verification commands" after changes
+- **OpenCode (claude-sonnet-4-5)**: Focused on tool efficiency, using "Edit operations extensively" with occasional Read operations
+
+### 3. **The Episode Consistency Problem**
+
+The **most significant performance differentiator** was episode-to-episode consistency:
+
+- **Claude-code**: "All three episodes achieved identical outcomes"
+- **OpenCode (claude-sonnet-4-5)**: "Episode 2 was more focused... Episodes 1 and 3 included comprehensive UI refinements"
+
+This suggests the evaluation system **penalizes scope variation** across episodes, even when individual episodes are successful. The 0.054 penalty for opencode/claude-sonnet-4-5 (vs 0.043 for others) directly correlates with its documented inconsistency.
+
+### 4. **Technical Failure Analysis**
+
+**Codex (gpt-5-codex)** encountered a critical infrastructure issue: "Body has already been used. It can only be read once." This is a **stream consumption error**, not an agent logic failure. Despite this, it achieved:
+- Base score: 0.813 (only 7% below the leader)
+- Same penalty structure: 0.043
+
+This suggests the agent **completed the task successfully** but failed during post-processing/summary generation, indicating a **tooling issue** rather than capability gap.
+
+## Performance Gaps Analysis
+
+### Largest Delta: 0.057 (claude-code vs codex)
+- **Primary factor**: Codex's base score (0.813 vs 0.870) — a 6.5% gap
+- **Secondary factor**: Summary generation failure suggests incomplete observability
+- **Implication**: The gap may be smaller than it appears if the technical issue masked successful work
+
+### Smallest Delta: 0.000 (claude-code vs opencode/big-pickle)
+- Both achieved identical final scores through different paths
+- Big-pickle emphasized **verification** (linting, builds)
+- Claude-code emphasized **planning** (todo tracking)
+- **Implication**: Multiple valid strategies exist for this task type
+
+## Agent Behavioral Tendencies
+
+### Safety vs. Completeness Trade-offs
+
+1. **Claude-code**: Prioritizes **reproducibility** — identical outcomes across episodes suggest conservative, proven approach
+2. **OpenCode (big-pickle)**: Prioritizes **validation** — explicit mention of quality checks indicates defensive programming
+3. **OpenCode (claude-sonnet-4-5)**: Prioritizes **efficiency** — variable scope suggests optimization for individual episode requirements
+
+### Tool Usage Patterns
+
+- **TodoWrite** (claude-code only): Explicit task management overhead, but may improve consistency
+- **Verification commands** (big-pickle only): Quality assurance overhead, but catches errors early
+- **Read-then-Edit** (claude-sonnet-4-5): More cautious file modification approach
+
+## Recommendations
+
+### 1. **For Evaluation System Improvement**
+- **Clarify penalty criteria**: Document whether episode consistency is required or if adaptive scope is acceptable
+- **Fix infrastructure issues**: Resolve stream consumption errors affecting summary generation
+- **Add consistency metrics**: Explicitly measure and report episode-to-episode variance
+
+### 2. **For Agent Development**
+
+**To match claude-code's performance:**
+- Implement explicit task tracking (TodoWrite or equivalent)
+- Standardize episode workflows to minimize variation
+- Front-load exploration to avoid mid-episode scope changes
+
+**To improve on current leaders:**
+- Combine claude-code's consistency with big-pickle's verification
+- Add automated testing to catch regressions
+- Implement episode planning phase to determine optimal scope upfront
+
+### 3. **For Future Experiments**
+
+**Test hypothesis**: Does episode consistency matter?
+- Run controlled experiment with intentionally variable vs. fixed scope
+- Measure penalty impact independently
+
+**Investigate codex failure:**
+- Reproduce stream consumption error
+- Determine if it's model-specific or infrastructure-wide
+- Assess true capability gap once technical issues resolved
+
+**Benchmark verification strategies:**
+- Compare outcomes with/without explicit linting steps
+- Measure correlation between verification overhead and final scores
+
+## Conclusion
+
+This benchmark reveals a **mature evaluation environment** where multiple agents achieve near-identical results through different optimization strategies. The 2.3% spread between top performers suggests diminishing returns on further optimization without changing the task complexity.
+
+The most actionable insight: **Episode consistency appears more valuable than per-episode optimization**. Claude-code's success stems from reproducible execution, not superior individual episode performance. Future development should prioritize workflow standardization over adaptive intelligence for this task class.
diff --git a/web/data/analysis-DataDog-datadog-lambda-python.zip b/web/data/analysis-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..f7194c6
Binary files /dev/null and b/web/data/analysis-DataDog-datadog-lambda-python.zip differ
diff --git a/web/data/analysis-DataDog-datadog-lambda-python/analysis-info.json b/web/data/analysis-DataDog-datadog-lambda-python/analysis-info.json
new file mode 100644
index 0000000..bf7c024
--- /dev/null
+++ b/web/data/analysis-DataDog-datadog-lambda-python/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "DataDog/datadog-lambda-python",
+ "safe": "DataDog-datadog-lambda-python",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051170#step:7:0"
+}
diff --git a/web/data/analysis-DataDog-datadog-lambda-python/analysis.txt b/web/data/analysis-DataDog-datadog-lambda-python/analysis.txt
new file mode 100644
index 0000000..a6d2deb
--- /dev/null
+++ b/web/data/analysis-DataDog-datadog-lambda-python/analysis.txt
@@ -0,0 +1,160 @@
+# Cross-Run Analysis: Batch Item Failures Metric Implementation
+
+## Executive Summary
+All four runs successfully implemented the same feature—tracking Lambda batch item failures as an enhanced metric—but with significant variations in execution quality, test coverage, and efficiency. The performance gap between the top performer (opencode/claude-sonnet-4-5 at 0.416) and bottom performer (codex/gpt-5-codex at 0.127) reveals important patterns about agent behavior and evaluation criteria.
+
+---
+
+## 1. Systematic Performance Patterns
+
+### Clear Tier Separation
+- **Top Tier** (0.30-0.42): Both Claude Sonnet 4-5 agents (opencode and claude-code)
+- **Bottom Tier** (0.13-0.17): Big-pickle and GPT-5-codex agents
+
+The Claude Sonnet 4-5 model demonstrates **2-3x better performance** regardless of agent framework, suggesting model capability is the dominant factor in this benchmark.
+
+### Penalty Analysis
+Interestingly, the penalty structure reveals inverse correlation with base scores:
+- **opencode/claude-sonnet-4-5**: Highest base (0.522) but also highest penalty (0.106) = 20% reduction
+- **codex/gpt-5-codex**: Lowest base (0.144) but also lowest penalty (0.017) = 12% reduction
+
+This suggests the evaluation system may penalize ambitious implementations that attempt more comprehensive solutions, or that higher-performing agents trigger more edge-case violations.
+
+---
+
+## 2. Implementation Quality Differences
+
+### Code Placement & Integration
+All agents placed the core function in `datadog_lambda/metric.py` and integrated it into `wrapper.py`, but with subtle differences:
+
+**Consistent Winners (Claude Sonnet 4-5):**
+- Precise line number documentation (e.g., "lines 231-271")
+- Clear integration points specified (e.g., "lines 294-297 or 366-370")
+- Explicit mention of `force_async=True` rationale
+
+**Inconsistent Performers (Big-pickle, GPT-5-codex):**
+- Vaguer location descriptions (e.g., "around line 367-386")
+- Less detail on integration strategy
+- GPT-5-codex used "lazy imports" to avoid circular dependencies—a defensive pattern that may indicate uncertainty
+
+### Validation Logic Depth
+All implementations included multi-layer validation, but descriptions vary:
+
+**opencode/claude-sonnet-4-5** explicitly lists 4 validation layers:
+1. Enhanced metrics enabled check
+2. Response is dictionary
+3. `batchItemFailures` exists and is a list
+4. Only submit when failures present
+
+**codex/gpt-5-codex** mentions similar checks but less systematically, focusing more on edge cases (None responses, non-dict responses).
+
+---
+
+## 3. Testing Strategy Divergence
+
+### Test Coverage Spectrum
+
+| Agent | Unit Tests | Integration Tests | Test File Strategy |
+|-------|-----------|-------------------|-------------------|
+| opencode/claude-sonnet-4-5 | 8-9 tests | 3-6 tests (Episode 3: extensive) | Existing files |
+| claude-code | 8-10 tests | Episode 3 only | Existing files |
+| opencode/big-pickle | 9 tests | 7-8 tests | **New file created** |
+| codex/gpt-5-codex | Comprehensive | Not documented | Existing files |
+
+**Critical Insight**: opencode/big-pickle created a **new test file** (`test_metric_with_batch_failures.py`) rather than extending existing test files. This deviation from codebase patterns likely contributed to its lower score, as it:
+- Increases maintenance burden
+- Fragments test organization
+- Suggests misunderstanding of project structure
+
+### Episode-by-Episode Consistency
+
+**opencode/claude-sonnet-4-5**: Shows progressive improvement across episodes, with Episode 3 adding "most extensive integration tests, including specific SQS and Kinesis batch scenarios"—demonstrating learning and refinement.
+
+**opencode/big-pickle**: Episodes 2 and 3 "found the implementation already complete"—suggesting the agent may have cached or reused Episode 1 work rather than re-implementing, which could explain lower scores if the evaluation penalizes this behavior.
+
+---
+
+## 4. Agent Behavioral Tendencies
+
+### Exploration vs. Execution Trade-offs
+
+**codex/gpt-5-codex** shows extreme variation in action counts:
+- Episode 1: 74 actions
+- Episode 2: **183 actions** (2.5x more)
+- Episode 3: 73 actions
+
+Episode 2's extensive exploration ("more extensive investigation of patching strategies") suggests the agent got stuck in analysis paralysis, potentially explaining its low score despite "strong pattern recognition."
+
+**Tool Usage Patterns:**
+- GPT-5-codex heavily relied on `bash` commands (`sed`, `grep`) for code inspection
+- Claude agents appear to have more direct code understanding, requiring less exploratory tooling
+- Missing `rg` (ripgrep) forced fallbacks in GPT-5-codex, indicating environment assumptions
+
+### Safety vs. Completeness
+
+**claude-code** summary notes: "The implementation was consistent across episodes, demonstrating a methodical approach"—prioritizing reliability.
+
+**opencode/big-pickle** emphasizes: "All episodes concluded with successful test execution and code quality validation using pytest and flake8, confirming zero regressions"—prioritizing safety verification.
+
+This defensive posture may explain why big-pickle scored lower: the evaluation may reward feature completeness over regression prevention.
+
+---
+
+## 5. Notable Contrasts & Anomalies
+
+### The "Already Complete" Phenomenon
+opencode/big-pickle's Episodes 2-3 finding work "already complete" is highly unusual and suggests:
+1. **Caching issue**: Agent reused previous episode state
+2. **Evaluation design**: Episodes may not properly isolate runs
+3. **Agent confusion**: Misidentified existing code as its own work
+
+This deserves investigation as it could indicate a fundamental evaluation flaw.
+
+### Documentation Quality Paradox
+The **most detailed summary** (opencode/claude-sonnet-4-5) correlates with the **highest score**, but also the **highest penalty**. This suggests:
+- Detailed documentation may reveal more issues to evaluators
+- Or, comprehensive implementations naturally have more edge cases to penalize
+- The penalty system may need recalibration to avoid punishing thoroughness
+
+### Model Dominance
+The fact that **both Claude Sonnet 4-5 agents** occupy the top tier regardless of agent framework (opencode vs. claude-code) indicates:
+- Model capability >> Agent framework design for this task
+- The benchmark effectively measures model reasoning over agent orchestration
+- Future improvements should focus on model selection over agent architecture
+
+---
+
+## 6. Recommendations
+
+### For Evaluation System Improvements
+1. **Investigate penalty calibration**: Why does higher base performance correlate with higher penalties? Consider whether this discourages comprehensive solutions.
+
+2. **Episode isolation verification**: The "already complete" issue in big-pickle runs suggests episodes may not be properly isolated. Verify each episode starts from clean state.
+
+3. **Clarify scoring criteria**: Document whether creating new test files vs. extending existing ones affects scores, and why.
+
+4. **Tool availability standardization**: Ensure all agents have access to expected tools (e.g., `rg`) or document fallback strategies.
+
+### For Agent Development
+1. **Pattern recognition training**: GPT-5-codex showed "strong pattern recognition" but still scored lowest—investigate whether this capability translates to correct implementation decisions.
+
+2. **Exploration efficiency**: Implement guardrails to prevent analysis paralysis (e.g., GPT-5-codex's 183-action Episode 2). Consider action budgets or progress metrics.
+
+3. **Codebase structure understanding**: Train agents to extend existing test files rather than creating new ones, following project conventions.
+
+4. **Integration point precision**: Top performers specified exact line numbers and integration rationale—this should be a training target.
+
+### For Future Benchmarks
+1. **Add complexity tiers**: This task may be too straightforward for Claude Sonnet 4-5, causing ceiling effects. Consider more challenging scenarios.
+
+2. **Measure efficiency**: Track actions-per-episode and correlate with quality to identify optimal exploration/execution ratios.
+
+3. **Test edge case handling**: The summaries mention validation logic but don't detail how agents handle malformed responses—add specific edge case scenarios to evaluation.
+
+4. **Cross-episode learning**: Evaluate whether agents improve from Episode 1→3 or simply repeat the same approach (as most did here).
+
+---
+
+## Conclusion
+
+This benchmark reveals a **model-dominated landscape** where Claude Sonnet 4-5's reasoning capabilities drive 70-80% of performance variance. However, the **penalty structure anomaly** and **episode isolation concerns** suggest evaluation system refinements could provide clearer signals. The most actionable insight: **comprehensive documentation and testing correlate with higher base scores but also higher penalties**—this relationship deserves deeper investigation to ensure the evaluation rewards rather than punishes thoroughness.
diff --git a/web/data/analysis-prismicio-community-course-fizzi-next.zip b/web/data/analysis-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..4ad7212
Binary files /dev/null and b/web/data/analysis-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json b/web/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json
new file mode 100644
index 0000000..0ed5d83
--- /dev/null
+++ b/web/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "prismicio-community/course-fizzi-next",
+ "safe": "prismicio-community-course-fizzi-next",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051205#step:7:0"
+}
diff --git a/web/data/analysis-prismicio-community-course-fizzi-next/analysis.txt b/web/data/analysis-prismicio-community-course-fizzi-next/analysis.txt
new file mode 100644
index 0000000..b7a4d5a
--- /dev/null
+++ b/web/data/analysis-prismicio-community-course-fizzi-next/analysis.txt
@@ -0,0 +1,117 @@
+# Cross-Run Analysis: Prismic Migration Benchmark
+
+## Executive Summary
+All four runs attempted the same task—replacing a Prismic repository migration script—but exhibited dramatically different execution patterns. The performance gap between the highest scorer (codex at 0.268) and lowest (opencode/big-pickle at 0.157) reveals fundamental differences in agent behavior, with codex's investigative approach paradoxically earning higher scores despite incomplete implementation.
+
+## Systematic Patterns
+
+### 1. **Investigation vs. Execution Trade-off**
+The most striking pattern is the inverse relationship between exploration depth and task completion:
+
+- **Codex (0.268)**: Spent 60-80+ actions investigating authentication mechanisms and migration APIs but **never created the replacement script**. Achieved highest score despite incomplete work.
+- **Other agents (0.157-0.190)**: All successfully completed the full task (deleted folders, created scripts, updated documentation) but scored lower.
+
+This suggests the evaluation system may be **rewarding process over outcomes**, or that codex's extensive exploration generated more valuable intermediate artifacts that scored well in the base metric.
+
+### 2. **Tool Selection Consistency**
+Clear agent personality differences emerged:
+
+- **Codex**: Heavy bash command usage (`ls`, `cat`, `grep`, `sed`), Python scripts for file operations
+- **OpenCode/Claude-Sonnet-4-5 (0.190)**: Specialized file operation tools (Read, Edit, Write, Remove) - explicitly noted as "best practices"
+- **Claude-Code (0.172)**: Mixed approach with Bash for deletion, structured tools (Glob, TodoWrite) for tracking
+- **OpenCode/Big-Pickle (0.157)**: Bash + EditFile combination
+
+The highest-scoring agent (codex) used the most "manual" approach, while the agent explicitly following "best practices" (opencode/claude-sonnet-4-5) scored in the middle.
+
+### 3. **Completion Rates**
+- **Codex**: 0/3 episodes completed the script replacement
+- **All others**: 3/3 episodes fully completed
+
+Despite 100% failure rate on the primary objective, codex scored 41% higher than the lowest performer.
+
+## Performance Gaps & Explanations
+
+### The Codex Paradox
+Codex's 0.268 score with incomplete work suggests several possibilities:
+
+1. **Base metric rewards exploration**: The 0.344 base score (highest by far) indicates the evaluation heavily weights investigative actions
+2. **Penalty calculation**: Codex's 0.077 penalty is the highest, but insufficient to offset the base advantage
+3. **Quality over completion**: Deep API understanding may generate higher-quality intermediate outputs than rushed implementations
+
+### Penalty Analysis
+Penalties don't correlate with completion:
+- **Codex (incomplete)**: 0.077 penalty
+- **OpenCode/Claude-Sonnet-4-5 (complete)**: 0.054 penalty
+- **Claude-Code (complete)**: 0.040 penalty
+- **OpenCode/Big-Pickle (complete)**: 0.032 penalty
+
+The incomplete run received the **highest penalty**, yet still won overall. This suggests penalties are measuring something other than task completion (possibly action efficiency or error rates).
+
+## Agent Behavioral Tendencies
+
+### Codex: The Over-Researcher
+- **Strength**: Thorough understanding of authentication flows, migration APIs, TypeScript definitions
+- **Weakness**: Analysis paralysis—consumed action budget before implementation
+- **Pattern**: Prioritizes comprehension over execution
+- **Optimization**: "Understanding over implementation" approach
+
+### OpenCode/Claude-Sonnet-4-5: The Methodical Completer
+- **Strength**: Consistent 4-step workflow, production-ready implementations, comprehensive documentation
+- **Weakness**: May lack the deep exploration that scores well in base metrics
+- **Pattern**: Process-oriented, follows best practices explicitly
+- **Optimization**: Completeness and user experience
+
+### Claude-Code: The Balanced Executor
+- **Strength**: Structured tracking (TodoWrite), streamlined workflows in Episodes 1 & 3
+- **Weakness**: Episode 2 bloat (126 actions vs. 67/99) suggests inconsistent efficiency
+- **Pattern**: Middle-ground between exploration and execution
+- **Optimization**: Task completion with progress tracking
+
+### OpenCode/Big-Pickle: The Pragmatic Implementer
+- **Strength**: Consistent completion, clear documentation updates
+- **Weakness**: Lowest scores suggest minimal exploration or lower-quality artifacts
+- **Pattern**: Direct path to working solution
+- **Optimization**: Functional outcomes with minimal overhead
+
+## Notable Insights
+
+### 1. **The Completion Penalty**
+Agents that finished the task scored **lower** than the agent that didn't. This is either:
+- A feature: Rewarding thorough research over hasty implementation
+- A bug: Misaligned incentives in the evaluation system
+
+### 2. **Documentation Correlation**
+All completing agents updated README.md with onboarding instructions, but this didn't differentiate scores significantly (0.157-0.190 range). Documentation quality appears less weighted than exploration depth.
+
+### 3. **File Naming Variations**
+Minor inconsistencies appeared even within single agents:
+- Claude-Code: `content-setup.ts` vs `setup-content.ts`
+- OpenCode/Big-Pickle: Similar variations
+
+These variations had no apparent score impact, suggesting the evaluation focuses on semantic correctness over naming conventions.
+
+### 4. **Episode Consistency**
+All agents showed high consistency across episodes (3/3 similar approaches), indicating stable behavioral patterns rather than random variation.
+
+## Recommendations
+
+### For Evaluation System Improvement:
+1. **Rebalance base vs. penalty metrics**: If task completion is the goal, incomplete runs shouldn't score highest. Consider adding explicit completion bonuses.
+2. **Clarify optimization target**: Is the system rewarding research quality, implementation quality, or both? Make this explicit in scoring.
+3. **Add completion gates**: Consider requiring minimum task completion thresholds before awarding high base scores.
+4. **Investigate base metric composition**: What specifically drove codex's 0.344 base score? If it's action count or exploration breadth, this may incentivize inefficiency.
+
+### For Agent Development:
+1. **Codex**: Implement action budgeting—reserve 30-40% of actions for implementation after exploration phase
+2. **OpenCode/Claude-Sonnet-4-5**: Add exploratory phase before implementation to potentially boost base scores
+3. **Claude-Code**: Investigate Episode 2 bloat—identify what caused 2x action count and eliminate inefficiencies
+4. **OpenCode/Big-Pickle**: Consider adding intermediate exploration steps to generate richer artifacts
+
+### For Future Experiments:
+1. **Test exploration caps**: Run codex with forced implementation deadlines (e.g., "must begin writing script by action 40")
+2. **Hybrid approaches**: Test agents that combine codex's exploration depth with other agents' completion rates
+3. **Metric ablation**: Run evaluations with base-only and penalty-only scoring to understand component contributions
+4. **Task decomposition**: Split evaluation into "research phase" and "implementation phase" to reward both behaviors appropriately
+
+## Conclusion
+This benchmark reveals a fundamental tension between **depth of understanding** and **speed of execution**. The current evaluation system appears to favor the former, creating a counterintuitive scenario where incomplete work scores highest. Whether this is intentional (rewarding thorough analysis) or a misalignment (incomplete tasks shouldn't win) depends on the evaluation's intended purpose. The data suggests all agents are competent but optimized for different objectives—clarifying the target metric would help align agent behavior with desired outcomes.
diff --git a/web/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip b/web/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..09f095f
Binary files /dev/null and b/web/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip b/web/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..ddeff30
Binary files /dev/null and b/web/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip differ
diff --git a/web/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip b/web/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..8bce914
Binary files /dev/null and b/web/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip b/web/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..6381109
Binary files /dev/null and b/web/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip b/web/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..d01e549
Binary files /dev/null and b/web/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip differ
diff --git a/web/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip b/web/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..5c5f618
Binary files /dev/null and b/web/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip b/web/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..3e8f169
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip b/web/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..6b7bab7
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip differ
diff --git a/web/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip b/web/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..342c1b8
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..dc773ce
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..34272d7
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip differ
diff --git a/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..a8d3daf
Binary files /dev/null and b/web/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/data/metadata.json b/web/data/metadata.json
new file mode 100644
index 0000000..5c5c2d8
--- /dev/null
+++ b/web/data/metadata.json
@@ -0,0 +1,103 @@
+{
+ "commit": "ea446df3c3284cf6be379486a9807d0c48ef7d78",
+ "workflowRun": {
+ "id": 19057352801,
+ "name": "Publish and Benchmark Preview Packages",
+ "status": "completed",
+ "conclusion": "success",
+ "createdAt": "2025-11-04T04:00:56Z"
+ },
+ "artifacts": [
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next",
+ "size": 26565,
+ "createdAt": "2025-11-04T04:08:55Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next",
+ "size": 25993,
+ "createdAt": "2025-11-04T04:09:14Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next",
+ "size": 26304,
+ "createdAt": "2025-11-04T04:10:19Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer",
+ "size": 23269,
+ "createdAt": "2025-11-04T04:14:20Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python",
+ "size": 30274,
+ "createdAt": "2025-11-04T04:14:42Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python",
+ "size": 28373,
+ "createdAt": "2025-11-04T04:14:42Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer",
+ "size": 23459,
+ "createdAt": "2025-11-04T04:15:24Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next",
+ "size": 29271,
+ "createdAt": "2025-11-04T04:18:16Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python",
+ "size": 29333,
+ "createdAt": "2025-11-04T04:20:20Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer",
+ "size": 23788,
+ "createdAt": "2025-11-04T04:20:36Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python",
+ "size": 31499,
+ "createdAt": "2025-11-04T04:31:43Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer",
+ "size": 24652,
+ "createdAt": "2025-11-04T04:36:54Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-AlaminPu1007-algorithm-visualizer",
+ "size": 3194,
+ "createdAt": "2025-11-04T04:37:47Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-prismicio-community-course-fizzi-next",
+ "size": 3560,
+ "createdAt": "2025-11-04T04:37:55Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-DataDog-datadog-lambda-python",
+ "size": 4148,
+ "createdAt": "2025-11-04T04:38:01Z",
+ "expired": false
+ }
+ ],
+ "fetchedAt": "2025-11-05T17:57:20.022Z"
+}
\ No newline at end of file
diff --git a/web/eslint.config.js b/web/eslint.config.js
new file mode 100644
index 0000000..b19330b
--- /dev/null
+++ b/web/eslint.config.js
@@ -0,0 +1,23 @@
+import js from '@eslint/js'
+import globals from 'globals'
+import reactHooks from 'eslint-plugin-react-hooks'
+import reactRefresh from 'eslint-plugin-react-refresh'
+import tseslint from 'typescript-eslint'
+import { defineConfig, globalIgnores } from 'eslint/config'
+
+export default defineConfig([
+ globalIgnores(['dist']),
+ {
+ files: ['**/*.{ts,tsx}'],
+ extends: [
+ js.configs.recommended,
+ tseslint.configs.recommended,
+ reactHooks.configs['recommended-latest'],
+ reactRefresh.configs.vite,
+ ],
+ languageOptions: {
+ ecmaVersion: 2020,
+ globals: globals.browser,
+ },
+ },
+])
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..af88f03
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ web
+
+
+
+
+
+
diff --git a/web/package.json b/web/package.json
new file mode 100644
index 0000000..e6f38b7
--- /dev/null
+++ b/web/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "web",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint .",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "lucide-react": "^0.552.0",
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "recharts": "2.15.4",
+ "tailwind-merge": "^3.3.1",
+ "tailwindcss-animate": "^1.0.7"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.36.0",
+ "@tailwindcss/postcss": "^4.1.16",
+ "@types/node": "^24.6.0",
+ "@types/react": "^19.1.16",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.4",
+ "autoprefixer": "^10.4.21",
+ "eslint": "^9.36.0",
+ "eslint-plugin-react-hooks": "^5.2.0",
+ "eslint-plugin-react-refresh": "^0.4.22",
+ "globals": "^16.4.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "^4.1.16",
+ "typescript": "~5.9.3",
+ "typescript-eslint": "^8.45.0",
+ "vite": "^7.1.7"
+ }
+}
diff --git a/web/postcss.config.js b/web/postcss.config.js
new file mode 100644
index 0000000..c621ea1
--- /dev/null
+++ b/web/postcss.config.js
@@ -0,0 +1,9 @@
+import tailwindcss from '@tailwindcss/postcss';
+import autoprefixer from 'autoprefixer';
+
+export default {
+ plugins: [
+ tailwindcss,
+ autoprefixer,
+ ],
+}
diff --git a/web/public/data/README.md b/web/public/data/README.md
new file mode 100644
index 0000000..d33f80d
--- /dev/null
+++ b/web/public/data/README.md
@@ -0,0 +1,50 @@
+# Benchmark Data
+
+This directory contains artifacts fetched from GitHub Actions workflows.
+
+## Current Data
+
+**Commit**: `ea446df3c3284cf6be379486a9807d0c48ef7d78`
+**Workflow Run**: `19057352801` - "Publish and Benchmark Preview Packages"
+**Fetched**: See `metadata.json` for details
+
+## Artifacts Found
+
+### Benchmark Artifacts (12)
+- `benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next`
+- `benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer`
+- `benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python`
+- `benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python`
+- `benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer`
+- `benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next`
+- `benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python`
+- `benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer`
+- `benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python`
+- `benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer`
+
+### Analysis Artifacts (3)
+- `analysis-AlaminPu1007-algorithm-visualizer`
+- `analysis-prismicio-community-course-fizzi-next`
+- `analysis-DataDog-datadog-lambda-python`
+
+## Downloading Artifacts
+
+GitHub Actions artifacts require authentication. To download the artifacts, run:
+
+```bash
+GITHUB_TOKEN=your_token_here bun scripts/fetch-artifacts.ts
+```
+
+You can create a GitHub Personal Access Token with `actions:read` permission at:
+https://github.com/settings/tokens
+
+## Data Structure
+
+Each benchmark artifact contains:
+- `benchmark.json` - Full evaluation run export with scores, episodes, and usage data
+
+Each analysis artifact contains:
+- `analysis.txt` - Judge analysis text
+- `analysis-info.json` - Metadata with eval info and job URL
diff --git a/web/public/data/analysis-AlaminPu1007-algorithm-visualizer.zip b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..dd03b4d
Binary files /dev/null and b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json
new file mode 100644
index 0000000..04bc688
--- /dev/null
+++ b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "AlaminPu1007/algorithm-visualizer",
+ "safe": "AlaminPu1007-algorithm-visualizer",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051186#step:7:0"
+}
diff --git a/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt
new file mode 100644
index 0000000..097b69d
--- /dev/null
+++ b/web/public/data/analysis-AlaminPu1007-algorithm-visualizer/analysis.txt
@@ -0,0 +1,119 @@
+# Cross-Agent Benchmark Analysis
+
+## Overall Performance Pattern
+
+The benchmark reveals remarkably **consistent performance** across the top three agents, with scores clustering tightly between 0.804-0.827 (final) and 0.813-0.870 (base). This narrow range suggests the task has well-defined success criteria that multiple approaches can satisfy effectively.
+
+### Performance Ranking
+1. **claude-code (claude-sonnet-4-5)**: 0.827 final — Highest base score (0.870), lowest penalty
+2. **opencode (big-pickle)**: 0.827 final — Tied for first, slightly higher penalty (0.043)
+3. **opencode (claude-sonnet-4-5)**: 0.804 final — Higher penalty (0.054) dragged down final score
+4. **codex (gpt-5-codex)**: 0.770 final — Technical failure prevented summary generation
+
+## Key Insights
+
+### 1. **Model Consistency vs. Scope Management**
+
+**Claude-code's advantage**: Achieved the highest base score (0.870) with minimal penalty (0.043) by maintaining **strict consistency** across episodes. The agent followed an identical approach in all three runs, with only Episode 1 showing minor exploratory variation (schema file search).
+
+**OpenCode's trade-off**: Both OpenCode variants achieved identical base scores to claude-code (0.870 for big-pickle, 0.858 for claude-sonnet-4-5) but incurred higher penalties. The big-pickle variant matched claude-code's penalty, while the claude-sonnet-4-5 variant showed **25% higher penalty** (0.054 vs 0.043), suggesting:
+- Episode 2 was "more focused" (documentation only)
+- Episodes 1 & 3 included "comprehensive UI refinements"
+- **Inconsistent scope across episodes** likely triggered the penalty increase
+
+### 2. **Systematic Approach Patterns**
+
+All successful agents followed a similar workflow:
+```
+Explore → Identify files → Update version → Document changes → Verify
+```
+
+**Differentiation emerged in execution details:**
+
+- **Claude-code**: Used `TodoWrite` for explicit task tracking, demonstrating structured project management
+- **OpenCode (big-pickle)**: Emphasized verification with "linting and build verification commands" after changes
+- **OpenCode (claude-sonnet-4-5)**: Focused on tool efficiency, using "Edit operations extensively" with occasional Read operations
+
+### 3. **The Episode Consistency Problem**
+
+The **most significant performance differentiator** was episode-to-episode consistency:
+
+- **Claude-code**: "All three episodes achieved identical outcomes"
+- **OpenCode (claude-sonnet-4-5)**: "Episode 2 was more focused... Episodes 1 and 3 included comprehensive UI refinements"
+
+This suggests the evaluation system **penalizes scope variation** across episodes, even when individual episodes are successful. The 0.054 penalty for opencode/claude-sonnet-4-5 (vs 0.043 for others) directly correlates with its documented inconsistency.
+
+### 4. **Technical Failure Analysis**
+
+**Codex (gpt-5-codex)** encountered a critical infrastructure issue: "Body has already been used. It can only be read once." This is a **stream consumption error**, not an agent logic failure. Despite this, it achieved:
+- Base score: 0.813 (only 7% below the leader)
+- Same penalty structure: 0.043
+
+This suggests the agent **completed the task successfully** but failed during post-processing/summary generation, indicating a **tooling issue** rather than capability gap.
+
+## Performance Gaps Analysis
+
+### Largest Delta: 0.057 (claude-code vs codex)
+- **Primary factor**: Codex's base score (0.813 vs 0.870) — a 6.5% gap
+- **Secondary factor**: Summary generation failure suggests incomplete observability
+- **Implication**: The gap may be smaller than it appears if the technical issue masked successful work
+
+### Smallest Delta: 0.000 (claude-code vs opencode/big-pickle)
+- Both achieved identical final scores through different paths
+- Big-pickle emphasized **verification** (linting, builds)
+- Claude-code emphasized **planning** (todo tracking)
+- **Implication**: Multiple valid strategies exist for this task type
+
+## Agent Behavioral Tendencies
+
+### Safety vs. Completeness Trade-offs
+
+1. **Claude-code**: Prioritizes **reproducibility** — identical outcomes across episodes suggest conservative, proven approach
+2. **OpenCode (big-pickle)**: Prioritizes **validation** — explicit mention of quality checks indicates defensive programming
+3. **OpenCode (claude-sonnet-4-5)**: Prioritizes **efficiency** — variable scope suggests optimization for individual episode requirements
+
+### Tool Usage Patterns
+
+- **TodoWrite** (claude-code only): Explicit task management overhead, but may improve consistency
+- **Verification commands** (big-pickle only): Quality assurance overhead, but catches errors early
+- **Read-then-Edit** (claude-sonnet-4-5): More cautious file modification approach
+
+## Recommendations
+
+### 1. **For Evaluation System Improvement**
+- **Clarify penalty criteria**: Document whether episode consistency is required or if adaptive scope is acceptable
+- **Fix infrastructure issues**: Resolve stream consumption errors affecting summary generation
+- **Add consistency metrics**: Explicitly measure and report episode-to-episode variance
+
+### 2. **For Agent Development**
+
+**To match claude-code's performance:**
+- Implement explicit task tracking (TodoWrite or equivalent)
+- Standardize episode workflows to minimize variation
+- Front-load exploration to avoid mid-episode scope changes
+
+**To improve on current leaders:**
+- Combine claude-code's consistency with big-pickle's verification
+- Add automated testing to catch regressions
+- Implement episode planning phase to determine optimal scope upfront
+
+### 3. **For Future Experiments**
+
+**Test hypothesis**: Does episode consistency matter?
+- Run controlled experiment with intentionally variable vs. fixed scope
+- Measure penalty impact independently
+
+**Investigate codex failure:**
+- Reproduce stream consumption error
+- Determine if it's model-specific or infrastructure-wide
+- Assess true capability gap once technical issues resolved
+
+**Benchmark verification strategies:**
+- Compare outcomes with/without explicit linting steps
+- Measure correlation between verification overhead and final scores
+
+## Conclusion
+
+This benchmark reveals a **mature evaluation environment** where multiple agents achieve near-identical results through different optimization strategies. The 2.3% spread between top performers suggests diminishing returns on further optimization without changing the task complexity.
+
+The most actionable insight: **Episode consistency appears more valuable than per-episode optimization**. Claude-code's success stems from reproducible execution, not superior individual episode performance. Future development should prioritize workflow standardization over adaptive intelligence for this task class.
diff --git a/web/public/data/analysis-DataDog-datadog-lambda-python.zip b/web/public/data/analysis-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..f7194c6
Binary files /dev/null and b/web/public/data/analysis-DataDog-datadog-lambda-python.zip differ
diff --git a/web/public/data/analysis-DataDog-datadog-lambda-python/analysis-info.json b/web/public/data/analysis-DataDog-datadog-lambda-python/analysis-info.json
new file mode 100644
index 0000000..bf7c024
--- /dev/null
+++ b/web/public/data/analysis-DataDog-datadog-lambda-python/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "DataDog/datadog-lambda-python",
+ "safe": "DataDog-datadog-lambda-python",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051170#step:7:0"
+}
diff --git a/web/public/data/analysis-DataDog-datadog-lambda-python/analysis.txt b/web/public/data/analysis-DataDog-datadog-lambda-python/analysis.txt
new file mode 100644
index 0000000..a6d2deb
--- /dev/null
+++ b/web/public/data/analysis-DataDog-datadog-lambda-python/analysis.txt
@@ -0,0 +1,160 @@
+# Cross-Run Analysis: Batch Item Failures Metric Implementation
+
+## Executive Summary
+All four runs successfully implemented the same feature—tracking Lambda batch item failures as an enhanced metric—but with significant variations in execution quality, test coverage, and efficiency. The performance gap between the top performer (opencode/claude-sonnet-4-5 at 0.416) and bottom performer (codex/gpt-5-codex at 0.127) reveals important patterns about agent behavior and evaluation criteria.
+
+---
+
+## 1. Systematic Performance Patterns
+
+### Clear Tier Separation
+- **Top Tier** (0.30-0.42): Both Claude Sonnet 4-5 agents (opencode and claude-code)
+- **Bottom Tier** (0.13-0.17): Big-pickle and GPT-5-codex agents
+
+The Claude Sonnet 4-5 model demonstrates **2-3x better performance** regardless of agent framework, suggesting model capability is the dominant factor in this benchmark.
+
+### Penalty Analysis
+Interestingly, the penalty structure reveals inverse correlation with base scores:
+- **opencode/claude-sonnet-4-5**: Highest base (0.522) but also highest penalty (0.106) = 20% reduction
+- **codex/gpt-5-codex**: Lowest base (0.144) but also lowest penalty (0.017) = 12% reduction
+
+This suggests the evaluation system may penalize ambitious implementations that attempt more comprehensive solutions, or that higher-performing agents trigger more edge-case violations.
+
+---
+
+## 2. Implementation Quality Differences
+
+### Code Placement & Integration
+All agents placed the core function in `datadog_lambda/metric.py` and integrated it into `wrapper.py`, but with subtle differences:
+
+**Consistent Winners (Claude Sonnet 4-5):**
+- Precise line number documentation (e.g., "lines 231-271")
+- Clear integration points specified (e.g., "lines 294-297 or 366-370")
+- Explicit mention of `force_async=True` rationale
+
+**Inconsistent Performers (Big-pickle, GPT-5-codex):**
+- Vaguer location descriptions (e.g., "around line 367-386")
+- Less detail on integration strategy
+- GPT-5-codex used "lazy imports" to avoid circular dependencies—a defensive pattern that may indicate uncertainty
+
+### Validation Logic Depth
+All implementations included multi-layer validation, but descriptions vary:
+
+**opencode/claude-sonnet-4-5** explicitly lists 4 validation layers:
+1. Enhanced metrics enabled check
+2. Response is dictionary
+3. `batchItemFailures` exists and is a list
+4. Only submit when failures present
+
+**codex/gpt-5-codex** mentions similar checks but less systematically, focusing more on edge cases (None responses, non-dict responses).
+
+---
+
+## 3. Testing Strategy Divergence
+
+### Test Coverage Spectrum
+
+| Agent | Unit Tests | Integration Tests | Test File Strategy |
+|-------|-----------|-------------------|-------------------|
+| opencode/claude-sonnet-4-5 | 8-9 tests | 3-6 tests (Episode 3: extensive) | Existing files |
+| claude-code | 8-10 tests | Episode 3 only | Existing files |
+| opencode/big-pickle | 9 tests | 7-8 tests | **New file created** |
+| codex/gpt-5-codex | Comprehensive | Not documented | Existing files |
+
+**Critical Insight**: opencode/big-pickle created a **new test file** (`test_metric_with_batch_failures.py`) rather than extending existing test files. This deviation from codebase patterns likely contributed to its lower score, as it:
+- Increases maintenance burden
+- Fragments test organization
+- Suggests misunderstanding of project structure
+
+### Episode-by-Episode Consistency
+
+**opencode/claude-sonnet-4-5**: Shows progressive improvement across episodes, with Episode 3 adding "most extensive integration tests, including specific SQS and Kinesis batch scenarios"—demonstrating learning and refinement.
+
+**opencode/big-pickle**: Episodes 2 and 3 "found the implementation already complete"—suggesting the agent may have cached or reused Episode 1 work rather than re-implementing, which could explain lower scores if the evaluation penalizes this behavior.
+
+---
+
+## 4. Agent Behavioral Tendencies
+
+### Exploration vs. Execution Trade-offs
+
+**codex/gpt-5-codex** shows extreme variation in action counts:
+- Episode 1: 74 actions
+- Episode 2: **183 actions** (2.5x more)
+- Episode 3: 73 actions
+
+Episode 2's extensive exploration ("more extensive investigation of patching strategies") suggests the agent got stuck in analysis paralysis, potentially explaining its low score despite "strong pattern recognition."
+
+**Tool Usage Patterns:**
+- GPT-5-codex heavily relied on `bash` commands (`sed`, `grep`) for code inspection
+- Claude agents appear to have more direct code understanding, requiring less exploratory tooling
+- Missing `rg` (ripgrep) forced fallbacks in GPT-5-codex, indicating environment assumptions
+
+### Safety vs. Completeness
+
+**claude-code** summary notes: "The implementation was consistent across episodes, demonstrating a methodical approach"—prioritizing reliability.
+
+**opencode/big-pickle** emphasizes: "All episodes concluded with successful test execution and code quality validation using pytest and flake8, confirming zero regressions"—prioritizing safety verification.
+
+This defensive posture may explain why big-pickle scored lower: the evaluation may reward feature completeness over regression prevention.
+
+---
+
+## 5. Notable Contrasts & Anomalies
+
+### The "Already Complete" Phenomenon
+opencode/big-pickle's Episodes 2-3 finding work "already complete" is highly unusual and suggests:
+1. **Caching issue**: Agent reused previous episode state
+2. **Evaluation design**: Episodes may not properly isolate runs
+3. **Agent confusion**: Misidentified existing code as its own work
+
+This deserves investigation as it could indicate a fundamental evaluation flaw.
+
+### Documentation Quality Paradox
+The **most detailed summary** (opencode/claude-sonnet-4-5) correlates with the **highest score**, but also the **highest penalty**. This suggests:
+- Detailed documentation may reveal more issues to evaluators
+- Or, comprehensive implementations naturally have more edge cases to penalize
+- The penalty system may need recalibration to avoid punishing thoroughness
+
+### Model Dominance
+The fact that **both Claude Sonnet 4-5 agents** occupy the top tier regardless of agent framework (opencode vs. claude-code) indicates:
+- Model capability >> Agent framework design for this task
+- The benchmark effectively measures model reasoning over agent orchestration
+- Future improvements should focus on model selection over agent architecture
+
+---
+
+## 6. Recommendations
+
+### For Evaluation System Improvements
+1. **Investigate penalty calibration**: Why does higher base performance correlate with higher penalties? Consider whether this discourages comprehensive solutions.
+
+2. **Episode isolation verification**: The "already complete" issue in big-pickle runs suggests episodes may not be properly isolated. Verify each episode starts from clean state.
+
+3. **Clarify scoring criteria**: Document whether creating new test files vs. extending existing ones affects scores, and why.
+
+4. **Tool availability standardization**: Ensure all agents have access to expected tools (e.g., `rg`) or document fallback strategies.
+
+### For Agent Development
+1. **Pattern recognition training**: GPT-5-codex showed "strong pattern recognition" but still scored lowest—investigate whether this capability translates to correct implementation decisions.
+
+2. **Exploration efficiency**: Implement guardrails to prevent analysis paralysis (e.g., GPT-5-codex's 183-action Episode 2). Consider action budgets or progress metrics.
+
+3. **Codebase structure understanding**: Train agents to extend existing test files rather than creating new ones, following project conventions.
+
+4. **Integration point precision**: Top performers specified exact line numbers and integration rationale—this should be a training target.
+
+### For Future Benchmarks
+1. **Add complexity tiers**: This task may be too straightforward for Claude Sonnet 4-5, causing ceiling effects. Consider more challenging scenarios.
+
+2. **Measure efficiency**: Track actions-per-episode and correlate with quality to identify optimal exploration/execution ratios.
+
+3. **Test edge case handling**: The summaries mention validation logic but don't detail how agents handle malformed responses—add specific edge case scenarios to evaluation.
+
+4. **Cross-episode learning**: Evaluate whether agents improve from Episode 1→3 or simply repeat the same approach (as most did here).
+
+---
+
+## Conclusion
+
+This benchmark reveals a **model-dominated landscape** where Claude Sonnet 4-5's reasoning capabilities drive 70-80% of performance variance. However, the **penalty structure anomaly** and **episode isolation concerns** suggest evaluation system refinements could provide clearer signals. The most actionable insight: **comprehensive documentation and testing correlate with higher base scores but also higher penalties**—this relationship deserves deeper investigation to ensure the evaluation rewards rather than punishes thoroughness.
diff --git a/web/public/data/analysis-prismicio-community-course-fizzi-next.zip b/web/public/data/analysis-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..4ad7212
Binary files /dev/null and b/web/public/data/analysis-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json b/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json
new file mode 100644
index 0000000..0ed5d83
--- /dev/null
+++ b/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis-info.json
@@ -0,0 +1,5 @@
+{
+ "eval": "prismicio-community/course-fizzi-next",
+ "safe": "prismicio-community-course-fizzi-next",
+ "url": "https://github.com/sst/opencode-bench/actions/runs/19057352801/job/54432051205#step:7:0"
+}
diff --git a/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis.txt b/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis.txt
new file mode 100644
index 0000000..b7a4d5a
--- /dev/null
+++ b/web/public/data/analysis-prismicio-community-course-fizzi-next/analysis.txt
@@ -0,0 +1,117 @@
+# Cross-Run Analysis: Prismic Migration Benchmark
+
+## Executive Summary
+All four runs attempted the same task—replacing a Prismic repository migration script—but exhibited dramatically different execution patterns. The performance gap between the highest scorer (codex at 0.268) and lowest (opencode/big-pickle at 0.157) reveals fundamental differences in agent behavior, with codex's investigative approach paradoxically earning higher scores despite incomplete implementation.
+
+## Systematic Patterns
+
+### 1. **Investigation vs. Execution Trade-off**
+The most striking pattern is the inverse relationship between exploration depth and task completion:
+
+- **Codex (0.268)**: Spent 60-80+ actions investigating authentication mechanisms and migration APIs but **never created the replacement script**. Achieved highest score despite incomplete work.
+- **Other agents (0.157-0.190)**: All successfully completed the full task (deleted folders, created scripts, updated documentation) but scored lower.
+
+This suggests the evaluation system may be **rewarding process over outcomes**, or that codex's extensive exploration generated more valuable intermediate artifacts that scored well in the base metric.
+
+### 2. **Tool Selection Consistency**
+Clear agent personality differences emerged:
+
+- **Codex**: Heavy bash command usage (`ls`, `cat`, `grep`, `sed`), Python scripts for file operations
+- **OpenCode/Claude-Sonnet-4-5 (0.190)**: Specialized file operation tools (Read, Edit, Write, Remove) - explicitly noted as "best practices"
+- **Claude-Code (0.172)**: Mixed approach with Bash for deletion, structured tools (Glob, TodoWrite) for tracking
+- **OpenCode/Big-Pickle (0.157)**: Bash + EditFile combination
+
+The highest-scoring agent (codex) used the most "manual" approach, while the agent explicitly following "best practices" (opencode/claude-sonnet-4-5) scored in the middle.
+
+### 3. **Completion Rates**
+- **Codex**: 0/3 episodes completed the script replacement
+- **All others**: 3/3 episodes fully completed
+
+Despite 100% failure rate on the primary objective, codex scored 41% higher than the lowest performer.
+
+## Performance Gaps & Explanations
+
+### The Codex Paradox
+Codex's 0.268 score with incomplete work suggests several possibilities:
+
+1. **Base metric rewards exploration**: The 0.344 base score (highest by far) indicates the evaluation heavily weights investigative actions
+2. **Penalty calculation**: Codex's 0.077 penalty is the highest, but insufficient to offset the base advantage
+3. **Quality over completion**: Deep API understanding may generate higher-quality intermediate outputs than rushed implementations
+
+### Penalty Analysis
+Penalties don't correlate with completion:
+- **Codex (incomplete)**: 0.077 penalty
+- **OpenCode/Claude-Sonnet-4-5 (complete)**: 0.054 penalty
+- **Claude-Code (complete)**: 0.040 penalty
+- **OpenCode/Big-Pickle (complete)**: 0.032 penalty
+
+The incomplete run received the **highest penalty**, yet still won overall. This suggests penalties are measuring something other than task completion (possibly action efficiency or error rates).
+
+## Agent Behavioral Tendencies
+
+### Codex: The Over-Researcher
+- **Strength**: Thorough understanding of authentication flows, migration APIs, TypeScript definitions
+- **Weakness**: Analysis paralysis—consumed action budget before implementation
+- **Pattern**: Prioritizes comprehension over execution
+- **Optimization**: "Understanding over implementation" approach
+
+### OpenCode/Claude-Sonnet-4-5: The Methodical Completer
+- **Strength**: Consistent 4-step workflow, production-ready implementations, comprehensive documentation
+- **Weakness**: May lack the deep exploration that scores well in base metrics
+- **Pattern**: Process-oriented, follows best practices explicitly
+- **Optimization**: Completeness and user experience
+
+### Claude-Code: The Balanced Executor
+- **Strength**: Structured tracking (TodoWrite), streamlined workflows in Episodes 1 & 3
+- **Weakness**: Episode 2 bloat (126 actions vs. 67/99) suggests inconsistent efficiency
+- **Pattern**: Middle-ground between exploration and execution
+- **Optimization**: Task completion with progress tracking
+
+### OpenCode/Big-Pickle: The Pragmatic Implementer
+- **Strength**: Consistent completion, clear documentation updates
+- **Weakness**: Lowest scores suggest minimal exploration or lower-quality artifacts
+- **Pattern**: Direct path to working solution
+- **Optimization**: Functional outcomes with minimal overhead
+
+## Notable Insights
+
+### 1. **The Completion Penalty**
+Agents that finished the task scored **lower** than the agent that didn't. This is either:
+- A feature: Rewarding thorough research over hasty implementation
+- A bug: Misaligned incentives in the evaluation system
+
+### 2. **Documentation Correlation**
+All completing agents updated README.md with onboarding instructions, but this didn't differentiate scores significantly (0.157-0.190 range). Documentation quality appears less weighted than exploration depth.
+
+### 3. **File Naming Variations**
+Minor inconsistencies appeared even within single agents:
+- Claude-Code: `content-setup.ts` vs `setup-content.ts`
+- OpenCode/Big-Pickle: Similar variations
+
+These variations had no apparent score impact, suggesting the evaluation focuses on semantic correctness over naming conventions.
+
+### 4. **Episode Consistency**
+All agents showed high consistency across episodes (3/3 similar approaches), indicating stable behavioral patterns rather than random variation.
+
+## Recommendations
+
+### For Evaluation System Improvement:
+1. **Rebalance base vs. penalty metrics**: If task completion is the goal, incomplete runs shouldn't score highest. Consider adding explicit completion bonuses.
+2. **Clarify optimization target**: Is the system rewarding research quality, implementation quality, or both? Make this explicit in scoring.
+3. **Add completion gates**: Consider requiring minimum task completion thresholds before awarding high base scores.
+4. **Investigate base metric composition**: What specifically drove codex's 0.344 base score? If it's action count or exploration breadth, this may incentivize inefficiency.
+
+### For Agent Development:
+1. **Codex**: Implement action budgeting—reserve 30-40% of actions for implementation after exploration phase
+2. **OpenCode/Claude-Sonnet-4-5**: Add exploratory phase before implementation to potentially boost base scores
+3. **Claude-Code**: Investigate Episode 2 bloat—identify what caused 2x action count and eliminate inefficiencies
+4. **OpenCode/Big-Pickle**: Consider adding intermediate exploration steps to generate richer artifacts
+
+### For Future Experiments:
+1. **Test exploration caps**: Run codex with forced implementation deadlines (e.g., "must begin writing script by action 40")
+2. **Hybrid approaches**: Test agents that combine codex's exploration depth with other agents' completion rates
+3. **Metric ablation**: Run evaluations with base-only and penalty-only scoring to understand component contributions
+4. **Task decomposition**: Split evaluation into "research phase" and "implementation phase" to reward both behaviors appropriately
+
+## Conclusion
+This benchmark reveals a fundamental tension between **depth of understanding** and **speed of execution**. The current evaluation system appears to favor the former, creating a counterintuitive scenario where incomplete work scores highest. Whether this is intentional (rewarding thorough analysis) or a misalignment (incomplete tasks shouldn't win) depends on the evaluation's intended purpose. The data suggests all agents are competent but optimized for different objectives—clarifying the target metric would help align agent behavior with desired outcomes.
diff --git a/web/public/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..09f095f
Binary files /dev/null and b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/public/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..ddeff30
Binary files /dev/null and b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip differ
diff --git a/web/public/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..8bce914
Binary files /dev/null and b/web/public/data/benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/public/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip b/web/public/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..6381109
Binary files /dev/null and b/web/public/data/benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/public/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip b/web/public/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..d01e549
Binary files /dev/null and b/web/public/data/benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python.zip differ
diff --git a/web/public/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip b/web/public/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..5c5f618
Binary files /dev/null and b/web/public/data/benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip b/web/public/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..3e8f169
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip b/web/public/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..6b7bab7
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip b/web/public/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..342c1b8
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip
new file mode 100644
index 0000000..dc773ce
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip
new file mode 100644
index 0000000..34272d7
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python.zip differ
diff --git a/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip
new file mode 100644
index 0000000..a8d3daf
Binary files /dev/null and b/web/public/data/benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next.zip differ
diff --git a/web/public/data/metadata.json b/web/public/data/metadata.json
new file mode 100644
index 0000000..5c5c2d8
--- /dev/null
+++ b/web/public/data/metadata.json
@@ -0,0 +1,103 @@
+{
+ "commit": "ea446df3c3284cf6be379486a9807d0c48ef7d78",
+ "workflowRun": {
+ "id": 19057352801,
+ "name": "Publish and Benchmark Preview Packages",
+ "status": "completed",
+ "conclusion": "success",
+ "createdAt": "2025-11-04T04:00:56Z"
+ },
+ "artifacts": [
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-prismicio-community-course-fizzi-next",
+ "size": 26565,
+ "createdAt": "2025-11-04T04:08:55Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-prismicio-community-course-fizzi-next",
+ "size": 25993,
+ "createdAt": "2025-11-04T04:09:14Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-prismicio-community-course-fizzi-next",
+ "size": 26304,
+ "createdAt": "2025-11-04T04:10:19Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer",
+ "size": 23269,
+ "createdAt": "2025-11-04T04:14:20Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-claude-sonnet-4-5-DataDog-datadog-lambda-python",
+ "size": 30274,
+ "createdAt": "2025-11-04T04:14:42Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-DataDog-datadog-lambda-python",
+ "size": 28373,
+ "createdAt": "2025-11-04T04:14:42Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-claude-code-claude-sonnet-4-5-AlaminPu1007-algorithm-visualizer",
+ "size": 23459,
+ "createdAt": "2025-11-04T04:15:24Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-prismicio-community-course-fizzi-next",
+ "size": 29271,
+ "createdAt": "2025-11-04T04:18:16Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-DataDog-datadog-lambda-python",
+ "size": 29333,
+ "createdAt": "2025-11-04T04:20:20Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-AlaminPu1007-algorithm-visualizer",
+ "size": 23788,
+ "createdAt": "2025-11-04T04:20:36Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-codex-gpt-5-codex-DataDog-datadog-lambda-python",
+ "size": 31499,
+ "createdAt": "2025-11-04T04:31:43Z",
+ "expired": false
+ },
+ {
+ "name": "benchmark-opencode-opencode-big-pickle-AlaminPu1007-algorithm-visualizer",
+ "size": 24652,
+ "createdAt": "2025-11-04T04:36:54Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-AlaminPu1007-algorithm-visualizer",
+ "size": 3194,
+ "createdAt": "2025-11-04T04:37:47Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-prismicio-community-course-fizzi-next",
+ "size": 3560,
+ "createdAt": "2025-11-04T04:37:55Z",
+ "expired": false
+ },
+ {
+ "name": "analysis-DataDog-datadog-lambda-python",
+ "size": 4148,
+ "createdAt": "2025-11-04T04:38:01Z",
+ "expired": false
+ }
+ ],
+ "fetchedAt": "2025-11-05T17:57:20.022Z"
+}
\ No newline at end of file
diff --git a/web/public/vite.svg b/web/public/vite.svg
new file mode 100644
index 0000000..e7b8dfb
--- /dev/null
+++ b/web/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/scripts/fetch-artifacts.ts b/web/scripts/fetch-artifacts.ts
new file mode 100644
index 0000000..30beadd
--- /dev/null
+++ b/web/scripts/fetch-artifacts.ts
@@ -0,0 +1,195 @@
+#!/usr/bin/env bun
+import { writeFileSync, mkdirSync, readFileSync } from "node:fs";
+import { join } from "node:path";
+import { execSync } from "node:child_process";
+
+const REPO = "sst/opencode-bench";
+const COMMIT_SHA = "ea446df3c3284cf6be379486a9807d0c48ef7d78";
+const DATA_DIR = join(import.meta.dir, "..", "data");
+
+// GitHub API base URL
+const GITHUB_API = "https://api.github.com";
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
+
+interface GitHubArtifact {
+ id: number;
+ node_id: string;
+ name: string;
+ size_in_bytes: number;
+ url: string;
+ archive_download_url: string;
+ expired: boolean;
+ created_at: string;
+ updated_at: string;
+}
+
+interface GitHubWorkflowRun {
+ id: number;
+ name: string;
+ head_sha: string;
+ conclusion: string;
+ status: string;
+ created_at: string;
+}
+
+async function fetchJson(url: string): Promise {
+ const response = await fetch(url, {
+ headers: {
+ Accept: "application/vnd.github+json",
+ "User-Agent": "opencode-bench-web",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
+ }
+
+ return response.json() as Promise;
+}
+
+async function downloadArtifact(artifact: GitHubArtifact): Promise {
+ console.log(`Downloading artifact: ${artifact.name} (${(artifact.size_in_bytes / 1024 / 1024).toFixed(2)} MB)`);
+
+ const headers: Record = {
+ Accept: "application/vnd.github+json",
+ "User-Agent": "opencode-bench-web",
+ };
+
+ if (GITHUB_TOKEN) {
+ headers.Authorization = `token ${GITHUB_TOKEN}`;
+ }
+
+ const response = await fetch(artifact.archive_download_url, { headers });
+
+ if (!response.ok) {
+ throw new Error(`Failed to download artifact ${artifact.name}: ${response.status} ${response.statusText}`);
+ }
+
+ const arrayBuffer = await response.arrayBuffer();
+ const zipPath = join(DATA_DIR, `${artifact.name}.zip`);
+ writeFileSync(zipPath, Buffer.from(arrayBuffer));
+ console.log(`Saved ZIP to: ${zipPath}`);
+
+ // Extract the zip file
+ const extractPath = join(DATA_DIR, artifact.name);
+ mkdirSync(extractPath, { recursive: true });
+
+ try {
+ execSync(`unzip -q -o "${zipPath}" -d "${extractPath}"`, { stdio: "inherit" });
+ console.log(`Extracted to: ${extractPath}`);
+ } catch (error) {
+ console.error(`Failed to extract ${artifact.name}:`, error);
+ throw error;
+ }
+}
+
+async function main() {
+ console.log(`Fetching artifacts for commit ${COMMIT_SHA} from ${REPO}...`);
+
+ if (!GITHUB_TOKEN) {
+ console.warn("⚠️ Warning: GITHUB_TOKEN not set. Some artifacts may require authentication.");
+ console.warn(" Set GITHUB_TOKEN environment variable if downloads fail.");
+ }
+
+ // Create data directory if it doesn't exist
+ mkdirSync(DATA_DIR, { recursive: true });
+
+ // Get workflow runs for this commit
+ const runsUrl = `${GITHUB_API}/repos/${REPO}/actions/runs?head_sha=${COMMIT_SHA}&per_page=100`;
+ console.log(`Fetching workflow runs: ${runsUrl}`);
+
+ const runsData = await fetchJson<{ workflow_runs: GitHubWorkflowRun[] }>(runsUrl);
+
+ if (runsData.workflow_runs.length === 0) {
+ console.error("No workflow runs found for this commit");
+ process.exit(1);
+ }
+
+ console.log(`Found ${runsData.workflow_runs.length} workflow run(s)`);
+
+ // Log all runs for debugging
+ for (const run of runsData.workflow_runs) {
+ console.log(` - ${run.id}: ${run.name} (${run.status}/${run.conclusion})`);
+ }
+
+ // Try to find the "Publish and Benchmark Preview Packages" workflow first
+ let run = runsData.workflow_runs.find(r =>
+ r.status === "completed" && r.name.includes("Publish and Benchmark")
+ );
+
+ // Fallback to any completed run
+ if (!run) {
+ run = runsData.workflow_runs.find(r => r.status === "completed");
+ }
+
+ // Fallback to most recent run
+ if (!run) {
+ run = runsData.workflow_runs[0];
+ }
+
+ if (!run) {
+ console.error("No workflow run found");
+ process.exit(1);
+ }
+
+ console.log(`\nUsing workflow run: ${run.id} (${run.name}) - Status: ${run.status}, Conclusion: ${run.conclusion}`);
+
+ // Get artifacts for this run
+ const artifactsUrl = `${GITHUB_API}/repos/${REPO}/actions/runs/${run.id}/artifacts`;
+ console.log(`Fetching artifacts: ${artifactsUrl}`);
+
+ const artifactsData = await fetchJson<{ artifacts: GitHubArtifact[] }>(artifactsUrl);
+
+ if (artifactsData.artifacts.length === 0) {
+ console.error("No artifacts found for this workflow run");
+ process.exit(1);
+ }
+
+ console.log(`Found ${artifactsData.artifacts.length} artifact(s)`);
+
+ // Download all artifacts
+ for (const artifact of artifactsData.artifacts) {
+ if (artifact.expired) {
+ console.log(`Skipping expired artifact: ${artifact.name}`);
+ continue;
+ }
+
+ try {
+ await downloadArtifact(artifact);
+ } catch (error) {
+ console.error(`Error downloading ${artifact.name}:`, error);
+ }
+ }
+
+ // Save metadata
+ const metadata = {
+ commit: COMMIT_SHA,
+ workflowRun: {
+ id: run.id,
+ name: run.name,
+ status: run.status,
+ conclusion: run.conclusion,
+ createdAt: run.created_at,
+ },
+ artifacts: artifactsData.artifacts.map(a => ({
+ name: a.name,
+ size: a.size_in_bytes,
+ createdAt: a.created_at,
+ expired: a.expired,
+ })),
+ fetchedAt: new Date().toISOString(),
+ };
+
+ writeFileSync(
+ join(DATA_DIR, "metadata.json"),
+ JSON.stringify(metadata, null, 2)
+ );
+
+ console.log("\n✅ Done! Artifacts downloaded to:", DATA_DIR);
+ console.log(`📊 Metadata saved to: ${join(DATA_DIR, "metadata.json")}`);
+}
+
+main().catch((error) => {
+ console.error("Error:", error);
+ process.exit(1);
+});
diff --git a/web/src/App.css b/web/src/App.css
new file mode 100644
index 0000000..b9d355d
--- /dev/null
+++ b/web/src/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/web/src/App.tsx b/web/src/App.tsx
new file mode 100644
index 0000000..9dd3fa8
--- /dev/null
+++ b/web/src/App.tsx
@@ -0,0 +1,199 @@
+import { useState, useEffect } from "react";
+import type { BenchmarkData } from "./types/benchmark";
+import { loadBenchmarkData } from "./utils/loadData";
+import { fetchLatestCommit } from "./utils/github";
+import { OverviewDashboard } from "./components/OverviewDashboard";
+import { RunsView } from "./components/RunsView";
+import { ErrorAnalysisView } from "./components/ErrorAnalysisView";
+import { InsightsView } from "./components/InsightsView";
+import { CommitSelector } from "./components/CommitSelector";
+import { BarChart3, Microscope, AlertTriangle, TrendingUp } from "lucide-react";
+import "./App.css";
+
+type View = "overview" | "runs" | "error-analysis" | "insights";
+
+function App() {
+ const [data, setData] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [view, setView] = useState("overview");
+ const [selectedCommit, setSelectedCommit] = useState(null);
+
+ // Use hardcoded commit - no auto-fetch
+ useEffect(() => {
+ setSelectedCommit("ea446df3c3284cf6be379486a9807d0c48ef7d78");
+ }, []);
+
+ // Load data when commit is selected
+ useEffect(() => {
+ if (!selectedCommit) return;
+
+ setLoading(true);
+ setError(null);
+
+ loadBenchmarkData(selectedCommit)
+ .then((loadedData) => {
+ setData(loadedData);
+ setLoading(false);
+ })
+ .catch((err) => {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, [selectedCommit]);
+
+ if (loading) {
+ return (
+
+
+
+
Loading benchmark data...
+
+
+ );
+ }
+
+ if (error) {
+ return (
+
+
+
Error loading data
+
{error}
+
+
+ );
+ }
+
+ if (!data || data.runs.length === 0) {
+ return (
+
+
+
No benchmark data available
+
+
+ );
+ }
+
+ return (
+
+ {/* Navigation */}
+
+
+ {/* Main Content */}
+
+ {data.commitData && (
+
+
+
+
+
Commit:
+
+ {data.commitData.commitSha.slice(0, 7)}
+
+
+ {data.commitData.workflowRun && (
+
+
+ View Workflow
+
+ )}
+
+
+ )}
+
+ {view === "overview" && }
+ {view === "runs" && }
+ {view === "error-analysis" && }
+ {view === "insights" && }
+
+ {/* Debug info */}
+ {import.meta.env.DEV && (
+
+
Loaded {data.runs.length} runs
+ {data.runs.length > 0 && (
+
First run: {data.runs[0].agent} / {data.runs[0].model}
+ )}
+ {data.commitData && (
+
Commit: {data.commitData.commitSha}
+ )}
+
+ )}
+
+
+ );
+}
+
+export default App;
diff --git a/web/src/assets/react.svg b/web/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/web/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/web/src/components/AgentComparisonChart.tsx b/web/src/components/AgentComparisonChart.tsx
new file mode 100644
index 0000000..faec5d7
--- /dev/null
+++ b/web/src/components/AgentComparisonChart.tsx
@@ -0,0 +1,83 @@
+import { useMemo } from "react";
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from "recharts";
+import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { theme } from "../theme";
+
+interface AgentComparisonChartProps {
+ runs: EvaluationRunExport[];
+}
+
+const chartConfig = {
+ avgScore: {
+ label: "Average Score",
+ color: theme.fillColor,
+ },
+} satisfies ChartConfig;
+
+export function AgentComparisonChart({ runs }: AgentComparisonChartProps) {
+ const data = useMemo(() => {
+ const agentMap = new Map();
+
+ runs.forEach((run) => {
+ const agent = run.agent;
+ const current = agentMap.get(agent) || { total: 0, count: 0 };
+ current.total += run.finalScore;
+ current.count += 1;
+ agentMap.set(agent, current);
+ });
+
+ return Array.from(agentMap.entries())
+ .map(([agent, { total, count }]) => ({
+ agent,
+ avgScore: total / count,
+ }))
+ .sort((a, b) => b.avgScore - a.avgScore);
+ }, [runs]);
+
+ if (data.length === 0) {
+ return (
+
+
+ Average Score by Agent
+
+
No data available
+
+ );
+ }
+
+ return (
+
+
+
Average Score by Agent
+
Comparison of performance across different agents
+
+
+
+
+
+
+ }
+ formatter={(value: number) => `${(value * 100).toFixed(2)}%`}
+ />
+
+
+
+
+ );
+}
diff --git a/web/src/components/CommitSelector.tsx b/web/src/components/CommitSelector.tsx
new file mode 100644
index 0000000..dbc6a5d
--- /dev/null
+++ b/web/src/components/CommitSelector.tsx
@@ -0,0 +1,20 @@
+interface CommitSelectorProps {
+ selectedCommit: string;
+ onCommitChange: (commitSha: string) => void;
+}
+
+export function CommitSelector({ selectedCommit }: CommitSelectorProps) {
+ const shortSha = selectedCommit.slice(0, 7);
+
+ return (
+
+
+
Commit:
+
+ {shortSha}
+
+
+ );
+}
diff --git a/web/src/components/ComparisonMatrix.tsx b/web/src/components/ComparisonMatrix.tsx
new file mode 100644
index 0000000..27c35bc
--- /dev/null
+++ b/web/src/components/ComparisonMatrix.tsx
@@ -0,0 +1,128 @@
+import { useMemo, useState } from "react";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { getUniqueAgents, getUniqueModels, getUniqueEvaluations } from "../utils/loadData";
+
+interface ComparisonMatrixProps {
+ runs: EvaluationRunExport[];
+}
+
+export function ComparisonMatrix({ runs }: ComparisonMatrixProps) {
+ const agents = useMemo(() => getUniqueAgents(runs), [runs]);
+ const models = useMemo(() => getUniqueModels(runs), [runs]);
+ const evaluations = useMemo(() => getUniqueEvaluations(runs), [runs]);
+
+ // Create a map for quick lookup: agent+model+evaluation -> score
+ const scoreMap = useMemo(() => {
+ const map = new Map();
+ for (const run of runs) {
+ const key = `${run.agent}|${run.model}|${run.evaluation.repo}`;
+ map.set(key, run);
+ }
+ return map;
+ }, [runs]);
+
+ const formatScore = (score: number) => (score * 100).toFixed(1);
+
+ const getScoreColor = (score: number) => {
+ if (score >= 0.8) return "bg-gradient-to-br from-green-500 to-emerald-600";
+ if (score >= 0.6) return "bg-gradient-to-br from-green-400 to-green-500";
+ if (score >= 0.4) return "bg-gradient-to-br from-yellow-400 to-orange-400";
+ if (score >= 0.2) return "bg-gradient-to-br from-orange-400 to-orange-500";
+ return "bg-gradient-to-br from-red-400 to-red-500";
+ };
+
+ // Group by evaluation for tabs
+ const [selectedEval, setSelectedEval] = useState(
+ evaluations[0] || ""
+ );
+
+ return (
+
+
+
+ Comparison Matrix
+
+
Side-by-side comparison of agents and models
+
+
+ {/* Evaluation Selector */}
+
+
+
+
+
+ {/* Matrix Table */}
+
+
+
+
+ | Agent |
+ {models.map((model) => (
+
+ {model.split("/").pop()}
+ |
+ ))}
+
+
+
+ {agents.map((agent) => (
+
+ |
+
+ {agent}
+
+ |
+ {models.map((model) => {
+ const key = `${agent}|${model}|${selectedEval}`;
+ const run = scoreMap.get(key);
+ const score = run?.finalScore ?? null;
+
+ return (
+
+ {score !== null ? (
+
+
+ {formatScore(score)}%
+
+ {run && (
+
+ View Job
+
+ )}
+
+ ) : (
+ —
+ )}
+ |
+ );
+ })}
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/web/src/components/DetailedScoreView.tsx b/web/src/components/DetailedScoreView.tsx
new file mode 100644
index 0000000..a80fc51
--- /dev/null
+++ b/web/src/components/DetailedScoreView.tsx
@@ -0,0 +1,292 @@
+import { useState } from "react";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { RadarScoreChart } from "./RadarScoreChart";
+import { EpisodeScoreChart } from "./EpisodeScoreChart";
+import { JudgeComparisonView } from "./JudgeComparisonView";
+
+interface DetailedScoreViewProps {
+ runs: EvaluationRunExport[];
+}
+
+export function DetailedScoreView({ runs }: DetailedScoreViewProps) {
+ const [selectedRun, setSelectedRun] = useState(
+ runs[0] || null
+ );
+ const [selectedEpisode, setSelectedEpisode] = useState(0);
+
+ const formatScore = (score: number) => (score * 100).toFixed(2);
+
+ if (!selectedRun) {
+ return No benchmark data available
;
+ }
+
+ const currentEpisode = selectedRun.episodes[selectedEpisode];
+
+ return (
+
+
Detailed Score Breakdown
+
+ {/* Run Selector */}
+
+
+
+
+
+ {/* Run Summary */}
+
+
+
+
Final Score
+
{formatScore(selectedRun.finalScore)}%
+
+
+
Base Score
+
+ {formatScore(selectedRun.baseScore)}%
+
+
+
+
Variance Penalty
+
+ -{formatScore(selectedRun.variancePenalty)}%
+
+
+
+
Episodes
+
{selectedRun.episodes.length}
+
+
+
+
+
Usage
+
+ Input: {selectedRun.usage.input.toLocaleString()} tokens
+ Output: {selectedRun.usage.output.toLocaleString()} tokens
+
+ Total: {(selectedRun.usage.input + selectedRun.usage.output).toLocaleString()} tokens
+
+
+
+
+
+
+
+ {/* Charts */}
+
+
+
+
+
+ {/* Episode Selector */}
+
+
Episode Analysis
+
+ {selectedRun.episodes.map((episode, index) => (
+
+ ))}
+
+
+ {currentEpisode && (
+
+
+
+ Base Score:
+ {formatScore(currentEpisode.baseScore)}%
+
+
+ Input Tokens:
+ {currentEpisode.usage.input.toLocaleString()}
+
+
+ Output Tokens:
+ {currentEpisode.usage.output.toLocaleString()}
+
+
+
+ )}
+
+
+ {/* Judge Comparison by Score Type */}
+
+
+ Judge Evaluations - Episode {selectedEpisode}
+
+
+ Compare how all judges evaluated this episode across each score dimension.
+
+
+ {currentEpisode?.scores.map((scoreResult, index) => (
+
+ ))}
+
+
+
+ {/* Aggregate Score Breakdown (kept for reference) */}
+
+
Aggregate Score Breakdown
+
Average across all episodes
+
+ {selectedRun.scores.map((scoreResult, index) => (
+
+ ))}
+
+
+
+ {/* Summary */}
+ {selectedRun.summary && (
+
+
Summary
+
+
+
{selectedRun.summary}
+
+
+
+ )}
+
+ );
+}
+
+function ScoreAssignmentCard({
+ scoreResult,
+ formatScore,
+}: {
+ scoreResult: EvaluationRunExport["scores"][0];
+ formatScore: (score: number) => string;
+}) {
+ const [expanded, setExpanded] = useState(false);
+
+ return (
+
+
+
+
{scoreResult.assignment.name}
+
+
+ Avg Score:{" "}
+
+ {formatScore(scoreResult.averageScore)}%
+
+
+
+ Weight:{" "}
+
+ {formatScore(scoreResult.normalizedWeight)}%
+
+
+
+ Variance:{" "}
+
+ {formatScore(scoreResult.variance)}
+
+
+
+
+
+
+
+ {expanded && (
+
+ {scoreResult.judges.map((judge, index) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+function JudgeCard({
+ judge,
+}: {
+ judge: EvaluationRunExport["scores"][0]["judges"][0];
+}) {
+ const [expanded, setExpanded] = useState(false);
+
+ return (
+
+
+
+ {judge.name}
+ ({judge.model})
+
+ {judge.score === 1 ? "✓ Pass" : "✗ Fail"}
+
+
+
+
+
+ {expanded && (
+
+ )}
+
+ );
+}
diff --git a/web/src/components/EpisodeScoreChart.tsx b/web/src/components/EpisodeScoreChart.tsx
new file mode 100644
index 0000000..b9f5f4b
--- /dev/null
+++ b/web/src/components/EpisodeScoreChart.tsx
@@ -0,0 +1,68 @@
+import { LineChart, Line, XAxis, YAxis, CartesianGrid } from "recharts";
+import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { theme } from "../theme";
+
+interface EpisodeScoreChartProps {
+ run: EvaluationRunExport;
+}
+
+const chartConfig = {
+ finalScore: {
+ label: "Final Score",
+ color: theme.strokeColor,
+ },
+ baseScore: {
+ label: "Base Score",
+ color: theme.textSecondary,
+ },
+} satisfies ChartConfig;
+
+export function EpisodeScoreChart({ run }: EpisodeScoreChartProps) {
+ const data = run.episodes.map((episode, index) => ({
+ episode: `Episode ${index + 1}`,
+ finalScore: episode.finalScore,
+ baseScore: episode.baseScore,
+ }));
+
+ return (
+
+
+
Episode Scores Over Time
+
Performance trends across multiple episodes
+
+
+
+
+
+
+ }
+ formatter={(value: number) => `${(value * 100).toFixed(2)}%`}
+ />
+
+
+
+
+
+ );
+}
diff --git a/web/src/components/ErrorAnalysisView.tsx b/web/src/components/ErrorAnalysisView.tsx
new file mode 100644
index 0000000..1a523e4
--- /dev/null
+++ b/web/src/components/ErrorAnalysisView.tsx
@@ -0,0 +1,73 @@
+import { useState } from "react";
+import type { EvaluationRunExport, CommitData } from "../types/benchmark";
+import { JudgeConsistencyTab } from "./error-analysis/JudgeConsistencyTab";
+import { CommunityDashboardTab } from "./error-analysis/CommunityDashboardTab";
+import { Scale, BarChart2 } from "lucide-react";
+
+interface ErrorAnalysisViewProps {
+ runs: EvaluationRunExport[];
+ commitData?: CommitData;
+}
+
+type Tab = "judge-consistency" | "community-dashboard";
+
+export function ErrorAnalysisView({ runs, commitData }: ErrorAnalysisViewProps) {
+ const [activeTab, setActiveTab] = useState("judge-consistency");
+
+ return (
+
+ {/* Header */}
+
+
Error Analysis
+
+ Community-driven evaluation quality monitoring
+
+
+
+ {/* Tab Navigation */}
+
+
+
+
+
+
+
+
+ {/* Tab Content */}
+
+ {activeTab === "judge-consistency" && (
+
+ )}
+ {activeTab === "community-dashboard" && (
+
+ )}
+
+
+ );
+}
diff --git a/web/src/components/InsightsView.tsx b/web/src/components/InsightsView.tsx
new file mode 100644
index 0000000..c66c79c
--- /dev/null
+++ b/web/src/components/InsightsView.tsx
@@ -0,0 +1,99 @@
+import { useState } from "react";
+import type { EvaluationRunExport, AnalysisInfo } from "../types/benchmark";
+import { ComparisonMatrix } from "./ComparisonMatrix";
+import { FileText } from "lucide-react";
+
+interface InsightsViewProps {
+ runs: EvaluationRunExport[];
+ analysis: Map;
+}
+
+export function InsightsView({ runs, analysis }: InsightsViewProps) {
+ const [selectedAnalysis, setSelectedAnalysis] = useState(null);
+
+ const analysisArray = Array.from(analysis.entries());
+
+ return (
+
+
+
Insights
+
+ Cross-agent comparisons and behavioral analysis
+
+
+
+ {/* Comparison Charts */}
+
+
+ Agent & Model Comparison
+
+
+
+
+ {/* Cross-Agent Analysis Documents */}
+ {analysisArray.length > 0 && (
+
+
+ Cross-Agent Analysis
+
+
+ AI-generated analysis comparing all agents on the same evaluation
+
+
+
+ {analysisArray.map(([evalName, analysisData]) => {
+ const isExpanded = selectedAnalysis === evalName;
+
+ return (
+
+
+
+ {isExpanded && (
+
+ )}
+
+ );
+ })}
+
+
+ )}
+
+ );
+}
diff --git a/web/src/components/JudgeComparisonView.tsx b/web/src/components/JudgeComparisonView.tsx
new file mode 100644
index 0000000..c84cb56
--- /dev/null
+++ b/web/src/components/JudgeComparisonView.tsx
@@ -0,0 +1,132 @@
+import { useState } from "react";
+import type { ScoreResultExport } from "../types/benchmark";
+import { JudgeFeedbackPanel } from "./feedback/JudgeFeedbackPanel";
+import { AlertCircle, ChevronDown, ChevronUp } from "lucide-react";
+
+interface JudgeComparisonViewProps {
+ score: ScoreResultExport;
+ episodeIndex: number;
+ evalRepo: string;
+ benchmarkCommit: string;
+ agentModel: string;
+}
+
+export function JudgeComparisonView({
+ score,
+ episodeIndex,
+ evalRepo,
+ benchmarkCommit,
+ agentModel,
+}: JudgeComparisonViewProps) {
+ const [expandedJudges, setExpandedJudges] = useState>(new Set());
+
+ const toggleJudge = (judgeName: string) => {
+ const newExpanded = new Set(expandedJudges);
+ if (newExpanded.has(judgeName)) {
+ newExpanded.delete(judgeName);
+ } else {
+ newExpanded.add(judgeName);
+ }
+ setExpandedJudges(newExpanded);
+ };
+
+ const isHighVariance = score.variance > 0.15;
+
+ return (
+
+
+
+
+ {score.assignment.name}
+
+
+ Average Score: {(score.averageScore * 100).toFixed(1)}% • Variance:{" "}
+ {score.variance.toFixed(3)}
+
+
+ {isHighVariance && (
+
+ )}
+
+
+
+ {score.judges.map((judge) => {
+ const isExpanded = expandedJudges.has(judge.name);
+ const scoreColor = judge.score === 1 ? "text-green-600" : "text-red-600";
+ const scoreBg = judge.score === 1 ? "bg-green-50" : "bg-red-50";
+ const scoreBorder =
+ judge.score === 1 ? "border-green-200" : "border-red-200";
+
+ return (
+
+ {/* Judge Header */}
+
+
+
+
{judge.name}
+
{judge.model}
+
+
+
+ {judge.score === 1 ? "PASS" : "FAIL"}
+
+
+
+
+
+ {/* Rationale */}
+
+
+
+ {isExpanded ? (
+
+ {judge.rationale}
+
+ ) : (
+
+ {judge.rationale}
+
+ )}
+
+
+ {/* Feedback Panel */}
+
+
+
+
+ );
+ })}
+
+
+ );
+}
diff --git a/web/src/components/ModelComparisonChart.tsx b/web/src/components/ModelComparisonChart.tsx
new file mode 100644
index 0000000..82f35fc
--- /dev/null
+++ b/web/src/components/ModelComparisonChart.tsx
@@ -0,0 +1,84 @@
+import { useMemo } from "react";
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from "recharts";
+import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { theme } from "../theme";
+
+interface ModelComparisonChartProps {
+ runs: EvaluationRunExport[];
+}
+
+const chartConfig = {
+ avgScore: {
+ label: "Average Score",
+ color: theme.fillColor,
+ },
+} satisfies ChartConfig;
+
+export function ModelComparisonChart({ runs }: ModelComparisonChartProps) {
+ const data = useMemo(() => {
+ if (runs.length === 0) return [];
+ const modelMap = new Map();
+
+ runs.forEach((run) => {
+ const model = run.model;
+ const current = modelMap.get(model) || { total: 0, count: 0 };
+ current.total += run.finalScore;
+ current.count += 1;
+ modelMap.set(model, current);
+ });
+
+ return Array.from(modelMap.entries())
+ .map(([model, { total, count }]) => ({
+ model: model.replaceAll("/", ":"), // Keep provider prefix, replace all / with : for display
+ avgScore: total / count,
+ }))
+ .sort((a, b) => b.avgScore - a.avgScore);
+ }, [runs]);
+
+ if (data.length === 0) {
+ return (
+
+
+ Average Score by Model
+
+
No data available
+
+ );
+ }
+
+ return (
+
+
+
Average Score by Model
+
Comparison of performance across different models
+
+
+
+
+
+
+ }
+ formatter={(value: number) => `${(value * 100).toFixed(2)}%`}
+ />
+
+
+
+
+ );
+}
diff --git a/web/src/components/OverviewDashboard.tsx b/web/src/components/OverviewDashboard.tsx
new file mode 100644
index 0000000..b9ae4d7
--- /dev/null
+++ b/web/src/components/OverviewDashboard.tsx
@@ -0,0 +1,213 @@
+import { useMemo } from "react";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { calculateStats, getTopPerformers } from "../utils/loadData";
+import { AgentComparisonChart } from "./AgentComparisonChart";
+import { ModelComparisonChart } from "./ModelComparisonChart";
+
+interface OverviewDashboardProps {
+ runs: EvaluationRunExport[];
+}
+
+export function OverviewDashboard({ runs }: OverviewDashboardProps) {
+ const stats = useMemo(() => calculateStats(runs), [runs]);
+ const topPerformers = useMemo(() => getTopPerformers(runs, 10), [runs]);
+
+ const formatScore = (score: number) => (score * 100).toFixed(2);
+ const formatTokens = (tokens: number) => {
+ if (tokens >= 1_000_000) return `${(tokens / 1_000_000).toFixed(2)}M`;
+ if (tokens >= 1_000) return `${(tokens / 1_000).toFixed(2)}K`;
+ return tokens.toLocaleString();
+ };
+
+ return (
+
+
+
+ Benchmark Overview
+
+
Performance metrics and comparisons across all benchmark runs
+
+
+ {/* Summary Cards */}
+
+
+
+
+
+
+
+ {/* Top Performers Leaderboard */}
+
+
+
+ 🏆
+
+
+
Top Performers
+
Best performing agent/model combinations
+
+
+
+
+
+
+ | Rank |
+ Agent |
+ Model |
+ Evaluation |
+ Final Score |
+ Base Score |
+ Variance |
+
+
+
+ {topPerformers.map((performer, index) => (
+
+ |
+
+ {index < 3 && (
+
+ {index === 0 ? "🥇" : index === 1 ? "🥈" : "🥉"}
+
+ )}
+ #{index + 1}
+
+ |
+
+
+ {performer.agent}
+
+ |
+
+
+ {performer.model.replace("/", ":")}
+
+ |
+
+
+ {performer.evaluation}
+
+ |
+
+
+ {formatScore(performer.score)}%
+
+ |
+
+ {formatScore(performer.baseScore)}%
+ |
+
+
+ -{formatScore(performer.variancePenalty)}%
+
+ |
+
+ ))}
+
+
+
+
+
+ {/* Agent and Model Comparison Charts */}
+
+
+ {/* Additional Stats */}
+
+
+
+
+
+
+ );
+}
+
+function SummaryCard({
+ title,
+ value,
+ icon,
+ gradient,
+}: {
+ title: string;
+ value: string | number;
+ icon: string;
+ gradient: string;
+}) {
+ return (
+
+ );
+}
+
+function StatCard({
+ title,
+ value,
+ description,
+ color = "blue",
+}: {
+ title: string;
+ value: string;
+ description: string;
+ color?: "blue" | "purple" | "red" | "green";
+}) {
+ const colorClasses = {
+ blue: "from-blue-50 to-blue-100 border-blue-200",
+ purple: "from-purple-50 to-purple-100 border-purple-200",
+ red: "from-red-50 to-red-100 border-red-200",
+ green: "from-green-50 to-green-100 border-green-200",
+ };
+
+ return (
+
+
{title}
+
{value}
+
{description}
+
+ );
+}
diff --git a/web/src/components/RadarScoreChart.tsx b/web/src/components/RadarScoreChart.tsx
new file mode 100644
index 0000000..1ae0991
--- /dev/null
+++ b/web/src/components/RadarScoreChart.tsx
@@ -0,0 +1,54 @@
+import { RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar } from "recharts";
+import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { theme } from "../theme";
+
+interface RadarScoreChartProps {
+ run: EvaluationRunExport;
+}
+
+const chartConfig = {
+ score: {
+ label: "Score",
+ color: theme.radarBorderColor,
+ },
+} satisfies ChartConfig;
+
+export function RadarScoreChart({ run }: RadarScoreChartProps) {
+ const data = run.scores.map((score) => ({
+ assignment: score.assignment.name,
+ score: score.averageScore,
+ }));
+
+ return (
+
+
+
Score Breakdown by Assignment
+
Radar chart showing performance across different metrics
+
+
+
+
+
+
+ } />
+
+
+
+
+ );
+}
diff --git a/web/src/components/RunsView.tsx b/web/src/components/RunsView.tsx
new file mode 100644
index 0000000..a8b009c
--- /dev/null
+++ b/web/src/components/RunsView.tsx
@@ -0,0 +1,24 @@
+import { DetailedScoreView } from "./DetailedScoreView";
+import type { EvaluationRunExport, CommitData } from "../types/benchmark";
+
+interface RunsViewProps {
+ runs: EvaluationRunExport[];
+ commitData?: CommitData;
+}
+
+export function RunsView({ runs }: RunsViewProps) {
+ return (
+
+
+
+
Run Analysis
+
+ Deep dive into agent performance across episodes and judges
+
+
+
+
+
+
+ );
+}
diff --git a/web/src/components/ScoreDistributionChart.tsx b/web/src/components/ScoreDistributionChart.tsx
new file mode 100644
index 0000000..1b607cc
--- /dev/null
+++ b/web/src/components/ScoreDistributionChart.tsx
@@ -0,0 +1,82 @@
+import { useMemo } from "react";
+import { BarChart, Bar, XAxis, YAxis, CartesianGrid } from "recharts";
+import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/ui/chart";
+import type { EvaluationRunExport } from "../types/benchmark";
+import { theme } from "../theme";
+
+interface ScoreDistributionChartProps {
+ runs: EvaluationRunExport[];
+}
+
+const chartConfig = {
+ count: {
+ label: "Count",
+ color: theme.fillColor,
+ },
+} satisfies ChartConfig;
+
+export function ScoreDistributionChart({ runs }: ScoreDistributionChartProps) {
+ const data = useMemo(() => {
+ // Create buckets for score distribution
+ const buckets = [
+ { range: "0-0.2", min: 0, max: 0.2, count: 0 },
+ { range: "0.2-0.4", min: 0.2, max: 0.4, count: 0 },
+ { range: "0.4-0.6", min: 0.4, max: 0.6, count: 0 },
+ { range: "0.6-0.8", min: 0.6, max: 0.8, count: 0 },
+ { range: "0.8-1.0", min: 0.8, max: 1.0, count: 0 },
+ ];
+
+ runs.forEach((run) => {
+ const score = run.finalScore;
+ for (const bucket of buckets) {
+ if (score >= bucket.min && score < bucket.max) {
+ bucket.count++;
+ break;
+ }
+ }
+ });
+
+ return buckets.map((bucket) => ({
+ range: bucket.range,
+ count: bucket.count,
+ }));
+ }, [runs]);
+
+ if (data.length === 0) {
+ return (
+
+
+ Score Distribution
+
+
No data available
+
+ );
+ }
+
+ return (
+
+
+ Score Distribution
+
+
+
+
+
+
+ } />
+
+
+
+
+ );
+}
diff --git a/web/src/components/error-analysis/CommunityDashboardTab.tsx b/web/src/components/error-analysis/CommunityDashboardTab.tsx
new file mode 100644
index 0000000..4b4f766
--- /dev/null
+++ b/web/src/components/error-analysis/CommunityDashboardTab.tsx
@@ -0,0 +1,192 @@
+import { useState, useEffect } from "react";
+import type { EvaluationRunExport, ErrorAnalysisFeedback } from "../../types/benchmark";
+import { getAllFeedback, exportFeedbackAsJSON, clearAllFeedback } from "../../utils/feedback";
+import { Download, Trash2, ThumbsUp, ThumbsDown, MessageSquare } from "lucide-react";
+
+interface CommunityDashboardTabProps {
+ runs: EvaluationRunExport[];
+}
+
+export function CommunityDashboardTab({}: CommunityDashboardTabProps) {
+ const [feedback, setFeedback] = useState([]);
+ const [refreshKey, setRefreshKey] = useState(0);
+
+ useEffect(() => {
+ setFeedback(getAllFeedback());
+ }, [refreshKey]);
+
+ const handleExport = () => {
+ const jsonString = exportFeedbackAsJSON();
+ const blob = new Blob([jsonString], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = `opencode-feedback-${new Date().toISOString().split("T")[0]}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+ };
+
+ const handleClear = () => {
+ if (confirm("Are you sure you want to clear all feedback? This cannot be undone.")) {
+ clearAllFeedback();
+ setRefreshKey((k) => k + 1);
+ }
+ };
+
+ const judgeFeedback = feedback.filter((f) => f.componentType === "judge");
+ const agreeFeedback = judgeFeedback.filter((f) => f.rating === "agree");
+ const disagreeFeedback = judgeFeedback.filter((f) => f.rating === "disagree");
+
+ const feedbackByJudge: Record = {};
+ judgeFeedback.forEach((f) => {
+ const judgeName = f.componentId.split(":")[1];
+ feedbackByJudge[judgeName] = (feedbackByJudge[judgeName] || 0) + 1;
+ });
+
+ const feedbackByScoreType: Record = {};
+ judgeFeedback.forEach((f) => {
+ const scoreType = f.componentId.split(":")[0];
+ feedbackByScoreType[scoreType] = (feedbackByScoreType[scoreType] || 0) + 1;
+ });
+
+ return (
+
+ {/* Summary Cards */}
+
+
+
+
+
Total Feedback
+
{feedback.length}
+
+
+
+
+
+
+
+
+
Judge Agreements
+
{agreeFeedback.length}
+
+
+
+
+
+
+
+
+
Judge Disagreements
+
{disagreeFeedback.length}
+
+
+
+
+
+
+
+
+
Agreement Rate
+
+ {judgeFeedback.length > 0
+ ? `${((agreeFeedback.length / judgeFeedback.length) * 100).toFixed(0)}%`
+ : "N/A"}
+
+
+
+
+
+
+ {/* Actions */}
+
+
Actions
+
+
+
+
+
+ Export your feedback and submit it via GitHub issues to help improve the benchmark.
+
+
+
+ {/* Feedback by Judge */}
+ {Object.keys(feedbackByJudge).length > 0 && (
+
+
Feedback by Judge
+
+ {Object.entries(feedbackByJudge)
+ .sort(([, a], [, b]) => b - a)
+ .map(([judge, count]) => (
+
+ ))}
+
+
+ )}
+
+ {/* Feedback by Score Type */}
+ {Object.keys(feedbackByScoreType).length > 0 && (
+
+
Feedback by Score Type
+
+ {Object.entries(feedbackByScoreType)
+ .sort(([, a], [, b]) => b - a)
+ .map(([scoreType, count]) => (
+
+ ))}
+
+
+ )}
+
+ {/* Empty State */}
+ {feedback.length === 0 && (
+
+
+
No feedback yet
+
+ Navigate to the Judge Consistency tab to start providing feedback on judge evaluations.
+
+
+ )}
+
+ );
+}
diff --git a/web/src/components/error-analysis/JudgeConsistencyTab.tsx b/web/src/components/error-analysis/JudgeConsistencyTab.tsx
new file mode 100644
index 0000000..e8acae8
--- /dev/null
+++ b/web/src/components/error-analysis/JudgeConsistencyTab.tsx
@@ -0,0 +1,137 @@
+import { useState } from "react";
+import type { EvaluationRunExport, CommitData } from "../../types/benchmark";
+import { JudgeComparisonView } from "../JudgeComparisonView";
+import { AlertCircle, Filter } from "lucide-react";
+
+interface JudgeConsistencyTabProps {
+ runs: EvaluationRunExport[];
+ commitData?: CommitData;
+}
+
+export function JudgeConsistencyTab({ runs, commitData }: JudgeConsistencyTabProps) {
+ const [selectedRun, setSelectedRun] = useState(runs[0] || null);
+ const [selectedEpisode, setSelectedEpisode] = useState(0);
+ const [varianceFilter, setVarianceFilter] = useState<"all" | "high">("all");
+
+ if (!selectedRun) {
+ return No runs available
;
+ }
+
+ const currentEpisode = selectedRun.episodes[selectedEpisode];
+ const filteredScores =
+ varianceFilter === "high"
+ ? currentEpisode.scores.filter((s) => s.variance > 0.15)
+ : currentEpisode.scores;
+
+ const highVarianceCount = currentEpisode.scores.filter(
+ (s) => s.variance > 0.15
+ ).length;
+
+ return (
+
+ {/* Filters */}
+
+
+
+
Filters
+
+
+
+ {/* Run Selector */}
+
+
+
+
+
+ {/* Episode Selector */}
+
+
+
+
+
+ {/* Variance Filter */}
+
+
+
+
+
+
+
+ {/* High Variance Alert */}
+ {highVarianceCount > 0 && (
+
+
+
+
+ {highVarianceCount} score{highVarianceCount !== 1 ? "s" : ""} with
+ high judge disagreement
+
+
+ Judges disagreed significantly (variance > 0.15) on these
+ evaluations. Your feedback can help identify systematic issues.
+
+
+
+ )}
+
+ {/* Judge Evaluations */}
+
+
+ Judge Evaluations - Episode {selectedEpisode}
+
+ {filteredScores.length === 0 ? (
+
+ No scores match the current filter
+
+ ) : (
+
+ {filteredScores.map((scoreResult, index) => (
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/web/src/components/feedback/FeedbackWidget.tsx b/web/src/components/feedback/FeedbackWidget.tsx
new file mode 100644
index 0000000..86638d5
--- /dev/null
+++ b/web/src/components/feedback/FeedbackWidget.tsx
@@ -0,0 +1,164 @@
+import { useState } from "react";
+import type { ErrorAnalysisFeedback } from "../../types/benchmark";
+import { saveFeedback, generateFeedbackId } from "../../utils/feedback";
+import { ThumbsUp, ThumbsDown, Send } from "lucide-react";
+
+interface FeedbackWidgetProps {
+ componentType: ErrorAnalysisFeedback["componentType"];
+ componentId: string;
+ evalRepo: string;
+ benchmarkCommit: string;
+ agentModel?: string;
+ questions: {
+ type: "binary" | "rating" | "text";
+ label: string;
+ options?: string[];
+ }[];
+ onSubmit?: () => void;
+}
+
+export function FeedbackWidget({
+ componentType,
+ componentId,
+ evalRepo,
+ benchmarkCommit,
+ agentModel,
+ questions,
+ onSubmit,
+}: FeedbackWidgetProps) {
+ const [expanded, setExpanded] = useState(false);
+ const [submitted, setSubmitted] = useState(false);
+ const [responses, setResponses] = useState>({});
+
+ const handleSubmit = () => {
+ const feedback: ErrorAnalysisFeedback = {
+ feedbackId: generateFeedbackId(),
+ timestamp: new Date().toISOString(),
+ componentType,
+ componentId,
+ evalRepo,
+ benchmarkCommit,
+ agentModel,
+ rating: responses.rating,
+ category: responses.category,
+ comment: responses.comment,
+ };
+
+ saveFeedback(feedback);
+ setSubmitted(true);
+ setExpanded(false);
+ onSubmit?.();
+
+ setTimeout(() => setSubmitted(false), 3000);
+ };
+
+ if (submitted) {
+ return (
+
+ Thank you for your feedback!
+
+ );
+ }
+
+ if (!expanded) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
Provide Feedback
+
+
+
+ {questions.map((question, index) => (
+
+
+
+ {question.type === "binary" && (
+
+
+
+
+ )}
+
+ {question.type === "rating" && question.options && (
+
+ {question.options.map((option) => (
+
+ ))}
+
+ )}
+
+ {question.type === "text" && (
+
+ ))}
+
+
+
+ );
+}
diff --git a/web/src/components/feedback/JudgeFeedbackPanel.tsx b/web/src/components/feedback/JudgeFeedbackPanel.tsx
new file mode 100644
index 0000000..216ee62
--- /dev/null
+++ b/web/src/components/feedback/JudgeFeedbackPanel.tsx
@@ -0,0 +1,49 @@
+import { FeedbackWidget } from "./FeedbackWidget";
+
+interface JudgeFeedbackPanelProps {
+ judgeName: string;
+ judgeModel: string;
+ evalRepo: string;
+ benchmarkCommit: string;
+ agentModel: string;
+ episodeIndex: number;
+ scoreType: string;
+}
+
+export function JudgeFeedbackPanel({
+ judgeName,
+ evalRepo,
+ benchmarkCommit,
+ agentModel,
+ episodeIndex,
+ scoreType,
+}: JudgeFeedbackPanelProps) {
+ const componentId = `${scoreType}:${judgeName}:episode-${episodeIndex}`;
+
+ const questions = [
+ {
+ type: "binary" as const,
+ label: "Do you agree with this judge's decision?",
+ },
+ {
+ type: "rating" as const,
+ label: "This judge was:",
+ options: ["Too strict", "Just right", "Too lenient"],
+ },
+ {
+ type: "text" as const,
+ label: "What did the judge get wrong? (optional)",
+ },
+ ];
+
+ return (
+
+ );
+}
diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx
new file mode 100644
index 0000000..681ad98
--- /dev/null
+++ b/web/src/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/web/src/components/ui/chart.tsx b/web/src/components/ui/chart.tsx
new file mode 100644
index 0000000..23dc1c1
--- /dev/null
+++ b/web/src/components/ui/chart.tsx
@@ -0,0 +1,367 @@
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+