foxygit / RPA-Remisser Log in
commit 08874ee7ca1a14bab1e0888d83c3c793ba4b7beb
Author:     Jens Kristoffersson <jens.kristoffersson.se@gmail.com>
AuthorDate: Fri Sep 4 13:28:46 2026 +0200
Commit:     Jens Kristoffersson <jens.kristoffersson.se@gmail.com>
CommitDate: Fri Sep 4 13:28:46 2026 +0200

    Fas 1-3: AI-koppling, node-canvas-UI och desktop-automation

    Gemensam stegschema (rpa/schema.py) som webui, MCP och NL-assistenten delar -
    en ny stegtyp laggs dar + i flow_engine sa syns den overallt.

    Fas 1 - AI:
    - rpa/mcp_server.py: MCP-server (stdio) for att lista/skapa/redigera/kora floden
    - rpa/assist.py + "AI-assistent"-panel: beskriv i text -> flode (Anthropic API)
    - rpa/flows.py: flodespersistens utbruten ur webui, med schema-validering

    Fas 2 - UI:
    - webui/frontend/: Vite + React + React Flow, node-canvas (byggt till webui/static/)
    - flodet lagras oforandrat som nastlad {steps}; graph.ts ar sommen mot framtida DAG
    - gamla webui/templates/index.html borttagen; app.py serverar webui/static/

    Fas 3 - desktop (Windows):
    - rpa/desktop.py: pywinauto (UIA) + pyautogui (bild/koordinat), lat import
    - 12 desktop_*-steg, rpa/desktop_inspect.py for att hitta selectors
    - startade appar stadas enligt close-mode

    Ovrigt:
    - skip_browser: floden utan webblasarsteg startar ingen webblasare (auto + tvingat)
    - install_win.ps1 / install_win.bat: Windows-installationsscript
    - tests/: 33 pytest + 5 vitest
---
 .gitignore                                        |    5 +
 README.md                                         |  145 +-
 TODO.md                                           |   59 +-
 install_win.bat                                   |    5 +
 install_win.ps1                                   |  104 +
 requirements-dev.txt                              |    2 +
 requirements.txt                                  |    8 +
 rpa/assist.py                                     |  160 +
 rpa/config.py                                     |    3 +-
 rpa/desktop.py                                    |  314 ++
 rpa/desktop_inspect.py                            |   41 +
 rpa/flow_engine.py                                |  221 +-
 rpa/flows.py                                      |   82 +
 rpa/mcp_server.py                                 |  180 ++
 rpa/runs.py                                       |    5 +-
 rpa/schema.py                                     |  906 ++++++
 run_mcp.py                                        |   10 +
 tests/test_assist.py                              |   72 +
 tests/test_desktop.py                             |   56 +
 tests/test_flows.py                               |   70 +
 tests/test_schema.py                              |  108 +
 tests/test_skip_browser.py                        |   83 +
 webui/app.py                                      |  112 +-
 webui/frontend/index.html                         |   12 +
 webui/frontend/package-lock.json                  | 3532 +++++++++++++++++++++
 webui/frontend/package.json                       |   29 +
 webui/frontend/src/App.tsx                        |  220 ++
 webui/frontend/src/components/AssistPanel.tsx     |   48 +
 webui/frontend/src/components/Canvas.tsx          |  177 ++
 webui/frontend/src/components/ConditionEditor.tsx |   44 +
 webui/frontend/src/components/Fields.tsx          |   98 +
 webui/frontend/src/components/Header.tsx          |   79 +
 webui/frontend/src/components/NestedSteps.tsx     |   92 +
 webui/frontend/src/components/RowFieldsEditor.tsx |   56 +
 webui/frontend/src/components/RunPanel.tsx        |   44 +
 webui/frontend/src/components/SettingsDialog.tsx  |   69 +
 webui/frontend/src/components/StepBody.tsx        |  132 +
 webui/frontend/src/components/StepNode.tsx        |  102 +
 webui/frontend/src/components/VarsPanel.tsx       |   50 +
 webui/frontend/src/lib/api.ts                     |  102 +
 webui/frontend/src/lib/graph.test.ts              |   83 +
 webui/frontend/src/lib/graph.ts                   |  133 +
 webui/frontend/src/lib/i18n.ts                    |  143 +
 webui/frontend/src/lib/ids.ts                     |    8 +
 webui/frontend/src/lib/layout.ts                  |   32 +
 webui/frontend/src/lib/schema.ts                  |   64 +
 webui/frontend/src/lib/stepops.ts                 |   62 +
 webui/frontend/src/main.tsx                       |   10 +
 webui/frontend/src/styles.css                     |  147 +
 webui/frontend/tsconfig.json                      |   22 +
 webui/frontend/vite.config.ts                     |   24 +
 webui/static/assets/index-CYM_2KrD.css            |    1 +
 webui/static/assets/index-ClN1xAxw.js             |   65 +
 webui/static/index.html                           |   13 +
 webui/templates/index.html                        | 1428 ---------
 55 files changed, 8359 insertions(+), 1513 deletions(-)

diff --git a/.gitignore b/.gitignore
index 9e0a3d3..3e21359 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,11 @@
 __pycache__/
 *.pyc

+# Frontend-bygget: node_modules ignoreras, men webui/static/ (byggd av
+# webui/frontend) CHECKAS IN så att run_webui.py funkar utan Node.
+webui/frontend/node_modules/
+webui/frontend/.vite/
+
 # Innehåller BankID-session, nedladdade dokument, traces, skärmdumpar och
 # exporterad data - kan innehålla personlig hälsoinformation. Se README.
 data/
diff --git a/README.md b/README.md
index 89d4b82..d41a142 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,22 @@ vid start:
 ./start.sh main  # aktiverar venv, kör main.py (fristående 1177-exempel)
 ```

-**Windows:**
+**Windows:** använd medföljande skript (engångssetup – skapar `.venv`,
+installerar paket + Playwright Chromium):
+
+```powershell
+powershell -ExecutionPolicy Bypass -File .\install_win.ps1
+```
+
+(eller dubbelklicka `install_win.bat`). Starta sedan:
+
+```powershell
+.venv\Scripts\Activate.ps1
+python run_webui.py     # webui:t på http://127.0.0.1:8765
+python main.py          # fristående 1177-exempel
+```
+
+Manuellt, om du hellre gör stegen själv:

 ```powershell
 python -m venv .venv
@@ -40,6 +55,108 @@ pip install -r requirements.txt
 python -m playwright install chromium
 ```

+Webb-UI:t är en färdigbyggd React-app (incheckad i `webui/static/`), så
+`python run_webui.py` funkar utan Node. Node behövs bara om du vill **ändra**
+UI:t – se "Utveckla webb-UI:t" nedan.
+
+## Webb-UI: node-canvas
+
+`python run_webui.py` → öppna http://127.0.0.1:8765. Flödet byggs på ett
+node-canvas (pan/zoom/minikarta): varje steg är en nod, kopplingarna visar
+ordningen, `loop`/`if` expanderas inuti sin nod. Dra en nod för att flytta den
+(positionen sparas med flödet), eller använd ↑/↓ på noden. "Autoplacera"
+återställer layouten.
+
+## Desktop-automation (Windows)
+
+Utöver webbläsaren kan flöden styra **Windows-appar** – både vanliga appar via
+Windows UI Automation och bild-/koordinatklick för appar utan tillgänglighets-API.
+Stegen heter `desktop_*` och syns i "Desktop (Windows)"-kategorin i nod-editorn.
+Kräver Windows + paketen i `requirements.txt` (`pywinauto`, `pyautogui`,
+`opencv-python` – installeras bara på Windows). Ett `desktop_*`-steg på annan
+plattform ger ett tydligt fel, inte en krasch.
+
+- **Starta app** (`desktop_launch`) – kör t.ex. `notepad.exe`. För paketerade
+  appar (Win11:s Notepad, Kalkylatorn m.fl.) har den startade processen inget
+  eget fönster – fyll i fältet **Fönstertitel** (regex) så hittas rätt fönster
+  ändå. **Anslut till fönster** (`desktop_connect`) attachar till en app som
+  redan körs.
+- **Klicka / Skriv i / Läs kontroll** – hitta en kontroll via
+  `name` (synlig text), `auto_id` (AutomationId), `control_type` (Button/Edit…)
+  eller `class_name`. `window` (titel-regex) begränsar till ett visst fönster;
+  tomt = "aktuellt" fönster (senast startat/anslutet).
+- **Tryck tangenter** (`desktop_press_key`) – pywinauto-syntax: `^s` = Ctrl+S,
+  `{ENTER}`, `%{F4}` …
+- **Klicka på bild / Vänta på bild** – lägg referensbilder i
+  `data/desktop_images/` och matcha dem på skärmen (pyautogui + OpenCV).
+- **Klicka på koordinat** (`desktop_click_xy`) – absoluta skärmkoordinater.
+
+Appar som ett flöde startat med `desktop_launch` städas enligt samma
+close-mode-val som webbläsaren (stäng alltid / håll öppen vid fel / håll alltid
+öppen).
+
+Ett flöde utan webbläsarsteg (bara `desktop_*` och variabelsteg) **startar ingen
+webbläsare** – det upptäcks automatiskt. Vill du tvinga bort den (t.ex. under
+utveckling) finns "Starta ingen webbläsare" i ⚙ Inställningar (`skip_browser`);
+ett webbläsarsteg med den påslagen ger då ett tydligt fel.
+
+Hitta rätt selectors med inspektionsverktyget:
+
+```powershell
+python -m rpa.desktop_inspect                  # listar synliga toppfönster
+python -m rpa.desktop_inspect "Anteckningar"   # dumpar kontroll-identifierare
+```
+
+## AI: bygg och kör flöden med en AI-agent
+
+Verktyget kan drivas av en AI på två sätt. Båda använder samma stegschema
+([`rpa/schema.py`](rpa/schema.py)) som webui:t, så en AI bara kan de steg som
+faktiskt finns.
+
+### 1. "Föreslå ändringar" i webui:t (beskriv i text → flöde)
+
+I sidopanelen finns en ruta "AI-assistent". Skriv på svenska vad flödet ska
+göra (t.ex. *"gå till example.com, vänta på inloggning, lista alla länkar och
+spara som CSV"*) och klicka **Föreslå ändringar**. Förslaget läggs in i editorn
+i osparat läge – granska, och tryck sedan **Spara** / **Kör flöde** som vanligt.
+
+Kräver en Claude-nyckel: sätt miljövariabeln `ANTHROPIC_API_KEY` (eller kör
+`ant auth login`) innan du startar `run_webui.py`. Modell styrs av
+`RPA_ASSIST_MODEL` (default `claude-opus-5`).
+
+### 2. MCP-server (Claude Code / Claude Desktop styr verktyget)
+
+[`rpa/mcp_server.py`](rpa/mcp_server.py) exponerar verktyget som en MCP-server:
+en AI-agent kan lista/hämta/skapa/redigera/ta bort flöden, validera dem, samt
+köra dem och läsa körloggen.
+
+Registrera i **Claude Code** (byt ut sökvägen mot din `.venv`-python):
+
+```powershell
+claude mcp add rpa -- C:\Users\<du>\Desktop\proj\RPA-Remisser\.venv\Scripts\python.exe -m rpa.mcp_server
+```
+
+Eller i **Claude Desktop** (`%AppData%\Claude\claude_desktop_config.json`):
+
+```json
+{
+  "mcpServers": {
+    "rpa": {
+      "command": "C:\\Users\\<du>\\Desktop\\proj\\RPA-Remisser\\.venv\\Scripts\\python.exe",
+      "args": ["-m", "rpa.mcp_server"]
+    }
+  }
+}
+```
+
+MCP-servern körs från projektmappen (den hittar `rpa/`-paketet via `-m`). Fråga
+sen agenten t.ex. *"lista mina RPA-flöden"*, *"skapa ett flöde som ..."*,
+*"kör flödet X och visa loggen"*.
+
+**OBS:** MCP-servern och webui:t delar samma Chromium-profilmapp
+(`data/browser_profile/`) – bara en körning åt gången. Stäng ett körande flöde i
+webui:t innan du kör ett via MCP, och tvärtom.
+
 ## Kom igång: hitta rätt selectors för en ny sida

 **Rekommenderat sätt: Playwright Codegen.** Den öppnar en webbläsare +
@@ -272,6 +389,32 @@ Task Scheduler. Tänk på att manuell inloggning (BankID/2FA) krävs varje
 gång sessionen i `data/browser_profile/` har gått ut, så helt obevakad
 körning fungerar bara så länge sessionen är giltig.

+## Utveckla webb-UI:t
+
+UI:t ligger i [`webui/frontend/`](webui/frontend/) (Vite + React + TypeScript,
+node-canvasen är [React Flow](https://reactflow.dev/)). Kräver Node 20+.
+
+```powershell
+cd webui\frontend
+npm install
+npm run dev      # dev-server på :5173, proxar /api till run_webui.py på :8765
+```
+
+Kör `python run_webui.py` i ett annat fönster samtidigt. Efter ändringar:
+
+```powershell
+npm run build    # bygger till ../static/
+npm test         # vitest (graf-round-trip)
+```
+
+**Checka in `webui/static/`** – den är själva UI:t som `run_webui.py` serverar.
+
+Stegtyperna kommer från `GET /api/step-schema` (dvs. [`rpa/schema.py`](rpa/schema.py)),
+så nya steg behöver bara läggas där + i `flow_engine.py` – UI:t renderar dem
+automatiskt. Flödet lagras oförändrat som `{steps, settings}`; `graph.ts`
+kompilerar den nästlade listan till/från noder+kanter (se kommentaren där om
+framtida äkta-DAG).
+
 ## Bygga en egen fristående automation

 Utöver webui:ts JSON-flöden går det förstås bra att skriva ett eget
diff --git a/TODO.md b/TODO.md
index 88b0079..d61fe67 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,28 +1,35 @@
-1. Desktop kontroll - styra datorn utanför web.
-   - Förslag: pywinauto (UIA-backend) som förstahandsval - hittar
-     kontroller via namn/automation-id/control-type, samma princip som
-     Playwright-selectorsen på browser-sidan. pyautogui som fallback
-     (bildmatchning/koordinater) för appar utan tillgänglighets-API.
-   - Nya stegtyper i flow_engine.py: desktop_click, desktop_type,
-     desktop_wait_for_window, desktop_screenshot, ev. desktop_click_image
-     (pyautogui-fallback). Samma by/value-mönster som befintliga
-     browser-steg.
-   - run_flow behöver hålla en desktop-referens vid sidan av page så ett
-     flöde kan blanda browser- och desktop-steg i samma sekvens.
-   - Lazy import av pywinauto inuti stegfunktionen (Windows-only) så
-     flow_engine.py inte kraschar vid import på icke-Windows.
-   - rpa/desktop_inspect.py i stil med discover.py - dumpar
-     control identifiers för aktivt fönster, för att hitta rätt
-     selectors manuellt (måste köras på Windows-maskinen med målappen).
-   - Webui: ny stegrupp "Desktop" i FIELD_DEFS/STEP_TYPES i index.html.
-   - OBS: om syftet är att automatisera BankID:s egen dialog (PIN/
-     godkännande) - troligen härdat mot UI Automation och mot BankIDs
-     användarvillkor. Klargör exakt vad som ska styras innan
-     implementation.
+## Status

-2. Lägga till att kunna köra custom python kod mellan
-   ex. lista extraheras
-   lista körs genom custom script för att få ut datan
-   lägg som vairabel
+- **Fas 1 (AI-koppling) – klar.** MCP-server ([rpa/mcp_server.py](rpa/mcp_server.py)),
+  NL-till-flöde ([rpa/assist.py](rpa/assist.py) + "AI-assistent"-panelen i webui:t),
+  och gemensamt stegschema ([rpa/schema.py](rpa/schema.py) – auktoritativ källa som
+  webui, MCP och assist alla använder). Se README-avsnittet "AI".
+- **Fas 2 (n8n-likt UI) – klar.** `webui/frontend/` (Vite + React +
+  React Flow), byggd till `webui/static/` (incheckad). Node-canvas, stegfält
+  renderas från `/api/step-schema`. Hybrid: flödet lagras oförändrat som
+  nästlad `{steps}`-lista; `webui/frontend/src/lib/graph.ts` är sömmen mot en
+  framtida äkta-DAG (loop/if redigeras just nu inuti sin nod, inte som egna
+  canvas-noder; fri kabeldragning saknas medvetet). `layout`-nyckeln i flödet
+  = frivilliga nodpositioner.
+- **Fas 3 (desktop-automation, Windows) – klar.** [rpa/desktop.py](rpa/desktop.py)
+  (pywinauto UIA + pyautogui bild/koordinat, lat import), 12 `desktop_*`-steg i
+  [rpa/schema.py](rpa/schema.py) + `flow_engine._DESKTOP_STEPS`/`_execute_desktop_step`.
+  `run_flow` håller en lat `DesktopSession` vid sidan av `page`; startade appar
+  städas enligt close-mode. [rpa/desktop_inspect.py](rpa/desktop_inspect.py) för
+  att hitta selectors. Syns i MCP/assist/UI via schemat. Se README "Desktop-automation".

-3. lägga till att kunna köra headless?
+---
+
+## Möjliga uppföljningar
+
+- **~~`skip_browser`~~ – klar.** Flöden utan webbläsarsteg startar ingen
+  webbläsare (auto-detektering via `flow_engine._flow_needs_browser`, eller
+  tvingat via inställningen `skip_browser`). `wait` använder nu `time.sleep`
+  i stället för `page.wait_for_timeout`.
+- **Äkta grafmotor / fri kabeldragning i UI:t** – sömmen finns i
+  `webui/frontend/src/lib/graph.ts` (se kommentaren där).
+- **Fler desktop-steg vid behov** – t.ex. `desktop_drag`, `desktop_menu_select`,
+  OCR-läsning. Lägg i `rpa/schema.py` + `flow_engine._execute_desktop_step`.
+
+Ursprungliga TODO-punkter 2 (kör custom python mellan steg) och 3 (headless) är
+redan lösta av `run_script`-steget respektive `headless`-inställningen.
diff --git a/install_win.bat b/install_win.bat
new file mode 100644
index 0000000..bdc5cd3
--- /dev/null
+++ b/install_win.bat
@@ -0,0 +1,5 @@
+@echo off
+REM Wrapper sa att install_win.ps1 kan koras med dubbelklick utan att
+REM behova andra PowerShells ExecutionPolicy.
+powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0install_win.ps1"
+pause
diff --git a/install_win.ps1 b/install_win.ps1
new file mode 100644
index 0000000..4031388
--- /dev/null
+++ b/install_win.ps1
@@ -0,0 +1,104 @@
+<#
+    Sätter upp projektet på Windows: skapar virtuell miljö (.venv),
+    installerar Python-paketen i requirements.txt samt Playwrights
+    Chromium-browser.
+
+    Körs en gång (eller på nytt om requirements.txt ändras):
+        powershell -ExecutionPolicy Bypass -File .\install_win.ps1
+
+    Starta sedan med:
+        .venv\Scripts\Activate.ps1
+        python run_webui.py        # webui:t på http://127.0.0.1:8765
+        python main.py             # fristående 1177-exempel
+#>
+
+$ErrorActionPreference = "Stop"
+Set-Location -Path $PSScriptRoot
+
+$VenvDir = ".venv"
+
+# Hitta en python-tolk. py-launchern föredras på Windows. Microsoft Store-
+# aliaset i ...\Microsoft\WindowsApps\ ignoreras (det är bara en stub som
+# öppnar Store och inte en riktig Python).
+function Test-PythonExe([string]$exe, [string[]]$prefix) {
+    try {
+        $out = & $exe @($prefix + "--version") 2>&1
+        return ($LASTEXITCODE -eq 0 -and "$out" -match "Python 3")
+    } catch { return $false }
+}
+
+function Get-PythonCmd {
+    # 1. py-launchern (finns oftast i PATH även när python.exe inte gör det)
+    $pyCmd = Get-Command py -ErrorAction SilentlyContinue
+    if ($pyCmd -and (Test-PythonExe $pyCmd.Source @("-3"))) {
+        return @{ Exe = $pyCmd.Source; Args = @("-3") }
+    }
+
+    # 2. python / python3 i PATH, men hoppa över WindowsApps-stubben
+    foreach ($name in "python", "python3") {
+        foreach ($cmd in @(Get-Command $name -All -ErrorAction SilentlyContinue)) {
+            if ($cmd.Source -like "*\Microsoft\WindowsApps\*") { continue }
+            if (Test-PythonExe $cmd.Source @()) {
+                return @{ Exe = $cmd.Source; Args = @() }
+            }
+        }
+    }
+
+    # 3. Vanliga installationsplatser (t.ex. om PATH inte lästs om ännu)
+    $roots = @(
+        "$env:LOCALAPPDATA\Programs\Python",
+        "$env:PROGRAMFILES\Python*",
+        "${env:ProgramFiles(x86)}\Python*",
+        "C:\Python3*"
+    )
+    foreach ($root in $roots) {
+        $found = Get-ChildItem -Path $root -Filter python.exe -Recurse -Depth 1 -ErrorAction SilentlyContinue |
+            Sort-Object FullName -Descending | Select-Object -First 1
+        if ($found -and (Test-PythonExe $found.FullName @())) {
+            return @{ Exe = $found.FullName; Args = @() }
+        }
+    }
+
+    throw @"
+Hittar ingen riktig Python-installation.
+
+Installera Python 3 fran https://www.python.org/downloads/ och kryssa i
+'Add python.exe to PATH' i installeraren. Oppna sedan ett NYTT
+terminalfonster (sa att PATH last om) och kor det har skriptet igen.
+
+Tips: kor du 'python' och det oppnar Microsoft Store ar det bara ett
+alias - stang av det under Installningar > Appar > Appkortkommandon for
+apputforande, eller installera fran python.org enligt ovan.
+"@
+}
+
+$py = Get-PythonCmd
+
+if (-not (Test-Path $VenvDir)) {
+    Write-Host "Skapar virtuell miljo i .\$VenvDir ..."
+    & $py.Exe @($py.Args + @("-m", "venv", $VenvDir))
+    if ($LASTEXITCODE -ne 0) { throw "Kunde inte skapa virtuell miljo." }
+} else {
+    Write-Host "Virtuell miljo .\$VenvDir finns redan, ateranvander den."
+}
+
+$VenvPython = Join-Path $VenvDir "Scripts\python.exe"
+if (-not (Test-Path $VenvPython)) { throw "Hittar inte $VenvPython efter venv-skapande." }
+
+Write-Host "Uppdaterar pip..."
+& $VenvPython -m pip install --upgrade pip
+if ($LASTEXITCODE -ne 0) { throw "pip-uppgradering misslyckades." }
+
+Write-Host "Installerar Python-paket fran requirements.txt..."
+& $VenvPython -m pip install -r requirements.txt
+if ($LASTEXITCODE -ne 0) { throw "Paketinstallation misslyckades." }
+
+Write-Host "Installerar Playwright Chromium..."
+& $VenvPython -m playwright install chromium
+if ($LASTEXITCODE -ne 0) { throw "Playwright-installation misslyckades." }
+
+Write-Host ""
+Write-Host "Klart! Starta med:"
+Write-Host "    .venv\Scripts\Activate.ps1"
+Write-Host "    python run_webui.py     # webui pa http://127.0.0.1:8765"
+Write-Host "    python main.py          # fristaende 1177-exempel"
diff --git a/requirements-dev.txt b/requirements-dev.txt
new file mode 100644
index 0000000..a266747
--- /dev/null
+++ b/requirements-dev.txt
@@ -0,0 +1,2 @@
+-r requirements.txt
+pytest>=8.0
diff --git a/requirements.txt b/requirements.txt
index 61b7d29..37011c6 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,11 @@ fastapi>=0.111
 uvicorn>=0.30
 pypdf>=4.0
 Pillow>=10.0
+anthropic>=1.0
+mcp[cli]>=2.1
+
+# Desktop-automation (fas 3) - bara Windows. Övriga plattformar hoppar över dem
+# och desktop_*-stegen ger ett tydligt fel i stället för att krascha vid import.
+pywinauto>=0.6.8; sys_platform == "win32"
+pyautogui>=0.9.54; sys_platform == "win32"
+opencv-python>=4.8; sys_platform == "win32"
diff --git a/rpa/assist.py b/rpa/assist.py
new file mode 100644
index 0000000..d126401
--- /dev/null
+++ b/rpa/assist.py
@@ -0,0 +1,160 @@
+"""NL → flöde: tar en instruktion på naturligt språk (svenska) och ett ev.
+befintligt flöde och ber en Claude-modell generera/ändra flödes-JSON.
+
+Samma stegschema (rpa/schema.py) som webui:t och MCP-servern använder ligger
+till grund för systemprompten och för valideringen av svaret.
+
+Nyckel: ANTHROPIC_API_KEY i miljön, eller en profil via `ant auth login`.
+Modell: env RPA_ASSIST_MODEL (default claude-opus-5).
+"""
+
+from __future__ import annotations
+
+import json
+import os
+from typing import Any
+
+from . import schema
+
+DEFAULT_MODEL = "claude-opus-5"
+_EMIT_TOOL = "emit_flow"
+
+
+class AssistUnavailable(RuntimeError):
+    """Ingen API-nyckel/inloggning konfigurerad - assistenten kan inte köras."""
+
+
+class AssistError(RuntimeError):
+    """Modellen svarade men resultatet gick inte att tolka till ett giltigt flöde."""
+
+
+def _client():
+    try:
+        import anthropic
+    except ModuleNotFoundError as exc:  # pragma: no cover
+        raise AssistUnavailable(
+            "Paketet 'anthropic' saknas - kör: pip install -r requirements.txt"
+        ) from exc
+
+    has_key = bool(os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("ANTHROPIC_AUTH_TOKEN"))
+    if not has_key and not _has_cli_profile():
+        raise AssistUnavailable(
+            "Ingen Claude-nyckel hittad. Sätt miljövariabeln ANTHROPIC_API_KEY "
+            "(eller kör `ant auth login`) och starta om servern."
+        )
+    try:
+        return anthropic.Anthropic()
+    except Exception as exc:  # noqa: BLE001  # pragma: no cover
+        raise AssistUnavailable(f"Kunde inte skapa Anthropic-klienten: {exc}") from exc
+
+
+def _has_cli_profile() -> bool:
+    from pathlib import Path
+
+    cfg = Path.home() / ".config" / "anthropic"
+    return cfg.exists() and any(cfg.iterdir())
+
+
+_SYSTEM = """Du är en assistent som bygger automationsflöden för ett Playwright-baserat RPA-verktyg.
+
+Du får en instruktion på svenska och (ibland) ett befintligt flöde. Svara genom att
+anropa verktyget `emit_flow` med det kompletta, uppdaterade flödet.
+
+Regler:
+- Ändrar användaren ett befintligt flöde: behåll de steg som fortfarande gäller, lägg till/ta bort/justera resten.
+- Använd bara stegtyper och fält som finns i referensen nedan. Hitta inte på fält.
+- Selector-fält: föredra `by="text"` med synlig text, eller `by="role"`. `by="css"` bara när text/roll inte räcker.
+- Ett typiskt inloggat flöde börjar med `goto` följt av `wait_for_login`.
+- Loopar över en lista: `list` (CSS-selector) → `loop` (over = samma variabel). Inuti loopen kan inre steg sätta `scope="item"`.
+- Sätt en kort `explanation` (svenska) som sammanfattar vad flödet gör.
+- Är instruktionen otydlig: gör ett rimligt antagande och nämn det i `explanation`.
+
+{reference}
+"""
+
+
+def _tool_schema() -> dict:
+    fjs = schema.flow_json_schema()
+    return {
+        "name": _EMIT_TOOL,
+        "description": "Leverera det kompletta flödet (steps + settings + explanation).",
+        "input_schema": {
+            "type": "object",
+            "properties": fjs["properties"],
+            "required": ["steps"],
+            "$defs": fjs["$defs"],
+        },
+    }
+
+
+def generate_flow(instruction: str, current_flow: dict | None = None, *, model: str | None = None) -> dict:
+    """Returnerar {"steps": [...], "settings": {...}, "explanation": "..."}.
+
+    Kastar AssistUnavailable om ingen nyckel finns, AssistError om modellen inte
+    lyckas producera ett giltigt flöde (även efter en reparationsrunda).
+    """
+    if not (instruction or "").strip():
+        raise AssistError("Tom instruktion.")
+
+    client = _client()
+    model = model or os.environ.get("RPA_ASSIST_MODEL") or DEFAULT_MODEL
+    system = _SYSTEM.format(reference=schema.step_reference_markdown())
+
+    user_parts = [f"Instruktion:\n{instruction.strip()}"]
+    if current_flow and current_flow.get("steps"):
+        user_parts.append(
+            "Befintligt flöde att utgå från:\n"
+            + json.dumps(
+                {"steps": current_flow.get("steps", []), "settings": current_flow.get("settings", {})},
+                ensure_ascii=False,
+                indent=2,
+            )
+        )
+    messages: list[dict[str, Any]] = [{"role": "user", "content": "\n\n".join(user_parts)}]
+
+    tool = _tool_schema()
+
+    for attempt in range(2):
+        response = client.messages.create(
+            model=model,
+            max_tokens=16000,
+            system=system,
+            thinking={"type": "adaptive"},
+            tools=[tool],
+            tool_choice={"type": "tool", "name": _EMIT_TOOL},
+            messages=messages,
+        )
+        flow = _extract_flow(response)
+        if flow is None:
+            raise AssistError("Modellen anropade inte verktyget emit_flow.")
+
+        normalized = {
+            "steps": flow.get("steps", []),
+            "settings": flow.get("settings", {}),
+        }
+        errors = schema.validate_flow(normalized)
+        if not errors:
+            normalized["explanation"] = str(flow.get("explanation", "")).strip()
+            return normalized
+
+        if attempt == 0:
+            messages.append({"role": "assistant", "content": response.content})
+            messages.append({
+                "role": "user",
+                "content": (
+                    "Flödet du skickade har valideringsfel. Rätta dem och anropa "
+                    "emit_flow igen med hela flödet:\n- " + "\n- ".join(errors)
+                ),
+            })
+            continue
+
+        raise AssistError("Flödet klarade inte valideringen:\n- " + "\n- ".join(errors))
+
+    raise AssistError("Kunde inte generera ett giltigt flöde.")  # pragma: no cover
+
+
+def _extract_flow(response) -> dict | None:
+    for block in response.content:
+        if getattr(block, "type", None) == "tool_use" and block.name == _EMIT_TOOL:
+            return block.input if isinstance(block.input, dict) else json.loads(block.input)
+    return None
diff --git a/rpa/config.py b/rpa/config.py
index 600c22d..1289b4d 100644
--- a/rpa/config.py
+++ b/rpa/config.py
@@ -9,6 +9,7 @@ FLOWS_DIR = DATA_DIR / "flows"
 SCREENSHOT_DIR = DATA_DIR / "screenshots"
 TRACE_DIR = DATA_DIR / "traces"
 EXPORT_DIR = DATA_DIR / "exports"
+DESKTOP_IMAGE_DIR = DATA_DIR / "desktop_images"
 STATE_FILE = DATA_DIR / "state.json"

 START_URL = "https://www.changeme.com/"
@@ -22,5 +23,5 @@ LOGGED_IN_INDICATORS = [
     "Min profil",
 ]

-for d in (DATA_DIR, BROWSER_PROFILE_DIR, DOWNLOAD_DIR, DISCOVER_DIR, FLOWS_DIR, SCREENSHOT_DIR, TRACE_DIR, EXPORT_DIR):
+for d in (DATA_DIR, BROWSER_PROFILE_DIR, DOWNLOAD_DIR, DISCOVER_DIR, FLOWS_DIR, SCREENSHOT_DIR, TRACE_DIR, EXPORT_DIR, DESKTOP_IMAGE_DIR):
     d.mkdir(parents=True, exist_ok=True)
diff --git a/rpa/desktop.py b/rpa/desktop.py
new file mode 100644
index 0000000..8892d4c
--- /dev/null
+++ b/rpa/desktop.py
@@ -0,0 +1,314 @@
+"""Desktop-automation för Windows: styr vanliga appar via Windows UI Automation
+(pywinauto) och gör bild-/koordinatklick (pyautogui) för appar utan
+tillgänglighets-API.
+
+Används av rpa/flow_engine.py för stegtyperna `desktop_*` (se rpa/schema.py).
+pywinauto/pyautogui importeras på modulnivå men bakom try/except så att
+flow_engine kan importeras även på icke-Windows – anropas ett desktop-steg där
+kastas DesktopUnavailable i stället för ImportError.
+
+Selector-modell (samma anda som browser-stegen):
+  by:  "name" | "auto_id" | "control_type" | "class_name"
+  value: söksträng enligt `by`
+  window: valfri toppfönstertitel (regex). Tom = "aktuellt" fönster
+          (senast startat/anslutet/fokuserat).
+  nth: 0 = första träffen
+"""
+
+from __future__ import annotations
+
+import time
+from pathlib import Path
+from typing import Callable
+
+from . import config
+
+try:  # Windows-only
+    import pywinauto
+    from pywinauto import Application, Desktop
+except Exception:  # noqa: BLE001  (ImportError, men även t.ex. comtypes-fel)
+    pywinauto = None
+    Application = None
+    Desktop = None
+
+try:
+    import pyautogui
+except Exception:  # noqa: BLE001
+    pyautogui = None
+
+
+DESKTOP_IMAGE_DIR = config.DESKTOP_IMAGE_DIR
+
+LogFn = Callable[[str], None]
+
+
+class DesktopUnavailable(RuntimeError):
+    """pywinauto/pyautogui saknas eller kunde inte laddas (icke-Windows?)."""
+
+
+class DesktopError(RuntimeError):
+    """Fönster/kontroll/bild hittades inte, e.dyl."""
+
+
+def _need_pywinauto() -> None:
+    if pywinauto is None:
+        raise DesktopUnavailable(
+            "pywinauto kunde inte laddas – desktop-steg fungerar bara på Windows. "
+            "Installera med: pip install -r requirements.txt"
+        )
+
+
+def _need_pyautogui() -> None:
+    if pyautogui is None:
+        raise DesktopUnavailable(
+            "pyautogui kunde inte laddas – installera med: pip install -r requirements.txt"
+        )
+
+
+_BY_TO_KWARG = {
+    "name": "title",
+    "title": "title",
+    "auto_id": "auto_id",
+    "control_type": "control_type",
+    "class_name": "class_name",
+}
+
+
+class DesktopSession:
+    def __init__(self, log: LogFn = print):
+        self.log = log
+        self.app = None  # senaste pywinauto.Application
+        self.win = None  # senaste WindowSpecification ("aktuellt fönster")
+        self.launched: list = []  # Application-objekt som detta flöde startat
+
+    # -- fönster --------------------------------------------------------------
+
+    def _find_window(self, title: str, timeout: float):
+        """WindowSpecification för första synliga toppfönstret vars titel matchar
+        (regex). Tål flera träffar. Kastar DesktopError vid timeout."""
+        end = time.time() + timeout
+        last: Exception | None = None
+        while time.time() < end:
+            try:
+                matches = Desktop(backend="uia").windows(title_re=title, visible_only=True, enabled_only=False)
+            except Exception as exc:  # noqa: BLE001
+                last = exc
+                matches = []
+            if matches:
+                return Desktop(backend="uia").window(handle=matches[0].handle)
+            time.sleep(0.4)
+        raise DesktopError(f"Hittade inget synligt fönster med titel som matchar {title!r} ({last}).")
+
+    def launch(
+        self, path: str, args: str = "", cwd: str | None = None, timeout: float = 20, title: str | None = None
+    ) -> None:
+        _need_pywinauto()
+        if not path:
+            raise DesktopError("desktop_launch kräver 'path'.")
+        cmd = path if not args else f'"{path}" {args}' if " " in path else f"{path} {args}"
+        app = Application(backend="uia").start(cmd, work_dir=cwd, timeout=timeout)
+        self.app = app
+        self.launched.append(app)
+        self.log(f"  Startade: {cmd}")
+
+        if title:
+            self.win = self._find_window(title, timeout)
+            self.log(f"  Fönster: {self.win.window_text()!r}")
+            return
+
+        end = time.time() + timeout
+        win = None
+        while time.time() < end and win is None:
+            try:
+                win = app.top_window()
+                win.wait("exists visible", timeout=1)
+            except Exception:  # noqa: BLE001
+                win = None
+                time.sleep(0.4)
+
+        if win is None:
+            # Paketerade appar (Win11 Notepad m.fl.) körs via en broker - den
+            # startade processen har inget eget fönster. Bästa gissning: det
+            # nyaste synliga toppfönstret.
+            try:
+                cands = [w for w in Desktop(backend="uia").windows(visible_only=True) if w.window_text()]
+                if cands:
+                    win = Desktop(backend="uia").window(handle=cands[0].handle)
+            except Exception:  # noqa: BLE001
+                pass
+
+        self.win = win
+        if win is None:
+            self.log(
+                "  (kunde inte fastställa fönstret automatiskt - ange 'title' på "
+                "desktop_launch, eller använd desktop_connect)"
+            )
+        else:
+            self.log(f"  Fönster: {win.window_text()!r}")
+
+    def connect(self, title: str, timeout: float = 20) -> None:
+        _need_pywinauto()
+        if not title:
+            raise DesktopError("desktop_connect kräver 'title'.")
+        self.win = self._find_window(title, timeout)
+        try:
+            self.app = Application(backend="uia").connect(handle=self.win.handle, timeout=1)
+        except Exception:  # noqa: BLE001
+            self.app = None
+        self.log(f"  Anslöt till fönster: {self.win.window_text()!r}")
+
+    def wait_for_window(self, title: str, timeout: float = 20) -> None:
+        _need_pywinauto()
+        self._find_window(title, timeout)
+        self.log(f"  Fönster finns: {title}")
+
+    def focus(self, title: str | None = None, timeout: float = 10) -> None:
+        win = self._window(title, timeout)
+        win.set_focus()
+        self.win = win
+        self.log("  Fokuserade fönstret")
+
+    def _window(self, title: str | None = None, timeout: float = 10):
+        _need_pywinauto()
+        if title:
+            return self._find_window(title, timeout)
+        if self.win is None:
+            raise DesktopError(
+                "Inget aktuellt fönster – kör desktop_launch/desktop_connect först "
+                "eller ange 'window'."
+            )
+        return self.win
+
+    # -- kontroller ---------------------------------------------------------
+
+    def _control(self, by: str, value: str, control_type: str, window: str | None, nth: int):
+        win = self._window(window)
+        kwargs: dict = {}
+        key = _BY_TO_KWARG.get(by, "title")
+        if value:
+            kwargs["title_re" if key == "title" else key] = value
+        if control_type:
+            kwargs["control_type"] = control_type
+        ctrl = win.child_window(**kwargs)
+        if nth:
+            ctrl = win.child_window(**kwargs, found_index=nth)
+        ctrl.wait("exists", timeout=10)
+        return ctrl
+
+    def click(self, by, value, control_type, window, nth, double=False) -> None:
+        ctrl = self._control(by, value, control_type, window, nth)
+        if double:
+            ctrl.double_click_input()
+        else:
+            ctrl.click_input()
+        self.log(f"  {'Dubbelklick' if double else 'Klick'}: {value or control_type}")
+
+    def set_text(self, by, value, control_type, window, nth, text: str) -> None:
+        ctrl = self._control(by, value, control_type, window, nth)
+        try:
+            ctrl.set_edit_text(text)
+        except Exception:  # noqa: BLE001  – inte ett Edit-fält
+            ctrl.set_focus()
+            ctrl.type_keys(_escape_type_keys(text), with_spaces=True, with_newlines=True)
+        self.log(f"  Skrev text i: {value or control_type}")
+
+    def press_key(self, keys: str, window: str | None = None) -> None:
+        _need_pywinauto()
+        if not keys:
+            raise DesktopError("desktop_press_key kräver 'keys'.")
+        win = self._window(window)
+        win.set_focus()
+        win.type_keys(keys, with_spaces=True, set_foreground=True)
+        self.log(f"  Tangenter: {keys}")
+
+    def read_text(self, by, value, control_type, window, nth) -> str:
+        ctrl = self._control(by, value, control_type, window, nth)
+        txt = ""
+        for getter in ("window_text", "get_value", "legacy_properties"):
+            try:
+                val = getattr(ctrl, getter)()
+                if getter == "legacy_properties":
+                    val = (val or {}).get("Value", "")
+                if val:
+                    txt = val
+                    break
+            except Exception:  # noqa: BLE001
+                continue
+        self.log(f"  Läste text: {txt[:60]!r}")
+        return txt or ""
+
+    def screenshot(self, name: str = "desktop", window: str | None = None) -> Path:
+        config.SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
+        target = config.SCREENSHOT_DIR / f"{name}.png"
+        if window or self.win is not None:
+            win = self._window(window)
+            win.capture_as_image().save(str(target))
+        else:
+            _need_pyautogui()
+            pyautogui.screenshot(str(target))
+        self.log(f"  Skärmdump: {target.name}")
+        return target
+
+    # -- bild / koordinat -------------------------------------------------
+
+    def _image_path(self, image: str) -> str:
+        p = Path(image)
+        if not p.is_absolute():
+            p = DESKTOP_IMAGE_DIR / image
+        if not p.exists():
+            raise DesktopError(f"Bildfilen finns inte: {p}")
+        return str(p)
+
+    def _locate(self, image: str, confidence: float, timeout: float):
+        _need_pyautogui()
+        path = self._image_path(image)
+        end = time.time() + timeout
+        last = None
+        while time.time() < end:
+            try:
+                pt = pyautogui.locateCenterOnScreen(path, confidence=confidence)
+            except Exception as exc:  # noqa: BLE001  – pyscreeze ImageNotFoundException m.fl.
+                last = exc
+                pt = None
+            if pt is not None:
+                return pt
+            time.sleep(0.5)
+        raise DesktopError(f"Hittade inte bilden {image!r} på skärmen inom {timeout}s ({last}).")
+
+    def wait_for_image(self, image: str, confidence: float = 0.9, timeout: float = 20) -> None:
+        self._locate(image, confidence, timeout)
+        self.log(f"  Bild funnen: {image}")
+
+    def click_image(
+        self, image: str, confidence: float = 0.9, timeout: float = 20, double: bool = False, button: str = "left"
+    ) -> None:
+        pt = self._locate(image, confidence, timeout)
+        pyautogui.click(pt.x, pt.y, clicks=2 if double else 1, button=button)
+        self.log(f"  Klick på bild {image} @ ({pt.x},{pt.y})")
+
+    def click_xy(self, x: int, y: int, double: bool = False, button: str = "left") -> None:
+        _need_pyautogui()
+        pyautogui.click(int(x), int(y), clicks=2 if double else 1, button=button)
+        self.log(f"  Klick @ ({x},{y})")
+
+    # -- städning ---------------------------------------------------------
+
+    def close(self, kill_launched: bool) -> None:
+        if not kill_launched:
+            if self.launched:
+                self.log("Lämnar startade appar igång (close-mode).")
+            return
+        for app in self.launched:
+            try:
+                app.kill(soft=False)
+            except Exception as exc:  # noqa: BLE001
+                self.log(f"  Kunde inte stänga en app: {exc}")
+        self.launched.clear()
+
+
+def _escape_type_keys(text: str) -> str:
+    # type_keys tolkar {}()+^%~ som specialtecken – escapa dem som literaler.
+    out = []
+    for ch in text:
+        out.append("{" + ch + "}" if ch in "{}()+^%~[]" else ch)
+    return "".join(out)
diff --git a/rpa/desktop_inspect.py b/rpa/desktop_inspect.py
new file mode 100644
index 0000000..318c325
--- /dev/null
+++ b/rpa/desktop_inspect.py
@@ -0,0 +1,41 @@
+"""Inspektera Windows-fönster för att hitta rätt selectors till desktop_*-stegen
+(motsvarigheten till rpa/discover.py fast för skrivbordsappar).
+
+    python -m rpa.desktop_inspect                 # listar synliga toppfönster
+    python -m rpa.desktop_inspect "Anteckningar"  # dumpar kontroll-identifierare
+
+Kör på Windows-maskinen med målappen igång. Använd 'name'/'auto_id'/
+'control_type' från utskriften i webui:ts desktop-steg.
+"""
+
+import sys
+
+
+def main() -> None:
+    try:
+        from pywinauto import Desktop
+    except Exception as exc:  # noqa: BLE001
+        sys.exit(f"pywinauto kunde inte laddas (Windows-only): {exc}")
+
+    desktop = Desktop(backend="uia")
+
+    if len(sys.argv) < 2:
+        print("Synliga toppfönster:\n")
+        for w in desktop.windows():
+            try:
+                if w.is_visible() and w.window_text():
+                    print(f"  {w.window_text()!r}   [{w.class_name()}]")
+            except Exception:  # noqa: BLE001
+                continue
+        print('\nKör igen med en titel (regex) för att dumpa kontroller, t.ex.:\n  python -m rpa.desktop_inspect "Anteckningar"')
+        return
+
+    title = sys.argv[1]
+    win = desktop.window(title_re=title)
+    win.wait("exists ready", timeout=10)
+    print(f"Kontroll-identifierare för fönster som matchar {title!r}:\n")
+    win.print_control_identifiers()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/rpa/flow_engine.py b/rpa/flow_engine.py
index d37d9db..df020ba 100644
--- a/rpa/flow_engine.py
+++ b/rpa/flow_engine.py
@@ -128,6 +128,7 @@ import shlex
 import subprocess
 import sys
 import tempfile
+import time
 import unicodedata
 from pathlib import Path
 from typing import Callable
@@ -138,6 +139,7 @@ from playwright.sync_api import Page, sync_playwright
 from . import config
 from .auth import wait_for_login
 from .browser import get_page, launch_context
+from .desktop import DesktopSession

 LogFn = Callable[[str], None]

@@ -146,6 +148,48 @@ class FlowError(Exception):
     pass


+# Desktop-stegtyper (Windows). Hanteras samlat av _execute_desktop_step som
+# skapar en DesktopSession lat vid första desktop-steget. Måste hållas i synk
+# med rpa/schema.py (tests/test_schema.py:test_engine_and_schema_step_types_match).
+_DESKTOP_STEPS = frozenset({
+    "desktop_launch", "desktop_connect", "desktop_wait_for_window", "desktop_focus_window",
+    "desktop_click", "desktop_type", "desktop_press_key", "desktop_read_text",
+    "desktop_screenshot", "desktop_click_image", "desktop_wait_for_image", "desktop_click_xy",
+})
+
+# Stegtyper som inte rör webbläsaren alls (utöver desktop_*). loop/if är
+# strukturella - deras inre steg (och if-villkor) avgör själva om de behöver den.
+_NO_BROWSER_STEPS = frozenset({
+    "set_var", "increment", "clear_var", "save_var", "load_var", "run_script",
+    "wait", "jump_to_label", "loop", "if",
+})
+
+
+def _condition_needs_browser(cond: dict) -> bool:
+    return cond.get("kind", "element_exists") in ("element_exists", "element_not_exists")
+
+
+def _step_needs_browser(step: dict) -> bool:
+    """True om steget (eller något nästlat steg / if-villkor) kräver Playwright."""
+    t = step.get("type")
+    if t in _DESKTOP_STEPS or (t in _NO_BROWSER_STEPS and t not in ("loop", "if")):
+        return False
+    if t == "loop":
+        return any(_step_needs_browser(s) for s in step.get("steps", []))
+    if t == "if":
+        for br in step.get("branches", []):
+            if _condition_needs_browser(br.get("condition", {})):
+                return True
+            if any(_step_needs_browser(s) for s in br.get("steps", [])):
+                return True
+        return any(_step_needs_browser(s) for s in (step.get("else") or []))
+    return True
+
+
+def _flow_needs_browser(steps: list) -> bool:
+    return any(_step_needs_browser(s) for s in steps)
+
+
 _BAD_FILENAME_CHARS = re.compile(r'[<>:"/\\|?*\x00-\x1f]')


@@ -333,6 +377,11 @@ def _evaluate_condition(page: Page, item_locator, vars_: dict, condition: dict)
     kind = condition.get("kind", "element_exists")

     if kind in ("element_exists", "element_not_exists"):
+        if page is None:
+            raise FlowError(
+                f"Villkoret '{kind}' kräver webbläsaren, men flödet körs utan den "
+                "(skip_browser)."
+            )
         loc = _resolve_locator(page, item_locator, condition, vars_)
         exists = loc.count() > 0
         return exists if kind == "element_exists" else not exists
@@ -356,9 +405,96 @@ MAX_JUMPS_PER_RUN = 1000



-def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_budget) -> None:
+def _truthy(v) -> bool:
+    return v is True or str(v).strip().lower() == "true"
+
+
+def _execute_desktop_step(step, t, log, vars_, desktop_holder) -> None:
+    """Kör ett desktop_*-steg. Skapar en DesktopSession lat vid första
+    desktop-steget så rena webbflöden aldrig rör pywinauto."""
+    if desktop_holder["session"] is None:
+        desktop_holder["session"] = DesktopSession(log)
+    d = desktop_holder["session"]
+
+    def v(key: str, default: str = "") -> str:
+        return _interpolate(str(step.get(key, default) or ""), vars_)
+
+    def selector():
+        return (
+            step.get("by", "name"),
+            v("value"),
+            str(step.get("control_type", "") or ""),
+            v("window") or None,
+            int(step.get("nth", 0) or 0),
+        )
+
+    if t == "desktop_launch":
+        log(f"Startar app: {step.get('path', '')}")
+        d.launch(v("path"), v("args"), v("cwd") or None, float(step.get("timeout", 20)), v("title") or None)
+    elif t == "desktop_connect":
+        log(f"Ansluter till fönster: {v('title')}")
+        d.connect(v("title"), float(step.get("timeout", 20)))
+    elif t == "desktop_wait_for_window":
+        log(f"Väntar på fönster: {v('title')}")
+        d.wait_for_window(v("title"), float(step.get("timeout", 20)))
+    elif t == "desktop_focus_window":
+        log("Fokuserar fönster")
+        d.focus(v("title") or None, float(step.get("timeout", 10)))
+    elif t == "desktop_click":
+        by, value, ct, win, nth = selector()
+        log(f"Desktop-klick: {value or ct}")
+        d.click(by, value, ct, win, nth, double=_truthy(step.get("double")))
+    elif t == "desktop_type":
+        by, value, ct, win, nth = selector()
+        log(f"Skriver i kontroll: {value or ct}")
+        d.set_text(by, value, ct, win, nth, v("text"))
+    elif t == "desktop_press_key":
+        log(f"Tangenter: {v('keys')}")
+        d.press_key(v("keys"), v("window") or None)
+    elif t == "desktop_read_text":
+        by, value, ct, win, nth = selector()
+        var_name = step.get("var")
+        if not var_name:
+            raise FlowError("desktop_read_text kräver 'var'.")
+        text = d.read_text(by, value, ct, win, nth)
+        vars_[var_name] = text
+        log(f"Läste kontrolltext -> variabel '{var_name}': {text[:60]!r}")
+    elif t == "desktop_screenshot":
+        d.screenshot(v("name") or "desktop", v("window") or None)
+    elif t == "desktop_click_image":
+        log(f"Klickar på bild: {step.get('image', '')}")
+        d.click_image(
+            v("image"),
+            float(step.get("confidence", 0.9)),
+            float(step.get("timeout", 20)),
+            double=_truthy(step.get("double")),
+            button=step.get("button", "left") or "left",
+        )
+    elif t == "desktop_wait_for_image":
+        log(f"Väntar på bild: {step.get('image', '')}")
+        d.wait_for_image(v("image"), float(step.get("confidence", 0.9)), float(step.get("timeout", 20)))
+    elif t == "desktop_click_xy":
+        x, y = int(step.get("x", 0)), int(step.get("y", 0))
+        log(f"Klickar @ ({x},{y})")
+        d.click_xy(x, y, double=_truthy(step.get("double")), button=step.get("button", "left") or "left")
+    else:  # pragma: no cover
+        raise FlowError(f"Ohanterad desktop-stegtyp: {t!r}")
+
+
+def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_budget, desktop_holder) -> None:
     """Kör ett enskilt steg. Undantag propagerar - anroparen avgör (via
     stegets "on_error") om körningen ska stanna eller fortsätta."""
+    if t in _DESKTOP_STEPS:
+        _execute_desktop_step(step, t, log, vars_, desktop_holder)
+        return
+
+    if page is None and t not in _NO_BROWSER_STEPS:
+        raise FlowError(
+            f"Steget '{t}' kräver webbläsaren, men flödet körs utan den "
+            "(skip_browser är på i Inställningar, eller flödet innehåller inga "
+            "andra webbläsarsteg)."
+        )
+
     if t == "goto":
         log(f"Går till {step['url']}")
         page.goto(step["url"], wait_until="domcontentloaded")
@@ -366,7 +502,7 @@ def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_bu
     elif t == "wait":
         seconds = float(step.get("seconds", 1))
         log(f"Väntar {seconds}s")
-        page.wait_for_timeout(seconds * 1000)
+        time.sleep(seconds)

     elif t == "wait_for_load":
         log("Väntar på att sidan laddas klart")
@@ -621,7 +757,7 @@ def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_bu
                 log(f"-- Post {post_idx + 1}/{count} --")
                 vars_[index_var] = post_idx + 1
                 current_item = page.locator(selector).nth(post_idx)
-                _run_steps(page, step.get("steps", []), log, vars_, current_item, download_dir, jump_budget)
+                _run_steps(page, step.get("steps", []), log, vars_, current_item, download_dir, jump_budget, desktop_holder)
         elif isinstance(over, list):
             # Loop över en vanlig listvariabel (t.ex. från "Lägg till i lista" eller
             # "Läs in variabel"). Inget DOM-element per varv - varje värde läggs i
@@ -633,7 +769,7 @@ def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_bu
                 log(f"-- Post {post_idx + 1}/{count} --")
                 vars_[index_var] = post_idx + 1
                 vars_[item_var] = item_value
-                _run_steps(page, step.get("steps", []), log, vars_, item_locator, download_dir, jump_budget)
+                _run_steps(page, step.get("steps", []), log, vars_, item_locator, download_dir, jump_budget, desktop_holder)
         else:
             raise FlowError(
                 f"Variabel '{over_name}' kan inte loopas över (varken en lista eller "
@@ -648,10 +784,10 @@ def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_bu
                 break
         if matched_steps is not None:
             log("Villkor uppfyllt, kör gren")
-            _run_steps(page, matched_steps, log, vars_, item_locator, download_dir, jump_budget)
+            _run_steps(page, matched_steps, log, vars_, item_locator, download_dir, jump_budget, desktop_holder)
         elif step.get("else") is not None:
             log("Inget villkor uppfyllt, kör 'else'-gren")
-            _run_steps(page, step["else"], log, vars_, item_locator, download_dir, jump_budget)
+            _run_steps(page, step["else"], log, vars_, item_locator, download_dir, jump_budget, desktop_holder)
         else:
             log("Inget villkor uppfyllt, ingen 'else'-gren - fortsätter")

@@ -875,13 +1011,14 @@ def _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_bu


 def _run_steps(
-    page: Page,
+    page: Page | None,
     steps: list,
     log: LogFn,
     vars_: dict,
     item_locator,
     download_dir: Path,
     jump_budget: dict,
+    desktop_holder: dict,
 ) -> None:
     i = 0
     while i < len(steps):
@@ -912,7 +1049,7 @@ def _run_steps(
             continue

         try:
-            _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_budget)
+            _execute_step(step, t, page, log, vars_, item_locator, download_dir, jump_budget, desktop_holder)
         except Exception as _step_exc:
             if step.get("on_error") == "continue":
                 log(f"Steget '{t}' misslyckades - fortsätter (on_error=continue): {_step_exc}")
@@ -969,31 +1106,65 @@ def run_flow(
     """
     settings = settings or {}
     download_dir = download_dir or config.DOWNLOAD_DIR
+
+    # Rena desktop-/variabelflöden startar ingen webbläsare. Sätts explicit via
+    # settings["skip_browser"] eller upptäcks automatiskt (inga webbläsarsteg).
+    skip_browser = bool(settings.get("skip_browser")) or not _flow_needs_browser(steps)
+
+    if skip_browser:
+        log("Kör utan webbläsare (skip_browser / inga webbläsarsteg).")
+        _run_and_finish(
+            None, None, steps, log, download_dir, close_mode, close_event,
+            on_finished, None, settings,
+        )
+        return
+
     with sync_playwright() as p:
         context = launch_context(p, settings)
         if trace_path is not None:
             context.tracing.start(screenshots=True, snapshots=True, sources=True)
         page = get_page(context)
-        error: Exception | None = None
-        vars_: dict = {}
-        jump_budget = {"remaining": MAX_JUMPS_PER_RUN}
-        try:
-            _run_steps(
-                page, steps, log, vars_=vars_, item_locator=None,
-                download_dir=download_dir, jump_budget=jump_budget,
-            )
-            log("Flöde klart.")
-        except Exception as exc:  # noqa: BLE001
-            error = exc
-            log(f"FEL: {exc}")
+        _run_and_finish(
+            context, page, steps, log, download_dir, close_mode, close_event,
+            on_finished, trace_path, settings,
+        )

-        if settings.get("notify_on_complete"):
-            _notify_run_complete(error)

-        if on_finished is not None:
-            on_finished(error, vars_)
+def _run_and_finish(
+    context, page, steps, log, download_dir, close_mode, close_event, on_finished, trace_path, settings,
+) -> None:
+    error: Exception | None = None
+    vars_: dict = {}
+    jump_budget = {"remaining": MAX_JUMPS_PER_RUN}
+    desktop_holder: dict = {"session": None}
+    try:
+        _run_steps(
+            page, steps, log, vars_=vars_, item_locator=None,
+            download_dir=download_dir, jump_budget=jump_budget,
+            desktop_holder=desktop_holder,
+        )
+        log("Flöde klart.")
+    except Exception as exc:  # noqa: BLE001
+        error = exc
+        log(f"FEL: {exc}")
+
+    if settings.get("notify_on_complete"):
+        _notify_run_complete(error)
+
+    if on_finished is not None:
+        on_finished(error, vars_)
+
+    should_close_now = close_mode == "always" or (close_mode == "on_success" and error is None)
+
+    # Desktop-appar som flödet startat städas enligt samma close-mode som
+    # webbläsaren (kill vid "always", eller "on_success" utan fel).
+    if desktop_holder["session"] is not None:
+        try:
+            desktop_holder["session"].close(kill_launched=should_close_now)
+        except Exception as exc:  # noqa: BLE001
+            log(f"Kunde inte städa desktop-sessionen: {exc}")

-        should_close_now = close_mode == "always" or (close_mode == "on_success" and error is None)
+    if context is not None:
         if not should_close_now:
             log('Webbläsaren hålls öppen. Klicka "Stäng webbläsare" i UI:t när du är klar.')
             if close_event is not None:
diff --git a/rpa/flows.py b/rpa/flows.py
new file mode 100644
index 0000000..4cf90b5
--- /dev/null
+++ b/rpa/flows.py
@@ -0,0 +1,82 @@
+"""Flödespersistens - läsa/skriva/lista/ta bort de JSON-flöden som ligger i
+data/flows/. Utbrutet ur webui så att både webui:t och MCP-servern
+(rpa/mcp_server.py) delar exakt samma regler (namnvalidering, schema-validering
+vid spara).
+"""
+
+from __future__ import annotations
+
+import json
+import re
+from pathlib import Path
+
+from . import config
+from .schema import validate_flow
+
+NAME_RE = re.compile(r"^[a-zA-Z0-9_\-åäöÅÄÖ ]{1,80}$")
+
+
+class InvalidFlowName(ValueError):
+    pass
+
+
+class FlowNotFound(FileNotFoundError):
+    pass
+
+
+class InvalidFlow(ValueError):
+    """Flödet strider mot schemat (rpa/schema.py:validate_flow)."""
+
+    def __init__(self, errors: list[str]):
+        self.errors = errors
+        super().__init__("Ogiltigt flöde:\n- " + "\n- ".join(errors))
+
+
+def flow_path(name: str) -> Path:
+    if not NAME_RE.match(name or ""):
+        raise InvalidFlowName(f"Ogiltigt flödesnamn: {name!r}")
+    return config.FLOWS_DIR / f"{name}.json"
+
+
+def list_flows() -> list[str]:
+    return sorted(p.stem for p in config.FLOWS_DIR.glob("*.json"))
+
+
+def load_flow(name: str) -> dict:
+    path = flow_path(name)
+    if not path.exists():
+        raise FlowNotFound(f"Flödet finns inte: {name}")
+    flow = json.loads(path.read_text(encoding="utf-8"))
+    flow.setdefault("steps", [])
+    flow.setdefault("settings", {})
+    flow.setdefault("layout", {})
+    return flow
+
+
+def save_flow(name: str, flow: dict, *, strict: bool = False) -> None:
+    """Sparar ett flöde. Kastar InvalidFlow om det strider mot schemat.
+
+    strict=False (default) tillåter halvfärdiga flöden med tomma obligatoriska
+    fält - webui:t autosparar under redigering. MCP-servern använder strict=True."""
+    path = flow_path(name)
+    normalized: dict = {
+        "steps": flow.get("steps", []),
+        "settings": flow.get("settings", {}),
+    }
+    errors = validate_flow(normalized, strict=strict)
+    if errors:
+        raise InvalidFlow(errors)
+    # layout = frivilliga nodpositioner från canvas-UI:t. Ingår inte i
+    # valideringen (motorn/schemat bryr sig inte) men sparas om den finns.
+    layout = flow.get("layout")
+    if isinstance(layout, dict) and layout:
+        normalized["layout"] = layout
+    path.write_text(json.dumps(normalized, ensure_ascii=False, indent=2), encoding="utf-8")
+
+
+def delete_flow(name: str) -> bool:
+    path = flow_path(name)
+    if path.exists():
+        path.unlink()
+        return True
+    return False
diff --git a/rpa/mcp_server.py b/rpa/mcp_server.py
new file mode 100644
index 0000000..fd628ce
--- /dev/null
+++ b/rpa/mcp_server.py
@@ -0,0 +1,180 @@
+"""MCP-server som exponerar RPA-verktyget för en AI-agent (Claude Code,
+Claude Desktop, eller annan MCP-klient).
+
+Kör:
+    python -m rpa.mcp_server          # stdio-transport
+
+Registrera i Claude Code:
+    claude mcp add rpa -- <sökväg-till-.venv-python> -m rpa.mcp_server
+
+Registrera i Claude Desktop (claude_desktop_config.json):
+    "rpa": { "command": "<.venv-python>", "args": ["-m", "rpa.mcp_server"] }
+
+OBS: MCP-servern och webui:t delar samma Chromium-profilmapp
+(data/browser_profile/). Bara EN körning kan använda den åt gången - stäng ett
+körande flöde i webui:t innan du kör ett via MCP, och tvärtom.
+"""
+
+from __future__ import annotations
+
+import json
+import logging
+import sys
+
+from mcp.server import MCPServer
+
+from . import flows, runs, schema
+from .flows import FlowNotFound, InvalidFlow, InvalidFlowName
+
+logging.basicConfig(level=logging.INFO, stream=sys.stderr)
+log = logging.getLogger("rpa.mcp")
+
+mcp = MCPServer(
+    "rpa-remisser",
+    instructions=(
+        "Bygg och kör webbautomationsflöden. Anropa describe_steps() först för att "
+        "se alla stegtyper och fält. Flöden sparas som JSON och körs i en synlig "
+        "webbläsare mot en beständig inloggningssession."
+    ),
+)
+
+_MAX_LOG_LINES = 60
+
+
+# --------------------------------------------------------------------------
+# Schema
+# --------------------------------------------------------------------------
+
+@mcp.tool()
+def describe_steps() -> dict:
+    """Alla stegtyper, deras fält, villkorstyper och körningsinställningar.
+    Använd detta som referens innan du skapar eller ändrar ett flöde."""
+    return {
+        "step_types": schema.STEP_TYPES,
+        "condition_kinds": schema.CONDITION_KINDS,
+        "settings": schema.SETTINGS_SCHEMA,
+        "close_modes": schema.CLOSE_MODES,
+        "reference_markdown": schema.step_reference_markdown(),
+    }
+
+
+@mcp.resource("rpa://schema", mime_type="application/json")
+def schema_resource() -> str:
+    return json.dumps(
+        {
+            "step_types": schema.STEP_TYPES,
+            "condition_kinds": schema.CONDITION_KINDS,
+            "settings": schema.SETTINGS_SCHEMA,
+        },
+        ensure_ascii=False,
+        indent=2,
+    )
+
+
+# --------------------------------------------------------------------------
+# Flöden (CRUD)
+# --------------------------------------------------------------------------
+
+@mcp.tool()
+def list_flows() -> list[str]:
+    """Namnen på alla sparade flöden."""
+    return flows.list_flows()
+
+
+@mcp.tool()
+def get_flow(name: str) -> dict:
+    """Hämtar ett sparat flöde ({steps, settings})."""
+    try:
+        return flows.load_flow(name)
+    except FlowNotFound:
+        return {"error": f"Flödet '{name}' finns inte. Tillgängliga: {flows.list_flows()}"}
+    except InvalidFlowName as exc:
+        return {"error": str(exc)}
+
+
+@mcp.tool()
+def save_flow(name: str, steps: list, settings: dict | None = None) -> dict:
+    """Skapar eller skriver över ett flöde. 'steps' är listan av steg (se
+    describe_steps). Returnerar valideringsfel utan att spara om flödet är ogiltigt."""
+    errors = schema.validate_flow({"steps": steps, "settings": settings or {}})
+    if errors:
+        return {"ok": False, "errors": errors}
+    try:
+        flows.save_flow(name, {"steps": steps, "settings": settings or {}})
+    except InvalidFlowName as exc:
+        return {"ok": False, "errors": [str(exc)]}
+    except InvalidFlow as exc:
+        return {"ok": False, "errors": exc.errors}
+    return {"ok": True, "name": name, "step_count": len(steps)}
+
+
+@mcp.tool()
+def delete_flow(name: str) -> dict:
+    """Tar bort ett sparat flöde."""
+    try:
+        existed = flows.delete_flow(name)
+    except InvalidFlowName as exc:
+        return {"ok": False, "error": str(exc)}
+    return {"ok": True, "existed": existed}
+
+
+@mcp.tool()
+def validate_flow(steps: list, settings: dict | None = None) -> dict:
+    """Validerar ett flöde utan att spara det."""
+    errors = schema.validate_flow({"steps": steps, "settings": settings or {}})
+    return {"valid": not errors, "errors": errors}
+
+
+# --------------------------------------------------------------------------
+# Körning
+# --------------------------------------------------------------------------
+
+@mcp.tool()
+def run_flow(name: str, close_mode: str = "always") -> dict:
+    """Startar en körning av ett sparat flöde i bakgrunden. close_mode:
+    always | on_success | never. Returnerar ett run_id - poll:a get_run(run_id)
+    för status och logg."""
+    if close_mode not in schema.CLOSE_MODES:
+        close_mode = "always"
+    try:
+        flow = flows.load_flow(name)
+    except FlowNotFound:
+        return {"error": f"Flödet '{name}' finns inte."}
+    except InvalidFlowName as exc:
+        return {"error": str(exc)}
+    run_id = runs.start_run(
+        flow.get("steps", []), close_mode=close_mode, settings=flow.get("settings", {})
+    )
+    return {"run_id": run_id, "status": "running"}
+
+
+@mcp.tool()
+def get_run(run_id: str) -> dict:
+    """Status, logg-svans och variabler för en körning."""
+    run = runs.get_run(run_id)
+    if run is None:
+        return {"error": f"Okänt run_id: {run_id}"}
+    log_lines = run["logs"]
+    return {
+        "status": run["status"],
+        "browser_open": run["browser_open"],
+        "trace_ready": run["trace_ready"],
+        "log_tail": log_lines[-_MAX_LOG_LINES:],
+        "log_truncated": len(log_lines) > _MAX_LOG_LINES,
+        "vars": run["vars"],
+    }
+
+
+@mcp.tool()
+def close_run_browser(run_id: str) -> dict:
+    """Stänger webbläsaren för en körning som hålls öppen (close_mode never/on_success)."""
+    return {"ok": runs.close_browser(run_id)}
+
+
+def main() -> None:
+    log.info("Startar RPA MCP-server (stdio)")
+    mcp.run(transport="stdio")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/rpa/runs.py b/rpa/runs.py
index 7d920af..92ebd78 100644
--- a/rpa/runs.py
+++ b/rpa/runs.py
@@ -1,3 +1,4 @@
+import sys
 import threading
 import uuid
 from datetime import datetime
@@ -14,7 +15,9 @@ def _append_log(run_id: str, message: str) -> None:
     line = f"[{datetime.now().strftime('%H:%M:%S')}] {message}"
     with _LOCK:
         _RUNS[run_id]["logs"].append(line)
-    print(f"[run {run_id}] {message}")
+    # stderr, inte stdout: MCP-servern (rpa/mcp_server.py) talar JSON-RPC över
+    # stdout och en körning startad därifrån får inte skriva dit.
+    print(f"[run {run_id}] {message}", file=sys.stderr)


 def start_run(steps: list, close_mode: str = "always", settings: dict | None = None) -> str:
diff --git a/rpa/schema.py b/rpa/schema.py
new file mode 100644
index 0000000..1644799
--- /dev/null
+++ b/rpa/schema.py
@@ -0,0 +1,906 @@
+"""Auktoritativ, maskinläsbar beskrivning av alla stegtyper i ett flöde.
+
+Detta är den gemensamma källan för:
+  * webui:t (fältrendering - i dagsläget speglat i index.html:s FIELD_DEFS)
+  * MCP-servern (rpa/mcp_server.py - verktyget describe_steps / resursen rpa://schema)
+  * NL-till-flöde (rpa/assist.py - systemprompt + validering av AI:ns svar)
+
+Innehållet är härlett ur rpa/flow_engine.py (_execute_step) och
+webui/templates/index.html (FIELD_DEFS / I18N). Ändras en stegtyp i motorn ska
+den speglas här - tests/test_schema.py har en drift-vakt som fångar glapp.
+
+Fältens "kind":
+  text    - fri text (kan innehålla {variabel} om interpolate=True)
+  number  - heltal/decimal
+  select  - ett av "options"
+"""
+
+from __future__ import annotations
+
+import json
+import re
+from typing import Any
+
+# --------------------------------------------------------------------------
+# Byggstenar
+# --------------------------------------------------------------------------
+
+# Fälten som pekar ut ett element (samma som SELECTOR_FIELDS i index.html).
+SELECTOR_FIELDS: list[dict] = [
+    {
+        "key": "by",
+        "kind": "select",
+        "options": ["text", "css", "role"],
+        "default": "text",
+        "label_sv": "Hitta via",
+        "label_en": "Find by",
+        "help": "text = synlig text (regex, skiftlägesokänsligt), css = CSS-selector, role = ARIA-roll",
+    },
+    {
+        "key": "value",
+        "kind": "text",
+        "interpolate": True,
+        "label_sv": "Text / CSS-selector / rollnamn",
+        "label_en": "Text / CSS selector / role name",
+        "help": "Tolkas enligt 'by'. {variabel} fungerar för by=text och by=role, inte för by=css.",
+    },
+    {
+        "key": "role",
+        "kind": "text",
+        "label_sv": "Roll (om by=role, ex. link, button)",
+        "label_en": "Role (if by=role, e.g. link, button)",
+    },
+    {
+        "key": "scope",
+        "kind": "select",
+        "options": ["page", "item"],
+        "default": "page",
+        "label_sv": "Sökområde",
+        "label_en": "Search scope",
+        "help": "item begränsar sökningen till aktuell loop-post (bara inuti en loop med DOM-poster).",
+    },
+    {
+        "key": "nth",
+        "kind": "number",
+        "default": 0,
+        "label_sv": "Index (0 = första matchning)",
+        "label_en": "Index (0 = first match)",
+    },
+]
+
+_TIMEOUT_FIELD = {
+    "key": "timeout",
+    "kind": "number",
+    "default": 15,
+    "label_sv": "Timeout (sekunder)",
+    "label_en": "Timeout (seconds)",
+}
+
+# Villkorstyper för "if"-grenar (CONDITION_KINDS / CONDITION_FIELD_DEFS i index.html).
+_VAR_TEXT_FIELDS = [
+    {"key": "var", "kind": "text", "required": True, "label_sv": "Variabelnamn", "label_en": "Variable name"},
+    {"key": "text", "kind": "text", "required": True, "label_sv": "Jämför med", "label_en": "Compare with"},
+]
+
+CONDITION_KINDS: list[dict] = [
+    {
+        "kind": "element_exists",
+        "label_sv": "Element finns",
+        "label_en": "Element exists",
+        "fields": SELECTOR_FIELDS,
+    },
+    {
+        "kind": "element_not_exists",
+        "label_sv": "Element finns inte",
+        "label_en": "Element does not exist",
+        "fields": SELECTOR_FIELDS,
+    },
+    {
+        "kind": "var_equals",
+        "label_sv": "Variabel är lika med",
+        "label_en": "Variable equals",
+        "fields": _VAR_TEXT_FIELDS,
+    },
+    {
+        "kind": "var_not_equals",
+        "label_sv": "Variabel är INTE lika med",
+        "label_en": "Variable does NOT equal",
+        "fields": _VAR_TEXT_FIELDS,
+    },
+    {
+        "kind": "var_contains",
+        "label_sv": "Variabel innehåller",
+        "label_en": "Variable contains",
+        "fields": _VAR_TEXT_FIELDS,
+    },
+]
+
+CONDITION_KIND_NAMES = [c["kind"] for c in CONDITION_KINDS]
+
+# Körningsinställningar (spegel av DEFAULT_SETTINGS i rpa/browser.py + notify).
+SETTINGS_SCHEMA: list[dict] = [
+    {"key": "headless", "kind": "bool", "default": False,
+     "label_sv": "Headless (inget synligt fönster)", "label_en": "Headless (no visible window)",
+     "help": "Kräver en redan giltig inloggningssession - BankID/2FA går inte att godkänna headless."},
+    {"key": "skip_browser", "kind": "bool", "default": False,
+     "label_sv": "Starta ingen webbläsare (rena desktop-flöden)",
+     "label_en": "Don't start a browser (desktop-only flows)",
+     "help": "Tvingar bort webbläsaren. Upptäcks annars automatiskt när flödet saknar webbläsarsteg. Ett webbläsarsteg med detta på ger fel."},
+    {"key": "viewport_width", "kind": "number", "default": 1280,
+     "label_sv": "Fönsterbredd", "label_en": "Viewport width"},
+    {"key": "viewport_height", "kind": "number", "default": 900,
+     "label_sv": "Fönsterhöjd", "label_en": "Viewport height"},
+    {"key": "locale", "kind": "text", "default": "sv-SE", "label_sv": "Locale", "label_en": "Locale"},
+    {"key": "slow_mo", "kind": "number", "default": 0,
+     "label_sv": "Slow motion (ms mellan åtgärder)", "label_en": "Slow motion (ms between actions)"},
+    {"key": "notify_on_complete", "kind": "bool", "default": False,
+     "label_sv": "Skrivbordsnotis när körningen är klar", "label_en": "Desktop notification when the run finishes"},
+]
+
+CLOSE_MODES = ["always", "on_success", "never"]
+
+# Nycklar som får finnas på vilket steg som helst (flow_engine._run_steps).
+COMMON_STEP_FIELDS: list[dict] = [
+    {"key": "label", "kind": "text", "label_sv": "Etikett", "label_en": "Label",
+     "help": "Namnger steget som mål för ett 'jump_to_label'-steg."},
+    {"key": "enabled", "kind": "bool", "default": True,
+     "label_sv": "Aktiverat", "label_en": "Enabled",
+     "help": "false = steget (och ev. inre steg) hoppas över vid körning."},
+    {"key": "on_error", "kind": "select", "options": ["stop", "continue"], "default": "stop",
+     "label_sv": "Vid fel", "label_en": "On error",
+     "help": "continue loggar felet och kör vidare. download/save_attachment/capture_pdf fortsätter alltid."},
+]
+
+
+def _sel(*extra: dict) -> list[dict]:
+    return [*SELECTOR_FIELDS, *extra]
+
+
+# Selector-fält för Windows UI Automation (pywinauto) - annan modell än webben.
+DESKTOP_SELECTOR_FIELDS: list[dict] = [
+    {
+        "key": "by",
+        "kind": "select",
+        "options": ["name", "auto_id", "control_type", "class_name"],
+        "default": "name",
+        "label_sv": "Hitta via",
+        "label_en": "Find by",
+        "help": "name = kontrollens synliga text (regex), auto_id = AutomationId, control_type = t.ex. Button/Edit, class_name = fönsterklass",
+    },
+    {"key": "value", "kind": "text", "interpolate": True,
+     "label_sv": "Söksträng", "label_en": "Search string"},
+    {"key": "control_type", "kind": "text",
+     "label_sv": "Kontrolltyp (valfri extra filtrering, t.ex. Button)",
+     "label_en": "Control type (optional extra filter, e.g. Button)"},
+    {"key": "window", "kind": "text",
+     "label_sv": "Fönstertitel (regex, tomt = aktuellt fönster)",
+     "label_en": "Window title (regex, blank = current window)"},
+    {"key": "nth", "kind": "number", "default": 0,
+     "label_sv": "Index (0 = första)", "label_en": "Index (0 = first)"},
+]
+
+
+def _dsel(*extra: dict) -> list[dict]:
+    return [*DESKTOP_SELECTOR_FIELDS, *extra]
+
+
+# --------------------------------------------------------------------------
+# Stegtyperna
+# --------------------------------------------------------------------------
+
+STEP_TYPES: list[dict] = [
+    # ---- Navigering ----
+    {
+        "type": "goto", "category": "navigation",
+        "label_sv": "Gå till URL", "label_en": "Go to URL",
+        "help": "Navigerar webbläsaren till en URL (wait_until=domcontentloaded).",
+        "fields": [{"key": "url", "kind": "text", "required": True, "label_sv": "URL", "label_en": "URL"}],
+    },
+    {
+        "type": "wait", "category": "navigation",
+        "label_sv": "Vänta (sekunder)", "label_en": "Wait (seconds)",
+        "help": "Fast paus.",
+        "fields": [{"key": "seconds", "kind": "number", "default": 1,
+                    "label_sv": "Sekunder", "label_en": "Seconds"}],
+    },
+    {
+        "type": "wait_for_load", "category": "navigation",
+        "label_sv": "Vänta på att sidan laddas", "label_en": "Wait for page to load",
+        "help": "Väntar på domcontentloaded.", "fields": [],
+    },
+    {
+        "type": "go_back", "category": "navigation",
+        "label_sv": "Gå tillbaka (webbläsarhistorik)", "label_en": "Go back (browser history)",
+        "help": "Webbläsarens bakåtknapp - t.ex. för att fortsätta en loop efter att ha klickat in på en post.",
+        "fields": [],
+    },
+    {
+        "type": "wait_for_login", "category": "navigation",
+        "label_sv": "Vänta på inloggning", "label_en": "Wait for login",
+        "help": "Pausar tills sidan visar inloggad status (för manuell BankID/2FA).",
+        "fields": [
+            {"key": "timeout", "kind": "number", "default": 180,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+            {"key": "indicators", "kind": "text",
+             "label_sv": "Text som visar inloggad (komma-separerad, tomt = standard)",
+             "label_en": "Logged-in text (comma-separated, empty = default)"},
+        ],
+    },
+    {
+        "type": "wait_for_selector", "category": "navigation",
+        "label_sv": "Vänta på element", "label_en": "Wait for element",
+        "help": "Pausar tills ett element via text/CSS/roll finns.",
+        "selector": True, "fields": _sel(dict(_TIMEOUT_FIELD)),
+    },
+    # ---- Interaktion ----
+    {
+        "type": "click", "category": "interaction",
+        "label_sv": "Klicka", "label_en": "Click",
+        "help": "Hittar ett element och klickar, väntar sedan på domcontentloaded.",
+        "selector": True, "fields": _sel(),
+    },
+    {
+        "type": "type", "category": "interaction",
+        "label_sv": "Skriv i textfält", "label_en": "Type in field",
+        "help": "Fyller i ett fält (fill). 'text' stödjer {variabel}.",
+        "selector": True,
+        "fields": _sel({"key": "text", "kind": "text", "interpolate": True, "required": True,
+                        "label_sv": "Text att skriva", "label_en": "Text to type"}),
+    },
+    {
+        "type": "press_key", "category": "interaction",
+        "label_sv": "Tryck tangent", "label_en": "Press key",
+        "help": "Trycker en tangent. Utan value/by: mot hela sidan.",
+        "fields": [
+            {"key": "key", "kind": "text", "default": "Enter", "required": True,
+             "label_sv": "Tangent (ex. Enter, Tab, Escape)", "label_en": "Key (e.g. Enter, Tab, Escape)"},
+            {"key": "value", "kind": "text", "interpolate": True,
+             "label_sv": "Text / CSS-selector / rollnamn (tomt = hela sidan)",
+             "label_en": "Text / CSS selector / role name (empty = whole page)"},
+            {"key": "by", "kind": "select", "options": ["text", "css", "role"], "default": "text",
+             "label_sv": "Hitta via (om value ifylld)", "label_en": "Find by (if value is set)"},
+            {"key": "role", "kind": "text", "label_sv": "Roll (om by=role)", "label_en": "Role (if by=role)"},
+            {"key": "scope", "kind": "select", "options": ["page", "item"], "default": "page",
+             "label_sv": "Sökområde", "label_en": "Search scope"},
+            {"key": "nth", "kind": "number", "default": 0,
+             "label_sv": "Index (0 = första)", "label_en": "Index (0 = first)"},
+        ],
+    },
+    {
+        "type": "select_option", "category": "interaction",
+        "label_sv": "Välj i dropdown", "label_en": "Select in dropdown",
+        "help": "Väljer ett alternativ (synlig text) i en <select>.",
+        "selector": True,
+        "fields": _sel({"key": "option", "kind": "text", "required": True,
+                        "label_sv": "Alternativ (synlig text)", "label_en": "Option (visible text)"}),
+    },
+    {
+        "type": "check", "category": "interaction",
+        "label_sv": "Kryssa checkbox/radio", "label_en": "Check checkbox/radio",
+        "help": "Kryssar i eller ur en checkbox/radioknapp.",
+        "selector": True,
+        "fields": _sel({"key": "checked", "kind": "select", "options": ["true", "false"], "default": "true",
+                        "label_sv": "Kryssa i (false = kryssa ur)", "label_en": "Check (false = uncheck)"}),
+    },
+    # ---- Data / variabler ----
+    {
+        "type": "extract_text", "category": "data",
+        "label_sv": "Läs text till variabel", "label_en": "Read text into variable",
+        "help": "Läser ett elements textinnehåll och sparar det i en variabel.",
+        "selector": True,
+        "fields": _sel({"key": "var", "kind": "text", "required": True,
+                        "label_sv": "Spara som variabel", "label_en": "Save as variable"}),
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "extract_row", "category": "data",
+        "label_sv": "Extrahera rad (flera kolumner)", "label_en": "Extract row (multiple columns)",
+        "help": "Läser flera namngivna fält (celler) från aktuell post till en rad-dict och lägger den i listvariabeln 'var'. Ger riktiga CSV-kolumner.",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True,
+             "label_sv": "Spara som variabel (lista)", "label_en": "Save as variable (list)"},
+            {"key": "scope", "kind": "select", "options": ["page", "item"], "default": "item",
+             "label_sv": "Sökområde", "label_en": "Search scope"},
+            {"key": "fields", "kind": "field_list", "required": True,
+             "label_sv": "Kolumner", "label_en": "Columns",
+             "help": "Lista av {name, by, value, role?} - name blir kolumnrubrik."},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "set_var", "category": "data",
+        "label_sv": "Sätt variabel", "label_en": "Set variable",
+        "help": "Sätter en variabel till ett literalt värde ('text' stödjer {variabel}).",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True, "label_sv": "Variabelnamn", "label_en": "Variable name"},
+            {"key": "text", "kind": "text", "interpolate": True, "label_sv": "Värde", "label_en": "Value"},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "increment", "category": "data",
+        "label_sv": "Öka variabel", "label_en": "Increment variable",
+        "help": "Ökar en heltalsvariabel med 'by' (negativt = minska). Saknad/icke-numerisk = 0.",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True, "label_sv": "Variabelnamn", "label_en": "Variable name"},
+            {"key": "by", "kind": "number", "default": 1,
+             "label_sv": "Öka med (negativt = minska)", "label_en": "Increase by (negative = decrease)"},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "clear_var", "category": "data",
+        "label_sv": "Nollställ variabel", "label_en": "Clear variable",
+        "help": "Tar bort en variabel. Lägg först i en loop för att nollställa en samlar-lista per varv.",
+        "fields": [{"key": "var", "kind": "text", "required": True,
+                    "label_sv": "Variabelnamn", "label_en": "Variable name"}],
+    },
+    {
+        "type": "list_append", "category": "data",
+        "label_sv": "Lägg till i lista", "label_en": "Append to list",
+        "help": "Lägger till i en listvariabel. Läser från ett element om selector satt, annars literalt 'text'.",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True, "label_sv": "Listvariabel", "label_en": "List variable"},
+            {"key": "text", "kind": "text", "interpolate": True,
+             "label_sv": "Literalt värde (om selector nedan tom)", "label_en": "Literal value (if selector below is empty)"},
+            {"key": "value", "kind": "text", "interpolate": True,
+             "label_sv": "Eller: Text / CSS-selector / rollnamn", "label_en": "Or: Text / CSS selector / role name"},
+            {"key": "by", "kind": "select", "options": ["text", "css", "role"], "default": "text",
+             "label_sv": "Hitta via (om value ifylld)", "label_en": "Find by (if value is set)"},
+            {"key": "role", "kind": "text", "label_sv": "Roll (om by=role)", "label_en": "Role (if by=role)"},
+            {"key": "scope", "kind": "select", "options": ["page", "item"], "default": "page",
+             "label_sv": "Sökområde", "label_en": "Search scope"},
+            {"key": "nth", "kind": "number", "default": 0,
+             "label_sv": "Index (0 = första)", "label_en": "Index (0 = first)"},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "save_var", "category": "data",
+        "label_sv": "Spara variabel (JSON/CSV)", "label_en": "Save variable (JSON/CSV)",
+        "help": "Sparar en variabel till data/exports/. Format gissas från filändelsen.",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True,
+             "label_sv": "Variabel att spara", "label_en": "Variable to save"},
+            {"key": "filename", "kind": "text", "required": True,
+             "label_sv": "Filnamn i data/exports/ (.json eller .csv)",
+             "label_en": "Filename in data/exports/ (.json or .csv)"},
+            {"key": "format", "kind": "select", "options": ["", "json", "csv"], "default": "",
+             "label_sv": "Format (tomt = gissa från filnamn)", "label_en": "Format (empty = guess from filename)"},
+            {"key": "mode", "kind": "select", "options": ["overwrite", "append"], "default": "overwrite",
+             "label_sv": "Läge", "label_en": "Mode"},
+        ],
+        "consumes_vars": ["var"],
+    },
+    {
+        "type": "load_var", "category": "data",
+        "label_sv": "Läs in variabel (JSON/CSV)", "label_en": "Load variable (JSON/CSV)",
+        "help": "Läser tillbaka en tidigare sparad fil från data/exports/ till en variabel.",
+        "fields": [
+            {"key": "var", "kind": "text", "required": True,
+             "label_sv": "Spara som variabel", "label_en": "Save as variable"},
+            {"key": "filename", "kind": "text", "required": True,
+             "label_sv": "Filnamn i data/exports/", "label_en": "Filename in data/exports/"},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "run_script", "category": "data",
+        "label_sv": "Kör Python-script", "label_en": "Run Python script",
+        "help": "Kör ett separat Python-script (sökväg relativt projektroten eller absolut). Loggar stdout/stderr, kan spara stdout som variabel (JSON om möjligt). Flödet stoppas vid felkod.",
+        "fields": [
+            {"key": "path", "kind": "text", "required": True,
+             "label_sv": "Sökväg till Python-script", "label_en": "Path to Python script"},
+            {"key": "args", "kind": "text", "label_sv": "Argument (mellanslagsseparerade)",
+             "label_en": "Arguments (space-separated)"},
+            {"key": "timeout", "kind": "number", "default": 60,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+            {"key": "var", "kind": "text",
+             "label_sv": "Spara stdout som variabel (valfritt)", "label_en": "Save stdout as variable (optional)"},
+        ],
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "screenshot", "category": "data",
+        "label_sv": "Ta skärmdump", "label_en": "Take screenshot",
+        "help": "Sparar en skärmdump i data/screenshots/.",
+        "fields": [{"key": "name", "kind": "text", "default": "screenshot",
+                    "label_sv": "Filnamn (utan .png)", "label_en": "Filename (without .png)"}],
+    },
+    {
+        "type": "list", "category": "data",
+        "label_sv": "Lista poster", "label_en": "List items",
+        "help": "Hittar alla element som matchar en CSS-selector och sparar antal+selector i 'var' (för en efterföljande loop).",
+        "fields": [
+            {"key": "value", "kind": "text", "required": True,
+             "label_sv": "CSS-selector", "label_en": "CSS selector"},
+            {"key": "var", "kind": "text", "required": True,
+             "label_sv": "Spara som variabel", "label_en": "Save as variable"},
+        ],
+        "produces_vars": ["var"],
+    },
+    # ---- Kontrollflöde ----
+    {
+        "type": "if", "category": "control",
+        "label_sv": "Om / annars om / annars", "label_en": "If / else if / else",
+        "help": "Första grenen vars villkor är sant körs. Valfri 'else'-gren om ingen matchar.",
+        "container": "branches", "fields": [],
+    },
+    {
+        "type": "jump_to_label", "category": "control",
+        "label_sv": "Hoppa till etikett", "label_en": "Jump to label",
+        "help": "Hoppar till steget vars 'label' matchar 'target', bland syskonsteg på samma nivå (inte in i/ut ur loop/if).",
+        "fields": [{"key": "target", "kind": "text", "required": True,
+                    "label_sv": "Hoppa till etikett", "label_en": "Jump to label"}],
+    },
+    {
+        "type": "loop", "category": "control",
+        "label_sv": "Loop (för varje post)", "label_en": "Loop (for each item)",
+        "help": "Itererar över ett 'list'-resultat (DOM-poster, scope=item i inre steg) ELLER en vanlig listvariabel (värdet i item_var, {item} i by=text/role).",
+        "container": "steps",
+        "fields": [
+            {"key": "over", "kind": "text", "required": True,
+             "label_sv": "Loopa över variabel", "label_en": "Loop over variable"},
+            {"key": "item_var", "kind": "text", "default": "item",
+             "label_sv": "Variabel för aktuell post", "label_en": "Current item variable"},
+            {"key": "index_var", "kind": "text", "default": "index",
+             "label_sv": "Variabel för varvräknare (1, 2, 3 …)", "label_en": "Iteration counter variable (1, 2, 3 …)"},
+        ],
+        "consumes_vars": ["over"],
+        "produces_vars": ["item_var", "index_var"],
+    },
+    # ---- Filer ----
+    {
+        "type": "download", "category": "files",
+        "label_sv": "Ladda ner", "label_en": "Download",
+        "help": "Klickar ett element och sparar den nedladdade filen i data/downloads/. Fortsätter alltid vid fel.",
+        "selector": True, "fields": _sel(),
+    },
+    {
+        "type": "save_attachment", "category": "files",
+        "label_sv": "Ladda ner bilaga", "label_en": "Download attachment",
+        "help": "Hittar en <a> i/under elementet och sparar filen i data/downloads/ (via href-hämtning eller klick-fångst). Fortsätter alltid vid fel.",
+        "selector": True,
+        "fields": _sel(
+            {"key": "filename", "kind": "text", "interpolate": True,
+             "label_sv": "Filnamn i data/downloads/ (tomt = från URL; stödjer {variabel})",
+             "label_en": "Filename in data/downloads/ (blank = from URL; supports {variable})"},
+            {"key": "var", "kind": "text",
+             "label_sv": "Lägg sparad sökväg i den här listvariabeln (valfritt)",
+             "label_en": "Append saved path to this list variable (optional)"},
+            dict(_TIMEOUT_FIELD),
+        ),
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "capture_pdf", "category": "files",
+        "label_sv": "Fånga PDF (skriv ut-knapp)", "label_en": "Capture PDF (print button)",
+        "help": "För 'Skriv ut'-knappar som öppnar skrivardialogen. Neutraliserar window.print, klickar ev. triggern (selector valfri) och fångar PDF:en. Sparar i data/downloads/. Fortsätter alltid vid fel.",
+        "selector": True,
+        "fields": _sel(
+            {"key": "filename", "kind": "text", "interpolate": True, "default": "utskrift.pdf",
+             "label_sv": "PDF-filnamn i data/downloads/ (stödjer {variabel})",
+             "label_en": "PDF filename in data/downloads/ (supports {variable})"},
+            {"key": "var", "kind": "text",
+             "label_sv": "Spara sökväg som variabel", "label_en": "Save path as variable"},
+            dict(_TIMEOUT_FIELD),
+        ),
+        "produces_vars": ["var"],
+    },
+    # ---- Desktop (Windows) ----
+    {
+        "type": "desktop_launch", "category": "desktop",
+        "label_sv": "Starta app", "label_en": "Launch app",
+        "help": "Startar ett program och gör dess fönster till 'aktuellt fönster'. Windows-only.",
+        "fields": [
+            {"key": "path", "kind": "text", "required": True,
+             "label_sv": "Sökväg / kommando (t.ex. notepad.exe)", "label_en": "Path / command (e.g. notepad.exe)"},
+            {"key": "args", "kind": "text", "label_sv": "Argument", "label_en": "Arguments"},
+            {"key": "cwd", "kind": "text", "label_sv": "Arbetskatalog (valfri)", "label_en": "Working directory (optional)"},
+            {"key": "title", "kind": "text",
+             "label_sv": "Fönstertitel att vänta på (regex; behövs för paketerade appar)",
+             "label_en": "Window title to wait for (regex; needed for packaged apps)"},
+            {"key": "timeout", "kind": "number", "default": 20,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+        ],
+    },
+    {
+        "type": "desktop_connect", "category": "desktop",
+        "label_sv": "Anslut till fönster", "label_en": "Connect to window",
+        "help": "Ansluter till ett redan körande fönster (titel som regex) och gör det till aktuellt fönster.",
+        "fields": [
+            {"key": "title", "kind": "text", "required": True,
+             "label_sv": "Fönstertitel (regex)", "label_en": "Window title (regex)"},
+            {"key": "timeout", "kind": "number", "default": 20,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+        ],
+    },
+    {
+        "type": "desktop_wait_for_window", "category": "desktop",
+        "label_sv": "Vänta på fönster", "label_en": "Wait for window",
+        "help": "Pausar tills ett fönster med matchande titel finns.",
+        "fields": [
+            {"key": "title", "kind": "text", "required": True,
+             "label_sv": "Fönstertitel (regex)", "label_en": "Window title (regex)"},
+            {"key": "timeout", "kind": "number", "default": 20,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+        ],
+    },
+    {
+        "type": "desktop_focus_window", "category": "desktop",
+        "label_sv": "Fokusera fönster", "label_en": "Focus window",
+        "help": "Tar fram fönstret i förgrunden. Tom titel = aktuellt fönster.",
+        "fields": [
+            {"key": "title", "kind": "text",
+             "label_sv": "Fönstertitel (regex, tomt = aktuellt)", "label_en": "Window title (regex, blank = current)"},
+            {"key": "timeout", "kind": "number", "default": 10,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+        ],
+    },
+    {
+        "type": "desktop_click", "category": "desktop",
+        "label_sv": "Klicka (kontroll)", "label_en": "Click (control)",
+        "help": "Hittar en UI-kontroll via Windows UI Automation och klickar (riktigt musklick).",
+        "fields": _dsel({"key": "double", "kind": "select", "options": ["false", "true"], "default": "false",
+                         "label_sv": "Dubbelklick", "label_en": "Double click"}),
+    },
+    {
+        "type": "desktop_type", "category": "desktop",
+        "label_sv": "Skriv i kontroll", "label_en": "Type in control",
+        "help": "Skriver text i ett fält. 'text' stödjer {variabel}.",
+        "fields": _dsel({"key": "text", "kind": "text", "interpolate": True, "required": True,
+                         "label_sv": "Text att skriva", "label_en": "Text to type"}),
+    },
+    {
+        "type": "desktop_press_key", "category": "desktop",
+        "label_sv": "Tryck tangenter", "label_en": "Press keys",
+        "help": "Skickar tangenttryck till fönstret (pywinauto-syntax: ^s = Ctrl+S, {ENTER}, %{F4} …). Tom titel = aktuellt fönster.",
+        "fields": [
+            {"key": "keys", "kind": "text", "required": True, "interpolate": True,
+             "label_sv": "Tangenter (^s, {ENTER}, {TAB} …)", "label_en": "Keys (^s, {ENTER}, {TAB} …)"},
+            {"key": "window", "kind": "text",
+             "label_sv": "Fönstertitel (regex, tomt = aktuellt)", "label_en": "Window title (regex, blank = current)"},
+        ],
+    },
+    {
+        "type": "desktop_read_text", "category": "desktop",
+        "label_sv": "Läs kontrolltext", "label_en": "Read control text",
+        "help": "Läser en kontrolls text/värde till en variabel.",
+        "fields": _dsel({"key": "var", "kind": "text", "required": True,
+                         "label_sv": "Spara som variabel", "label_en": "Save as variable"}),
+        "produces_vars": ["var"],
+    },
+    {
+        "type": "desktop_screenshot", "category": "desktop",
+        "label_sv": "Skärmdump (desktop)", "label_en": "Screenshot (desktop)",
+        "help": "Sparar en skärmdump av aktuellt fönster (eller helskärm om inget fönster) i data/screenshots/.",
+        "fields": [
+            {"key": "name", "kind": "text", "default": "desktop",
+             "label_sv": "Filnamn (utan .png)", "label_en": "Filename (without .png)"},
+            {"key": "window", "kind": "text",
+             "label_sv": "Fönstertitel (regex, tomt = aktuellt/helskärm)", "label_en": "Window title (regex, blank = current/full screen)"},
+        ],
+    },
+    {
+        "type": "desktop_click_image", "category": "desktop",
+        "label_sv": "Klicka på bild", "label_en": "Click image",
+        "help": "Letar upp en referensbild på skärmen (pyautogui) och klickar i dess mitt. För appar utan tillgänglighets-API.",
+        "fields": [
+            {"key": "image", "kind": "text", "required": True,
+             "label_sv": "Bildfil i data/desktop_images/ (eller absolut sökväg)",
+             "label_en": "Image file in data/desktop_images/ (or absolute path)"},
+            {"key": "confidence", "kind": "number", "default": 0.9,
+             "label_sv": "Träffsäkerhet 0–1", "label_en": "Match confidence 0–1"},
+            {"key": "timeout", "kind": "number", "default": 20,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+            {"key": "double", "kind": "select", "options": ["false", "true"], "default": "false",
+             "label_sv": "Dubbelklick", "label_en": "Double click"},
+            {"key": "button", "kind": "select", "options": ["left", "right", "middle"], "default": "left",
+             "label_sv": "Musknapp", "label_en": "Mouse button"},
+        ],
+    },
+    {
+        "type": "desktop_wait_for_image", "category": "desktop",
+        "label_sv": "Vänta på bild", "label_en": "Wait for image",
+        "help": "Pausar tills en referensbild syns på skärmen.",
+        "fields": [
+            {"key": "image", "kind": "text", "required": True,
+             "label_sv": "Bildfil i data/desktop_images/", "label_en": "Image file in data/desktop_images/"},
+            {"key": "confidence", "kind": "number", "default": 0.9,
+             "label_sv": "Träffsäkerhet 0–1", "label_en": "Match confidence 0–1"},
+            {"key": "timeout", "kind": "number", "default": 20,
+             "label_sv": "Timeout (sekunder)", "label_en": "Timeout (seconds)"},
+        ],
+    },
+    {
+        "type": "desktop_click_xy", "category": "desktop",
+        "label_sv": "Klicka på koordinat", "label_en": "Click coordinates",
+        "help": "Klickar på absoluta skärmkoordinater (pyautogui).",
+        "fields": [
+            {"key": "x", "kind": "number", "required": True, "label_sv": "X", "label_en": "X"},
+            {"key": "y", "kind": "number", "required": True, "label_sv": "Y", "label_en": "Y"},
+            {"key": "double", "kind": "select", "options": ["false", "true"], "default": "false",
+             "label_sv": "Dubbelklick", "label_en": "Double click"},
+            {"key": "button", "kind": "select", "options": ["left", "right", "middle"], "default": "left",
+             "label_sv": "Musknapp", "label_en": "Mouse button"},
+        ],
+    },
+]
+
+STEP_TYPE_NAMES = [s["type"] for s in STEP_TYPES]
+STEP_BY_TYPE = {s["type"]: s for s in STEP_TYPES}
+
+
+# --------------------------------------------------------------------------
+# Validering
+# --------------------------------------------------------------------------
+
+def validate_flow(flow: Any, *, strict: bool = True) -> list[str]:
+    """Strukturell validering av ett flöde. Returnerar en lista felmeddelanden
+    (tom lista = giltigt). Fångar det som skulle krascha eller tyst missförstås
+    av flow_engine - inte allt semantiskt (t.ex. att en variabel faktiskt finns
+    vid körning).
+
+    strict=False hoppar över "obligatoriskt fält saknas" - används av webui:t som
+    autosparar halvfärdiga flöden under redigering. MCP-servern och NL-till-flöde
+    använder strict=True."""
+    errors: list[str] = []
+    if not isinstance(flow, dict):
+        return ["Flödet måste vara ett objekt med nycklarna 'steps' och (valfritt) 'settings'."]
+
+    steps = flow.get("steps", [])
+    if not isinstance(steps, list):
+        errors.append("'steps' måste vara en lista.")
+    else:
+        _validate_steps(steps, "steps", errors, strict)
+
+    settings = flow.get("settings", {})
+    if not isinstance(settings, dict):
+        errors.append("'settings' måste vara ett objekt.")
+    else:
+        allowed = {s["key"] for s in SETTINGS_SCHEMA}
+        for key in settings:
+            if key not in allowed:
+                errors.append(f"settings.{key}: okänd inställning (tillåtna: {', '.join(sorted(allowed))}).")
+
+    return errors
+
+
+def _validate_steps(steps: list, path: str, errors: list[str], strict: bool = True) -> None:
+    for i, step in enumerate(steps):
+        p = f"{path}[{i}]"
+        if not isinstance(step, dict):
+            errors.append(f"{p}: steget måste vara ett objekt.")
+            continue
+        t = step.get("type")
+        if t not in STEP_BY_TYPE:
+            errors.append(f"{p}.type: okänd stegtyp {t!r} (tillåtna: {', '.join(STEP_TYPE_NAMES)}).")
+            continue
+
+        spec = STEP_BY_TYPE[t]
+
+        # Gemensamma fält.
+        if "on_error" in step and step["on_error"] not in ("stop", "continue"):
+            errors.append(f"{p}.on_error: måste vara 'stop' eller 'continue'.")
+        if "enabled" in step and not isinstance(step["enabled"], bool):
+            errors.append(f"{p}.enabled: måste vara true/false.")
+
+        # Obligatoriska fält.
+        if strict:
+            for f in spec["fields"]:
+                if f.get("required") and _is_blank(step.get(f["key"])):
+                    errors.append(f"{p}.{f['key']}: obligatoriskt för stegtypen '{t}'.")
+
+        # Enum-fält (drivs av fältets egna 'options').
+        for f in spec["fields"]:
+            if f.get("kind") != "select" or not f.get("options"):
+                continue
+            val = step.get(f["key"])
+            if val not in (None, "") and val not in f["options"]:
+                errors.append(f"{p}.{f['key']}: måste vara ett av {', '.join(o for o in f['options'] if o)}.")
+
+        # Behållarsteg.
+        if t == "loop":
+            inner = step.get("steps", [])
+            if not isinstance(inner, list):
+                errors.append(f"{p}.steps: måste vara en lista.")
+            else:
+                _validate_steps(inner, f"{p}.steps", errors, strict)
+        elif t == "if":
+            branches = step.get("branches")
+            if not isinstance(branches, list) or not branches:
+                errors.append(f"{p}.branches: 'if' kräver minst en gren.")
+            else:
+                for bi, branch in enumerate(branches):
+                    bp = f"{p}.branches[{bi}]"
+                    if not isinstance(branch, dict):
+                        errors.append(f"{bp}: grenen måste vara ett objekt.")
+                        continue
+                    _validate_condition(branch.get("condition", {}), f"{bp}.condition", errors)
+                    binner = branch.get("steps", [])
+                    if not isinstance(binner, list):
+                        errors.append(f"{bp}.steps: måste vara en lista.")
+                    else:
+                        _validate_steps(binner, f"{bp}.steps", errors, strict)
+            if step.get("else") is not None:
+                if not isinstance(step["else"], list):
+                    errors.append(f"{p}.else: måste vara en lista.")
+                else:
+                    _validate_steps(step["else"], f"{p}.else", errors, strict)
+        elif t == "extract_row":
+            fields = step.get("fields", [])
+            if not isinstance(fields, list) or not fields:
+                errors.append(f"{p}.fields: extract_row kräver minst en kolumn ({{name, by, value}}).")
+            else:
+                for fi, col in enumerate(fields):
+                    if not isinstance(col, dict) or _is_blank(col.get("name")):
+                        errors.append(f"{p}.fields[{fi}].name: kolumnen måste ha ett namn.")
+
+
+def _validate_condition(cond: Any, path: str, errors: list[str]) -> None:
+    if not isinstance(cond, dict):
+        errors.append(f"{path}: villkoret måste vara ett objekt.")
+        return
+    kind = cond.get("kind")
+    spec = next((c for c in CONDITION_KINDS if c["kind"] == kind), None)
+    if spec is None:
+        errors.append(f"{path}.kind: okänt villkor {kind!r} (tillåtna: {', '.join(CONDITION_KIND_NAMES)}).")
+        return
+    for f in spec["fields"]:
+        if f.get("required") and _is_blank(cond.get(f["key"])):
+            errors.append(f"{path}.{f['key']}: obligatoriskt för villkoret '{kind}'.")
+
+
+def _is_blank(value: Any) -> bool:
+    return value is None or (isinstance(value, str) and value.strip() == "")
+
+
+# --------------------------------------------------------------------------
+# JSON Schema + LLM-referens
+# --------------------------------------------------------------------------
+
+def flow_json_schema() -> dict:
+    """JSON Schema för ett helt flöde ({steps, settings}), rekursivt för loop/if.
+    Används som vägledning till LLM:en - den precisa kontrollen görs av
+    validate_flow()."""
+    all_field_keys: set[str] = set()
+    for spec in STEP_TYPES:
+        for f in spec["fields"]:
+            all_field_keys.add(f["key"])
+    for f in COMMON_STEP_FIELDS:
+        all_field_keys.add(f["key"])
+
+    step_props: dict[str, Any] = {
+        "type": {"type": "string", "enum": STEP_TYPE_NAMES},
+        "label": {"type": "string"},
+        "enabled": {"type": "boolean"},
+        "on_error": {"type": "string", "enum": ["stop", "continue"]},
+        "steps": {"type": "array", "items": {"$ref": "#/$defs/step"}},
+        "branches": {
+            "type": "array",
+            "items": {
+                "type": "object",
+                "properties": {
+                    "condition": {"$ref": "#/$defs/condition"},
+                    "steps": {"type": "array", "items": {"$ref": "#/$defs/step"}},
+                },
+                "required": ["condition", "steps"],
+            },
+        },
+        "else": {"type": "array", "items": {"$ref": "#/$defs/step"}},
+        "fields": {"type": "array", "items": {"type": "object"}},
+    }
+    for key in sorted(all_field_keys):
+        step_props.setdefault(key, {"type": ["string", "number", "boolean"]})
+
+    return {
+        "$schema": "https://json-schema.org/draft/2020-12/schema",
+        "type": "object",
+        "properties": {
+            "steps": {"type": "array", "items": {"$ref": "#/$defs/step"}},
+            "settings": {
+                "type": "object",
+                "properties": {s["key"]: _setting_schema(s) for s in SETTINGS_SCHEMA},
+            },
+            "explanation": {"type": "string", "description": "Kort förklaring på svenska av vad flödet gör."},
+        },
+        "required": ["steps"],
+        "$defs": {
+            "step": {"type": "object", "properties": step_props, "required": ["type"]},
+            "condition": {
+                "type": "object",
+                "properties": {
+                    "kind": {"type": "string", "enum": CONDITION_KIND_NAMES},
+                    "by": {"type": "string", "enum": ["text", "css", "role"]},
+                    "value": {"type": "string"},
+                    "role": {"type": "string"},
+                    "scope": {"type": "string", "enum": ["page", "item"]},
+                    "nth": {"type": "number"},
+                    "var": {"type": "string"},
+                    "text": {"type": "string"},
+                },
+                "required": ["kind"],
+            },
+        },
+    }
+
+
+def _setting_schema(s: dict) -> dict:
+    return {
+        "bool": {"type": "boolean"},
+        "number": {"type": "number"},
+        "text": {"type": "string"},
+    }[s["kind"]]
+
+
+def step_reference_markdown() -> str:
+    """Människo/LLM-läsbar referens över alla stegtyper, genererad ur STEP_TYPES.
+    Bäddas in i systemprompten i rpa/assist.py."""
+    out: list[str] = []
+    out.append("## Stegtyper\n")
+    out.append(
+        "Ett flöde är JSON: {\"steps\": [ ...steg... ], \"settings\": { ... }}. "
+        "Varje steg är {\"type\": \"<typ>\", ...fält...}. Alla fältvärden är strängar "
+        "om inget annat sägs. Fält markerade (obl.) måste vara ifyllda.\n"
+    )
+    by_cat: dict[str, list[dict]] = {}
+    for spec in STEP_TYPES:
+        by_cat.setdefault(spec["category"], []).append(spec)
+
+    cat_labels = {
+        "navigation": "Navigering", "interaction": "Interaktion", "data": "Data / variabler",
+        "control": "Kontrollflöde", "files": "Filer", "desktop": "Desktop (Windows)",
+    }
+    for cat, specs in by_cat.items():
+        out.append(f"\n### {cat_labels.get(cat, cat)}\n")
+        for spec in specs:
+            out.append(f"- **`{spec['type']}`** – {spec['label_sv']}. {spec.get('help', '')}")
+            for f in spec["fields"]:
+                bits = [f"`{f['key']}`"]
+                if f.get("required"):
+                    bits.append("(obl.)")
+                if f.get("options"):
+                    bits.append("= " + " | ".join(repr(o) for o in f["options"] if o))
+                if f.get("default") not in (None, ""):
+                    bits.append(f"(default {f['default']!r})")
+                if f.get("help"):
+                    bits.append("– " + f["help"])
+                out.append(f"    - {' '.join(bits)}")
+            if spec.get("container") == "steps":
+                out.append("    - `steps`: lista av inre steg")
+            if spec.get("container") == "branches":
+                out.append("    - `branches`: lista av {condition, steps}; valfri `else`: lista av steg")
+            if spec["type"] == "extract_row":
+                out.append("    - `fields`: lista av {name, by, value, role?} - name blir kolumnrubrik")
+
+    out.append("\n### Villkor (för `if`-grenar)\n")
+    for c in CONDITION_KINDS:
+        keys = ", ".join(f"`{f['key']}`" for f in c["fields"])
+        out.append(f"- `{c['kind']}` – {c['label_sv']} (fält: {keys})")
+
+    out.append("\n### Interpolering\n")
+    out.append(
+        "Textfält (by=text/role-värden samt literala textfält i `type`/`set_var`/"
+        "`list_append`/`save_attachment.filename`/`capture_pdf.filename`) kan innehålla "
+        "`{variabelnamn}` eller `{variabelnamn.fält}`. Fungerar INTE i CSS-selectors, "
+        "URL:er eller andra filnamn.\n"
+    )
+    out.append("\n### Inställningar (`settings`)\n")
+    for s in SETTINGS_SCHEMA:
+        out.append(f"- `{s['key']}` ({s['kind']}, default {s['default']!r}) – {s['label_sv']}")
+
+    return "\n".join(out)
+
+
+def engine_step_types_from_source(flow_engine_source: str) -> set[str]:
+    """Plockar ut stegtyperna som motorn faktiskt hanterar, för drift-vakten i
+    tests/test_schema.py. Matchar `if t == "..."` / `elif t == "..."` samt
+    desktop_*-literalerna i `_DESKTOP_STEPS`-mängden."""
+    found = set(re.findall(r'\b(?:el)?if t == "([a-z_]+)":', flow_engine_source))
+    found |= set(re.findall(r'"(desktop_[a-z_]+)"', flow_engine_source))
+    return found
+
+
+if __name__ == "__main__":  # snabb manuell inspektion
+    print(json.dumps({"step_types": STEP_TYPE_NAMES}, ensure_ascii=False, indent=2))
+    print(step_reference_markdown())
diff --git a/run_mcp.py b/run_mcp.py
new file mode 100644
index 0000000..84087c2
--- /dev/null
+++ b/run_mcp.py
@@ -0,0 +1,10 @@
+"""Startar RPA-verktygets MCP-server (stdio) - motsvarigheten till run_webui.py.
+
+Vanligtvis startas den av MCP-klienten (Claude Code / Claude Desktop), inte för
+hand. Se rpa/mcp_server.py för hur man registrerar den.
+"""
+
+from rpa.mcp_server import main
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/test_assist.py b/tests/test_assist.py
new file mode 100644
index 0000000..24e435f
--- /dev/null
+++ b/tests/test_assist.py
@@ -0,0 +1,72 @@
+import pytest
+
+from rpa import assist
+
+
+class _Block:
+    def __init__(self, data):
+        self.type = "tool_use"
+        self.name = "emit_flow"
+        self.input = data
+
+
+class _Resp:
+    def __init__(self, data):
+        self.content = [_Block(data)]
+
+
+class _FakeMessages:
+    def __init__(self, replies):
+        self._replies = list(replies)
+        self.calls = []
+
+    def create(self, **kwargs):
+        self.calls.append(kwargs)
+        return _Resp(self._replies.pop(0))
+
+
+class _FakeClient:
+    def __init__(self, replies):
+        self.messages = _FakeMessages(replies)
+
+
+def _patch(monkeypatch, replies):
+    client = _FakeClient(replies)
+    monkeypatch.setattr(assist, "_client", lambda: client)
+    return client
+
+
+def test_generate_flow_happy_path(monkeypatch):
+    _patch(monkeypatch, [{
+        "steps": [{"type": "goto", "url": "https://example.com/"}],
+        "settings": {},
+        "explanation": "Går till example.com.",
+    }])
+    result = assist.generate_flow("gå till example.com")
+    assert result["steps"][0]["url"] == "https://example.com/"
+    assert result["explanation"] == "Går till example.com."
+
+
+def test_generate_flow_repairs_once(monkeypatch):
+    client = _patch(monkeypatch, [
+        {"steps": [{"type": "goto"}], "settings": {}},               # url saknas -> ogiltigt
+        {"steps": [{"type": "goto", "url": "https://ok.se/"}], "settings": {}},  # reparerat
+    ])
+    result = assist.generate_flow("gå till ok.se")
+    assert result["steps"][0]["url"] == "https://ok.se/"
+    assert len(client.messages.calls) == 2
+
+
+def test_generate_flow_gives_up_after_repair(monkeypatch):
+    _patch(monkeypatch, [
+        {"steps": [{"type": "goto"}], "settings": {}},
+        {"steps": [{"type": "goto"}], "settings": {}},
+    ])
+    with pytest.raises(assist.AssistError):
+        assist.generate_flow("trasigt")
+
+
+def test_empty_instruction_rejected(monkeypatch):
+    _patch(monkeypatch, [])
+    with pytest.raises(assist.AssistError):
+        assist.generate_flow("   ")
diff --git a/tests/test_desktop.py b/tests/test_desktop.py
new file mode 100644
index 0000000..fa09316
--- /dev/null
+++ b/tests/test_desktop.py
@@ -0,0 +1,56 @@
+"""Desktop-lagret (rpa/desktop.py) - testar det som går utan en riktig GUI:
+lat import, tydliga fel när pywinauto/pyautogui saknas, close-mode-städning."""
+
+import pytest
+
+from rpa import desktop, schema
+
+
+def test_desktop_module_guards_optional_imports():
+    """rpa/desktop.py måste importeras rent även utan pywinauto/pyautogui -
+    modulnamnen finns alltid (som None om paketen saknas)."""
+    assert hasattr(desktop, "pywinauto")
+    assert hasattr(desktop, "pyautogui")
+
+    import rpa.flow_engine as fe
+
+    assert "desktop_click" in fe._DESKTOP_STEPS and len(fe._DESKTOP_STEPS) == 12
+
+
+def test_methods_raise_desktop_unavailable_without_backends(monkeypatch):
+    monkeypatch.setattr(desktop, "pywinauto", None)
+    monkeypatch.setattr(desktop, "Application", None)
+    monkeypatch.setattr(desktop, "Desktop", None)
+    monkeypatch.setattr(desktop, "pyautogui", None)
+    d = desktop.DesktopSession(log=lambda _m: None)
+
+    with pytest.raises(desktop.DesktopUnavailable):
+        d.launch("notepad.exe")
+    with pytest.raises(desktop.DesktopUnavailable):
+        d.click_xy(1, 2)
+    with pytest.raises(desktop.DesktopUnavailable):
+        d.wait_for_window("x")
+
+
+def test_close_leaves_or_kills_launched(monkeypatch):
+    killed = []
+
+    class FakeApp:
+        def kill(self, soft=False):  # noqa: ARG002
+            killed.append(True)
+
+    d = desktop.DesktopSession(log=lambda _m: None)
+    d.launched = [FakeApp(), FakeApp()]
+
+    d.close(kill_launched=False)
+    assert killed == []
+
+    d.close(kill_launched=True)
+    assert killed == [True, True]
+
+
+def test_all_engine_desktop_steps_are_in_schema():
+    """Drift-vakt (även täckt av test_schema, men här som lokal check)."""
+    import rpa.flow_engine as fe
+
+    assert fe._DESKTOP_STEPS == {s["type"] for s in schema.STEP_TYPES if s["type"].startswith("desktop_")}
diff --git a/tests/test_flows.py b/tests/test_flows.py
new file mode 100644
index 0000000..7ea9af4
--- /dev/null
+++ b/tests/test_flows.py
@@ -0,0 +1,70 @@
+import pytest
+
+from rpa import config, flows
+
+
+@pytest.fixture(autouse=True)
+def tmp_flows_dir(tmp_path, monkeypatch):
+    d = tmp_path / "flows"
+    d.mkdir()
+    monkeypatch.setattr(config, "FLOWS_DIR", d)
+    return d
+
+
+def test_crud_round_trip():
+    assert flows.list_flows() == []
+
+    flows.save_flow("mitt flöde", {"steps": [{"type": "goto", "url": "https://x.se"}], "settings": {}})
+    assert flows.list_flows() == ["mitt flöde"]
+
+    loaded = flows.load_flow("mitt flöde")
+    assert loaded["steps"][0]["url"] == "https://x.se"
+    assert loaded["settings"] == {}
+
+    assert flows.delete_flow("mitt flöde") is True
+    assert flows.list_flows() == []
+    assert flows.delete_flow("mitt flöde") is False
+
+
+def test_invalid_name_rejected():
+    with pytest.raises(flows.InvalidFlowName):
+        flows.flow_path("../secret")
+    with pytest.raises(flows.InvalidFlowName):
+        flows.flow_path("a" * 200)
+
+
+def test_load_missing_raises():
+    with pytest.raises(flows.FlowNotFound):
+        flows.load_flow("nope")
+
+
+def test_save_lenient_by_default_strict_on_request():
+    half = {"steps": [{"type": "goto"}], "settings": {}}  # url saknas
+    flows.save_flow("wip", half)  # lenient - ok
+    with pytest.raises(flows.InvalidFlow):
+        flows.save_flow("wip", half, strict=True)
+
+
+def test_save_rejects_structural_errors_even_lenient():
+    with pytest.raises(flows.InvalidFlow):
+        flows.save_flow("bad", {"steps": [{"type": "nonesuch"}]})
+
+
+def test_layout_and_step_ids_round_trip():
+    flow = {
+        "steps": [{"type": "goto", "id": "abc123", "url": "https://x.se"}],
+        "settings": {},
+        "layout": {"abc123": {"x": 40, "y": 120}},
+    }
+    flows.save_flow("laid out", flow)
+    loaded = flows.load_flow("laid out")
+    assert loaded["steps"][0]["id"] == "abc123"
+    assert loaded["layout"] == {"abc123": {"x": 40, "y": 120}}
+
+
+def test_layout_omitted_when_empty():
+    flows.save_flow("nolayout", {"steps": [], "settings": {}, "layout": {}})
+    import json
+
+    raw = json.loads((flows.config.FLOWS_DIR / "nolayout.json").read_text(encoding="utf-8"))
+    assert "layout" not in raw
diff --git a/tests/test_schema.py b/tests/test_schema.py
new file mode 100644
index 0000000..0093a47
--- /dev/null
+++ b/tests/test_schema.py
@@ -0,0 +1,108 @@
+from pathlib import Path
+
+from rpa import schema
+
+ROOT = Path(__file__).resolve().parent.parent
+
+GOOD_FLOW = {
+    "steps": [
+        {"type": "goto", "url": "https://example.com/"},
+        {"type": "wait_for_login", "timeout": 120},
+        {"type": "list", "value": "a", "var": "links"},
+        {
+            "type": "loop",
+            "over": "links",
+            "steps": [
+                {"type": "extract_text", "by": "css", "value": ":scope", "scope": "item", "var": "lnk"},
+                {"type": "list_append", "var": "collected", "text": "{lnk}"},
+            ],
+        },
+        {
+            "type": "if",
+            "branches": [
+                {"condition": {"kind": "var_contains", "var": "collected", "text": "x"},
+                 "steps": [{"type": "screenshot", "name": "hit"}]},
+            ],
+            "else": [{"type": "screenshot", "name": "miss"}],
+        },
+        {"type": "save_var", "var": "collected", "filename": "out.csv"},
+    ],
+    "settings": {"headless": True},
+}
+
+
+DESKTOP_FLOW = {
+    "steps": [
+        {"type": "desktop_launch", "path": "notepad.exe"},
+        {"type": "desktop_type", "by": "control_type", "value": "Edit", "text": "hej {namn}"},
+        {"type": "desktop_press_key", "keys": "^s"},
+        {"type": "desktop_click_xy", "x": 10, "y": 20},
+    ],
+    "settings": {},
+}
+
+
+def test_good_flow_validates():
+    assert schema.validate_flow(GOOD_FLOW) == []
+
+
+def test_desktop_flow_validates():
+    assert schema.validate_flow(DESKTOP_FLOW) == []
+
+
+def test_desktop_steps_have_category():
+    desktop = [s for s in schema.STEP_TYPES if s["type"].startswith("desktop_")]
+    assert len(desktop) == 12
+    assert all(s["category"] == "desktop" for s in desktop)
+
+
+def test_unknown_step_type_rejected():
+    errors = schema.validate_flow({"steps": [{"type": "teleport"}]})
+    assert any("okänd stegtyp" in e for e in errors)
+
+
+def test_missing_required_field_strict_only():
+    flow = {"steps": [{"type": "goto"}]}  # url saknas
+    assert any("url" in e for e in schema.validate_flow(flow, strict=True))
+    assert schema.validate_flow(flow, strict=False) == []
+
+
+def test_bad_enum_rejected_even_non_strict():
+    flow = {"steps": [{"type": "click", "value": "Knapp", "by": "telepathy"}]}
+    assert any(".by" in e for e in schema.validate_flow(flow, strict=False))
+
+
+def test_if_requires_branch():
+    errors = schema.validate_flow({"steps": [{"type": "if", "branches": []}]})
+    assert any("kräver minst en gren" in e for e in errors)
+
+
+def test_unknown_setting_rejected():
+    errors = schema.validate_flow({"steps": [], "settings": {"turbo": True}})
+    assert any("turbo" in e for e in errors)
+
+
+def test_engine_and_schema_step_types_match():
+    """Drift-vakt: varje stegtyp som flow_engine hanterar ska finnas i STEP_TYPES
+    och tvärtom."""
+    src = (ROOT / "rpa" / "flow_engine.py").read_text(encoding="utf-8")
+    engine_types = schema.engine_step_types_from_source(src)
+    schema_types = set(schema.STEP_TYPE_NAMES)
+    assert engine_types == schema_types, (
+        f"bara i motorn: {engine_types - schema_types}; "
+        f"bara i schema: {schema_types - engine_types}"
+    )
+
+
+def test_json_schema_is_serializable_and_recursive():
+    import json
+
+    js = schema.flow_json_schema()
+    json.dumps(js)
+    assert js["$defs"]["step"]["properties"]["steps"]["items"] == {"$ref": "#/$defs/step"}
+
+
+def test_reference_markdown_mentions_every_step():
+    md = schema.step_reference_markdown()
+    for name in schema.STEP_TYPE_NAMES:
+        assert f"`{name}`" in md
diff --git a/tests/test_skip_browser.py b/tests/test_skip_browser.py
new file mode 100644
index 0000000..4ce1b4c
--- /dev/null
+++ b/tests/test_skip_browser.py
@@ -0,0 +1,83 @@
+"""skip_browser: rena desktop-/variabelflöden startar ingen webbläsare."""
+
+import pytest
+
+from rpa import flow_engine
+
+
+# --- ren logik: _flow_needs_browser --------------------------------------
+
+def test_var_only_flow_needs_no_browser():
+    steps = [
+        {"type": "set_var", "var": "x", "text": "1"},
+        {"type": "increment", "var": "x"},
+        {"type": "save_var", "var": "x", "filename": "x.json"},
+        {"type": "wait", "seconds": 0},
+    ]
+    assert flow_engine._flow_needs_browser(steps) is False
+
+
+def test_desktop_only_flow_needs_no_browser():
+    steps = [
+        {"type": "desktop_launch", "path": "notepad.exe"},
+        {"type": "loop", "over": "x", "steps": [{"type": "desktop_click", "by": "name", "value": "OK"}]},
+        {"type": "if", "branches": [{"condition": {"kind": "var_equals", "var": "x", "text": "1"},
+                                     "steps": [{"type": "desktop_screenshot"}]}]},
+    ]
+    assert flow_engine._flow_needs_browser(steps) is False
+
+
+def test_goto_needs_browser():
+    assert flow_engine._flow_needs_browser([{"type": "goto", "url": "https://x"}]) is True
+
+
+def test_loop_with_browser_body_needs_browser():
+    steps = [{"type": "loop", "over": "x", "steps": [{"type": "click", "by": "text", "value": "A"}]}]
+    assert flow_engine._flow_needs_browser(steps) is True
+
+
+def test_if_with_element_condition_needs_browser():
+    steps = [{"type": "if", "branches": [{"condition": {"kind": "element_exists", "by": "css", "value": ".x"},
+                                          "steps": [{"type": "desktop_screenshot"}]}]}]
+    assert flow_engine._flow_needs_browser(steps) is True
+
+
+# --- integration: webbläsaren startas inte -------------------------------
+
+def test_run_flow_skips_browser_for_var_only(tmp_path, monkeypatch):
+    def boom(*a, **k):
+        raise AssertionError("launch_context anropades trots att flödet inte behöver webbläsaren")
+
+    monkeypatch.setattr(flow_engine, "launch_context", boom)
+    monkeypatch.setattr(flow_engine.config, "EXPORT_DIR", tmp_path)
+
+    logs: list[str] = []
+    result: dict = {}
+    flow_engine.run_flow(
+        [
+            {"type": "set_var", "var": "a", "text": "hej"},
+            {"type": "save_var", "var": "a", "filename": "a.json"},
+        ],
+        log=logs.append,
+        on_finished=lambda err, v: result.update({"err": err, "vars": v}),
+    )
+    assert result["err"] is None
+    assert result["vars"]["a"] == "hej"
+    assert (tmp_path / "a.json").exists()
+    assert any("utan webbläsare" in m for m in logs)
+
+
+def test_run_flow_forced_skip_then_browser_step_errors(monkeypatch):
+    monkeypatch.setattr(
+        flow_engine, "launch_context",
+        lambda *a, **k: (_ for _ in ()).throw(AssertionError("should not launch")),
+    )
+    result: dict = {}
+    flow_engine.run_flow(
+        [{"type": "goto", "url": "https://example.com/"}],
+        log=lambda _m: None,
+        settings={"skip_browser": True},
+        on_finished=lambda err, v: result.update({"err": err}),
+    )
+    assert isinstance(result["err"], flow_engine.FlowError)
+    assert "kräver webbläsaren" in str(result["err"])
diff --git a/webui/app.py b/webui/app.py
index 7f26a27..b753185 100644
--- a/webui/app.py
+++ b/webui/app.py
@@ -1,43 +1,46 @@
-import json
-import re
 import subprocess
 import sys
 from pathlib import Path

 from fastapi import FastAPI, HTTPException
 from fastapi.responses import HTMLResponse, PlainTextResponse
+from fastapi.staticfiles import StaticFiles
 from pydantic import BaseModel

-from rpa import config
+from rpa import assist, config, flows, schema
 from rpa.export import generate_standalone_script
+from rpa.flows import FlowNotFound, InvalidFlow, InvalidFlowName
 from rpa.runs import close_browser, get_run, get_trace_path, start_run

 app = FastAPI(title="Egenremiss RPA - flödesbyggare")

-TEMPLATES_DIR = Path(__file__).parent / "templates"
+STATIC_DIR = Path(__file__).parent / "static"
 WIKI_PATH = config.ROOT_DIR / "docs" / "wiki.html"
-NAME_RE = re.compile(r"^[a-zA-Z0-9_\-åäöÅÄÖ ]{1,80}$")
-CLOSE_MODES = {"always", "on_success", "never"}
+CLOSE_MODES = set(schema.CLOSE_MODES)


 class Flow(BaseModel):
     steps: list = []
     settings: dict = {}
+    layout: dict = {}


 class RunOptions(BaseModel):
     close_mode: str = "always"


-def _flow_path(name: str) -> Path:
-    if not NAME_RE.match(name):
-        raise HTTPException(400, "Ogiltigt flödesnamn")
-    return config.FLOWS_DIR / f"{name}.json"
+class AssistRequest(BaseModel):
+    instruction: str
+    steps: list = []
+    settings: dict = {}


-@app.get("/", response_class=HTMLResponse)
-def index() -> str:
-    return (TEMPLATES_DIR / "index.html").read_text(encoding="utf-8")
+def _require_name(name: str) -> str:
+    try:
+        flows.flow_path(name)
+    except InvalidFlowName as exc:
+        raise HTTPException(400, str(exc)) from exc
+    return name


 @app.get("/wiki", response_class=HTMLResponse)
@@ -47,40 +50,72 @@ def wiki() -> str:
     return WIKI_PATH.read_text(encoding="utf-8")


+@app.get("/api/step-schema")
+def step_schema() -> dict:
+    """Auktoritativ stegschema (rpa/schema.py) - samma som MCP-servern och
+    NL-till-flöde använder."""
+    return {
+        "step_types": schema.STEP_TYPES,
+        "condition_kinds": schema.CONDITION_KINDS,
+        "settings": schema.SETTINGS_SCHEMA,
+        "close_modes": schema.CLOSE_MODES,
+    }
+
+
 @app.get("/api/flows")
 def list_flows() -> list[str]:
-    return sorted(p.stem for p in config.FLOWS_DIR.glob("*.json"))
+    return flows.list_flows()


 @app.get("/api/flows/{name}")
 def get_flow(name: str) -> dict:
-    path = _flow_path(name)
-    if not path.exists():
-        return {"steps": []}
-    return json.loads(path.read_text(encoding="utf-8"))
+    _require_name(name)
+    try:
+        return flows.load_flow(name)
+    except FlowNotFound:
+        return {"steps": [], "settings": {}, "layout": {}}


 @app.put("/api/flows/{name}")
 def save_flow(name: str, flow: Flow) -> dict:
-    path = _flow_path(name)
-    path.write_text(json.dumps(flow.model_dump(), ensure_ascii=False, indent=2), encoding="utf-8")
+    _require_name(name)
+    try:
+        flows.save_flow(name, flow.model_dump())
+    except InvalidFlow as exc:
+        raise HTTPException(422, {"errors": exc.errors}) from exc
     return {"ok": True}


 @app.delete("/api/flows/{name}")
 def delete_flow(name: str) -> dict:
-    path = _flow_path(name)
-    if path.exists():
-        path.unlink()
+    _require_name(name)
+    flows.delete_flow(name)
     return {"ok": True}


+@app.post("/api/flows/{name}/assist")
+def assist_flow(name: str, req: AssistRequest) -> dict:
+    """NL → flöde. Genererar/ändrar ett flöde utifrån en instruktion men SPARAR
+    INTE - klienten visar förslaget och användaren trycker Spara/Kör själv."""
+    _require_name(name)
+    try:
+        result = assist.generate_flow(
+            req.instruction, current_flow={"steps": req.steps, "settings": req.settings}
+        )
+    except assist.AssistUnavailable as exc:
+        raise HTTPException(501, str(exc)) from exc
+    except assist.AssistError as exc:
+        raise HTTPException(422, str(exc)) from exc
+    return result
+
+
 @app.post("/api/flows/{name}/run")
 def run_flow_endpoint(name: str, options: RunOptions = RunOptions()) -> dict:
-    path = _flow_path(name)
-    if not path.exists():
-        raise HTTPException(404, "Flödet finns inte")
-    flow = json.loads(path.read_text(encoding="utf-8"))
+    _require_name(name)
+    try:
+        flow = flows.load_flow(name)
+    except FlowNotFound as exc:
+        raise HTTPException(404, "Flödet finns inte") from exc
     close_mode = options.close_mode if options.close_mode in CLOSE_MODES else "always"
     run_id = start_run(flow.get("steps", []), close_mode=close_mode, settings=flow.get("settings", {}))
     return {"run_id": run_id}
@@ -88,10 +123,11 @@ def run_flow_endpoint(name: str, options: RunOptions = RunOptions()) -> dict:

 @app.get("/api/flows/{name}/export")
 def export_flow(name: str) -> PlainTextResponse:
-    path = _flow_path(name)
-    if not path.exists():
-        raise HTTPException(404, "Flödet finns inte")
-    flow = json.loads(path.read_text(encoding="utf-8"))
+    _require_name(name)
+    try:
+        flow = flows.load_flow(name)
+    except FlowNotFound as exc:
+        raise HTTPException(404, "Flödet finns inte") from exc
     script = generate_standalone_script(name, flow)
     return PlainTextResponse(
         script,
@@ -125,3 +161,17 @@ def show_trace_endpoint(run_id: str) -> dict:
     # inte blockeras.
     subprocess.Popen([sys.executable, "-m", "playwright", "show-trace", str(trace_path)])
     return {"ok": True}
+
+
+# Frontend (byggd av webui/frontend, incheckad). Monteras SIST så att alla
+# /api- och /wiki-rutter matchas först. Kör `npm run build` i webui/frontend
+# efter UI-ändringar.
+if STATIC_DIR.exists():
+    app.mount("/", StaticFiles(directory=str(STATIC_DIR), html=True), name="static")
+else:  # pragma: no cover
+    @app.get("/", response_class=HTMLResponse)
+    def _no_build() -> str:
+        return (
+            "<h1>Frontend inte byggd</h1><p>Kör <code>cd webui/frontend &amp;&amp; "
+            "npm install &amp;&amp; npm run build</code>.</p>"
+        )
diff --git a/webui/frontend/index.html b/webui/frontend/index.html
new file mode 100644
index 0000000..a6762d2
--- /dev/null
+++ b/webui/frontend/index.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>RPA Tool – flow builder</title>
+  </head>
+  <body>
+    <div id="root"></div>
+    <script type="module" src="/src/main.tsx"></script>
+  </body>
+</html>
diff --git a/webui/frontend/package-lock.json b/webui/frontend/package-lock.json
new file mode 100644
index 0000000..e2f4367
--- /dev/null
+++ b/webui/frontend/package-lock.json
@@ -0,0 +1,3532 @@
+{
+  "name": "rpa-remisser-frontend",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "rpa-remisser-frontend",
+      "version": "0.0.0",
+      "dependencies": {
+        "@xyflow/react": "^12.11.0",
+        "react": "^18.3.1",
+        "react-dom": "^18.3.1"
+      },
+      "devDependencies": {
+        "@types/react": "^18.3.12",
+        "@types/react-dom": "^18.3.1",
+        "@vitejs/plugin-react": "^4.3.4",
+        "typescript": "^5.7.2",
+        "vite": "^6.0.5",
+        "vitest": "^2.1.8"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+      "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+      "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+      "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.7",
+        "@babel/helper-compilation-targets": "^7.29.7",
+        "@babel/helper-module-transforms": "^7.29.7",
+        "@babel/helpers": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/template": "^7.29.7",
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7",
+        "@jridgewell/remapping": "^2.3.5",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz",
+      "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.29.8",
+        "@babel/types": "^7.29.8",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+      "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.29.7",
+        "@babel/helper-validator-option": "^7.29.7",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+      "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+      "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+      "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7",
+        "@babel/traverse": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+      "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+      "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+      "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+      "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+      "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
+      "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.29.8"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-react-jsx-self": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+      "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-react-jsx-source": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+      "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.29.7",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+      "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/parser": "^7.29.7",
+        "@babel/types": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz",
+      "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.29.7",
+        "@babel/generator": "^7.29.8",
+        "@babel/helper-globals": "^7.29.7",
+        "@babel/parser": "^7.29.8",
+        "@babel/template": "^7.29.7",
+        "@babel/types": "^7.29.8",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.29.8",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
+      "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.29.7",
+        "@babel/helper-validator-identifier": "^7.29.7"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+      "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+      "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+      "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+      "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+      "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+      "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+      "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+      "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+      "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+      "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+      "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+      "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+      "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+      "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+      "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+      "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+      "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+      "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/openharmony-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+      "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+      "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+      "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+      "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+      "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/remapping": {
+      "version": "2.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+      "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+      "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+      "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+      "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": "^22.20 || ^24.12 || >=25"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.27",
+      "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
+      "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz",
+      "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz",
+      "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz",
+      "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz",
+      "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz",
+      "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz",
+      "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz",
+      "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz",
+      "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz",
+      "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz",
+      "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz",
+      "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loong64-musl": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz",
+      "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz",
+      "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-musl": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz",
+      "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz",
+      "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz",
+      "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz",
+      "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz",
+      "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "libc": [
+        "glibc"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz",
+      "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "libc": [
+        "musl"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-openbsd-x64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz",
+      "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-openharmony-arm64": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz",
+      "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openharmony"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz",
+      "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz",
+      "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz",
+      "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz",
+      "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@types/babel__core": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+      "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.20.7",
+        "@babel/types": "^7.20.7",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "node_modules/@types/babel__generator": {
+      "version": "7.27.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+      "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__template": {
+      "version": "7.4.4",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+      "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "node_modules/@types/babel__traverse": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+      "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.2"
+      }
+    },
+    "node_modules/@types/d3-color": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-drag": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+      "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-interpolate": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-color": "*"
+      }
+    },
+    "node_modules/@types/d3-selection": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+      "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-transition": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+      "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-zoom": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+      "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-interpolate": "*",
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+      "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/prop-types": {
+      "version": "15.7.15",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+      "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/react": {
+      "version": "18.3.31",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
+      "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/prop-types": "*",
+        "csstype": "^3.2.2"
+      }
+    },
+    "node_modules/@types/react-dom": {
+      "version": "18.3.7",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
+      "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
+      "devOptional": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "^18.0.0"
+      }
+    },
+    "node_modules/@vitejs/plugin-react": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
+      "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.28.0",
+        "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+        "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+        "@rolldown/pluginutils": "1.0.0-beta.27",
+        "@types/babel__core": "^7.20.5",
+        "react-refresh": "^0.17.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
+      "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
+      "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
+      "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "2.1.9",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
+      "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
+      "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^3.0.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
+      "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "loupe": "^3.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@xyflow/react": {
+      "version": "12.11.6",
+      "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.11.6.tgz",
+      "integrity": "sha512-9XsEJNHjatKYndszKTF/bsU7FOP9dJ6V/EQwzy3oMdtqgBuUq7BjKSwkEo+C7s4qHstHQfwwoHA3E8QfpPxZZQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@xyflow/system": "0.0.82",
+        "classcat": "^5.0.3",
+        "zustand": "^4.4.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=17",
+        "@types/react-dom": ">=17",
+        "react": ">=17",
+        "react-dom": ">=17"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@xyflow/system": {
+      "version": "0.0.82",
+      "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.82.tgz",
+      "integrity": "sha512-4DKnL3CGtCGLRSmgDqaajRVgeksMXq/Yw4wPfdMfm7JvdIiWHGzVYOfFUztiATwdBXZqUU6HhehwUdbV9G23PQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-drag": "^3.0.7",
+        "@types/d3-interpolate": "^3.0.4",
+        "@types/d3-selection": "^3.0.10",
+        "@types/d3-transition": "^3.0.8",
+        "@types/d3-zoom": "^3.0.8",
+        "d3-drag": "^3.0.0",
+        "d3-interpolate": "^3.0.1",
+        "d3-selection": "^3.0.0",
+        "d3-zoom": "^3.0.0"
+      }
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.11.21",
+      "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.21.tgz",
+      "integrity": "sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.cjs"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.8",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+      "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "baseline-browser-mapping": "^2.11.12",
+        "caniuse-lite": "^1.0.30001809",
+        "electron-to-chromium": "^1.5.402",
+        "node-releases": "^2.0.53",
+        "update-browserslist-db": "^1.3.0"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001810",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+      "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "CC-BY-4.0"
+    },
+    "node_modules/chai": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
+      "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
+      "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/classcat": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
+      "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==",
+      "license": "MIT"
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/csstype": {
+      "version": "3.2.3",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "d3-selection": "2 - 3"
+      }
+    },
+    "node_modules/d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.422",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.422.tgz",
+      "integrity": "sha512-UvA/32XqrLDdZSn7Jllo1AYNcWji/G0d5M0GTViE7KoGBiMunw3a34Sb2KO4ZZyrSEhqsxFoVhWWJshdyfKqJA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "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"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
+      "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/jsesc": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/loupe": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
+      "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.21",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+      "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.5"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.18",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
+      "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.54",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
+      "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/picomatch": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
+      "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "8.5.28",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
+      "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.18",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/react": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+      "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+      "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "scheduler": "^0.23.2"
+      },
+      "peerDependencies": {
+        "react": "^18.3.1"
+      }
+    },
+    "node_modules/react-refresh": {
+      "version": "0.17.0",
+      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+      "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.63.1",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz",
+      "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.9"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+        "@rollup/rollup-android-arm-eabi": "4.63.1",
+        "@rollup/rollup-android-arm64": "4.63.1",
+        "@rollup/rollup-darwin-arm64": "4.63.1",
+        "@rollup/rollup-darwin-x64": "4.63.1",
+        "@rollup/rollup-freebsd-arm64": "4.63.1",
+        "@rollup/rollup-freebsd-x64": "4.63.1",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.63.1",
+        "@rollup/rollup-linux-arm-musleabihf": "4.63.1",
+        "@rollup/rollup-linux-arm64-gnu": "4.63.1",
+        "@rollup/rollup-linux-arm64-musl": "4.63.1",
+        "@rollup/rollup-linux-loong64-gnu": "4.63.1",
+        "@rollup/rollup-linux-loong64-musl": "4.63.1",
+        "@rollup/rollup-linux-ppc64-gnu": "4.63.1",
+        "@rollup/rollup-linux-ppc64-musl": "4.63.1",
+        "@rollup/rollup-linux-riscv64-gnu": "4.63.1",
+        "@rollup/rollup-linux-riscv64-musl": "4.63.1",
+        "@rollup/rollup-linux-s390x-gnu": "4.63.1",
+        "@rollup/rollup-linux-x64-gnu": "4.63.1",
+        "@rollup/rollup-linux-x64-musl": "4.63.1",
+        "@rollup/rollup-openbsd-x64": "4.63.1",
+        "@rollup/rollup-openharmony-arm64": "4.63.1",
+        "@rollup/rollup-win32-arm64-msvc": "4.63.1",
+        "@rollup/rollup-win32-ia32-msvc": "4.63.1",
+        "@rollup/rollup-win32-x64-gnu": "4.63.1",
+        "@rollup/rollup-win32-x64-msvc": "4.63.1",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/scheduler": {
+      "version": "0.23.2",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+      "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      }
+    },
+    "node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/std-env": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
+      "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.17",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.5.0",
+        "picomatch": "^4.0.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
+    "node_modules/tinypool": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+      "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+      "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+      "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+      "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "escalade": "^3.2.0",
+        "picocolors": "^1.1.1"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/use-sync-external-store": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+      "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/vite": {
+      "version": "6.4.3",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+      "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2",
+        "postcss": "^8.5.3",
+        "rollup": "^4.34.9",
+        "tinyglobby": "^0.2.13"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "jiti": ">=1.21.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-node": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
+      "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.3.7",
+        "es-module-lexer": "^1.5.4",
+        "pathe": "^1.1.2",
+        "vite": "^5.0.0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite-node/node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/vite-node/node_modules/vite": {
+      "version": "5.4.21",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+      "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
+      "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/expect": "2.1.9",
+        "@vitest/mocker": "2.1.9",
+        "@vitest/pretty-format": "^2.1.9",
+        "@vitest/runner": "2.1.9",
+        "@vitest/snapshot": "2.1.9",
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "debug": "^4.3.7",
+        "expect-type": "^1.1.0",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2",
+        "std-env": "^3.8.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.1",
+        "tinypool": "^1.0.1",
+        "tinyrainbow": "^1.2.0",
+        "vite": "^5.0.0",
+        "vite-node": "2.1.9",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "@vitest/browser": "2.1.9",
+        "@vitest/ui": "2.1.9",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vitest/node_modules/@vitest/mocker": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
+      "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.12"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vitest/node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/vitest/node_modules/vite": {
+      "version": "5.4.21",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
+      "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/zustand": {
+      "version": "4.5.7",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz",
+      "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==",
+      "license": "MIT",
+      "dependencies": {
+        "use-sync-external-store": "^1.2.2"
+      },
+      "engines": {
+        "node": ">=12.7.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=16.8",
+        "immer": ">=9.0.6",
+        "react": ">=16.8"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
+    }
+  }
+}
diff --git a/webui/frontend/package.json b/webui/frontend/package.json
new file mode 100644
index 0000000..1da893c
--- /dev/null
+++ b/webui/frontend/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "rpa-remisser-frontend",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "tsc && vite build",
+    "preview": "vite preview",
+    "test": "vitest run"
+  },
+  "dependencies": {
+    "@xyflow/react": "^12.11.0",
+    "react": "^18.3.1",
+    "react-dom": "^18.3.1"
+  },
+  "devDependencies": {
+    "@types/react": "^18.3.12",
+    "@types/react-dom": "^18.3.1",
+    "@vitejs/plugin-react": "^4.3.4",
+    "typescript": "^5.7.2",
+    "vite": "^6.0.5",
+    "vitest": "^2.1.8"
+  },
+  "allowScripts": {
+    "esbuild@0.25.12": true,
+    "esbuild@0.21.5": true
+  }
+}
diff --git a/webui/frontend/src/App.tsx b/webui/frontend/src/App.tsx
new file mode 100644
index 0000000..b64aa4d
--- /dev/null
+++ b/webui/frontend/src/App.tsx
@@ -0,0 +1,220 @@
+import { useCallback, useEffect, useRef, useState } from "react";
+import { ApiError, api, type Flow, type RunState, type Step } from "./lib/api";
+import type { Schema } from "./lib/schema";
+import { makeT, type Lang } from "./lib/i18n";
+import { ensureIds, flowToGraph } from "./lib/graph";
+import { newStep } from "./lib/stepops";
+import { Header } from "./components/Header";
+import { Canvas } from "./components/Canvas";
+import { RunPanel } from "./components/RunPanel";
+import { VarsPanel } from "./components/VarsPanel";
+import { AssistPanel } from "./components/AssistPanel";
+import { SettingsDialog } from "./components/SettingsDialog";
+
+const EMPTY: Flow = { steps: [], settings: {}, layout: {} };
+
+export default function App() {
+  const [schema, setSchema] = useState<Schema | null>(null);
+  const [lang, setLang] = useState<Lang>(() => (localStorage.getItem("rpa_lang") as Lang) || "en");
+  const [closeMode, setCloseMode] = useState(() => localStorage.getItem("rpa_close_mode") || "always");
+  const [flowNames, setFlowNames] = useState<string[]>([]);
+  const [currentName, setCurrentName] = useState<string | null>(null);
+  const [flow, setFlow] = useState<Flow>(EMPTY);
+  const [dirty, setDirty] = useState(false);
+  const [reloadToken, setReloadToken] = useState(0);
+  const [settingsOpen, setSettingsOpen] = useState(false);
+  const [toast, setToast] = useState<string | null>(null);
+
+  const [runId, setRunId] = useState<string | null>(null);
+  const [run, setRun] = useState<RunState | null>(null);
+  const pollTimer = useRef<number | null>(null);
+
+  const t = makeT(lang);
+
+  useEffect(() => {
+    localStorage.setItem("rpa_lang", lang);
+    document.title = t("page_title");
+  }, [lang]); // eslint-disable-line
+
+  useEffect(() => localStorage.setItem("rpa_close_mode", closeMode), [closeMode]);
+
+  // --- init ---
+  useEffect(() => {
+    (async () => {
+      const s = await api.schema();
+      setSchema(s);
+      const names = await api.listFlows();
+      setFlowNames(names);
+      if (names.length) await loadFlow(names[0]);
+      else {
+        const f: Flow = {
+          steps: [ensureIds(newStep(s, "goto")), ensureIds(newStep(s, "wait_for_login"))],
+          settings: {},
+          layout: {},
+        };
+        (f.steps[0] as Step).url = "https://www.1177.se/";
+        setCurrentName("standard");
+        setFlow(f);
+        setDirty(true);
+        setReloadToken((n) => n + 1);
+      }
+    })();
+  }, []); // eslint-disable-line
+
+  const loadFlow = useCallback(async (name: string) => {
+    const f = await api.getFlow(name);
+    f.steps = (f.steps ?? []).map((s) => ensureIds({ ...s }));
+    f.settings = f.settings ?? {};
+    f.layout = f.layout ?? {};
+    setCurrentName(name);
+    setFlow(f);
+    setDirty(false);
+    setReloadToken((n) => n + 1);
+  }, []);
+
+  const onCanvasChange = useCallback(
+    (partial: { steps: Step[]; layout: Record<string, { x: number; y: number }> }) => {
+      setFlow((prev) => ({ ...prev, steps: partial.steps, layout: partial.layout }));
+      setDirty(true);
+    },
+    [],
+  );
+
+  const save = useCallback(async (): Promise<boolean> => {
+    if (!currentName) return false;
+    try {
+      await api.saveFlow(currentName, flow);
+      setDirty(false);
+      const names = await api.listFlows();
+      setFlowNames(names);
+      return true;
+    } catch (e) {
+      const detail = e instanceof ApiError ? JSON.stringify(e.detail) : String(e);
+      setToast(t("save_failed", { msg: detail }));
+      return false;
+    }
+  }, [currentName, flow, t]);
+
+  async function newFlow() {
+    const name = prompt(t("new_flow_prompt"));
+    if (!name || !schema) return;
+    const f: Flow = { steps: [], settings: {}, layout: {} };
+    setCurrentName(name);
+    setFlow(f);
+    setReloadToken((n) => n + 1);
+    try {
+      await api.saveFlow(name, f);
+      setFlowNames(await api.listFlows());
+      setDirty(false);
+    } catch {
+      setDirty(true);
+    }
+  }
+
+  async function deleteFlow() {
+    if (!currentName) return;
+    if (!confirm(t("delete_flow_confirm", { name: currentName }))) return;
+    await api.deleteFlow(currentName);
+    const names = await api.listFlows();
+    setFlowNames(names);
+    if (names.length) await loadFlow(names[0]);
+    else {
+      setCurrentName(null);
+      setFlow(EMPTY);
+      setReloadToken((n) => n + 1);
+    }
+  }
+
+  const stopPoll = () => {
+    if (pollTimer.current) window.clearTimeout(pollTimer.current);
+    pollTimer.current = null;
+  };
+
+  const poll = useCallback((id: string) => {
+    api.run(id).then((data) => {
+      setRun(data);
+      if (data.status === "running" || data.browser_open) {
+        pollTimer.current = window.setTimeout(() => poll(id), 1000);
+      }
+    });
+  }, []);
+
+  async function runFlow() {
+    if (!currentName) return;
+    await save();
+    stopPoll();
+    setRun(null);
+    const { run_id } = await api.runFlow(currentName, closeMode);
+    setRunId(run_id);
+    poll(run_id);
+  }
+
+  useEffect(() => stopPoll, []);
+
+  if (!schema) return <div className="loading">…</div>;
+
+  return (
+    <div className="app">
+      <Header
+        t={t}
+        lang={lang}
+        setLang={setLang}
+        schema={schema}
+        flowNames={flowNames}
+        currentName={currentName}
+        onSelect={loadFlow}
+        onNew={newFlow}
+        onDelete={deleteFlow}
+        onSave={save}
+        onRun={runFlow}
+        dirty={dirty}
+        running={run?.status === "running"}
+        closeMode={closeMode}
+        setCloseMode={setCloseMode}
+        onOpenSettings={() => setSettingsOpen(true)}
+      />
+
+      <main>
+        <Canvas key={reloadToken} flow={flow} schema={schema} lang={lang} t={t} onChange={onCanvasChange} />
+        <aside className="side">
+          <AssistPanel
+            t={t}
+            currentName={currentName}
+            flow={flow}
+            onApply={(f) => {
+              f.steps = f.steps.map((s) => ensureIds({ ...s }));
+              // säkerställ layout-nyckel så Canvas seed:ar rätt
+              f.layout = f.layout ?? {};
+              flowToGraph(f); // fyller i ev. saknade id rekursivt
+              setFlow(f);
+              setDirty(true);
+              setReloadToken((n) => n + 1);
+            }}
+          />
+          <RunPanel t={t} runId={runId} run={run} />
+          <VarsPanel t={t} vars={run?.vars ?? {}} />
+        </aside>
+      </main>
+
+      <SettingsDialog
+        open={settingsOpen}
+        schema={schema}
+        lang={lang}
+        t={t}
+        settings={flow.settings}
+        onClose={() => setSettingsOpen(false)}
+        onSave={(settings) => {
+          setFlow((prev) => ({ ...prev, settings }));
+          setDirty(true);
+          setSettingsOpen(false);
+        }}
+      />
+
+      {toast && (
+        <div className="toast" onClick={() => setToast(null)}>
+          {toast}
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/AssistPanel.tsx b/webui/frontend/src/components/AssistPanel.tsx
new file mode 100644
index 0000000..07e5852
--- /dev/null
+++ b/webui/frontend/src/components/AssistPanel.tsx
@@ -0,0 +1,48 @@
+import { useState } from "react";
+import { ApiError, api, type Flow } from "../lib/api";
+import type { T } from "../lib/i18n";
+
+interface Props {
+  t: T;
+  currentName: string | null;
+  flow: Flow;
+  onApply: (flow: Flow) => void;
+}
+
+export function AssistPanel({ t, currentName, flow, onApply }: Props) {
+  const [text, setText] = useState("");
+  const [busy, setBusy] = useState(false);
+  const [msg, setMsg] = useState<{ text: string; error: boolean } | null>(null);
+
+  async function submit() {
+    if (!text.trim() || !currentName) return;
+    setBusy(true);
+    setMsg(null);
+    try {
+      const res = await api.assist(currentName, text.trim(), flow);
+      onApply({ steps: res.steps, settings: res.settings, layout: {} });
+      setMsg({ text: t("assist_applied") + (res.explanation ? "\n\n" + res.explanation : ""), error: false });
+    } catch (e) {
+      if (e instanceof ApiError && e.status === 501) {
+        setMsg({ text: t("assist_unavailable"), error: true });
+      } else {
+        const detail = e instanceof ApiError ? String(e.detail) : String(e);
+        setMsg({ text: t("assist_failed", { msg: detail }), error: true });
+      }
+    } finally {
+      setBusy(false);
+    }
+  }
+
+  return (
+    <div className="panel">
+      <strong>{t("assist_title")}</strong>
+      <p className="hint">{t("assist_hint")}</p>
+      <textarea rows={3} placeholder={t("assist_ph")} value={text} onChange={(e) => setText(e.target.value)} />
+      <button onClick={submit} disabled={busy || !currentName}>
+        {busy ? t("assist_working") : t("assist_btn")}
+      </button>
+      {msg && <p className={"assist-msg" + (msg.error ? " error" : "")}>{msg.text}</p>}
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/Canvas.tsx b/webui/frontend/src/components/Canvas.tsx
new file mode 100644
index 0000000..2cb1d89
--- /dev/null
+++ b/webui/frontend/src/components/Canvas.tsx
@@ -0,0 +1,177 @@
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import {
+  Background,
+  Controls,
+  MiniMap,
+  Panel,
+  ReactFlow,
+  type Edge,
+  type Node,
+  type NodeChange,
+} from "@xyflow/react";
+import "@xyflow/react/dist/style.css";
+
+import type { Flow, Step } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+import { flowToGraph } from "../lib/graph";
+import { stackLayout, type Measured } from "../lib/layout";
+import { insertAt, move, newStep, removeAt } from "../lib/stepops";
+import { StepNode } from "./StepNode";
+
+const nodeTypes = { step: StepNode };
+
+interface Props {
+  flow: Flow;
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  /** Emitteras vid varje ändring av steg eller layout. Inställningar ägs av App. */
+  onChange: (partial: { steps: Step[]; layout: Record<string, { x: number; y: number }> }) => void;
+}
+
+function estimateHeight(step: Step): number {
+  let h = 150;
+  if (step.type === "loop") h += 70 + (step.steps?.length ?? 0) * 120;
+  if (step.type === "if") {
+    for (const b of step.branches ?? []) h += 150 + b.steps.length * 120;
+    if (Array.isArray(step.else)) h += 90 + step.else.length * 120;
+  }
+  if (step.type === "extract_row") h += 60 + (step.fields?.length ?? 0) * 130;
+  return h;
+}
+
+export function Canvas({ flow, schema, lang, t, onChange }: Props) {
+  const [steps, setSteps] = useState<Step[]>(() => flowToGraph(flow).nodes.map((n) => n.step));
+  // "pinned" = positioner användaren dragit; övriga auto-staplas.
+  const [pinned, setPinned] = useState<Record<string, { x: number; y: number }>>(() => ({ ...(flow.layout ?? {}) }));
+  const measured = useRef<Map<string, Measured>>(new Map());
+  const firstEmit = useRef(true);
+
+  // Positioner: pinnade respekteras, resten staplas efter uppskattad höjd.
+  const positions = useMemo(() => {
+    const m = new Map<string, Measured>();
+    for (const s of steps) m.set(s.id!, measured.current.get(s.id!) ?? { width: 340, height: estimateHeight(s) });
+    return stackLayout(
+      steps.map((s) => s.id!),
+      m,
+      pinned,
+    );
+  }, [steps, pinned]);
+
+  useEffect(() => {
+    if (firstEmit.current) {
+      firstEmit.current = false;
+      return;
+    }
+    onChange({ steps, layout: pinned });
+  }, [steps, pinned, onChange]);
+
+  const updateStep = useCallback(
+    (id: string, next: Step) => setSteps((prev) => prev.map((s) => (s.id === id ? next : s))),
+    [],
+  );
+
+  const nodes: Node[] = useMemo(
+    () =>
+      steps.map((step, i) => ({
+        id: step.id!,
+        type: "step",
+        position: positions[step.id!] ?? { x: 0, y: i * 200 },
+        data: {
+          step,
+          schema,
+          lang,
+          t,
+          index: i,
+          count: steps.length,
+          onChange: (s: Step) => updateStep(step.id!, s),
+          onDelete: () => setSteps((prev) => removeAt(prev, prev.findIndex((x) => x.id === step.id))),
+          onMove: (dir: -1 | 1) =>
+            setSteps((prev) => {
+              const idx = prev.findIndex((x) => x.id === step.id);
+              return move(prev, idx, idx + dir);
+            }),
+          onAddAfter: () =>
+            setSteps((prev) => {
+              const idx = prev.findIndex((x) => x.id === step.id);
+              return insertAt(prev, idx + 1, newStep(schema, "click"));
+            }),
+        },
+      })),
+    [steps, positions, schema, lang, t, updateStep],
+  );
+
+  const edges: Edge[] = useMemo(() => {
+    const es: Edge[] = [];
+    for (let i = 0; i < steps.length - 1; i++) {
+      es.push({ id: `seq-${steps[i].id}-${steps[i + 1].id}`, source: steps[i].id!, target: steps[i + 1].id!, animated: false });
+    }
+    const byLabel = new Map<string, string>();
+    for (const s of steps) if (s.label) byLabel.set(s.label, s.id!);
+    for (const s of steps) {
+      if (s.type === "jump_to_label" && typeof s.target === "string" && byLabel.has(s.target)) {
+        es.push({
+          id: `jump-${s.id}`,
+          source: s.id!,
+          target: byLabel.get(s.target)!,
+          style: { stroke: "#d1453b", strokeDasharray: "5 5" },
+          label: "goto",
+        });
+      }
+    }
+    return es;
+  }, [steps]);
+
+  const onNodesChange = useCallback((changes: NodeChange[]) => {
+    for (const c of changes) {
+      if (c.type === "dimensions" && c.dimensions) {
+        measured.current.set(c.id, { width: c.dimensions.width, height: c.dimensions.height });
+      }
+    }
+    // Fånga drag: uppdatera pinnad position
+    setPinned((prev) => {
+      let next = prev;
+      for (const c of changes) {
+        if (c.type === "position" && c.position && c.dragging) {
+          if (next === prev) next = { ...prev };
+          next[c.id] = { x: Math.round(c.position.x), y: Math.round(c.position.y) };
+        }
+      }
+      return next;
+    });
+  }, []);
+
+  const onNodeDragStop = useCallback(() => {
+    // Ordna om stegen efter visuell y-position.
+    setSteps((prev) => [...prev].sort((a, b) => (positions[a.id!]?.y ?? 0) - (positions[b.id!]?.y ?? 0)));
+  }, [positions]);
+
+  return (
+    <div className="canvas">
+      <ReactFlow
+        nodes={nodes}
+        edges={edges}
+        nodeTypes={nodeTypes}
+        onNodesChange={onNodesChange}
+        onNodeDragStop={onNodeDragStop}
+        nodesConnectable={false}
+        fitView
+        minZoom={0.2}
+        proOptions={{ hideAttribution: true }}
+      >
+        <Background />
+        <MiniMap pannable zoomable />
+        <Controls />
+        <Panel position="top-left" className="canvas-panel">
+          <button className="secondary" onClick={() => setSteps((prev) => [...prev, newStep(schema, "goto")])}>
+            {t("add_step")}
+          </button>
+          <button className="secondary" onClick={() => setPinned({})}>
+            {t("relayout")}
+          </button>
+        </Panel>
+      </ReactFlow>
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/ConditionEditor.tsx b/webui/frontend/src/components/ConditionEditor.tsx
new file mode 100644
index 0000000..6cfa0cf
--- /dev/null
+++ b/webui/frontend/src/components/ConditionEditor.tsx
@@ -0,0 +1,44 @@
+import type { Condition } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import { conditionKind, fieldLabel } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+import { Fields } from "./Fields";
+
+interface Props {
+  condition: Condition;
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  showScope: boolean;
+  onChange: (c: Condition) => void;
+}
+
+export function ConditionEditor({ condition, schema, lang, t, showScope, onChange }: Props) {
+  const spec = conditionKind(schema, condition.kind);
+  return (
+    <div className="condition">
+      <label className="field">
+        <span>{t("condition")}</span>
+        <select
+          value={condition.kind}
+          onChange={(e) => onChange({ kind: e.target.value })}
+        >
+          {schema.condition_kinds.map((c) => (
+            <option key={c.kind} value={c.kind}>
+              {fieldLabel(c, lang)}
+            </option>
+          ))}
+        </select>
+      </label>
+      {spec && (
+        <Fields
+          values={condition}
+          fields={spec.fields}
+          lang={lang}
+          showScope={showScope}
+          onChange={(patch) => onChange({ ...condition, ...patch })}
+        />
+      )}
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/Fields.tsx b/webui/frontend/src/components/Fields.tsx
new file mode 100644
index 0000000..5b6ec81
--- /dev/null
+++ b/webui/frontend/src/components/Fields.tsx
@@ -0,0 +1,98 @@
+import type { ReactNode } from "react";
+import type { Field } from "../lib/schema";
+import { fieldLabel } from "../lib/schema";
+import type { Lang } from "../lib/i18n";
+
+interface Props {
+  values: Record<string, unknown>;
+  fields: Field[];
+  lang: Lang;
+  /** true om fältet "scope" ska visas (steget ligger i en loop, är loop självt,
+   *  eller är extract_row). */
+  showScope?: boolean;
+  onChange: (patch: Record<string, unknown>) => void;
+}
+
+/** Renderar schemadrivna fält (steg eller villkor). field_list hanteras separat. */
+export function Fields({ values, fields, lang, showScope, onChange }: Props) {
+  const visible = fields.filter((f) => {
+    if (f.kind === "field_list") return false;
+    if (f.key === "scope") return !!showScope;
+    return true;
+  });
+  if (visible.length === 0) return null;
+
+  return (
+    <div className="fields">
+      {visible.map((f) => (
+        <FieldRow key={f.key} field={f} value={values[f.key]} lang={lang} onChange={(v) => onChange({ [f.key]: v })} />
+      ))}
+    </div>
+  );
+}
+
+function FieldRow({
+  field,
+  value,
+  lang,
+  onChange,
+}: {
+  field: Field;
+  value: unknown;
+  lang: Lang;
+  onChange: (v: unknown) => void;
+}) {
+  const label = fieldLabel(field, lang);
+  const title = field.help ?? label;
+
+  let input: ReactNode;
+  if (field.kind === "select") {
+    input = (
+      <select
+        value={String(value ?? field.default ?? field.options?.[0] ?? "")}
+        title={title}
+        onChange={(e) => onChange(e.target.value)}
+      >
+        {(field.options ?? []).map((o) => (
+          <option key={o} value={o}>
+            {o === "" ? "—" : o}
+          </option>
+        ))}
+      </select>
+    );
+  } else if (field.kind === "bool") {
+    input = (
+      <input
+        type="checkbox"
+        checked={value === true || value === "true"}
+        title={title}
+        onChange={(e) => onChange(e.target.checked)}
+      />
+    );
+  } else if (field.kind === "number") {
+    input = (
+      <input
+        type="number"
+        value={value === undefined || value === null ? "" : String(value)}
+        title={title}
+        onChange={(e) => onChange(e.target.value === "" ? undefined : Number(e.target.value))}
+      />
+    );
+  } else {
+    input = (
+      <input
+        type="text"
+        value={value === undefined || value === null ? "" : String(value)}
+        title={title}
+        onChange={(e) => onChange(e.target.value)}
+      />
+    );
+  }
+
+  return (
+    <label className={"field" + (field.kind === "bool" ? " field-bool" : "")}>
+      <span>{label}</span>
+      {input}
+    </label>
+  );
+}
diff --git a/webui/frontend/src/components/Header.tsx b/webui/frontend/src/components/Header.tsx
new file mode 100644
index 0000000..ea9e5ad
--- /dev/null
+++ b/webui/frontend/src/components/Header.tsx
@@ -0,0 +1,79 @@
+import { api } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+
+interface Props {
+  t: T;
+  lang: Lang;
+  setLang: (l: Lang) => void;
+  schema: Schema;
+  flowNames: string[];
+  currentName: string | null;
+  onSelect: (name: string) => void;
+  onNew: () => void;
+  onDelete: () => void;
+  onSave: () => void;
+  onRun: () => void;
+  dirty: boolean;
+  running: boolean;
+  closeMode: string;
+  setCloseMode: (m: string) => void;
+  onOpenSettings: () => void;
+}
+
+export function Header(p: Props) {
+  const { t } = p;
+  return (
+    <header>
+      <h1>{t("app_title")}</h1>
+      <select value={p.lang} onChange={(e) => p.setLang(e.target.value as Lang)}>
+        <option value="en">English</option>
+        <option value="sv">Svenska</option>
+      </select>
+
+      <select value={p.currentName ?? ""} onChange={(e) => p.onSelect(e.target.value)}>
+        {p.flowNames.map((n) => (
+          <option key={n} value={n}>
+            {n}
+          </option>
+        ))}
+      </select>
+      <button className="secondary" onClick={p.onNew}>
+        {t("new_flow")}
+      </button>
+      <button className="danger" onClick={p.onDelete} disabled={!p.currentName}>
+        {t("delete_flow")}
+      </button>
+
+      <a className="secondary" href="/wiki" target="_blank" rel="noopener">
+        {t("wiki")}
+      </a>
+      <button className="secondary" onClick={p.onOpenSettings}>
+        {t("settings")}
+      </button>
+      <a
+        className="secondary"
+        href={p.currentName ? api.exportUrl(p.currentName) : "#"}
+        target="_blank"
+        rel="noopener"
+      >
+        {t("export")}
+      </a>
+
+      <span className="spacer" />
+
+      <span className={"dirty-dot " + (p.dirty ? "on" : "")} title={p.dirty ? t("unsaved") : t("saved")} />
+      <select value={p.closeMode} onChange={(e) => p.setCloseMode(e.target.value)}>
+        <option value="always">{t("close_always")}</option>
+        <option value="on_success">{t("close_on_success")}</option>
+        <option value="never">{t("close_never")}</option>
+      </select>
+      <button className="secondary" onClick={p.onSave}>
+        {t("save")}
+      </button>
+      <button onClick={p.onRun} disabled={p.running || !p.currentName}>
+        {t("run")}
+      </button>
+    </header>
+  );
+}
diff --git a/webui/frontend/src/components/NestedSteps.tsx b/webui/frontend/src/components/NestedSteps.tsx
new file mode 100644
index 0000000..0942a1b
--- /dev/null
+++ b/webui/frontend/src/components/NestedSteps.tsx
@@ -0,0 +1,92 @@
+import type { Step } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import { fieldLabel } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+import { insertAt, move, newStep, removeAt, replaceAt, retype } from "../lib/stepops";
+import { StepBody } from "./StepBody";
+
+interface Props {
+  steps: Step[];
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  inLoop: boolean;
+  onChange: (steps: Step[]) => void;
+}
+
+/** Nästlad steglista (loop-kropp, if-gren, else). Renderar mini-kort. */
+export function NestedSteps({ steps, schema, lang, t, inLoop, onChange }: Props) {
+  const add = () => onChange([...steps, newStep(schema, "click")]);
+
+  return (
+    <div className="nested-steps">
+      {steps.map((step, i) => (
+        <div className={"mini-card" + (step.enabled === false ? " disabled" : "")} key={step.id ?? i}>
+          <div className="mini-head">
+            <input
+              type="checkbox"
+              checked={step.enabled !== false}
+              title={t("enabled_title")}
+              onChange={(e) => onChange(replaceAt(steps, i, { ...step, enabled: e.target.checked ? undefined : false }))}
+            />
+            <select
+              value={step.type}
+              onChange={(e) => onChange(replaceAt(steps, i, retype(schema, step, e.target.value)))}
+            >
+              {schema.step_types.map((s) => (
+                <option key={s.type} value={s.type}>
+                  {fieldLabel(s, lang)}
+                </option>
+              ))}
+            </select>
+            <input
+              type="text"
+              className="label-input"
+              placeholder={t("label_ph")}
+              value={step.label ?? ""}
+              onChange={(e) => onChange(replaceAt(steps, i, { ...step, label: e.target.value || undefined }))}
+            />
+            <select
+              value={step.on_error === "continue" ? "continue" : "stop"}
+              onChange={(e) =>
+                onChange(replaceAt(steps, i, { ...step, on_error: e.target.value === "continue" ? "continue" : undefined }))
+              }
+            >
+              <option value="stop">{t("on_error_stop")}</option>
+              <option value="continue">{t("on_error_continue")}</option>
+            </select>
+            <span className="spacer" />
+            <button className="icon" title={t("move_up")} disabled={i === 0} onClick={() => onChange(move(steps, i, i - 1))}>
+              ↑
+            </button>
+            <button
+              className="icon"
+              title={t("move_down")}
+              disabled={i === steps.length - 1}
+              onClick={() => onChange(move(steps, i, i + 1))}
+            >
+              ↓
+            </button>
+            <button className="danger" onClick={() => onChange(removeAt(steps, i))}>
+              {t("remove")}
+            </button>
+          </div>
+          <StepBody
+            step={step}
+            schema={schema}
+            lang={lang}
+            t={t}
+            inLoop={inLoop || step.type === "loop"}
+            onChange={(s) => onChange(replaceAt(steps, i, s))}
+          />
+          <button className="secondary tiny" onClick={() => onChange(insertAt(steps, i + 1, newStep(schema, "click")))}>
+            +
+          </button>
+        </div>
+      ))}
+      <button className="secondary" onClick={add}>
+        {t("add_step_in")}
+      </button>
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/RowFieldsEditor.tsx b/webui/frontend/src/components/RowFieldsEditor.tsx
new file mode 100644
index 0000000..c5e8470
--- /dev/null
+++ b/webui/frontend/src/components/RowFieldsEditor.tsx
@@ -0,0 +1,56 @@
+import type { RowField } from "../lib/api";
+import type { Lang, T } from "../lib/i18n";
+
+interface Props {
+  fields: RowField[];
+  lang: Lang;
+  t: T;
+  onChange: (fields: RowField[]) => void;
+}
+
+/** Kolumn-editorn för extract_row: lista av {name, by, value, role?}. */
+export function RowFieldsEditor({ fields, t, onChange }: Props) {
+  const set = (i: number, patch: Partial<RowField>) =>
+    onChange(fields.map((f, j) => (j === i ? { ...f, ...patch } : f)));
+
+  return (
+    <div className="row-fields">
+      {fields.map((f, i) => (
+        <div className="row-field" key={i}>
+          <div className="row-field-head">
+            <input
+              type="text"
+              placeholder={t("column_name")}
+              value={f.name}
+              onChange={(e) => set(i, { name: e.target.value })}
+            />
+            <button className="danger" onClick={() => onChange(fields.filter((_, j) => j !== i))}>
+              {t("remove")}
+            </button>
+          </div>
+          <div className="fields">
+            <label className="field">
+              <span>by</span>
+              <select value={f.by ?? "css"} onChange={(e) => set(i, { by: e.target.value })}>
+                {["text", "css", "role"].map((o) => (
+                  <option key={o}>{o}</option>
+                ))}
+              </select>
+            </label>
+            <label className="field">
+              <span>value</span>
+              <input type="text" value={f.value ?? ""} onChange={(e) => set(i, { value: e.target.value })} />
+            </label>
+            <label className="field">
+              <span>role</span>
+              <input type="text" value={f.role ?? ""} onChange={(e) => set(i, { role: e.target.value })} />
+            </label>
+          </div>
+        </div>
+      ))}
+      <button className="secondary" onClick={() => onChange([...fields, { name: "", by: "css", value: "" }])}>
+        {t("add_field")}
+      </button>
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/RunPanel.tsx b/webui/frontend/src/components/RunPanel.tsx
new file mode 100644
index 0000000..4a5ef01
--- /dev/null
+++ b/webui/frontend/src/components/RunPanel.tsx
@@ -0,0 +1,44 @@
+import { useEffect, useRef } from "react";
+import { api, type RunState } from "../lib/api";
+import type { T } from "../lib/i18n";
+
+interface Props {
+  t: T;
+  runId: string | null;
+  run: RunState | null;
+}
+
+export function RunPanel({ t, runId, run }: Props) {
+  const logRef = useRef<HTMLPreElement>(null);
+  useEffect(() => {
+    if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
+  }, [run?.logs]);
+
+  const status = run?.status;
+  return (
+    <div className="panel">
+      <div className="panel-head">
+        <strong>{t("run_log")}</strong>
+        {status && (
+          <span className={"badge badge-" + status}>
+            {status === "running" ? t("status_running") : status === "done" ? t("status_done") : t("status_error")}
+          </span>
+        )}
+        <span className="spacer" />
+        {run?.browser_open && runId && (
+          <button className="secondary" onClick={() => api.closeBrowser(runId)}>
+            {t("close_browser")}
+          </button>
+        )}
+        {run?.trace_ready && runId && (
+          <button className="secondary" onClick={() => api.showTrace(runId)}>
+            {t("show_trace")}
+          </button>
+        )}
+      </div>
+      <pre className="log" ref={logRef}>
+        {(run?.logs ?? []).join("\n")}
+      </pre>
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/SettingsDialog.tsx b/webui/frontend/src/components/SettingsDialog.tsx
new file mode 100644
index 0000000..24c6fe6
--- /dev/null
+++ b/webui/frontend/src/components/SettingsDialog.tsx
@@ -0,0 +1,69 @@
+import { useEffect, useRef, type FormEvent } from "react";
+import type { Schema } from "../lib/schema";
+import { fieldLabel } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+
+interface Props {
+  open: boolean;
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  settings: Record<string, unknown>;
+  onClose: () => void;
+  onSave: (settings: Record<string, unknown>) => void;
+}
+
+export function SettingsDialog({ open, schema, lang, t, settings, onClose, onSave }: Props) {
+  const ref = useRef<HTMLDialogElement>(null);
+  useEffect(() => {
+    const d = ref.current;
+    if (!d) return;
+    if (open && !d.open) d.showModal();
+    if (!open && d.open) d.close();
+  }, [open]);
+
+  function save(e: FormEvent) {
+    e.preventDefault();
+    const form = e.currentTarget as HTMLFormElement;
+    const next: Record<string, unknown> = {};
+    for (const s of schema.settings) {
+      const el = form.elements.namedItem(s.key) as HTMLInputElement | null;
+      if (!el) continue;
+      if (s.kind === "bool") next[s.key] = el.checked;
+      else if (s.kind === "number") next[s.key] = Number(el.value) || Number(s.default) || 0;
+      else next[s.key] = el.value || String(s.default);
+    }
+    onSave(next);
+  }
+
+  return (
+    <dialog ref={ref} onClose={onClose}>
+      <form onSubmit={save} method="dialog">
+        <h2>{t("settings")}</h2>
+        <p className="hint">{t("settings_hint")}</p>
+        <div className="fields">
+          {schema.settings.map((s) => (
+            <label className={"field" + (s.kind === "bool" ? " field-bool" : "")} key={s.key}>
+              <span>{fieldLabel(s, lang)}</span>
+              {s.kind === "bool" ? (
+                <input type="checkbox" name={s.key} defaultChecked={settings[s.key] === true} />
+              ) : (
+                <input
+                  type={s.kind === "number" ? "number" : "text"}
+                  name={s.key}
+                  defaultValue={String(settings[s.key] ?? s.default)}
+                />
+              )}
+            </label>
+          ))}
+        </div>
+        <div className="dialog-actions">
+          <button type="button" className="secondary" onClick={onClose}>
+            {t("cancel")}
+          </button>
+          <button type="submit">{t("save")}</button>
+        </div>
+      </form>
+    </dialog>
+  );
+}
diff --git a/webui/frontend/src/components/StepBody.tsx b/webui/frontend/src/components/StepBody.tsx
new file mode 100644
index 0000000..359d1cd
--- /dev/null
+++ b/webui/frontend/src/components/StepBody.tsx
@@ -0,0 +1,132 @@
+import type { Branch, Step } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+import { newBranch } from "../lib/stepops";
+import { Fields } from "./Fields";
+import { ConditionEditor } from "./ConditionEditor";
+import { RowFieldsEditor } from "./RowFieldsEditor";
+import { NestedSteps } from "./NestedSteps";
+
+interface Props {
+  step: Step;
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  inLoop: boolean;
+  onChange: (step: Step) => void;
+}
+
+/** Fält + behållar-UI för ett steg. Delas av canvas-noden och de nästlade korten. */
+export function StepBody({ step, schema, lang, t, inLoop, onChange }: Props) {
+  const spec = schema.step_types.find((s) => s.type === step.type);
+  if (!spec) return <div className="hint">Okänd stegtyp: {step.type}</div>;
+
+  const patch = (p: Record<string, unknown>) => {
+    const next: Step = { ...step };
+    for (const [k, v] of Object.entries(p)) {
+      if (v === undefined || v === "") delete (next as Record<string, unknown>)[k];
+      else (next as Record<string, unknown>)[k] = v;
+    }
+    onChange(next);
+  };
+
+  const showScope = inLoop || step.type === "loop" || step.type === "extract_row";
+
+  return (
+    <>
+      <Fields values={step} fields={spec.fields} lang={lang} showScope={showScope} onChange={patch} />
+
+      {step.type === "extract_row" && (
+        <RowFieldsEditor
+          fields={step.fields ?? []}
+          lang={lang}
+          t={t}
+          onChange={(fields) => onChange({ ...step, fields })}
+        />
+      )}
+
+      {step.type === "loop" && (
+        <div className="nested">
+          <NestedSteps
+            steps={step.steps ?? []}
+            schema={schema}
+            lang={lang}
+            t={t}
+            inLoop
+            onChange={(steps) => onChange({ ...step, steps })}
+          />
+        </div>
+      )}
+
+      {step.type === "if" && (
+        <IfBranches step={step} schema={schema} lang={lang} t={t} inLoop={inLoop} onChange={onChange} />
+      )}
+    </>
+  );
+}
+
+function IfBranches({ step, schema, lang, t, inLoop, onChange }: Props) {
+  const branches: Branch[] = step.branches ?? [{ condition: { kind: "element_exists" }, steps: [] }];
+  const setBranch = (i: number, b: Branch) =>
+    onChange({ ...step, branches: branches.map((x, j) => (j === i ? b : x)) });
+
+  return (
+    <div className="nested">
+      {branches.map((b, i) => (
+        <div className="branch" key={i}>
+          <div className="branch-head">
+            <strong>{i === 0 ? t("branch_if") : t("branch_else_if")}</strong>
+            {i > 0 && (
+              <button
+                className="danger"
+                onClick={() => onChange({ ...step, branches: branches.filter((_, j) => j !== i) })}
+              >
+                {t("remove_branch")}
+              </button>
+            )}
+          </div>
+          <ConditionEditor
+            condition={b.condition}
+            schema={schema}
+            lang={lang}
+            t={t}
+            showScope={inLoop}
+            onChange={(condition) => setBranch(i, { ...b, condition })}
+          />
+          <NestedSteps
+            steps={b.steps}
+            schema={schema}
+            lang={lang}
+            t={t}
+            inLoop={inLoop}
+            onChange={(steps) => setBranch(i, { ...b, steps })}
+          />
+        </div>
+      ))}
+      <button className="secondary" onClick={() => onChange({ ...step, branches: [...branches, newBranch()] })}>
+        {t("add_branch")}
+      </button>
+
+      <label className="branch else-toggle">
+        <input
+          type="checkbox"
+          checked={Array.isArray(step.else)}
+          onChange={(e) => onChange({ ...step, else: e.target.checked ? [] : undefined })}
+        />
+        <span>{t("else_toggle")}</span>
+      </label>
+      {Array.isArray(step.else) && (
+        <div className="branch">
+          <NestedSteps
+            steps={step.else}
+            schema={schema}
+            lang={lang}
+            t={t}
+            inLoop={inLoop}
+            onChange={(els) => onChange({ ...step, else: els })}
+          />
+        </div>
+      )}
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/StepNode.tsx b/webui/frontend/src/components/StepNode.tsx
new file mode 100644
index 0000000..03b8f0f
--- /dev/null
+++ b/webui/frontend/src/components/StepNode.tsx
@@ -0,0 +1,102 @@
+import { Handle, Position, type NodeProps } from "@xyflow/react";
+import type { Step } from "../lib/api";
+import type { Schema } from "../lib/schema";
+import { fieldLabel } from "../lib/schema";
+import type { Lang, T } from "../lib/i18n";
+import { retype } from "../lib/stepops";
+import { StepBody } from "./StepBody";
+
+export interface StepNodeData {
+  step: Step;
+  schema: Schema;
+  lang: Lang;
+  t: T;
+  index: number;
+  count: number;
+  onChange: (step: Step) => void;
+  onDelete: () => void;
+  onMove: (dir: -1 | 1) => void;
+  onAddAfter: () => void;
+  [k: string]: unknown;
+}
+
+export function StepNode({ data }: NodeProps) {
+  const d = data as StepNodeData;
+  const { step, schema, lang, t } = d;
+  const spec = schema.step_types.find((s) => s.type === step.type);
+
+  return (
+    <div className={"step-node cat-" + (spec?.category ?? "x") + (step.enabled === false ? " disabled" : "")}>
+      <Handle type="target" position={Position.Top} />
+      <div className="node-head">
+        <input
+          type="checkbox"
+          className="nodrag"
+          checked={step.enabled !== false}
+          title={t("enabled_title")}
+          onChange={(e) => d.onChange({ ...step, enabled: e.target.checked ? undefined : false })}
+        />
+        <select
+          className="nodrag type-select"
+          value={step.type}
+          onChange={(e) => d.onChange(retype(schema, step, e.target.value))}
+        >
+          {schema.step_types.map((s) => (
+            <option key={s.type} value={s.type}>
+              {fieldLabel(s, lang)}
+            </option>
+          ))}
+        </select>
+        <span className="spacer" />
+        <button className="icon nodrag" title={t("move_up")} disabled={d.index === 0} onClick={() => d.onMove(-1)}>
+          ↑
+        </button>
+        <button
+          className="icon nodrag"
+          title={t("move_down")}
+          disabled={d.index === d.count - 1}
+          onClick={() => d.onMove(1)}
+        >
+          ↓
+        </button>
+        <button className="danger nodrag" onClick={d.onDelete}>
+          ✕
+        </button>
+      </div>
+
+      <div className="node-subhead">
+        <input
+          type="text"
+          className="nodrag label-input"
+          placeholder={t("label_ph")}
+          value={step.label ?? ""}
+          onChange={(e) => d.onChange({ ...step, label: e.target.value || undefined })}
+        />
+        <select
+          className="nodrag"
+          value={step.on_error === "continue" ? "continue" : "stop"}
+          onChange={(e) => d.onChange({ ...step, on_error: e.target.value === "continue" ? "continue" : undefined })}
+        >
+          <option value="stop">{t("on_error_stop")}</option>
+          <option value="continue">{t("on_error_continue")}</option>
+        </select>
+      </div>
+
+      <div className="node-body nodrag">
+        <StepBody
+          step={step}
+          schema={schema}
+          lang={lang}
+          t={t}
+          inLoop={false}
+          onChange={d.onChange}
+        />
+      </div>
+
+      <button className="add-after nodrag" title={t("add_step_after")} onClick={d.onAddAfter}>
+        +
+      </button>
+      <Handle type="source" position={Position.Bottom} />
+    </div>
+  );
+}
diff --git a/webui/frontend/src/components/VarsPanel.tsx b/webui/frontend/src/components/VarsPanel.tsx
new file mode 100644
index 0000000..2cf5a1a
--- /dev/null
+++ b/webui/frontend/src/components/VarsPanel.tsx
@@ -0,0 +1,50 @@
+import { useState } from "react";
+import type { T } from "../lib/i18n";
+
+interface Props {
+  t: T;
+  vars: Record<string, unknown>;
+}
+
+function describe(t: T, v: unknown): string {
+  if (Array.isArray(v)) return t("var_list", { n: v.length });
+  if (v && typeof v === "object") return t("var_object");
+  if (typeof v === "string") return t("var_text", { n: v.length });
+  return typeof v;
+}
+
+export function VarsPanel({ t, vars }: Props) {
+  const [open, setOpen] = useState<Set<string>>(new Set());
+  const names = Object.keys(vars ?? {});
+
+  return (
+    <div className="panel">
+      <strong>{t("variables")}</strong>
+      <p className="hint">{t("vars_hint")}</p>
+      {names.length === 0 && <p className="hint">{t("no_vars")}</p>}
+      {names.map((name) => {
+        const isOpen = open.has(name);
+        const val = vars[name];
+        return (
+          <div className="var-row" key={name}>
+            <div
+              className="var-head"
+              onClick={() =>
+                setOpen((prev) => {
+                  const n = new Set(prev);
+                  n.has(name) ? n.delete(name) : n.add(name);
+                  return n;
+                })
+              }
+            >
+              {name} <span className="hint">({describe(t, val)})</span>
+            </div>
+            {isOpen && (
+              <pre className="var-value">{typeof val === "string" ? val : JSON.stringify(val, null, 2)}</pre>
+            )}
+          </div>
+        );
+      })}
+    </div>
+  );
+}
diff --git a/webui/frontend/src/lib/api.ts b/webui/frontend/src/lib/api.ts
new file mode 100644
index 0000000..830c249
--- /dev/null
+++ b/webui/frontend/src/lib/api.ts
@@ -0,0 +1,102 @@
+import type { Schema } from "./schema";
+
+export interface Flow {
+  steps: Step[];
+  settings: Record<string, unknown>;
+  layout?: Record<string, { x: number; y: number }>;
+}
+
+export interface Step {
+  type: string;
+  id?: string;
+  label?: string;
+  enabled?: boolean;
+  on_error?: "stop" | "continue";
+  steps?: Step[];
+  branches?: Branch[];
+  else?: Step[];
+  fields?: RowField[];
+  [k: string]: unknown;
+}
+
+export interface Branch {
+  condition: Condition;
+  steps: Step[];
+}
+
+export interface Condition {
+  kind: string;
+  [k: string]: unknown;
+}
+
+export interface RowField {
+  name: string;
+  by?: string;
+  value?: string;
+  role?: string;
+}
+
+export interface RunState {
+  status: "running" | "done" | "error";
+  logs: string[];
+  browser_open: boolean;
+  trace_ready: boolean;
+  vars: Record<string, unknown>;
+}
+
+export interface AssistResult {
+  steps: Step[];
+  settings: Record<string, unknown>;
+  explanation?: string;
+}
+
+async function j<T>(res: Response): Promise<T> {
+  if (!res.ok) {
+    let detail: unknown;
+    try {
+      detail = (await res.json()).detail;
+    } catch {
+      detail = res.statusText;
+    }
+    throw new ApiError(res.status, detail);
+  }
+  return res.json() as Promise<T>;
+}
+
+export class ApiError extends Error {
+  constructor(public status: number, public detail: unknown) {
+    super(typeof detail === "string" ? detail : JSON.stringify(detail));
+  }
+}
+
+export const api = {
+  schema: () => fetch("/api/step-schema").then((r) => j<Schema>(r)),
+  listFlows: () => fetch("/api/flows").then((r) => j<string[]>(r)),
+  getFlow: (name: string) => fetch(`/api/flows/${encodeURIComponent(name)}`).then((r) => j<Flow>(r)),
+  saveFlow: (name: string, flow: Flow) =>
+    fetch(`/api/flows/${encodeURIComponent(name)}`, {
+      method: "PUT",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify(flow),
+    }).then((r) => j<{ ok: boolean }>(r)),
+  deleteFlow: (name: string) =>
+    fetch(`/api/flows/${encodeURIComponent(name)}`, { method: "DELETE" }).then((r) => j<{ ok: boolean }>(r)),
+  runFlow: (name: string, closeMode: string) =>
+    fetch(`/api/flows/${encodeURIComponent(name)}/run`, {
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify({ close_mode: closeMode }),
+    }).then((r) => j<{ run_id: string }>(r)),
+  assist: (name: string, instruction: string, flow: Flow) =>
+    fetch(`/api/flows/${encodeURIComponent(name)}/assist`, {
+      method: "POST",
+      headers: { "Content-Type": "application/json" },
+      body: JSON.stringify({ instruction, steps: flow.steps, settings: flow.settings }),
+    }).then((r) => j<AssistResult>(r)),
+  run: (runId: string) => fetch(`/api/runs/${runId}`).then((r) => j<RunState>(r)),
+  closeBrowser: (runId: string) =>
+    fetch(`/api/runs/${runId}/close-browser`, { method: "POST" }).then((r) => j<{ ok: boolean }>(r)),
+  showTrace: (runId: string) =>
+    fetch(`/api/runs/${runId}/show-trace`, { method: "POST" }).then((r) => j<{ ok: boolean }>(r)),
+  exportUrl: (name: string) => `/api/flows/${encodeURIComponent(name)}/export`,
+};
diff --git a/webui/frontend/src/lib/graph.test.ts b/webui/frontend/src/lib/graph.test.ts
new file mode 100644
index 0000000..2947460
--- /dev/null
+++ b/webui/frontend/src/lib/graph.test.ts
@@ -0,0 +1,83 @@
+import { describe, expect, it } from "vitest";
+import type { Flow } from "./api";
+import { NonSequentialGraphError, flowToGraph, graphToFlow } from "./graph";
+
+function roundTrip(flow: Flow): Flow {
+  const { nodes, edges } = flowToGraph(flow);
+  return graphToFlow(nodes, edges, flow.settings ?? {});
+}
+
+const LINEAR: Flow = {
+  steps: [
+    { type: "goto", url: "https://example.com/" },
+    { type: "wait_for_login", timeout: 120 },
+    { type: "screenshot", name: "x" },
+  ],
+  settings: {},
+};
+
+const NESTED: Flow = {
+  steps: [
+    { type: "goto", url: "https://x.se/" },
+    { type: "list", value: "a", var: "links" },
+    {
+      type: "loop",
+      over: "links",
+      steps: [
+        { type: "extract_text", by: "css", value: ":scope", scope: "item", var: "l" },
+        { type: "list_append", var: "acc", text: "{l}" },
+      ],
+    },
+    {
+      type: "if",
+      branches: [{ condition: { kind: "var_contains", var: "acc", text: "z" }, steps: [{ type: "screenshot", name: "hit" }] }],
+      else: [{ type: "screenshot", name: "miss" }],
+    },
+    { type: "label-holder", label: "end", url: "" } as never,
+    { type: "jump_to_label", target: "end" },
+  ],
+  settings: { headless: true },
+};
+
+describe("flow <-> graph round-trip", () => {
+  it("linear flow is preserved", () => {
+    const out = roundTrip(LINEAR);
+    expect(out.steps.map((s) => s.type)).toEqual(["goto", "wait_for_login", "screenshot"]);
+    expect(out.steps[0].url).toBe("https://example.com/");
+  });
+
+  it("nested loop/if/jump structure is preserved", () => {
+    const out = roundTrip(NESTED);
+    expect(out.steps.map((s) => s.type)).toEqual(["goto", "list", "loop", "if", "label-holder", "jump_to_label"]);
+    const loop = out.steps[2];
+    expect(loop.steps?.map((s) => s.type)).toEqual(["extract_text", "list_append"]);
+    const iff = out.steps[3];
+    expect(iff.branches?.[0].steps[0].type).toBe("screenshot");
+    expect(iff.else?.[0].name).toBe("miss");
+    expect(out.settings.headless).toBe(true);
+  });
+
+  it("assigns stable ids to every step (incl. nested)", () => {
+    const { nodes } = flowToGraph(structuredClone(NESTED));
+    const loopStep = nodes.find((n) => n.step.type === "loop")!.step;
+    expect(loopStep.id).toBeTruthy();
+    expect(loopStep.steps?.every((s) => !!s.id)).toBe(true);
+  });
+
+  it("preserves manual layout positions", () => {
+    const f: Flow = { ...LINEAR, steps: LINEAR.steps.map((s) => ({ ...s })) };
+    const { nodes, edges } = flowToGraph(f);
+    nodes[1].position = { x: 42, y: 99 };
+    const out = graphToFlow(nodes, edges, {});
+    expect(out.layout![nodes[1].id]).toEqual({ x: 42, y: 99 });
+  });
+
+  it("throws NonSequentialGraphError when a node has two successors", () => {
+    const { nodes } = flowToGraph(structuredClone(LINEAR));
+    const bad = [
+      { id: "seq-a", source: nodes[0].id, target: nodes[1].id, kind: "seq" as const },
+      { id: "seq-b", source: nodes[0].id, target: nodes[2].id, kind: "seq" as const },
+    ];
+    expect(() => graphToFlow(nodes, bad, {})).toThrow(NonSequentialGraphError);
+  });
+});
diff --git a/webui/frontend/src/lib/graph.ts b/webui/frontend/src/lib/graph.ts
new file mode 100644
index 0000000..f390be1
--- /dev/null
+++ b/webui/frontend/src/lib/graph.ts
@@ -0,0 +1,133 @@
+// ---------------------------------------------------------------------------
+// HYBRID-SÖMMEN mot en framtida äkta grafmotor.
+//
+// Motorn (rpa/flow_engine.py) kör en NÄSTLAD LISTA sekventiellt. Här översätter
+// vi den listan till/från en nod+kant-graf för canvasen.
+//
+// v1: bara toppnivåstegen blir canvas-noder; loop/if-kroppar redigeras inuti
+// nodens kort (NestedSteps). Kanterna är alltid en enkel kedja i syskonordning
+// (+ en streckad "jump"-kant för jump_to_label). graphToFlow KRÄVER att
+// seq-kant-grafen är en enkel kedja och kastar NonSequentialGraphError annars.
+//
+// När äkta DAG ska införas: (1) ny exekverare i Python som går på kanter,
+// (2) lyft kedje-kravet i graphToFlow, (3) representera loop/if-kroppar som
+// riktiga grupp-noder. Inget annat i kodbasen behöver röras.
+// ---------------------------------------------------------------------------
+
+import type { Flow, Step } from "./api";
+import { uid } from "./ids";
+
+export const Y_GAP = 150;
+export const X_COL = 0;
+
+export interface RpaNode {
+  id: string;
+  step: Step;
+  position: { x: number; y: number };
+}
+
+export interface RpaEdge {
+  id: string;
+  source: string;
+  target: string;
+  kind: "seq" | "jump";
+}
+
+export class NonSequentialGraphError extends Error {
+  constructor(msg: string) {
+    super(msg);
+    this.name = "NonSequentialGraphError";
+  }
+}
+
+/** Ser till att steget och alla nästlade steg har ett stabilt id. Muterar. */
+export function ensureIds(step: Step): Step {
+  if (!step.id) step.id = uid();
+  step.steps?.forEach(ensureIds);
+  step.else?.forEach(ensureIds);
+  step.branches?.forEach((b) => b.steps.forEach(ensureIds));
+  return step;
+}
+
+export function flowToGraph(flow: Flow): { nodes: RpaNode[]; edges: RpaEdge[] } {
+  const steps = (flow.steps ?? []).map((s) => ensureIds({ ...s }));
+  const layout = flow.layout ?? {};
+
+  const nodes: RpaNode[] = steps.map((step, i) => ({
+    id: step.id!,
+    step,
+    position: layout[step.id!] ?? { x: X_COL, y: i * Y_GAP },
+  }));
+
+  const edges: RpaEdge[] = [];
+  for (let i = 0; i < steps.length - 1; i++) {
+    edges.push({ id: `seq-${steps[i].id}-${steps[i + 1].id}`, source: steps[i].id!, target: steps[i + 1].id!, kind: "seq" });
+  }
+
+  // jump_to_label -> siblingsteg med matchande label (bara toppnivå i v1)
+  const byLabel = new Map<string, string>();
+  for (const s of steps) if (s.label) byLabel.set(s.label, s.id!);
+  for (const s of steps) {
+    if (s.type === "jump_to_label" && typeof s.target === "string" && byLabel.has(s.target)) {
+      const tgt = byLabel.get(s.target)!;
+      edges.push({ id: `jump-${s.id}-${tgt}`, source: s.id!, target: tgt, kind: "jump" });
+    }
+  }
+
+  return { nodes, edges };
+}
+
+export function graphToFlow(
+  nodes: RpaNode[],
+  edges: RpaEdge[],
+  settings: Record<string, unknown>,
+): Flow {
+  if (nodes.length === 0) return { steps: [], settings, layout: {} };
+
+  const seq = edges.filter((e) => e.kind === "seq");
+  const outgoing = new Map<string, string[]>();
+  const incoming = new Map<string, string[]>();
+  const push = (m: Map<string, string[]>, k: string, v: string) => {
+    const arr = m.get(k) ?? [];
+    arr.push(v);
+    m.set(k, arr);
+  };
+  for (const e of seq) {
+    push(outgoing, e.source, e.target);
+    push(incoming, e.target, e.source);
+  }
+  for (const [id, outs] of outgoing) if (outs.length > 1) throw new NonSequentialGraphError(`Nod ${id} har flera utgående kanter.`);
+  for (const [id, ins] of incoming) if (ins.length > 1) throw new NonSequentialGraphError(`Nod ${id} har flera ingående kanter.`);
+
+  const nodeById = new Map<string, RpaNode>(nodes.map((n) => [n.id, n] as [string, RpaNode]));
+  const starts = nodes.filter((n) => !incoming.has(n.id));
+  if (starts.length !== 1 && seq.length > 0) {
+    throw new NonSequentialGraphError(`Grafen har ${starts.length} startnoder – förväntade 1.`);
+  }
+
+  // Om inga seq-kanter finns (t.ex. en enda nod, eller nyskapade lösa noder):
+  // fall tillbaka på y-ordning så inget tappas bort.
+  let ordered: RpaNode[];
+  if (seq.length === 0) {
+    ordered = [...nodes].sort((a, b) => a.position.y - b.position.y);
+  } else {
+    ordered = [];
+    const seen = new Set<string>();
+    let curId: string | undefined = starts[0]?.id;
+    while (curId) {
+      if (seen.has(curId)) throw new NonSequentialGraphError("Cykel i seq-kanterna.");
+      seen.add(curId);
+      const node = nodeById.get(curId);
+      if (node) ordered.push(node);
+      curId = outgoing.get(curId)?.[0];
+    }
+    // Lösa noder utan kant (nyss tillagda) – lägg sist i y-ordning.
+    const loose = nodes.filter((n) => !seen.has(n.id)).sort((a, b) => a.position.y - b.position.y);
+    ordered.push(...loose);
+  }
+
+  const layout: Record<string, { x: number; y: number }> = {};
+  for (const n of ordered) layout[n.id] = { x: Math.round(n.position.x), y: Math.round(n.position.y) };
+
+  return { steps: ordered.map((n) => n.step), settings, layout };
+}
diff --git a/webui/frontend/src/lib/i18n.ts b/webui/frontend/src/lib/i18n.ts
new file mode 100644
index 0000000..64a71b8
--- /dev/null
+++ b/webui/frontend/src/lib/i18n.ts
@@ -0,0 +1,143 @@
+// Bara app-kromet. Steg-/fält-/villkorsnamn kommer tvåspråkigt från
+// /api/step-schema (rpa/schema.py) och slås upp med fieldLabel().
+
+export type Lang = "sv" | "en";
+
+const STRINGS = {
+  en: {
+    app_title: "RPA Tool",
+    page_title: "RPA Tool – flow builder",
+    new_flow: "New flow",
+    delete_flow: "Delete flow",
+    save: "Save",
+    saved: "Saved",
+    unsaved: "Unsaved changes",
+    run: "Run flow",
+    close_browser: "Close browser",
+    show_trace: "Show trace",
+    wiki: "Wiki",
+    settings: "⚙ Settings",
+    export: "Export as script",
+    close_always: "Close the browser",
+    close_on_success: "Keep open on error",
+    close_never: "Always keep open",
+    add_step: "+ Add step",
+    add_step_after: "Add step after",
+    add_step_in: "+ Add step",
+    add_branch: "+ Add 'else if' branch",
+    else_toggle: "…else (else)",
+    remove: "Remove",
+    remove_branch: "Remove branch",
+    branch_if: "If",
+    branch_else_if: "…else if",
+    add_field: "+ Add column",
+    column_name: "Column name",
+    label_ph: "Label (optional)",
+    on_error_stop: "On error: stop",
+    on_error_continue: "On error: continue",
+    enabled_title: "Enabled (unchecked = skipped when running)",
+    move_up: "Move up",
+    move_down: "Move down",
+    run_log: "Run log",
+    variables: "Variables",
+    no_vars: "No variables yet.",
+    vars_hint: "Click a name to see its content (filled in once the flow finishes).",
+    assist_title: "AI assistant",
+    assist_hint:
+      "Describe in plain language what the flow should do. The suggestion replaces the canvas (unsaved) so you can review it before saving/running.",
+    assist_ph: "e.g. Go to example.com, wait for login, list all links and save them as CSV",
+    assist_btn: "Suggest changes",
+    assist_working: "Thinking…",
+    assist_applied: "Suggestion applied — review, then Save / Run flow.",
+    assist_unavailable: "Set ANTHROPIC_API_KEY (or run `ant auth login`) and restart the server to use this.",
+    assist_failed: "Could not generate a flow: {msg}",
+    status_running: "Running…",
+    status_done: "Done",
+    status_error: "Error",
+    condition: "Condition",
+    new_flow_prompt: "Name of new flow:",
+    delete_flow_confirm: 'Delete the flow "{name}"?',
+    settings_hint: "Saved with the flow, so they travel along when you export it as a standalone script.",
+    cancel: "Cancel",
+    fit_view: "Fit to view",
+    relayout: "Auto-arrange",
+    var_list: "list, {n} item(s)",
+    var_object: "object",
+    var_text: "text, {n} chars",
+    save_failed: "Save failed: {msg}",
+  },
+  sv: {
+    app_title: "RPA-verktyg",
+    page_title: "RPA-verktyg – flödesbyggare",
+    new_flow: "Nytt flöde",
+    delete_flow: "Ta bort flöde",
+    save: "Spara",
+    saved: "Sparat",
+    unsaved: "Osparade ändringar",
+    run: "Kör flöde",
+    close_browser: "Stäng webbläsare",
+    show_trace: "Visa trace",
+    wiki: "Wiki",
+    settings: "⚙ Inställningar",
+    export: "Exportera som script",
+    close_always: "Stäng webbläsaren",
+    close_on_success: "Håll öppen vid fel",
+    close_never: "Håll alltid öppen",
+    add_step: "+ Lägg till steg",
+    add_step_after: "Lägg till steg efter",
+    add_step_in: "+ Lägg till steg",
+    add_branch: "+ Lägg till 'else if'-gren",
+    else_toggle: "…annars (else)",
+    remove: "Ta bort",
+    remove_branch: "Ta bort gren",
+    branch_if: "Om",
+    branch_else_if: "…annars om",
+    add_field: "+ Lägg till kolumn",
+    column_name: "Kolumnnamn",
+    label_ph: "Etikett (valfri)",
+    on_error_stop: "Vid fel: stanna",
+    on_error_continue: "Vid fel: fortsätt",
+    enabled_title: "Aktiverat (avbockad = hoppas över vid körning)",
+    move_up: "Flytta upp",
+    move_down: "Flytta ner",
+    run_log: "Körlogg",
+    variables: "Variabler",
+    no_vars: "Inga variabler ännu.",
+    vars_hint: "Klicka på ett namn för att se innehållet (fylls i när flödet är klart).",
+    assist_title: "AI-assistent",
+    assist_hint:
+      "Beskriv i klartext vad flödet ska göra. Förslaget ersätter canvasen (osparat) så du kan granska det innan du sparar/kör.",
+    assist_ph: "t.ex. Gå till example.com, vänta på inloggning, lista alla länkar och spara som CSV",
+    assist_btn: "Föreslå ändringar",
+    assist_working: "Tänker…",
+    assist_applied: "Förslag infogat — granska, sedan Spara / Kör flöde.",
+    assist_unavailable: "Sätt ANTHROPIC_API_KEY (eller kör `ant auth login`) och starta om servern för att använda detta.",
+    assist_failed: "Kunde inte generera ett flöde: {msg}",
+    status_running: "Kör…",
+    status_done: "Klar",
+    status_error: "Fel",
+    condition: "Villkor",
+    new_flow_prompt: "Namn på nytt flöde:",
+    delete_flow_confirm: 'Ta bort flödet "{name}"?',
+    settings_hint: "Sparas med flödet, så de följer med om du exporterar det som ett fristående script.",
+    cancel: "Avbryt",
+    fit_view: "Anpassa till vy",
+    relayout: "Autoplacera",
+    var_list: "lista, {n} post(er)",
+    var_object: "objekt",
+    var_text: "text, {n} tecken",
+    save_failed: "Kunde inte spara: {msg}",
+  },
+} as const;
+
+export type Key = keyof (typeof STRINGS)["en"];
+
+export function makeT(lang: Lang) {
+  return (key: Key, params?: Record<string, string | number>): string => {
+    let s: string = STRINGS[lang][key] ?? STRINGS.en[key] ?? key;
+    if (params) for (const [k, v] of Object.entries(params)) s = s.replace(`{${k}}`, String(v));
+    return s;
+  };
+}
+
+export type T = ReturnType<typeof makeT>;
diff --git a/webui/frontend/src/lib/ids.ts b/webui/frontend/src/lib/ids.ts
new file mode 100644
index 0000000..6d14cc8
--- /dev/null
+++ b/webui/frontend/src/lib/ids.ts
@@ -0,0 +1,8 @@
+export function uid(): string {
+  // Kort, stabilt id per steg. crypto.randomUUID finns i alla moderna webbläsare.
+  try {
+    return crypto.randomUUID().slice(0, 12);
+  } catch {
+    return Math.random().toString(36).slice(2, 14);
+  }
+}
diff --git a/webui/frontend/src/lib/layout.ts b/webui/frontend/src/lib/layout.ts
new file mode 100644
index 0000000..ae7a29f
--- /dev/null
+++ b/webui/frontend/src/lib/layout.ts
@@ -0,0 +1,32 @@
+// Enkel vertikal staplning av toppnivånoderna. Använder uppmätta höjder när de
+// finns, annars en gissning. Fästa positioner (användaren har dragit noden)
+// respekteras om `pinned` innehåller id:t.
+
+const GAP = 40;
+const FALLBACK_H = 180;
+const X = 0;
+
+export interface Measured {
+  width: number;
+  height: number;
+}
+
+export function stackLayout(
+  order: string[],
+  measured: Map<string, Measured>,
+  pinned: Record<string, { x: number; y: number }> = {},
+): Record<string, { x: number; y: number }> {
+  const pos: Record<string, { x: number; y: number }> = {};
+  let y = 0;
+  for (const id of order) {
+    if (pinned[id]) {
+      pos[id] = pinned[id];
+      const h = measured.get(id)?.height ?? FALLBACK_H;
+      y = Math.max(y, pinned[id].y + h + GAP);
+      continue;
+    }
+    pos[id] = { x: X, y };
+    y += (measured.get(id)?.height ?? FALLBACK_H) + GAP;
+  }
+  return pos;
+}
diff --git a/webui/frontend/src/lib/schema.ts b/webui/frontend/src/lib/schema.ts
new file mode 100644
index 0000000..10de6a3
--- /dev/null
+++ b/webui/frontend/src/lib/schema.ts
@@ -0,0 +1,64 @@
+// Speglar rpa/schema.py. Hämtas i runtime från GET /api/step-schema så det bara
+// finns EN källa (Python-sidan).
+
+export type FieldKind = "text" | "number" | "select" | "bool" | "field_list";
+
+export interface Field {
+  key: string;
+  kind: FieldKind;
+  options?: string[];
+  default?: string | number | boolean;
+  required?: boolean;
+  label_sv: string;
+  label_en: string;
+  help?: string;
+  interpolate?: boolean;
+}
+
+export interface StepType {
+  type: string;
+  category: string;
+  label_sv: string;
+  label_en: string;
+  help?: string;
+  fields: Field[];
+  container?: "steps" | "branches";
+  selector?: boolean;
+  produces_vars?: string[];
+  consumes_vars?: string[];
+}
+
+export interface ConditionKind {
+  kind: string;
+  label_sv: string;
+  label_en: string;
+  fields: Field[];
+}
+
+export interface Setting {
+  key: string;
+  kind: "text" | "number" | "bool";
+  default: string | number | boolean;
+  label_sv: string;
+  label_en: string;
+  help?: string;
+}
+
+export interface Schema {
+  step_types: StepType[];
+  condition_kinds: ConditionKind[];
+  settings: Setting[];
+  close_modes: string[];
+}
+
+export function stepType(schema: Schema, type: string): StepType | undefined {
+  return schema.step_types.find((s) => s.type === type);
+}
+
+export function conditionKind(schema: Schema, kind: string): ConditionKind | undefined {
+  return schema.condition_kinds.find((c) => c.kind === kind);
+}
+
+export function fieldLabel(f: { label_sv: string; label_en: string }, lang: "sv" | "en"): string {
+  return lang === "sv" ? f.label_sv : f.label_en;
+}
diff --git a/webui/frontend/src/lib/stepops.ts b/webui/frontend/src/lib/stepops.ts
new file mode 100644
index 0000000..e9e0757
--- /dev/null
+++ b/webui/frontend/src/lib/stepops.ts
@@ -0,0 +1,62 @@
+import type { Branch, Step } from "./api";
+import { uid } from "./ids";
+import type { Schema, StepType } from "./schema";
+
+/** Nytt steg av given typ med defaultvärden ur schemat. */
+export function newStep(schema: Schema, type: string): Step {
+  const spec = schema.step_types.find((s) => s.type === type);
+  const step: Step = { type, id: uid() };
+  for (const f of spec?.fields ?? []) {
+    if (f.default !== undefined && f.default !== "") step[f.key] = f.default;
+  }
+  if (type === "loop") step.steps = [];
+  if (type === "if") step.branches = [{ condition: { kind: "element_exists" }, steps: [] }];
+  if (type === "extract_row") step.fields = [{ name: "", by: "css", value: "" }];
+  return step;
+}
+
+/** Byter typ men behåller id/label/enabled/on_error. */
+export function retype(schema: Schema, step: Step, type: string): Step {
+  const fresh = newStep(schema, type);
+  fresh.id = step.id;
+  if (step.label) fresh.label = step.label;
+  if (step.enabled === false) fresh.enabled = false;
+  if (step.on_error === "continue") fresh.on_error = "continue";
+  return fresh;
+}
+
+export function isContainer(spec: StepType | undefined): boolean {
+  return spec?.container === "steps" || spec?.container === "branches";
+}
+
+// -- immutabla list-operationer (returnerar ny array) --
+
+export function insertAt(list: Step[], index: number, step: Step): Step[] {
+  const copy = list.slice();
+  copy.splice(index, 0, step);
+  return copy;
+}
+
+export function removeAt(list: Step[], index: number): Step[] {
+  const copy = list.slice();
+  copy.splice(index, 1);
+  return copy;
+}
+
+export function move(list: Step[], from: number, to: number): Step[] {
+  if (to < 0 || to >= list.length) return list;
+  const copy = list.slice();
+  const [item] = copy.splice(from, 1);
+  copy.splice(to, 0, item);
+  return copy;
+}
+
+export function replaceAt(list: Step[], index: number, step: Step): Step[] {
+  const copy = list.slice();
+  copy[index] = step;
+  return copy;
+}
+
+export function newBranch(): Branch {
+  return { condition: { kind: "element_exists" }, steps: [] };
+}
diff --git a/webui/frontend/src/main.tsx b/webui/frontend/src/main.tsx
new file mode 100644
index 0000000..036a943
--- /dev/null
+++ b/webui/frontend/src/main.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import { createRoot } from "react-dom/client";
+import App from "./App";
+import "./styles.css";
+
+createRoot(document.getElementById("root")!).render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>,
+);
diff --git a/webui/frontend/src/styles.css b/webui/frontend/src/styles.css
new file mode 100644
index 0000000..aa5179b
--- /dev/null
+++ b/webui/frontend/src/styles.css
@@ -0,0 +1,147 @@
+:root {
+  color-scheme: light dark;
+  --bg: #f5f5f7;
+  --panel: #ffffff;
+  --border: #d8d8dd;
+  --text: #1c1c1e;
+  --muted: #6b6b70;
+  --accent: #2f6fed;
+  --accent-text: #ffffff;
+  --danger: #d1453b;
+  --nest: #eef2fb;
+}
+@media (prefers-color-scheme: dark) {
+  :root {
+    --bg: #1c1c1e;
+    --panel: #2a2a2d;
+    --border: #444448;
+    --text: #f2f2f3;
+    --muted: #9a9a9e;
+    --nest: #232b3d;
+  }
+}
+* { box-sizing: border-box; }
+html, body, #root { height: 100%; margin: 0; }
+body {
+  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
+  background: var(--bg);
+  color: var(--text);
+}
+.app { display: flex; flex-direction: column; height: 100%; }
+.loading { display: grid; place-items: center; height: 100%; color: var(--muted); }
+
+select, input, button, textarea, a.secondary {
+  font: inherit;
+  padding: 6px 9px;
+  border-radius: 6px;
+  border: 1px solid var(--border);
+  background: var(--panel);
+  color: var(--text);
+}
+textarea { width: 100%; resize: vertical; }
+button { cursor: pointer; background: var(--accent); color: var(--accent-text); border: none; }
+button.secondary, a.secondary {
+  background: transparent; color: var(--text); border: 1px solid var(--border);
+  text-decoration: none; display: inline-flex; align-items: center;
+}
+button.danger { background: var(--danger); color: #fff; }
+button.icon { padding: 2px 7px; background: transparent; color: var(--text); border: 1px solid var(--border); }
+button.tiny { padding: 1px 8px; font-size: 12px; }
+button:disabled { opacity: 0.4; cursor: not-allowed; }
+
+header {
+  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
+  padding: 10px 14px; background: var(--panel); border-bottom: 1px solid var(--border);
+}
+header h1 { font-size: 15px; margin: 0 10px 0 0; white-space: nowrap; }
+.spacer { flex: 1; }
+.dirty-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--border); }
+.dirty-dot.on { background: #f5b942; }
+
+main { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 360px; min-height: 0; }
+.canvas { position: relative; min-width: 0; }
+.side { border-left: 1px solid var(--border); background: var(--bg); overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 12px; }
+
+.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }
+.panel-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
+.hint { color: var(--muted); font-size: 12px; margin: 4px 0; }
+
+.canvas-panel { display: flex; gap: 6px; }
+
+/* --- Nodes --- */
+.step-node {
+  width: 340px;
+  background: var(--panel);
+  border: 1px solid var(--border);
+  border-radius: 10px;
+  padding: 8px 10px;
+  font-size: 13px;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
+}
+.step-node.disabled { opacity: 0.5; }
+.step-node.cat-control { border-color: #8a63d2; }
+.step-node.cat-files { border-color: #3aa757; }
+.step-node.cat-desktop { border-color: #d98a2b; }
+.node-head, .node-subhead, .mini-head { display: flex; align-items: center; gap: 6px; }
+.node-subhead { margin-top: 6px; }
+.node-body { margin-top: 8px; }
+.type-select { font-weight: 600; }
+.label-input { width: 110px; }
+.add-after {
+  position: absolute; left: 50%; bottom: -12px; transform: translateX(-50%);
+  width: 22px; height: 22px; border-radius: 50%; padding: 0; line-height: 1;
+  background: var(--panel); color: var(--accent); border: 1px solid var(--accent);
+}
+
+.fields { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; margin-top: 6px; }
+.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--muted); }
+.field span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.field input, .field select { width: 100%; color: var(--text); }
+.field-bool { flex-direction: row; align-items: center; }
+.field-bool input { width: auto; }
+
+.nested { margin-top: 8px; padding: 8px; border-left: 3px solid var(--accent); border-radius: 6px; background: var(--nest); }
+.nested-steps { display: flex; flex-direction: column; gap: 6px; }
+.mini-card { border: 1px solid var(--border); border-radius: 6px; padding: 6px; background: var(--panel); }
+.mini-card.disabled { opacity: 0.5; }
+.branch { border: 1px dashed var(--border); border-radius: 6px; padding: 6px; margin-bottom: 6px; }
+.branch-head { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
+.else-toggle { display: flex; flex-direction: row; align-items: center; gap: 6px; border-style: solid; }
+.condition { margin: 4px 0; }
+.row-field { border: 1px dashed var(--border); border-radius: 6px; padding: 6px; margin-bottom: 6px; }
+.row-field-head { display: flex; gap: 6px; margin-bottom: 4px; }
+
+/* --- Run / vars --- */
+.badge { padding: 2px 8px; border-radius: 999px; font-size: 12px; }
+.badge-running { background: #f5b942; color: #222; }
+.badge-done { background: #3aa757; color: #fff; }
+.badge-error { background: var(--danger); color: #fff; }
+.log {
+  background: #111114; color: #d6d6d8; border-radius: 8px; padding: 10px;
+  font-family: ui-monospace, Consolas, monospace; font-size: 12px;
+  height: 320px; overflow-y: auto; white-space: pre-wrap; margin: 0;
+}
+.var-row { border: 1px solid var(--border); border-radius: 6px; margin-bottom: 6px; overflow: hidden; }
+.var-head { padding: 6px 9px; cursor: pointer; font-family: ui-monospace, Consolas, monospace; font-size: 12px; background: var(--bg); }
+.var-head:hover { background: var(--nest); }
+.var-value { margin: 0; padding: 8px 9px; background: #111114; color: #d6d6d8; font-family: ui-monospace, Consolas, monospace; font-size: 12px; max-height: 240px; overflow: auto; white-space: pre-wrap; }
+
+.assist-msg { font-size: 12px; white-space: pre-wrap; margin-top: 6px; }
+.assist-msg.error { color: var(--danger); }
+
+dialog {
+  background: var(--panel); color: var(--text); border: 1px solid var(--border);
+  border-radius: 10px; padding: 20px; width: min(520px, 92vw);
+}
+dialog::backdrop { background: rgba(0,0,0,0.4); }
+dialog h2 { margin: 0 0 4px; font-size: 16px; }
+.dialog-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }
+
+.toast {
+  position: fixed; bottom: 16px; left: 50%; transform: translateX(-50%);
+  background: var(--danger); color: #fff; padding: 10px 16px; border-radius: 8px;
+  cursor: pointer; max-width: 80vw; font-size: 13px;
+}
+
+/* React Flow tweaks */
+.react-flow__attribution { display: none; }
diff --git a/webui/frontend/tsconfig.json b/webui/frontend/tsconfig.json
new file mode 100644
index 0000000..160f4c4
--- /dev/null
+++ b/webui/frontend/tsconfig.json
@@ -0,0 +1,22 @@
+{
+  "compilerOptions": {
+    "target": "ES2022",
+    "useDefineForClassFields": true,
+    "lib": ["ES2022", "DOM", "DOM.Iterable"],
+    "module": "ESNext",
+    "skipLibCheck": true,
+    "moduleResolution": "bundler",
+    "allowImportingTsExtensions": true,
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "moduleDetection": "force",
+    "noEmit": true,
+    "jsx": "react-jsx",
+    "strict": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noFallthroughCasesInSwitch": true,
+    "types": ["vitest/globals"]
+  },
+  "include": ["src"]
+}
diff --git a/webui/frontend/vite.config.ts b/webui/frontend/vite.config.ts
new file mode 100644
index 0000000..d364813
--- /dev/null
+++ b/webui/frontend/vite.config.ts
@@ -0,0 +1,24 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+// Byggs till ../static (som webui/app.py serverar). npm run dev proxar API-anrop
+// till FastAPI-servern (python run_webui.py) på 8765.
+export default defineConfig({
+  plugins: [react()],
+  base: "/",
+  build: {
+    outDir: "../static",
+    emptyOutDir: true,
+  },
+  server: {
+    port: 5173,
+    proxy: {
+      "/api": "http://127.0.0.1:8765",
+      "/wiki": "http://127.0.0.1:8765",
+    },
+  },
+  test: {
+    globals: true,
+    environment: "node",
+  },
+});
diff --git a/webui/static/assets/index-CYM_2KrD.css b/webui/static/assets/index-CYM_2KrD.css
new file mode 100644
index 0000000..abd0b43
--- /dev/null
+++ b/webui/static/assets/index-CYM_2KrD.css
@@ -0,0 +1 @@
+.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #555;--xy-background-pattern-lines-color-default: #333;--xy-background-pattern-cross-color-default: #333;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1;touch-action:none}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{color-scheme:light dark;--bg: #f5f5f7;--panel: #ffffff;--border: #d8d8dd;--text: #1c1c1e;--muted: #6b6b70;--accent: #2f6fed;--accent-text: #ffffff;--danger: #d1453b;--nest: #eef2fb}@media(prefers-color-scheme:dark){:root{--bg: #1c1c1e;--panel: #2a2a2d;--border: #444448;--text: #f2f2f3;--muted: #9a9a9e;--nest: #232b3d}}*{box-sizing:border-box}html,body,#root{height:100%;margin:0}body{font-family:system-ui,-apple-system,Segoe UI,sans-serif;background:var(--bg);color:var(--text)}.app{display:flex;flex-direction:column;height:100%}.loading{display:grid;place-items:center;height:100%;color:var(--muted)}select,input,button,textarea,a.secondary{font:inherit;padding:6px 9px;border-radius:6px;border:1px solid var(--border);background:var(--panel);color:var(--text)}textarea{width:100%;resize:vertical}button{cursor:pointer;background:var(--accent);color:var(--accent-text);border:none}button.secondary,a.secondary{background:transparent;color:var(--text);border:1px solid var(--border);text-decoration:none;display:inline-flex;align-items:center}button.danger{background:var(--danger);color:#fff}button.icon{padding:2px 7px;background:transparent;color:var(--text);border:1px solid var(--border)}button.tiny{padding:1px 8px;font-size:12px}button:disabled{opacity:.4;cursor:not-allowed}header{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 14px;background:var(--panel);border-bottom:1px solid var(--border)}header h1{font-size:15px;margin:0 10px 0 0;white-space:nowrap}.spacer{flex:1}.dirty-dot{width:9px;height:9px;border-radius:50%;background:var(--border)}.dirty-dot.on{background:#f5b942}main{flex:1;display:grid;grid-template-columns:minmax(0,1fr) 360px;min-height:0}.canvas{position:relative;min-width:0}.side{border-left:1px solid var(--border);background:var(--bg);overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:12px}.panel{background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:12px}.panel-head{display:flex;align-items:center;gap:8px;margin-bottom:8px}.hint{color:var(--muted);font-size:12px;margin:4px 0}.canvas-panel{display:flex;gap:6px}.step-node{width:340px;background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:8px 10px;font-size:13px;box-shadow:0 1px 3px #00000014}.step-node.disabled{opacity:.5}.step-node.cat-control{border-color:#8a63d2}.step-node.cat-files{border-color:#3aa757}.step-node.cat-desktop{border-color:#d98a2b}.node-head,.node-subhead,.mini-head{display:flex;align-items:center;gap:6px}.node-subhead{margin-top:6px}.node-body{margin-top:8px}.type-select{font-weight:600}.label-input{width:110px}.add-after{position:absolute;left:50%;bottom:-12px;transform:translate(-50%);width:22px;height:22px;border-radius:50%;padding:0;line-height:1;background:var(--panel);color:var(--accent);border:1px solid var(--accent)}.fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:6px;margin-top:6px}.field{display:flex;flex-direction:column;gap:2px;font-size:11px;color:var(--muted)}.field span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.field input,.field select{width:100%;color:var(--text)}.field-bool{flex-direction:row;align-items:center}.field-bool input{width:auto}.nested{margin-top:8px;padding:8px;border-left:3px solid var(--accent);border-radius:6px;background:var(--nest)}.nested-steps{display:flex;flex-direction:column;gap:6px}.mini-card{border:1px solid var(--border);border-radius:6px;padding:6px;background:var(--panel)}.mini-card.disabled{opacity:.5}.branch{border:1px dashed var(--border);border-radius:6px;padding:6px;margin-bottom:6px}.branch-head{display:flex;align-items:center;gap:6px;margin-bottom:4px}.else-toggle{display:flex;flex-direction:row;align-items:center;gap:6px;border-style:solid}.condition{margin:4px 0}.row-field{border:1px dashed var(--border);border-radius:6px;padding:6px;margin-bottom:6px}.row-field-head{display:flex;gap:6px;margin-bottom:4px}.badge{padding:2px 8px;border-radius:999px;font-size:12px}.badge-running{background:#f5b942;color:#222}.badge-done{background:#3aa757;color:#fff}.badge-error{background:var(--danger);color:#fff}.log{background:#111114;color:#d6d6d8;border-radius:8px;padding:10px;font-family:ui-monospace,Consolas,monospace;font-size:12px;height:320px;overflow-y:auto;white-space:pre-wrap;margin:0}.var-row{border:1px solid var(--border);border-radius:6px;margin-bottom:6px;overflow:hidden}.var-head{padding:6px 9px;cursor:pointer;font-family:ui-monospace,Consolas,monospace;font-size:12px;background:var(--bg)}.var-head:hover{background:var(--nest)}.var-value{margin:0;padding:8px 9px;background:#111114;color:#d6d6d8;font-family:ui-monospace,Consolas,monospace;font-size:12px;max-height:240px;overflow:auto;white-space:pre-wrap}.assist-msg{font-size:12px;white-space:pre-wrap;margin-top:6px}.assist-msg.error{color:var(--danger)}dialog{background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:10px;padding:20px;width:min(520px,92vw)}dialog::backdrop{background:#0006}dialog h2{margin:0 0 4px;font-size:16px}.dialog-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:14px}.toast{position:fixed;bottom:16px;left:50%;transform:translate(-50%);background:var(--danger);color:#fff;padding:10px 16px;border-radius:8px;cursor:pointer;max-width:80vw;font-size:13px}.react-flow__attribution{display:none}
diff --git a/webui/static/assets/index-ClN1xAxw.js b/webui/static/assets/index-ClN1xAxw.js
new file mode 100644
index 0000000..cd010ee
--- /dev/null
+++ b/webui/static/assets/index-ClN1xAxw.js
@@ -0,0 +1,65 @@
+(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const a of u)if(a.type==="childList")for(const c of a.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&l(c)}).observe(document,{childList:!0,subtree:!0});function i(u){const a={};return u.integrity&&(a.integrity=u.integrity),u.referrerPolicy&&(a.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?a.credentials="include":u.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function l(u){if(u.ep)return;u.ep=!0;const a=i(u);fetch(u.href,a)}})();function up(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var va={exports:{}},Jo={},xa={exports:{}},Ne={};/**
+ * @license React
+ * react.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var Hd;function $0(){if(Hd)return Ne;Hd=1;var t=Symbol.for("react.element"),r=Symbol.for("react.portal"),i=Symbol.for("react.fragment"),l=Symbol.for("react.strict_mode"),u=Symbol.for("react.profiler"),a=Symbol.for("react.provider"),c=Symbol.for("react.context"),d=Symbol.for("react.forward_ref"),p=Symbol.for("react.suspense"),g=Symbol.for("react.memo"),v=Symbol.for("react.lazy"),y=Symbol.iterator;function m(M){return M===null||typeof M!="object"?null:(M=y&&M[y]||M["@@iterator"],typeof M=="function"?M:null)}var w={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},S=Object.assign,P={};function C(M,L,te){this.props=M,this.context=L,this.refs=P,this.updater=te||w}C.prototype.isReactComponent={},C.prototype.setState=function(M,L){if(typeof M!="object"&&typeof M!="function"&&M!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,M,L,"setState")},C.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function k(){}k.prototype=C.prototype;function j(M,L,te){this.props=M,this.context=L,this.refs=P,this.updater=te||w}var _=j.prototype=new k;_.constructor=j,S(_,C.prototype),_.isPureReactComponent=!0;var T=Array.isArray,F=Object.prototype.hasOwnProperty,D={current:null},V={key:!0,ref:!0,__self:!0,__source:!0};function Q(M,L,te){var re,ue={},ce=null,ae=null;if(L!=null)for(re in L.ref!==void 0&&(ae=L.ref),L.key!==void 0&&(ce=""+L.key),L)F.call(L,re)&&!V.hasOwnProperty(re)&&(ue[re]=L[re]);var G=arguments.length-2;if(G===1)ue.children=te;else if(1<G){for(var se=Array(G),pe=0;pe<G;pe++)se[pe]=arguments[pe+2];ue.children=se}if(M&&M.defaultProps)for(re in G=M.defaultProps,G)ue[re]===void 0&&(ue[re]=G[re]);return{$$typeof:t,type:M,key:ce,ref:ae,props:ue,_owner:D.current}}function Y(M,L){return{$$typeof:t,type:M.type,key:L,ref:M.ref,props:M.props,_owner:M._owner}}function ee(M){return typeof M=="object"&&M!==null&&M.$$typeof===t}function q(M){var L={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(te){return L[te]})}var J=/\/+/g;function Z(M,L){return typeof M=="object"&&M!==null&&M.key!=null?q(""+M.key):L.toString(36)}function N(M,L,te,re,ue){var ce=typeof M;(ce==="undefined"||ce==="boolean")&&(M=null);var ae=!1;if(M===null)ae=!0;else switch(ce){case"string":case"number":ae=!0;break;case"object":switch(M.$$typeof){case t:case r:ae=!0}}if(ae)return ae=M,ue=ue(ae),M=re===""?"."+Z(ae,0):re,T(ue)?(te="",M!=null&&(te=M.replace(J,"$&/")+"/"),N(ue,L,te,"",function(pe){return pe})):ue!=null&&(ee(ue)&&(ue=Y(ue,te+(!ue.key||ae&&ae.key===ue.key?"":(""+ue.key).replace(J,"$&/")+"/")+M)),L.push(ue)),1;if(ae=0,re=re===""?".":re+":",T(M))for(var G=0;G<M.length;G++){ce=M[G];var se=re+Z(ce,G);ae+=N(ce,L,te,se,ue)}else if(se=m(M),typeof se=="function")for(M=se.call(M),G=0;!(ce=M.next()).done;)ce=ce.value,se=re+Z(ce,G++),ae+=N(ce,L,te,se,ue);else if(ce==="object")throw L=String(M),Error("Objects are not valid as a React child (found: "+(L==="[object Object]"?"object with keys {"+Object.keys(M).join(", ")+"}":L)+"). If you meant to render a collection of children, use an array instead.");return ae}function b(M,L,te){if(M==null)return M;var re=[],ue=0;return N(M,re,"","",function(ce){return L.call(te,ce,ue++)}),re}function H(M){if(M._status===-1){var L=M._result;L=L(),L.then(function(te){(M._status===0||M._status===-1)&&(M._status=1,M._result=te)},function(te){(M._status===0||M._status===-1)&&(M._status=2,M._result=te)}),M._status===-1&&(M._status=0,M._result=L)}if(M._status===1)return M._result.default;throw M._result}var B={current:null},z={transition:null},A={ReactCurrentDispatcher:B,ReactCurrentBatchConfig:z,ReactCurrentOwner:D};function O(){throw Error("act(...) is not supported in production builds of React.")}return Ne.Children={map:b,forEach:function(M,L,te){b(M,function(){L.apply(this,arguments)},te)},count:function(M){var L=0;return b(M,function(){L++}),L},toArray:function(M){return b(M,function(L){return L})||[]},only:function(M){if(!ee(M))throw Error("React.Children.only expected to receive a single React element child.");return M}},Ne.Component=C,Ne.Fragment=i,Ne.Profiler=u,Ne.PureComponent=j,Ne.StrictMode=l,Ne.Suspense=p,Ne.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=A,Ne.act=O,Ne.cloneElement=function(M,L,te){if(M==null)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+M+".");var re=S({},M.props),ue=M.key,ce=M.ref,ae=M._owner;if(L!=null){if(L.ref!==void 0&&(ce=L.ref,ae=D.current),L.key!==void 0&&(ue=""+L.key),M.type&&M.type.defaultProps)var G=M.type.defaultProps;for(se in L)F.call(L,se)&&!V.hasOwnProperty(se)&&(re[se]=L[se]===void 0&&G!==void 0?G[se]:L[se])}var se=arguments.length-2;if(se===1)re.children=te;else if(1<se){G=Array(se);for(var pe=0;pe<se;pe++)G[pe]=arguments[pe+2];re.children=G}return{$$typeof:t,type:M.type,key:ue,ref:ce,props:re,_owner:ae}},Ne.createContext=function(M){return M={$$typeof:c,_currentValue:M,_currentValue2:M,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null},M.Provider={$$typeof:a,_context:M},M.Consumer=M},Ne.createElement=Q,Ne.createFactory=function(M){var L=Q.bind(null,M);return L.type=M,L},Ne.createRef=function(){return{current:null}},Ne.forwardRef=function(M){return{$$typeof:d,render:M}},Ne.isValidElement=ee,Ne.lazy=function(M){return{$$typeof:v,_payload:{_status:-1,_result:M},_init:H}},Ne.memo=function(M,L){return{$$typeof:g,type:M,compare:L===void 0?null:L}},Ne.startTransition=function(M){var L=z.transition;z.transition={};try{M()}finally{z.transition=L}},Ne.unstable_act=O,Ne.useCallback=function(M,L){return B.current.useCallback(M,L)},Ne.useContext=function(M){return B.current.useContext(M)},Ne.useDebugValue=function(){},Ne.useDeferredValue=function(M){return B.current.useDeferredValue(M)},Ne.useEffect=function(M,L){return B.current.useEffect(M,L)},Ne.useId=function(){return B.current.useId()},Ne.useImperativeHandle=function(M,L,te){return B.current.useImperativeHandle(M,L,te)},Ne.useInsertionEffect=function(M,L){return B.current.useInsertionEffect(M,L)},Ne.useLayoutEffect=function(M,L){return B.current.useLayoutEffect(M,L)},Ne.useMemo=function(M,L){return B.current.useMemo(M,L)},Ne.useReducer=function(M,L,te){return B.current.useReducer(M,L,te)},Ne.useRef=function(M){return B.current.useRef(M)},Ne.useState=function(M){return B.current.useState(M)},Ne.useSyncExternalStore=function(M,L,te){return B.current.useSyncExternalStore(M,L,te)},Ne.useTransition=function(){return B.current.useTransition()},Ne.version="18.3.1",Ne}var Vd;function mi(){return Vd||(Vd=1,xa.exports=$0()),xa.exports}/**
+ * @license React
+ * react-jsx-runtime.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var bd;function O0(){if(bd)return Jo;bd=1;var t=mi(),r=Symbol.for("react.element"),i=Symbol.for("react.fragment"),l=Object.prototype.hasOwnProperty,u=t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,a={key:!0,ref:!0,__self:!0,__source:!0};function c(d,p,g){var v,y={},m=null,w=null;g!==void 0&&(m=""+g),p.key!==void 0&&(m=""+p.key),p.ref!==void 0&&(w=p.ref);for(v in p)l.call(p,v)&&!a.hasOwnProperty(v)&&(y[v]=p[v]);if(d&&d.defaultProps)for(v in p=d.defaultProps,p)y[v]===void 0&&(y[v]=p[v]);return{$$typeof:r,type:d,key:m,ref:w,props:y,_owner:u.current}}return Jo.Fragment=i,Jo.jsx=c,Jo.jsxs=c,Jo}var Bd;function F0(){return Bd||(Bd=1,va.exports=O0()),va.exports}var E=F0(),U=mi();const ap=up(U);var bs={},wa={exports:{}},mt={},_a={exports:{}},Sa={};/**
+ * @license React
+ * scheduler.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var Ud;function H0(){return Ud||(Ud=1,(function(t){function r(z,A){var O=z.length;z.push(A);e:for(;0<O;){var M=O-1>>>1,L=z[M];if(0<u(L,A))z[M]=A,z[O]=L,O=M;else break e}}function i(z){return z.length===0?null:z[0]}function l(z){if(z.length===0)return null;var A=z[0],O=z.pop();if(O!==A){z[0]=O;e:for(var M=0,L=z.length,te=L>>>1;M<te;){var re=2*(M+1)-1,ue=z[re],ce=re+1,ae=z[ce];if(0>u(ue,O))ce<L&&0>u(ae,ue)?(z[M]=ae,z[ce]=O,M=ce):(z[M]=ue,z[re]=O,M=re);else if(ce<L&&0>u(ae,O))z[M]=ae,z[ce]=O,M=ce;else break e}}return A}function u(z,A){var O=z.sortIndex-A.sortIndex;return O!==0?O:z.id-A.id}if(typeof performance=="object"&&typeof performance.now=="function"){var a=performance;t.unstable_now=function(){return a.now()}}else{var c=Date,d=c.now();t.unstable_now=function(){return c.now()-d}}var p=[],g=[],v=1,y=null,m=3,w=!1,S=!1,P=!1,C=typeof setTimeout=="function"?setTimeout:null,k=typeof clearTimeout=="function"?clearTimeout:null,j=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function _(z){for(var A=i(g);A!==null;){if(A.callback===null)l(g);else if(A.startTime<=z)l(g),A.sortIndex=A.expirationTime,r(p,A);else break;A=i(g)}}function T(z){if(P=!1,_(z),!S)if(i(p)!==null)S=!0,H(F);else{var A=i(g);A!==null&&B(T,A.startTime-z)}}function F(z,A){S=!1,P&&(P=!1,k(Q),Q=-1),w=!0;var O=m;try{for(_(A),y=i(p);y!==null&&(!(y.expirationTime>A)||z&&!q());){var M=y.callback;if(typeof M=="function"){y.callback=null,m=y.priorityLevel;var L=M(y.expirationTime<=A);A=t.unstable_now(),typeof L=="function"?y.callback=L:y===i(p)&&l(p),_(A)}else l(p);y=i(p)}if(y!==null)var te=!0;else{var re=i(g);re!==null&&B(T,re.startTime-A),te=!1}return te}finally{y=null,m=O,w=!1}}var D=!1,V=null,Q=-1,Y=5,ee=-1;function q(){return!(t.unstable_now()-ee<Y)}function J(){if(V!==null){var z=t.unstable_now();ee=z;var A=!0;try{A=V(!0,z)}finally{A?Z():(D=!1,V=null)}}else D=!1}var Z;if(typeof j=="function")Z=function(){j(J)};else if(typeof MessageChannel<"u"){var N=new MessageChannel,b=N.port2;N.port1.onmessage=J,Z=function(){b.postMessage(null)}}else Z=function(){C(J,0)};function H(z){V=z,D||(D=!0,Z())}function B(z,A){Q=C(function(){z(t.unstable_now())},A)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(z){z.callback=null},t.unstable_continueExecution=function(){S||w||(S=!0,H(F))},t.unstable_forceFrameRate=function(z){0>z||125<z?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):Y=0<z?Math.floor(1e3/z):5},t.unstable_getCurrentPriorityLevel=function(){return m},t.unstable_getFirstCallbackNode=function(){return i(p)},t.unstable_next=function(z){switch(m){case 1:case 2:case 3:var A=3;break;default:A=m}var O=m;m=A;try{return z()}finally{m=O}},t.unstable_pauseExecution=function(){},t.unstable_requestPaint=function(){},t.unstable_runWithPriority=function(z,A){switch(z){case 1:case 2:case 3:case 4:case 5:break;default:z=3}var O=m;m=z;try{return A()}finally{m=O}},t.unstable_scheduleCallback=function(z,A,O){var M=t.unstable_now();switch(typeof O=="object"&&O!==null?(O=O.delay,O=typeof O=="number"&&0<O?M+O:M):O=M,z){case 1:var L=-1;break;case 2:L=250;break;case 5:L=1073741823;break;case 4:L=1e4;break;default:L=5e3}return L=O+L,z={id:v++,callback:A,priorityLevel:z,startTime:O,expirationTime:L,sortIndex:-1},O>M?(z.sortIndex=O,r(g,z),i(p)===null&&z===i(g)&&(P?(k(Q),Q=-1):P=!0,B(T,O-M))):(z.sortIndex=L,r(p,z),S||w||(S=!0,H(F))),z},t.unstable_shouldYield=q,t.unstable_wrapCallback=function(z){var A=m;return function(){var O=m;m=A;try{return z.apply(this,arguments)}finally{m=O}}}})(Sa)),Sa}var Wd;function V0(){return Wd||(Wd=1,_a.exports=H0()),_a.exports}/**
+ * @license React
+ * react-dom.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var Yd;function b0(){if(Yd)return mt;Yd=1;var t=mi(),r=V0();function i(e){for(var n="https://reactjs.org/docs/error-decoder.html?invariant="+e,o=1;o<arguments.length;o++)n+="&args[]="+encodeURIComponent(arguments[o]);return"Minified React error #"+e+"; visit "+n+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var l=new Set,u={};function a(e,n){c(e,n),c(e+"Capture",n)}function c(e,n){for(u[e]=n,e=0;e<n.length;e++)l.add(n[e])}var d=!(typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),p=Object.prototype.hasOwnProperty,g=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,v={},y={};function m(e){return p.call(y,e)?!0:p.call(v,e)?!1:g.test(e)?y[e]=!0:(v[e]=!0,!1)}function w(e,n,o,s){if(o!==null&&o.type===0)return!1;switch(typeof n){case"function":case"symbol":return!0;case"boolean":return s?!1:o!==null?!o.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function S(e,n,o,s){if(n===null||typeof n>"u"||w(e,n,o,s))return!0;if(s)return!1;if(o!==null)switch(o.type){case 3:return!n;case 4:return n===!1;case 5:return isNaN(n);case 6:return isNaN(n)||1>n}return!1}function P(e,n,o,s,f,h,x){this.acceptsBooleans=n===2||n===3||n===4,this.attributeName=s,this.attributeNamespace=f,this.mustUseProperty=o,this.propertyName=e,this.type=n,this.sanitizeURL=h,this.removeEmptyString=x}var C={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){C[e]=new P(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var n=e[0];C[n]=new P(n,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){C[e]=new P(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){C[e]=new P(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){C[e]=new P(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){C[e]=new P(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){C[e]=new P(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){C[e]=new P(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){C[e]=new P(e,5,!1,e.toLowerCase(),null,!1,!1)});var k=/[\-:]([a-z])/g;function j(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var n=e.replace(k,j);C[n]=new P(n,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var n=e.replace(k,j);C[n]=new P(n,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var n=e.replace(k,j);C[n]=new P(n,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){C[e]=new P(e,1,!1,e.toLowerCase(),null,!1,!1)}),C.xlinkHref=new P("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){C[e]=new P(e,1,!1,e.toLowerCase(),null,!0,!0)});function _(e,n,o,s){var f=C.hasOwnProperty(n)?C[n]:null;(f!==null?f.type!==0:s||!(2<n.length)||n[0]!=="o"&&n[0]!=="O"||n[1]!=="n"&&n[1]!=="N")&&(S(n,o,f,s)&&(o=null),s||f===null?m(n)&&(o===null?e.removeAttribute(n):e.setAttribute(n,""+o)):f.mustUseProperty?e[f.propertyName]=o===null?f.type===3?!1:"":o:(n=f.attributeName,s=f.attributeNamespace,o===null?e.removeAttribute(n):(f=f.type,o=f===3||f===4&&o===!0?"":""+o,s?e.setAttributeNS(s,n,o):e.setAttribute(n,o))))}var T=t.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,F=Symbol.for("react.element"),D=Symbol.for("react.portal"),V=Symbol.for("react.fragment"),Q=Symbol.for("react.strict_mode"),Y=Symbol.for("react.profiler"),ee=Symbol.for("react.provider"),q=Symbol.for("react.context"),J=Symbol.for("react.forward_ref"),Z=Symbol.for("react.suspense"),N=Symbol.for("react.suspense_list"),b=Symbol.for("react.memo"),H=Symbol.for("react.lazy"),B=Symbol.for("react.offscreen"),z=Symbol.iterator;function A(e){return e===null||typeof e!="object"?null:(e=z&&e[z]||e["@@iterator"],typeof e=="function"?e:null)}var O=Object.assign,M;function L(e){if(M===void 0)try{throw Error()}catch(o){var n=o.stack.trim().match(/\n( *(at )?)/);M=n&&n[1]||""}return`
+`+M+e}var te=!1;function re(e,n){if(!e||te)return"";te=!0;var o=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(n)if(n=function(){throw Error()},Object.defineProperty(n.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(n,[])}catch(K){var s=K}Reflect.construct(e,[],n)}else{try{n.call()}catch(K){s=K}e.call(n.prototype)}else{try{throw Error()}catch(K){s=K}e()}}catch(K){if(K&&s&&typeof K.stack=="string"){for(var f=K.stack.split(`
+`),h=s.stack.split(`
+`),x=f.length-1,I=h.length-1;1<=x&&0<=I&&f[x]!==h[I];)I--;for(;1<=x&&0<=I;x--,I--)if(f[x]!==h[I]){if(x!==1||I!==1)do if(x--,I--,0>I||f[x]!==h[I]){var R=`
+`+f[x].replace(" at new "," at ");return e.displayName&&R.includes("<anonymous>")&&(R=R.replace("<anonymous>",e.displayName)),R}while(1<=x&&0<=I);break}}}finally{te=!1,Error.prepareStackTrace=o}return(e=e?e.displayName||e.name:"")?L(e):""}function ue(e){switch(e.tag){case 5:return L(e.type);case 16:return L("Lazy");case 13:return L("Suspense");case 19:return L("SuspenseList");case 0:case 2:case 15:return e=re(e.type,!1),e;case 11:return e=re(e.type.render,!1),e;case 1:return e=re(e.type,!0),e;default:return""}}function ce(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case V:return"Fragment";case D:return"Portal";case Y:return"Profiler";case Q:return"StrictMode";case Z:return"Suspense";case N:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case q:return(e.displayName||"Context")+".Consumer";case ee:return(e._context.displayName||"Context")+".Provider";case J:var n=e.render;return e=e.displayName,e||(e=n.displayName||n.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case b:return n=e.displayName||null,n!==null?n:ce(e.type)||"Memo";case H:n=e._payload,e=e._init;try{return ce(e(n))}catch{}}return null}function ae(e){var n=e.type;switch(e.tag){case 24:return"Cache";case 9:return(n.displayName||"Context")+".Consumer";case 10:return(n._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=n.render,e=e.displayName||e.name||"",n.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return n;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return ce(n);case 8:return n===Q?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof n=="function")return n.displayName||n.name||null;if(typeof n=="string")return n}return null}function G(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function se(e){var n=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(n==="checkbox"||n==="radio")}function pe(e){var n=se(e)?"checked":"value",o=Object.getOwnPropertyDescriptor(e.constructor.prototype,n),s=""+e[n];if(!e.hasOwnProperty(n)&&typeof o<"u"&&typeof o.get=="function"&&typeof o.set=="function"){var f=o.get,h=o.set;return Object.defineProperty(e,n,{configurable:!0,get:function(){return f.call(this)},set:function(x){s=""+x,h.call(this,x)}}),Object.defineProperty(e,n,{enumerable:o.enumerable}),{getValue:function(){return s},setValue:function(x){s=""+x},stopTracking:function(){e._valueTracker=null,delete e[n]}}}}function xe(e){e._valueTracker||(e._valueTracker=pe(e))}function ye(e){if(!e)return!1;var n=e._valueTracker;if(!n)return!0;var o=n.getValue(),s="";return e&&(s=se(e)?e.checked?"true":"false":e.value),e=s,e!==o?(n.setValue(e),!0):!1}function we(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function ke(e,n){var o=n.checked;return O({},n,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:o??e._wrapperState.initialChecked})}function Ce(e,n){var o=n.defaultValue==null?"":n.defaultValue,s=n.checked!=null?n.checked:n.defaultChecked;o=G(n.value!=null?n.value:o),e._wrapperState={initialChecked:s,initialValue:o,controlled:n.type==="checkbox"||n.type==="radio"?n.checked!=null:n.value!=null}}function Me(e,n){n=n.checked,n!=null&&_(e,"checked",n,!1)}function ze(e,n){Me(e,n);var o=G(n.value),s=n.type;if(o!=null)s==="number"?(o===0&&e.value===""||e.value!=o)&&(e.value=""+o):e.value!==""+o&&(e.value=""+o);else if(s==="submit"||s==="reset"){e.removeAttribute("value");return}n.hasOwnProperty("value")?at(e,n.type,o):n.hasOwnProperty("defaultValue")&&at(e,n.type,G(n.defaultValue)),n.checked==null&&n.defaultChecked!=null&&(e.defaultChecked=!!n.defaultChecked)}function it(e,n,o){if(n.hasOwnProperty("value")||n.hasOwnProperty("defaultValue")){var s=n.type;if(!(s!=="submit"&&s!=="reset"||n.value!==void 0&&n.value!==null))return;n=""+e._wrapperState.initialValue,o||n===e.value||(e.value=n),e.defaultValue=n}o=e.name,o!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,o!==""&&(e.name=o)}function at(e,n,o){(n!=="number"||we(e.ownerDocument)!==e)&&(o==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+o&&(e.defaultValue=""+o))}var Je=Array.isArray;function et(e,n,o,s){if(e=e.options,n){n={};for(var f=0;f<o.length;f++)n["$"+o[f]]=!0;for(o=0;o<e.length;o++)f=n.hasOwnProperty("$"+e[o].value),e[o].selected!==f&&(e[o].selected=f),f&&s&&(e[o].defaultSelected=!0)}else{for(o=""+G(o),n=null,f=0;f<e.length;f++){if(e[f].value===o){e[f].selected=!0,s&&(e[f].defaultSelected=!0);return}n!==null||e[f].disabled||(n=e[f])}n!==null&&(n.selected=!0)}}function sn(e,n){if(n.dangerouslySetInnerHTML!=null)throw Error(i(91));return O({},n,{value:void 0,defaultValue:void 0,children:""+e._wrapperState.initialValue})}function Qt(e,n){var o=n.value;if(o==null){if(o=n.children,n=n.defaultValue,o!=null){if(n!=null)throw Error(i(92));if(Je(o)){if(1<o.length)throw Error(i(93));o=o[0]}n=o}n==null&&(n=""),o=n}e._wrapperState={initialValue:G(o)}}function Kt(e,n){var o=G(n.value),s=G(n.defaultValue);o!=null&&(o=""+o,o!==e.value&&(e.value=o),n.defaultValue==null&&e.defaultValue!==o&&(e.defaultValue=o)),s!=null&&(e.defaultValue=""+s)}function ct(e){var n=e.textContent;n===e._wrapperState.initialValue&&n!==""&&n!==null&&(e.value=n)}function Mt(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function vt(e,n){return e==null||e==="http://www.w3.org/1999/xhtml"?Mt(n):e==="http://www.w3.org/2000/svg"&&n==="foreignObject"?"http://www.w3.org/1999/xhtml":e}var Pt,Sr=(function(e){return typeof MSApp<"u"&&MSApp.execUnsafeLocalFunction?function(n,o,s,f){MSApp.execUnsafeLocalFunction(function(){return e(n,o,s,f)})}:e})(function(e,n){if(e.namespaceURI!=="http://www.w3.org/2000/svg"||"innerHTML"in e)e.innerHTML=n;else{for(Pt=Pt||document.createElement("div"),Pt.innerHTML="<svg>"+n.valueOf().toString()+"</svg>",n=Pt.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;n.firstChild;)e.appendChild(n.firstChild)}});function ln(e,n){if(n){var o=e.firstChild;if(o&&o===e.lastChild&&o.nodeType===3){o.nodeValue=n;return}}e.textContent=n}var Xn={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Nl=["Webkit","ms","Moz","O"];Object.keys(Xn).forEach(function(e){Nl.forEach(function(n){n=n+e.charAt(0).toUpperCase()+e.substring(1),Xn[n]=Xn[e]})});function Ci(e,n,o){return n==null||typeof n=="boolean"||n===""?"":o||typeof n!="number"||n===0||Xn.hasOwnProperty(e)&&Xn[e]?(""+n).trim():n+"px"}function Ni(e,n){e=e.style;for(var o in n)if(n.hasOwnProperty(o)){var s=o.indexOf("--")===0,f=Ci(o,n[o],s);o==="float"&&(o="cssFloat"),s?e.setProperty(o,f):e[o]=f}}var Ml=O({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ao(e,n){if(n){if(Ml[e]&&(n.children!=null||n.dangerouslySetInnerHTML!=null))throw Error(i(137,e));if(n.dangerouslySetInnerHTML!=null){if(n.children!=null)throw Error(i(60));if(typeof n.dangerouslySetInnerHTML!="object"||!("__html"in n.dangerouslySetInnerHTML))throw Error(i(61))}if(n.style!=null&&typeof n.style!="object")throw Error(i(62))}}function co(e,n){if(e.indexOf("-")===-1)return typeof n.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var fo=null;function ho(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var po=null,wn=null,_n=null;function Mi(e){if(e=Oo(e)){if(typeof po!="function")throw Error(i(280));var n=e.stateNode;n&&(n=rs(n),po(e.stateNode,e.type,n))}}function Pi(e){wn?_n?_n.push(e):_n=[e]:wn=e}function Ti(){if(wn){var e=wn,n=_n;if(_n=wn=null,Mi(e),n)for(e=0;e<n.length;e++)Mi(n[e])}}function Ii(e,n){return e(n)}function zi(){}var go=!1;function ji(e,n,o){if(go)return e(n,o);go=!0;try{return Ii(e,n,o)}finally{go=!1,(wn!==null||_n!==null)&&(zi(),Ti())}}function Qn(e,n){var o=e.stateNode;if(o===null)return null;var s=rs(o);if(s===null)return null;o=s[n];e:switch(n){case"onClick":case"onClickCapture":case"onDoubleClick":case"onDoubleClickCapture":case"onMouseDown":case"onMouseDownCapture":case"onMouseMove":case"onMouseMoveCapture":case"onMouseUp":case"onMouseUpCapture":case"onMouseEnter":(s=!s.disabled)||(e=e.type,s=!(e==="button"||e==="input"||e==="select"||e==="textarea")),e=!s;break e;default:e=!1}if(e)return null;if(o&&typeof o!="function")throw Error(i(231,n,typeof o));return o}var mo=!1;if(d)try{var Kn={};Object.defineProperty(Kn,"passive",{get:function(){mo=!0}}),window.addEventListener("test",Kn,Kn),window.removeEventListener("test",Kn,Kn)}catch{mo=!1}function Pl(e,n,o,s,f,h,x,I,R){var K=Array.prototype.slice.call(arguments,3);try{n.apply(o,K)}catch(oe){this.onError(oe)}}var Gn=!1,kr=null,Er=!1,yo=null,Tl={onError:function(e){Gn=!0,kr=e}};function Il(e,n,o,s,f,h,x,I,R){Gn=!1,kr=null,Pl.apply(Tl,arguments)}function zl(e,n,o,s,f,h,x,I,R){if(Il.apply(this,arguments),Gn){if(Gn){var K=kr;Gn=!1,kr=null}else throw Error(i(198));Er||(Er=!0,yo=K)}}function Gt(e){var n=e,o=e;if(e.alternate)for(;n.return;)n=n.return;else{e=n;do n=e,(n.flags&4098)!==0&&(o=n.return),e=n.return;while(e)}return n.tag===3?o:null}function vo(e){if(e.tag===13){var n=e.memoizedState;if(n===null&&(e=e.alternate,e!==null&&(n=e.memoizedState)),n!==null)return n.dehydrated}return null}function xo(e){if(Gt(e)!==e)throw Error(i(188))}function jl(e){var n=e.alternate;if(!n){if(n=Gt(e),n===null)throw Error(i(188));return n!==e?null:e}for(var o=e,s=n;;){var f=o.return;if(f===null)break;var h=f.alternate;if(h===null){if(s=f.return,s!==null){o=s;continue}break}if(f.child===h.child){for(h=f.child;h;){if(h===o)return xo(f),e;if(h===s)return xo(f),n;h=h.sibling}throw Error(i(188))}if(o.return!==s.return)o=f,s=h;else{for(var x=!1,I=f.child;I;){if(I===o){x=!0,o=f,s=h;break}if(I===s){x=!0,s=f,o=h;break}I=I.sibling}if(!x){for(I=h.child;I;){if(I===o){x=!0,o=h,s=f;break}if(I===s){x=!0,s=h,o=f;break}I=I.sibling}if(!x)throw Error(i(189))}}if(o.alternate!==s)throw Error(i(190))}if(o.tag!==3)throw Error(i(188));return o.stateNode.current===o?e:n}function Ai(e){return e=jl(e),e!==null?Li(e):null}function Li(e){if(e.tag===5||e.tag===6)return e;for(e=e.child;e!==null;){var n=Li(e);if(n!==null)return n;e=e.sibling}return null}var Ri=r.unstable_scheduleCallback,Di=r.unstable_cancelCallback,Al=r.unstable_shouldYield,$i=r.unstable_requestPaint,Oe=r.unstable_now,Ll=r.unstable_getCurrentPriorityLevel,wo=r.unstable_ImmediatePriority,Oi=r.unstable_UserBlockingPriority,Cr=r.unstable_NormalPriority,Rl=r.unstable_LowPriority,Fi=r.unstable_IdlePriority,Nr=null,xt=null;function Dl(e){if(xt&&typeof xt.onCommitFiberRoot=="function")try{xt.onCommitFiberRoot(Nr,e,void 0,(e.current.flags&128)===128)}catch{}}var wt=Math.clz32?Math.clz32:Fl,$l=Math.log,Ol=Math.LN2;function Fl(e){return e>>>=0,e===0?32:31-($l(e)/Ol|0)|0}var Mr=64,Pr=4194304;function qn(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function un(e,n){var o=e.pendingLanes;if(o===0)return 0;var s=0,f=e.suspendedLanes,h=e.pingedLanes,x=o&268435455;if(x!==0){var I=x&~f;I!==0?s=qn(I):(h&=x,h!==0&&(s=qn(h)))}else x=o&~f,x!==0?s=qn(x):h!==0&&(s=qn(h));if(s===0)return 0;if(n!==0&&n!==s&&(n&f)===0&&(f=s&-s,h=n&-n,f>=h||f===16&&(h&4194240)!==0))return n;if((s&4)!==0&&(s|=o&16),n=e.entangledLanes,n!==0)for(e=e.entanglements,n&=s;0<n;)o=31-wt(n),f=1<<o,s|=e[o],n&=~f;return s}function Hl(e,n){switch(e){case 1:case 2:case 4:return n+250;case 8:case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return n+5e3;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return-1;case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Hi(e,n){for(var o=e.suspendedLanes,s=e.pingedLanes,f=e.expirationTimes,h=e.pendingLanes;0<h;){var x=31-wt(h),I=1<<x,R=f[x];R===-1?((I&o)===0||(I&s)!==0)&&(f[x]=Hl(I,n)):R<=n&&(e.expiredLanes|=I),h&=~I}}function _o(e){return e=e.pendingLanes&-1073741825,e!==0?e:e&1073741824?1073741824:0}function Vi(){var e=Mr;return Mr<<=1,(Mr&4194240)===0&&(Mr=64),e}function Zn(e){for(var n=[],o=0;31>o;o++)n.push(e);return n}function Jn(e,n,o){e.pendingLanes|=n,n!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,n=31-wt(n),e[n]=o}function Vl(e,n){var o=e.pendingLanes&~n;e.pendingLanes=n,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=n,e.mutableReadLanes&=n,e.entangledLanes&=n,n=e.entanglements;var s=e.eventTimes;for(e=e.expirationTimes;0<o;){var f=31-wt(o),h=1<<f;n[f]=0,s[f]=-1,e[f]=-1,o&=~h}}function Tr(e,n){var o=e.entangledLanes|=n;for(e=e.entanglements;o;){var s=31-wt(o),f=1<<s;f&n|e[s]&n&&(e[s]|=n),o&=~f}}var je=0;function yc(e){return e&=-e,1<e?4<e?(e&268435455)!==0?16:536870912:4:1}var vc,bl,xc,wc,_c,Bl=!1,bi=[],Sn=null,kn=null,En=null,So=new Map,ko=new Map,Cn=[],im="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split(" ");function Sc(e,n){switch(e){case"focusin":case"focusout":Sn=null;break;case"dragenter":case"dragleave":kn=null;break;case"mouseover":case"mouseout":En=null;break;case"pointerover":case"pointerout":So.delete(n.pointerId);break;case"gotpointercapture":case"lostpointercapture":ko.delete(n.pointerId)}}function Eo(e,n,o,s,f,h){return e===null||e.nativeEvent!==h?(e={blockedOn:n,domEventName:o,eventSystemFlags:s,nativeEvent:h,targetContainers:[f]},n!==null&&(n=Oo(n),n!==null&&bl(n)),e):(e.eventSystemFlags|=s,n=e.targetContainers,f!==null&&n.indexOf(f)===-1&&n.push(f),e)}function sm(e,n,o,s,f){switch(n){case"focusin":return Sn=Eo(Sn,e,n,o,s,f),!0;case"dragenter":return kn=Eo(kn,e,n,o,s,f),!0;case"mouseover":return En=Eo(En,e,n,o,s,f),!0;case"pointerover":var h=f.pointerId;return So.set(h,Eo(So.get(h)||null,e,n,o,s,f)),!0;case"gotpointercapture":return h=f.pointerId,ko.set(h,Eo(ko.get(h)||null,e,n,o,s,f)),!0}return!1}function kc(e){var n=er(e.target);if(n!==null){var o=Gt(n);if(o!==null){if(n=o.tag,n===13){if(n=vo(o),n!==null){e.blockedOn=n,_c(e.priority,function(){xc(o)});return}}else if(n===3&&o.stateNode.current.memoizedState.isDehydrated){e.blockedOn=o.tag===3?o.stateNode.containerInfo:null;return}}}e.blockedOn=null}function Bi(e){if(e.blockedOn!==null)return!1;for(var n=e.targetContainers;0<n.length;){var o=Wl(e.domEventName,e.eventSystemFlags,n[0],e.nativeEvent);if(o===null){o=e.nativeEvent;var s=new o.constructor(o.type,o);fo=s,o.target.dispatchEvent(s),fo=null}else return n=Oo(o),n!==null&&bl(n),e.blockedOn=o,!1;n.shift()}return!0}function Ec(e,n,o){Bi(e)&&o.delete(n)}function lm(){Bl=!1,Sn!==null&&Bi(Sn)&&(Sn=null),kn!==null&&Bi(kn)&&(kn=null),En!==null&&Bi(En)&&(En=null),So.forEach(Ec),ko.forEach(Ec)}function Co(e,n){e.blockedOn===n&&(e.blockedOn=null,Bl||(Bl=!0,r.unstable_scheduleCallback(r.unstable_NormalPriority,lm)))}function No(e){function n(f){return Co(f,e)}if(0<bi.length){Co(bi[0],e);for(var o=1;o<bi.length;o++){var s=bi[o];s.blockedOn===e&&(s.blockedOn=null)}}for(Sn!==null&&Co(Sn,e),kn!==null&&Co(kn,e),En!==null&&Co(En,e),So.forEach(n),ko.forEach(n),o=0;o<Cn.length;o++)s=Cn[o],s.blockedOn===e&&(s.blockedOn=null);for(;0<Cn.length&&(o=Cn[0],o.blockedOn===null);)kc(o),o.blockedOn===null&&Cn.shift()}var Ir=T.ReactCurrentBatchConfig,Ui=!0;function um(e,n,o,s){var f=je,h=Ir.transition;Ir.transition=null;try{je=1,Ul(e,n,o,s)}finally{je=f,Ir.transition=h}}function am(e,n,o,s){var f=je,h=Ir.transition;Ir.transition=null;try{je=4,Ul(e,n,o,s)}finally{je=f,Ir.transition=h}}function Ul(e,n,o,s){if(Ui){var f=Wl(e,n,o,s);if(f===null)uu(e,n,s,Wi,o),Sc(e,s);else if(sm(f,e,n,o,s))s.stopPropagation();else if(Sc(e,s),n&4&&-1<im.indexOf(e)){for(;f!==null;){var h=Oo(f);if(h!==null&&vc(h),h=Wl(e,n,o,s),h===null&&uu(e,n,s,Wi,o),h===f)break;f=h}f!==null&&s.stopPropagation()}else uu(e,n,s,null,o)}}var Wi=null;function Wl(e,n,o,s){if(Wi=null,e=ho(s),e=er(e),e!==null)if(n=Gt(e),n===null)e=null;else if(o=n.tag,o===13){if(e=vo(n),e!==null)return e;e=null}else if(o===3){if(n.stateNode.current.memoizedState.isDehydrated)return n.tag===3?n.stateNode.containerInfo:null;e=null}else n!==e&&(e=null);return Wi=e,null}function Cc(e){switch(e){case"cancel":case"click":case"close":case"contextmenu":case"copy":case"cut":case"auxclick":case"dblclick":case"dragend":case"dragstart":case"drop":case"focusin":case"focusout":case"input":case"invalid":case"keydown":case"keypress":case"keyup":case"mousedown":case"mouseup":case"paste":case"pause":case"play":case"pointercancel":case"pointerdown":case"pointerup":case"ratechange":case"reset":case"resize":case"seeked":case"submit":case"touchcancel":case"touchend":case"touchstart":case"volumechange":case"change":case"selectionchange":case"textInput":case"compositionstart":case"compositionend":case"compositionupdate":case"beforeblur":case"afterblur":case"beforeinput":case"blur":case"fullscreenchange":case"focus":case"hashchange":case"popstate":case"select":case"selectstart":return 1;case"drag":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"mousemove":case"mouseout":case"mouseover":case"pointermove":case"pointerout":case"pointerover":case"scroll":case"toggle":case"touchmove":case"wheel":case"mouseenter":case"mouseleave":case"pointerenter":case"pointerleave":return 4;case"message":switch(Ll()){case wo:return 1;case Oi:return 4;case Cr:case Rl:return 16;case Fi:return 536870912;default:return 16}default:return 16}}var Nn=null,Yl=null,Yi=null;function Nc(){if(Yi)return Yi;var e,n=Yl,o=n.length,s,f="value"in Nn?Nn.value:Nn.textContent,h=f.length;for(e=0;e<o&&n[e]===f[e];e++);var x=o-e;for(s=1;s<=x&&n[o-s]===f[h-s];s++);return Yi=f.slice(e,1<s?1-s:void 0)}function Xi(e){var n=e.keyCode;return"charCode"in e?(e=e.charCode,e===0&&n===13&&(e=13)):e=n,e===10&&(e=13),32<=e||e===13?e:0}function Qi(){return!0}function Mc(){return!1}function _t(e){function n(o,s,f,h,x){this._reactName=o,this._targetInst=f,this.type=s,this.nativeEvent=h,this.target=x,this.currentTarget=null;for(var I in e)e.hasOwnProperty(I)&&(o=e[I],this[I]=o?o(h):h[I]);return this.isDefaultPrevented=(h.defaultPrevented!=null?h.defaultPrevented:h.returnValue===!1)?Qi:Mc,this.isPropagationStopped=Mc,this}return O(n.prototype,{preventDefault:function(){this.defaultPrevented=!0;var o=this.nativeEvent;o&&(o.preventDefault?o.preventDefault():typeof o.returnValue!="unknown"&&(o.returnValue=!1),this.isDefaultPrevented=Qi)},stopPropagation:function(){var o=this.nativeEvent;o&&(o.stopPropagation?o.stopPropagation():typeof o.cancelBubble!="unknown"&&(o.cancelBubble=!0),this.isPropagationStopped=Qi)},persist:function(){},isPersistent:Qi}),n}var zr={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},Xl=_t(zr),Mo=O({},zr,{view:0,detail:0}),cm=_t(Mo),Ql,Kl,Po,Ki=O({},Mo,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:ql,button:0,buttons:0,relatedTarget:function(e){return e.relatedTarget===void 0?e.fromElement===e.srcElement?e.toElement:e.fromElement:e.relatedTarget},movementX:function(e){return"movementX"in e?e.movementX:(e!==Po&&(Po&&e.type==="mousemove"?(Ql=e.screenX-Po.screenX,Kl=e.screenY-Po.screenY):Kl=Ql=0,Po=e),Ql)},movementY:function(e){return"movementY"in e?e.movementY:Kl}}),Pc=_t(Ki),fm=O({},Ki,{dataTransfer:0}),dm=_t(fm),hm=O({},Mo,{relatedTarget:0}),Gl=_t(hm),pm=O({},zr,{animationName:0,elapsedTime:0,pseudoElement:0}),gm=_t(pm),mm=O({},zr,{clipboardData:function(e){return"clipboardData"in e?e.clipboardData:window.clipboardData}}),ym=_t(mm),vm=O({},zr,{data:0}),Tc=_t(vm),xm={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},wm={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},_m={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};function Sm(e){var n=this.nativeEvent;return n.getModifierState?n.getModifierState(e):(e=_m[e])?!!n[e]:!1}function ql(){return Sm}var km=O({},Mo,{key:function(e){if(e.key){var n=xm[e.key]||e.key;if(n!=="Unidentified")return n}return e.type==="keypress"?(e=Xi(e),e===13?"Enter":String.fromCharCode(e)):e.type==="keydown"||e.type==="keyup"?wm[e.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,repeat:0,locale:0,getModifierState:ql,charCode:function(e){return e.type==="keypress"?Xi(e):0},keyCode:function(e){return e.type==="keydown"||e.type==="keyup"?e.keyCode:0},which:function(e){return e.type==="keypress"?Xi(e):e.type==="keydown"||e.type==="keyup"?e.keyCode:0}}),Em=_t(km),Cm=O({},Ki,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),Ic=_t(Cm),Nm=O({},Mo,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,ctrlKey:0,shiftKey:0,getModifierState:ql}),Mm=_t(Nm),Pm=O({},zr,{propertyName:0,elapsedTime:0,pseudoElement:0}),Tm=_t(Pm),Im=O({},Ki,{deltaX:function(e){return"deltaX"in e?e.deltaX:"wheelDeltaX"in e?-e.wheelDeltaX:0},deltaY:function(e){return"deltaY"in e?e.deltaY:"wheelDeltaY"in e?-e.wheelDeltaY:"wheelDelta"in e?-e.wheelDelta:0},deltaZ:0,deltaMode:0}),zm=_t(Im),jm=[9,13,27,32],Zl=d&&"CompositionEvent"in window,To=null;d&&"documentMode"in document&&(To=document.documentMode);var Am=d&&"TextEvent"in window&&!To,zc=d&&(!Zl||To&&8<To&&11>=To),jc=" ",Ac=!1;function Lc(e,n){switch(e){case"keyup":return jm.indexOf(n.keyCode)!==-1;case"keydown":return n.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Rc(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var jr=!1;function Lm(e,n){switch(e){case"compositionend":return Rc(n);case"keypress":return n.which!==32?null:(Ac=!0,jc);case"textInput":return e=n.data,e===jc&&Ac?null:e;default:return null}}function Rm(e,n){if(jr)return e==="compositionend"||!Zl&&Lc(e,n)?(e=Nc(),Yi=Yl=Nn=null,jr=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(n.ctrlKey||n.altKey||n.metaKey)||n.ctrlKey&&n.altKey){if(n.char&&1<n.char.length)return n.char;if(n.which)return String.fromCharCode(n.which)}return null;case"compositionend":return zc&&n.locale!=="ko"?null:n.data;default:return null}}var Dm={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};function Dc(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return n==="input"?!!Dm[e.type]:n==="textarea"}function $c(e,n,o,s){Pi(s),n=es(n,"onChange"),0<n.length&&(o=new Xl("onChange","change",null,o,s),e.push({event:o,listeners:n}))}var Io=null,zo=null;function $m(e){tf(e,0)}function Gi(e){var n=$r(e);if(ye(n))return e}function Om(e,n){if(e==="change")return n}var Oc=!1;if(d){var Jl;if(d){var eu="oninput"in document;if(!eu){var Fc=document.createElement("div");Fc.setAttribute("oninput","return;"),eu=typeof Fc.oninput=="function"}Jl=eu}else Jl=!1;Oc=Jl&&(!document.documentMode||9<document.documentMode)}function Hc(){Io&&(Io.detachEvent("onpropertychange",Vc),zo=Io=null)}function Vc(e){if(e.propertyName==="value"&&Gi(zo)){var n=[];$c(n,zo,e,ho(e)),ji($m,n)}}function Fm(e,n,o){e==="focusin"?(Hc(),Io=n,zo=o,Io.attachEvent("onpropertychange",Vc)):e==="focusout"&&Hc()}function Hm(e){if(e==="selectionchange"||e==="keyup"||e==="keydown")return Gi(zo)}function Vm(e,n){if(e==="click")return Gi(n)}function bm(e,n){if(e==="input"||e==="change")return Gi(n)}function Bm(e,n){return e===n&&(e!==0||1/e===1/n)||e!==e&&n!==n}var Rt=typeof Object.is=="function"?Object.is:Bm;function jo(e,n){if(Rt(e,n))return!0;if(typeof e!="object"||e===null||typeof n!="object"||n===null)return!1;var o=Object.keys(e),s=Object.keys(n);if(o.length!==s.length)return!1;for(s=0;s<o.length;s++){var f=o[s];if(!p.call(n,f)||!Rt(e[f],n[f]))return!1}return!0}function bc(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function Bc(e,n){var o=bc(e);e=0;for(var s;o;){if(o.nodeType===3){if(s=e+o.textContent.length,e<=n&&s>=n)return{node:o,offset:n-e};e=s}e:{for(;o;){if(o.nextSibling){o=o.nextSibling;break e}o=o.parentNode}o=void 0}o=bc(o)}}function Uc(e,n){return e&&n?e===n?!0:e&&e.nodeType===3?!1:n&&n.nodeType===3?Uc(e,n.parentNode):"contains"in e?e.contains(n):e.compareDocumentPosition?!!(e.compareDocumentPosition(n)&16):!1:!1}function Wc(){for(var e=window,n=we();n instanceof e.HTMLIFrameElement;){try{var o=typeof n.contentWindow.location.href=="string"}catch{o=!1}if(o)e=n.contentWindow;else break;n=we(e.document)}return n}function tu(e){var n=e&&e.nodeName&&e.nodeName.toLowerCase();return n&&(n==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||n==="textarea"||e.contentEditable==="true")}function Um(e){var n=Wc(),o=e.focusedElem,s=e.selectionRange;if(n!==o&&o&&o.ownerDocument&&Uc(o.ownerDocument.documentElement,o)){if(s!==null&&tu(o)){if(n=s.start,e=s.end,e===void 0&&(e=n),"selectionStart"in o)o.selectionStart=n,o.selectionEnd=Math.min(e,o.value.length);else if(e=(n=o.ownerDocument||document)&&n.defaultView||window,e.getSelection){e=e.getSelection();var f=o.textContent.length,h=Math.min(s.start,f);s=s.end===void 0?h:Math.min(s.end,f),!e.extend&&h>s&&(f=s,s=h,h=f),f=Bc(o,h);var x=Bc(o,s);f&&x&&(e.rangeCount!==1||e.anchorNode!==f.node||e.anchorOffset!==f.offset||e.focusNode!==x.node||e.focusOffset!==x.offset)&&(n=n.createRange(),n.setStart(f.node,f.offset),e.removeAllRanges(),h>s?(e.addRange(n),e.extend(x.node,x.offset)):(n.setEnd(x.node,x.offset),e.addRange(n)))}}for(n=[],e=o;e=e.parentNode;)e.nodeType===1&&n.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;o<n.length;o++)e=n[o],e.element.scrollLeft=e.left,e.element.scrollTop=e.top}}var Wm=d&&"documentMode"in document&&11>=document.documentMode,Ar=null,nu=null,Ao=null,ru=!1;function Yc(e,n,o){var s=o.window===o?o.document:o.nodeType===9?o:o.ownerDocument;ru||Ar==null||Ar!==we(s)||(s=Ar,"selectionStart"in s&&tu(s)?s={start:s.selectionStart,end:s.selectionEnd}:(s=(s.ownerDocument&&s.ownerDocument.defaultView||window).getSelection(),s={anchorNode:s.anchorNode,anchorOffset:s.anchorOffset,focusNode:s.focusNode,focusOffset:s.focusOffset}),Ao&&jo(Ao,s)||(Ao=s,s=es(nu,"onSelect"),0<s.length&&(n=new Xl("onSelect","select",null,n,o),e.push({event:n,listeners:s}),n.target=Ar)))}function qi(e,n){var o={};return o[e.toLowerCase()]=n.toLowerCase(),o["Webkit"+e]="webkit"+n,o["Moz"+e]="moz"+n,o}var Lr={animationend:qi("Animation","AnimationEnd"),animationiteration:qi("Animation","AnimationIteration"),animationstart:qi("Animation","AnimationStart"),transitionend:qi("Transition","TransitionEnd")},ou={},Xc={};d&&(Xc=document.createElement("div").style,"AnimationEvent"in window||(delete Lr.animationend.animation,delete Lr.animationiteration.animation,delete Lr.animationstart.animation),"TransitionEvent"in window||delete Lr.transitionend.transition);function Zi(e){if(ou[e])return ou[e];if(!Lr[e])return e;var n=Lr[e],o;for(o in n)if(n.hasOwnProperty(o)&&o in Xc)return ou[e]=n[o];return e}var Qc=Zi("animationend"),Kc=Zi("animationiteration"),Gc=Zi("animationstart"),qc=Zi("transitionend"),Zc=new Map,Jc="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");function Mn(e,n){Zc.set(e,n),a(n,[e])}for(var iu=0;iu<Jc.length;iu++){var su=Jc[iu],Ym=su.toLowerCase(),Xm=su[0].toUpperCase()+su.slice(1);Mn(Ym,"on"+Xm)}Mn(Qc,"onAnimationEnd"),Mn(Kc,"onAnimationIteration"),Mn(Gc,"onAnimationStart"),Mn("dblclick","onDoubleClick"),Mn("focusin","onFocus"),Mn("focusout","onBlur"),Mn(qc,"onTransitionEnd"),c("onMouseEnter",["mouseout","mouseover"]),c("onMouseLeave",["mouseout","mouseover"]),c("onPointerEnter",["pointerout","pointerover"]),c("onPointerLeave",["pointerout","pointerover"]),a("onChange","change click focusin focusout input keydown keyup selectionchange".split(" ")),a("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" ")),a("onBeforeInput",["compositionend","keypress","textInput","paste"]),a("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" ")),a("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" ")),a("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));var Lo="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Qm=new Set("cancel close invalid load scroll toggle".split(" ").concat(Lo));function ef(e,n,o){var s=e.type||"unknown-event";e.currentTarget=o,zl(s,n,void 0,e),e.currentTarget=null}function tf(e,n){n=(n&4)!==0;for(var o=0;o<e.length;o++){var s=e[o],f=s.event;s=s.listeners;e:{var h=void 0;if(n)for(var x=s.length-1;0<=x;x--){var I=s[x],R=I.instance,K=I.currentTarget;if(I=I.listener,R!==h&&f.isPropagationStopped())break e;ef(f,I,K),h=R}else for(x=0;x<s.length;x++){if(I=s[x],R=I.instance,K=I.currentTarget,I=I.listener,R!==h&&f.isPropagationStopped())break e;ef(f,I,K),h=R}}}if(Er)throw e=yo,Er=!1,yo=null,e}function Le(e,n){var o=n[pu];o===void 0&&(o=n[pu]=new Set);var s=e+"__bubble";o.has(s)||(nf(n,e,2,!1),o.add(s))}function lu(e,n,o){var s=0;n&&(s|=4),nf(o,e,s,n)}var Ji="_reactListening"+Math.random().toString(36).slice(2);function Ro(e){if(!e[Ji]){e[Ji]=!0,l.forEach(function(o){o!=="selectionchange"&&(Qm.has(o)||lu(o,!1,e),lu(o,!0,e))});var n=e.nodeType===9?e:e.ownerDocument;n===null||n[Ji]||(n[Ji]=!0,lu("selectionchange",!1,n))}}function nf(e,n,o,s){switch(Cc(n)){case 1:var f=um;break;case 4:f=am;break;default:f=Ul}o=f.bind(null,n,o,e),f=void 0,!mo||n!=="touchstart"&&n!=="touchmove"&&n!=="wheel"||(f=!0),s?f!==void 0?e.addEventListener(n,o,{capture:!0,passive:f}):e.addEventListener(n,o,!0):f!==void 0?e.addEventListener(n,o,{passive:f}):e.addEventListener(n,o,!1)}function uu(e,n,o,s,f){var h=s;if((n&1)===0&&(n&2)===0&&s!==null)e:for(;;){if(s===null)return;var x=s.tag;if(x===3||x===4){var I=s.stateNode.containerInfo;if(I===f||I.nodeType===8&&I.parentNode===f)break;if(x===4)for(x=s.return;x!==null;){var R=x.tag;if((R===3||R===4)&&(R=x.stateNode.containerInfo,R===f||R.nodeType===8&&R.parentNode===f))return;x=x.return}for(;I!==null;){if(x=er(I),x===null)return;if(R=x.tag,R===5||R===6){s=h=x;continue e}I=I.parentNode}}s=s.return}ji(function(){var K=h,oe=ho(o),ie=[];e:{var ne=Zc.get(e);if(ne!==void 0){var fe=Xl,he=e;switch(e){case"keypress":if(Xi(o)===0)break e;case"keydown":case"keyup":fe=Em;break;case"focusin":he="focus",fe=Gl;break;case"focusout":he="blur",fe=Gl;break;case"beforeblur":case"afterblur":fe=Gl;break;case"click":if(o.button===2)break e;case"auxclick":case"dblclick":case"mousedown":case"mousemove":case"mouseup":case"mouseout":case"mouseover":case"contextmenu":fe=Pc;break;case"drag":case"dragend":case"dragenter":case"dragexit":case"dragleave":case"dragover":case"dragstart":case"drop":fe=dm;break;case"touchcancel":case"touchend":case"touchmove":case"touchstart":fe=Mm;break;case Qc:case Kc:case Gc:fe=gm;break;case qc:fe=Tm;break;case"scroll":fe=cm;break;case"wheel":fe=zm;break;case"copy":case"cut":case"paste":fe=ym;break;case"gotpointercapture":case"lostpointercapture":case"pointercancel":case"pointerdown":case"pointermove":case"pointerout":case"pointerover":case"pointerup":fe=Ic}var ge=(n&4)!==0,Be=!ge&&e==="scroll",W=ge?ne!==null?ne+"Capture":null:ne;ge=[];for(var $=K,X;$!==null;){X=$;var le=X.stateNode;if(X.tag===5&&le!==null&&(X=le,W!==null&&(le=Qn($,W),le!=null&&ge.push(Do($,le,X)))),Be)break;$=$.return}0<ge.length&&(ne=new fe(ne,he,null,o,oe),ie.push({event:ne,listeners:ge}))}}if((n&7)===0){e:{if(ne=e==="mouseover"||e==="pointerover",fe=e==="mouseout"||e==="pointerout",ne&&o!==fo&&(he=o.relatedTarget||o.fromElement)&&(er(he)||he[an]))break e;if((fe||ne)&&(ne=oe.window===oe?oe:(ne=oe.ownerDocument)?ne.defaultView||ne.parentWindow:window,fe?(he=o.relatedTarget||o.toElement,fe=K,he=he?er(he):null,he!==null&&(Be=Gt(he),he!==Be||he.tag!==5&&he.tag!==6)&&(he=null)):(fe=null,he=K),fe!==he)){if(ge=Pc,le="onMouseLeave",W="onMouseEnter",$="mouse",(e==="pointerout"||e==="pointerover")&&(ge=Ic,le="onPointerLeave",W="onPointerEnter",$="pointer"),Be=fe==null?ne:$r(fe),X=he==null?ne:$r(he),ne=new ge(le,$+"leave",fe,o,oe),ne.target=Be,ne.relatedTarget=X,le=null,er(oe)===K&&(ge=new ge(W,$+"enter",he,o,oe),ge.target=X,ge.relatedTarget=Be,le=ge),Be=le,fe&&he)t:{for(ge=fe,W=he,$=0,X=ge;X;X=Rr(X))$++;for(X=0,le=W;le;le=Rr(le))X++;for(;0<$-X;)ge=Rr(ge),$--;for(;0<X-$;)W=Rr(W),X--;for(;$--;){if(ge===W||W!==null&&ge===W.alternate)break t;ge=Rr(ge),W=Rr(W)}ge=null}else ge=null;fe!==null&&rf(ie,ne,fe,ge,!1),he!==null&&Be!==null&&rf(ie,Be,he,ge,!0)}}e:{if(ne=K?$r(K):window,fe=ne.nodeName&&ne.nodeName.toLowerCase(),fe==="select"||fe==="input"&&ne.type==="file")var me=Om;else if(Dc(ne))if(Oc)me=bm;else{me=Hm;var _e=Fm}else(fe=ne.nodeName)&&fe.toLowerCase()==="input"&&(ne.type==="checkbox"||ne.type==="radio")&&(me=Vm);if(me&&(me=me(e,K))){$c(ie,me,o,oe);break e}_e&&_e(e,ne,K),e==="focusout"&&(_e=ne._wrapperState)&&_e.controlled&&ne.type==="number"&&at(ne,"number",ne.value)}switch(_e=K?$r(K):window,e){case"focusin":(Dc(_e)||_e.contentEditable==="true")&&(Ar=_e,nu=K,Ao=null);break;case"focusout":Ao=nu=Ar=null;break;case"mousedown":ru=!0;break;case"contextmenu":case"mouseup":case"dragend":ru=!1,Yc(ie,o,oe);break;case"selectionchange":if(Wm)break;case"keydown":case"keyup":Yc(ie,o,oe)}var Se;if(Zl)e:{switch(e){case"compositionstart":var Ee="onCompositionStart";break e;case"compositionend":Ee="onCompositionEnd";break e;case"compositionupdate":Ee="onCompositionUpdate";break e}Ee=void 0}else jr?Lc(e,o)&&(Ee="onCompositionEnd"):e==="keydown"&&o.keyCode===229&&(Ee="onCompositionStart");Ee&&(zc&&o.locale!=="ko"&&(jr||Ee!=="onCompositionStart"?Ee==="onCompositionEnd"&&jr&&(Se=Nc()):(Nn=oe,Yl="value"in Nn?Nn.value:Nn.textContent,jr=!0)),_e=es(K,Ee),0<_e.length&&(Ee=new Tc(Ee,e,null,o,oe),ie.push({event:Ee,listeners:_e}),Se?Ee.data=Se:(Se=Rc(o),Se!==null&&(Ee.data=Se)))),(Se=Am?Lm(e,o):Rm(e,o))&&(K=es(K,"onBeforeInput"),0<K.length&&(oe=new Tc("onBeforeInput","beforeinput",null,o,oe),ie.push({event:oe,listeners:K}),oe.data=Se))}tf(ie,n)})}function Do(e,n,o){return{instance:e,listener:n,currentTarget:o}}function es(e,n){for(var o=n+"Capture",s=[];e!==null;){var f=e,h=f.stateNode;f.tag===5&&h!==null&&(f=h,h=Qn(e,o),h!=null&&s.unshift(Do(e,h,f)),h=Qn(e,n),h!=null&&s.push(Do(e,h,f))),e=e.return}return s}function Rr(e){if(e===null)return null;do e=e.return;while(e&&e.tag!==5);return e||null}function rf(e,n,o,s,f){for(var h=n._reactName,x=[];o!==null&&o!==s;){var I=o,R=I.alternate,K=I.stateNode;if(R!==null&&R===s)break;I.tag===5&&K!==null&&(I=K,f?(R=Qn(o,h),R!=null&&x.unshift(Do(o,R,I))):f||(R=Qn(o,h),R!=null&&x.push(Do(o,R,I)))),o=o.return}x.length!==0&&e.push({event:n,listeners:x})}var Km=/\r\n?/g,Gm=/\u0000|\uFFFD/g;function of(e){return(typeof e=="string"?e:""+e).replace(Km,`
+`).replace(Gm,"")}function ts(e,n,o){if(n=of(n),of(e)!==n&&o)throw Error(i(425))}function ns(){}var au=null,cu=null;function fu(e,n){return e==="textarea"||e==="noscript"||typeof n.children=="string"||typeof n.children=="number"||typeof n.dangerouslySetInnerHTML=="object"&&n.dangerouslySetInnerHTML!==null&&n.dangerouslySetInnerHTML.__html!=null}var du=typeof setTimeout=="function"?setTimeout:void 0,qm=typeof clearTimeout=="function"?clearTimeout:void 0,sf=typeof Promise=="function"?Promise:void 0,Zm=typeof queueMicrotask=="function"?queueMicrotask:typeof sf<"u"?function(e){return sf.resolve(null).then(e).catch(Jm)}:du;function Jm(e){setTimeout(function(){throw e})}function hu(e,n){var o=n,s=0;do{var f=o.nextSibling;if(e.removeChild(o),f&&f.nodeType===8)if(o=f.data,o==="/$"){if(s===0){e.removeChild(f),No(n);return}s--}else o!=="$"&&o!=="$?"&&o!=="$!"||s++;o=f}while(o);No(n)}function Pn(e){for(;e!=null;e=e.nextSibling){var n=e.nodeType;if(n===1||n===3)break;if(n===8){if(n=e.data,n==="$"||n==="$!"||n==="$?")break;if(n==="/$")return null}}return e}function lf(e){e=e.previousSibling;for(var n=0;e;){if(e.nodeType===8){var o=e.data;if(o==="$"||o==="$!"||o==="$?"){if(n===0)return e;n--}else o==="/$"&&n++}e=e.previousSibling}return null}var Dr=Math.random().toString(36).slice(2),qt="__reactFiber$"+Dr,$o="__reactProps$"+Dr,an="__reactContainer$"+Dr,pu="__reactEvents$"+Dr,e0="__reactListeners$"+Dr,t0="__reactHandles$"+Dr;function er(e){var n=e[qt];if(n)return n;for(var o=e.parentNode;o;){if(n=o[an]||o[qt]){if(o=n.alternate,n.child!==null||o!==null&&o.child!==null)for(e=lf(e);e!==null;){if(o=e[qt])return o;e=lf(e)}return n}e=o,o=e.parentNode}return null}function Oo(e){return e=e[qt]||e[an],!e||e.tag!==5&&e.tag!==6&&e.tag!==13&&e.tag!==3?null:e}function $r(e){if(e.tag===5||e.tag===6)return e.stateNode;throw Error(i(33))}function rs(e){return e[$o]||null}var gu=[],Or=-1;function Tn(e){return{current:e}}function Re(e){0>Or||(e.current=gu[Or],gu[Or]=null,Or--)}function Ae(e,n){Or++,gu[Or]=e.current,e.current=n}var In={},tt=Tn(In),ft=Tn(!1),tr=In;function Fr(e,n){var o=e.type.contextTypes;if(!o)return In;var s=e.stateNode;if(s&&s.__reactInternalMemoizedUnmaskedChildContext===n)return s.__reactInternalMemoizedMaskedChildContext;var f={},h;for(h in o)f[h]=n[h];return s&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=n,e.__reactInternalMemoizedMaskedChildContext=f),f}function dt(e){return e=e.childContextTypes,e!=null}function os(){Re(ft),Re(tt)}function uf(e,n,o){if(tt.current!==In)throw Error(i(168));Ae(tt,n),Ae(ft,o)}function af(e,n,o){var s=e.stateNode;if(n=n.childContextTypes,typeof s.getChildContext!="function")return o;s=s.getChildContext();for(var f in s)if(!(f in n))throw Error(i(108,ae(e)||"Unknown",f));return O({},o,s)}function is(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||In,tr=tt.current,Ae(tt,e),Ae(ft,ft.current),!0}function cf(e,n,o){var s=e.stateNode;if(!s)throw Error(i(169));o?(e=af(e,n,tr),s.__reactInternalMemoizedMergedChildContext=e,Re(ft),Re(tt),Ae(tt,e)):Re(ft),Ae(ft,o)}var cn=null,ss=!1,mu=!1;function ff(e){cn===null?cn=[e]:cn.push(e)}function n0(e){ss=!0,ff(e)}function zn(){if(!mu&&cn!==null){mu=!0;var e=0,n=je;try{var o=cn;for(je=1;e<o.length;e++){var s=o[e];do s=s(!0);while(s!==null)}cn=null,ss=!1}catch(f){throw cn!==null&&(cn=cn.slice(e+1)),Ri(wo,zn),f}finally{je=n,mu=!1}}return null}var Hr=[],Vr=0,ls=null,us=0,Tt=[],It=0,nr=null,fn=1,dn="";function rr(e,n){Hr[Vr++]=us,Hr[Vr++]=ls,ls=e,us=n}function df(e,n,o){Tt[It++]=fn,Tt[It++]=dn,Tt[It++]=nr,nr=e;var s=fn;e=dn;var f=32-wt(s)-1;s&=~(1<<f),o+=1;var h=32-wt(n)+f;if(30<h){var x=f-f%5;h=(s&(1<<x)-1).toString(32),s>>=x,f-=x,fn=1<<32-wt(n)+f|o<<f|s,dn=h+e}else fn=1<<h|o<<f|s,dn=e}function yu(e){e.return!==null&&(rr(e,1),df(e,1,0))}function vu(e){for(;e===ls;)ls=Hr[--Vr],Hr[Vr]=null,us=Hr[--Vr],Hr[Vr]=null;for(;e===nr;)nr=Tt[--It],Tt[It]=null,dn=Tt[--It],Tt[It]=null,fn=Tt[--It],Tt[It]=null}var St=null,kt=null,$e=!1,Dt=null;function hf(e,n){var o=Lt(5,null,null,0);o.elementType="DELETED",o.stateNode=n,o.return=e,n=e.deletions,n===null?(e.deletions=[o],e.flags|=16):n.push(o)}function pf(e,n){switch(e.tag){case 5:var o=e.type;return n=n.nodeType!==1||o.toLowerCase()!==n.nodeName.toLowerCase()?null:n,n!==null?(e.stateNode=n,St=e,kt=Pn(n.firstChild),!0):!1;case 6:return n=e.pendingProps===""||n.nodeType!==3?null:n,n!==null?(e.stateNode=n,St=e,kt=null,!0):!1;case 13:return n=n.nodeType!==8?null:n,n!==null?(o=nr!==null?{id:fn,overflow:dn}:null,e.memoizedState={dehydrated:n,treeContext:o,retryLane:1073741824},o=Lt(18,null,null,0),o.stateNode=n,o.return=e,e.child=o,St=e,kt=null,!0):!1;default:return!1}}function xu(e){return(e.mode&1)!==0&&(e.flags&128)===0}function wu(e){if($e){var n=kt;if(n){var o=n;if(!pf(e,n)){if(xu(e))throw Error(i(418));n=Pn(o.nextSibling);var s=St;n&&pf(e,n)?hf(s,o):(e.flags=e.flags&-4097|2,$e=!1,St=e)}}else{if(xu(e))throw Error(i(418));e.flags=e.flags&-4097|2,$e=!1,St=e}}}function gf(e){for(e=e.return;e!==null&&e.tag!==5&&e.tag!==3&&e.tag!==13;)e=e.return;St=e}function as(e){if(e!==St)return!1;if(!$e)return gf(e),$e=!0,!1;var n;if((n=e.tag!==3)&&!(n=e.tag!==5)&&(n=e.type,n=n!=="head"&&n!=="body"&&!fu(e.type,e.memoizedProps)),n&&(n=kt)){if(xu(e))throw mf(),Error(i(418));for(;n;)hf(e,n),n=Pn(n.nextSibling)}if(gf(e),e.tag===13){if(e=e.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(i(317));e:{for(e=e.nextSibling,n=0;e;){if(e.nodeType===8){var o=e.data;if(o==="/$"){if(n===0){kt=Pn(e.nextSibling);break e}n--}else o!=="$"&&o!=="$!"&&o!=="$?"||n++}e=e.nextSibling}kt=null}}else kt=St?Pn(e.stateNode.nextSibling):null;return!0}function mf(){for(var e=kt;e;)e=Pn(e.nextSibling)}function br(){kt=St=null,$e=!1}function _u(e){Dt===null?Dt=[e]:Dt.push(e)}var r0=T.ReactCurrentBatchConfig;function Fo(e,n,o){if(e=o.ref,e!==null&&typeof e!="function"&&typeof e!="object"){if(o._owner){if(o=o._owner,o){if(o.tag!==1)throw Error(i(309));var s=o.stateNode}if(!s)throw Error(i(147,e));var f=s,h=""+e;return n!==null&&n.ref!==null&&typeof n.ref=="function"&&n.ref._stringRef===h?n.ref:(n=function(x){var I=f.refs;x===null?delete I[h]:I[h]=x},n._stringRef=h,n)}if(typeof e!="string")throw Error(i(284));if(!o._owner)throw Error(i(290,e))}return e}function cs(e,n){throw e=Object.prototype.toString.call(n),Error(i(31,e==="[object Object]"?"object with keys {"+Object.keys(n).join(", ")+"}":e))}function yf(e){var n=e._init;return n(e._payload)}function vf(e){function n(W,$){if(e){var X=W.deletions;X===null?(W.deletions=[$],W.flags|=16):X.push($)}}function o(W,$){if(!e)return null;for(;$!==null;)n(W,$),$=$.sibling;return null}function s(W,$){for(W=new Map;$!==null;)$.key!==null?W.set($.key,$):W.set($.index,$),$=$.sibling;return W}function f(W,$){return W=Fn(W,$),W.index=0,W.sibling=null,W}function h(W,$,X){return W.index=X,e?(X=W.alternate,X!==null?(X=X.index,X<$?(W.flags|=2,$):X):(W.flags|=2,$)):(W.flags|=1048576,$)}function x(W){return e&&W.alternate===null&&(W.flags|=2),W}function I(W,$,X,le){return $===null||$.tag!==6?($=da(X,W.mode,le),$.return=W,$):($=f($,X),$.return=W,$)}function R(W,$,X,le){var me=X.type;return me===V?oe(W,$,X.props.children,le,X.key):$!==null&&($.elementType===me||typeof me=="object"&&me!==null&&me.$$typeof===H&&yf(me)===$.type)?(le=f($,X.props),le.ref=Fo(W,$,X),le.return=W,le):(le=Ls(X.type,X.key,X.props,null,W.mode,le),le.ref=Fo(W,$,X),le.return=W,le)}function K(W,$,X,le){return $===null||$.tag!==4||$.stateNode.containerInfo!==X.containerInfo||$.stateNode.implementation!==X.implementation?($=ha(X,W.mode,le),$.return=W,$):($=f($,X.children||[]),$.return=W,$)}function oe(W,$,X,le,me){return $===null||$.tag!==7?($=fr(X,W.mode,le,me),$.return=W,$):($=f($,X),$.return=W,$)}function ie(W,$,X){if(typeof $=="string"&&$!==""||typeof $=="number")return $=da(""+$,W.mode,X),$.return=W,$;if(typeof $=="object"&&$!==null){switch($.$$typeof){case F:return X=Ls($.type,$.key,$.props,null,W.mode,X),X.ref=Fo(W,null,$),X.return=W,X;case D:return $=ha($,W.mode,X),$.return=W,$;case H:var le=$._init;return ie(W,le($._payload),X)}if(Je($)||A($))return $=fr($,W.mode,X,null),$.return=W,$;cs(W,$)}return null}function ne(W,$,X,le){var me=$!==null?$.key:null;if(typeof X=="string"&&X!==""||typeof X=="number")return me!==null?null:I(W,$,""+X,le);if(typeof X=="object"&&X!==null){switch(X.$$typeof){case F:return X.key===me?R(W,$,X,le):null;case D:return X.key===me?K(W,$,X,le):null;case H:return me=X._init,ne(W,$,me(X._payload),le)}if(Je(X)||A(X))return me!==null?null:oe(W,$,X,le,null);cs(W,X)}return null}function fe(W,$,X,le,me){if(typeof le=="string"&&le!==""||typeof le=="number")return W=W.get(X)||null,I($,W,""+le,me);if(typeof le=="object"&&le!==null){switch(le.$$typeof){case F:return W=W.get(le.key===null?X:le.key)||null,R($,W,le,me);case D:return W=W.get(le.key===null?X:le.key)||null,K($,W,le,me);case H:var _e=le._init;return fe(W,$,X,_e(le._payload),me)}if(Je(le)||A(le))return W=W.get(X)||null,oe($,W,le,me,null);cs($,le)}return null}function he(W,$,X,le){for(var me=null,_e=null,Se=$,Ee=$=0,Ge=null;Se!==null&&Ee<X.length;Ee++){Se.index>Ee?(Ge=Se,Se=null):Ge=Se.sibling;var Ie=ne(W,Se,X[Ee],le);if(Ie===null){Se===null&&(Se=Ge);break}e&&Se&&Ie.alternate===null&&n(W,Se),$=h(Ie,$,Ee),_e===null?me=Ie:_e.sibling=Ie,_e=Ie,Se=Ge}if(Ee===X.length)return o(W,Se),$e&&rr(W,Ee),me;if(Se===null){for(;Ee<X.length;Ee++)Se=ie(W,X[Ee],le),Se!==null&&($=h(Se,$,Ee),_e===null?me=Se:_e.sibling=Se,_e=Se);return $e&&rr(W,Ee),me}for(Se=s(W,Se);Ee<X.length;Ee++)Ge=fe(Se,W,Ee,X[Ee],le),Ge!==null&&(e&&Ge.alternate!==null&&Se.delete(Ge.key===null?Ee:Ge.key),$=h(Ge,$,Ee),_e===null?me=Ge:_e.sibling=Ge,_e=Ge);return e&&Se.forEach(function(Hn){return n(W,Hn)}),$e&&rr(W,Ee),me}function ge(W,$,X,le){var me=A(X);if(typeof me!="function")throw Error(i(150));if(X=me.call(X),X==null)throw Error(i(151));for(var _e=me=null,Se=$,Ee=$=0,Ge=null,Ie=X.next();Se!==null&&!Ie.done;Ee++,Ie=X.next()){Se.index>Ee?(Ge=Se,Se=null):Ge=Se.sibling;var Hn=ne(W,Se,Ie.value,le);if(Hn===null){Se===null&&(Se=Ge);break}e&&Se&&Hn.alternate===null&&n(W,Se),$=h(Hn,$,Ee),_e===null?me=Hn:_e.sibling=Hn,_e=Hn,Se=Ge}if(Ie.done)return o(W,Se),$e&&rr(W,Ee),me;if(Se===null){for(;!Ie.done;Ee++,Ie=X.next())Ie=ie(W,Ie.value,le),Ie!==null&&($=h(Ie,$,Ee),_e===null?me=Ie:_e.sibling=Ie,_e=Ie);return $e&&rr(W,Ee),me}for(Se=s(W,Se);!Ie.done;Ee++,Ie=X.next())Ie=fe(Se,W,Ee,Ie.value,le),Ie!==null&&(e&&Ie.alternate!==null&&Se.delete(Ie.key===null?Ee:Ie.key),$=h(Ie,$,Ee),_e===null?me=Ie:_e.sibling=Ie,_e=Ie);return e&&Se.forEach(function(D0){return n(W,D0)}),$e&&rr(W,Ee),me}function Be(W,$,X,le){if(typeof X=="object"&&X!==null&&X.type===V&&X.key===null&&(X=X.props.children),typeof X=="object"&&X!==null){switch(X.$$typeof){case F:e:{for(var me=X.key,_e=$;_e!==null;){if(_e.key===me){if(me=X.type,me===V){if(_e.tag===7){o(W,_e.sibling),$=f(_e,X.props.children),$.return=W,W=$;break e}}else if(_e.elementType===me||typeof me=="object"&&me!==null&&me.$$typeof===H&&yf(me)===_e.type){o(W,_e.sibling),$=f(_e,X.props),$.ref=Fo(W,_e,X),$.return=W,W=$;break e}o(W,_e);break}else n(W,_e);_e=_e.sibling}X.type===V?($=fr(X.props.children,W.mode,le,X.key),$.return=W,W=$):(le=Ls(X.type,X.key,X.props,null,W.mode,le),le.ref=Fo(W,$,X),le.return=W,W=le)}return x(W);case D:e:{for(_e=X.key;$!==null;){if($.key===_e)if($.tag===4&&$.stateNode.containerInfo===X.containerInfo&&$.stateNode.implementation===X.implementation){o(W,$.sibling),$=f($,X.children||[]),$.return=W,W=$;break e}else{o(W,$);break}else n(W,$);$=$.sibling}$=ha(X,W.mode,le),$.return=W,W=$}return x(W);case H:return _e=X._init,Be(W,$,_e(X._payload),le)}if(Je(X))return he(W,$,X,le);if(A(X))return ge(W,$,X,le);cs(W,X)}return typeof X=="string"&&X!==""||typeof X=="number"?(X=""+X,$!==null&&$.tag===6?(o(W,$.sibling),$=f($,X),$.return=W,W=$):(o(W,$),$=da(X,W.mode,le),$.return=W,W=$),x(W)):o(W,$)}return Be}var Br=vf(!0),xf=vf(!1),fs=Tn(null),ds=null,Ur=null,Su=null;function ku(){Su=Ur=ds=null}function Eu(e){var n=fs.current;Re(fs),e._currentValue=n}function Cu(e,n,o){for(;e!==null;){var s=e.alternate;if((e.childLanes&n)!==n?(e.childLanes|=n,s!==null&&(s.childLanes|=n)):s!==null&&(s.childLanes&n)!==n&&(s.childLanes|=n),e===o)break;e=e.return}}function Wr(e,n){ds=e,Su=Ur=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&n)!==0&&(ht=!0),e.firstContext=null)}function zt(e){var n=e._currentValue;if(Su!==e)if(e={context:e,memoizedValue:n,next:null},Ur===null){if(ds===null)throw Error(i(308));Ur=e,ds.dependencies={lanes:0,firstContext:e}}else Ur=Ur.next=e;return n}var or=null;function Nu(e){or===null?or=[e]:or.push(e)}function wf(e,n,o,s){var f=n.interleaved;return f===null?(o.next=o,Nu(n)):(o.next=f.next,f.next=o),n.interleaved=o,hn(e,s)}function hn(e,n){e.lanes|=n;var o=e.alternate;for(o!==null&&(o.lanes|=n),o=e,e=e.return;e!==null;)e.childLanes|=n,o=e.alternate,o!==null&&(o.childLanes|=n),o=e,e=e.return;return o.tag===3?o.stateNode:null}var jn=!1;function Mu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function _f(e,n){e=e.updateQueue,n.updateQueue===e&&(n.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function pn(e,n){return{eventTime:e,lane:n,tag:0,payload:null,callback:null,next:null}}function An(e,n,o){var s=e.updateQueue;if(s===null)return null;if(s=s.shared,(Pe&2)!==0){var f=s.pending;return f===null?n.next=n:(n.next=f.next,f.next=n),s.pending=n,hn(e,o)}return f=s.interleaved,f===null?(n.next=n,Nu(s)):(n.next=f.next,f.next=n),s.interleaved=n,hn(e,o)}function hs(e,n,o){if(n=n.updateQueue,n!==null&&(n=n.shared,(o&4194240)!==0)){var s=n.lanes;s&=e.pendingLanes,o|=s,n.lanes=o,Tr(e,o)}}function Sf(e,n){var o=e.updateQueue,s=e.alternate;if(s!==null&&(s=s.updateQueue,o===s)){var f=null,h=null;if(o=o.firstBaseUpdate,o!==null){do{var x={eventTime:o.eventTime,lane:o.lane,tag:o.tag,payload:o.payload,callback:o.callback,next:null};h===null?f=h=x:h=h.next=x,o=o.next}while(o!==null);h===null?f=h=n:h=h.next=n}else f=h=n;o={baseState:s.baseState,firstBaseUpdate:f,lastBaseUpdate:h,shared:s.shared,effects:s.effects},e.updateQueue=o;return}e=o.lastBaseUpdate,e===null?o.firstBaseUpdate=n:e.next=n,o.lastBaseUpdate=n}function ps(e,n,o,s){var f=e.updateQueue;jn=!1;var h=f.firstBaseUpdate,x=f.lastBaseUpdate,I=f.shared.pending;if(I!==null){f.shared.pending=null;var R=I,K=R.next;R.next=null,x===null?h=K:x.next=K,x=R;var oe=e.alternate;oe!==null&&(oe=oe.updateQueue,I=oe.lastBaseUpdate,I!==x&&(I===null?oe.firstBaseUpdate=K:I.next=K,oe.lastBaseUpdate=R))}if(h!==null){var ie=f.baseState;x=0,oe=K=R=null,I=h;do{var ne=I.lane,fe=I.eventTime;if((s&ne)===ne){oe!==null&&(oe=oe.next={eventTime:fe,lane:0,tag:I.tag,payload:I.payload,callback:I.callback,next:null});e:{var he=e,ge=I;switch(ne=n,fe=o,ge.tag){case 1:if(he=ge.payload,typeof he=="function"){ie=he.call(fe,ie,ne);break e}ie=he;break e;case 3:he.flags=he.flags&-65537|128;case 0:if(he=ge.payload,ne=typeof he=="function"?he.call(fe,ie,ne):he,ne==null)break e;ie=O({},ie,ne);break e;case 2:jn=!0}}I.callback!==null&&I.lane!==0&&(e.flags|=64,ne=f.effects,ne===null?f.effects=[I]:ne.push(I))}else fe={eventTime:fe,lane:ne,tag:I.tag,payload:I.payload,callback:I.callback,next:null},oe===null?(K=oe=fe,R=ie):oe=oe.next=fe,x|=ne;if(I=I.next,I===null){if(I=f.shared.pending,I===null)break;ne=I,I=ne.next,ne.next=null,f.lastBaseUpdate=ne,f.shared.pending=null}}while(!0);if(oe===null&&(R=ie),f.baseState=R,f.firstBaseUpdate=K,f.lastBaseUpdate=oe,n=f.shared.interleaved,n!==null){f=n;do x|=f.lane,f=f.next;while(f!==n)}else h===null&&(f.shared.lanes=0);lr|=x,e.lanes=x,e.memoizedState=ie}}function kf(e,n,o){if(e=n.effects,n.effects=null,e!==null)for(n=0;n<e.length;n++){var s=e[n],f=s.callback;if(f!==null){if(s.callback=null,s=o,typeof f!="function")throw Error(i(191,f));f.call(s)}}}var Ho={},Zt=Tn(Ho),Vo=Tn(Ho),bo=Tn(Ho);function ir(e){if(e===Ho)throw Error(i(174));return e}function Pu(e,n){switch(Ae(bo,n),Ae(Vo,e),Ae(Zt,Ho),e=n.nodeType,e){case 9:case 11:n=(n=n.documentElement)?n.namespaceURI:vt(null,"");break;default:e=e===8?n.parentNode:n,n=e.namespaceURI||null,e=e.tagName,n=vt(n,e)}Re(Zt),Ae(Zt,n)}function Yr(){Re(Zt),Re(Vo),Re(bo)}function Ef(e){ir(bo.current);var n=ir(Zt.current),o=vt(n,e.type);n!==o&&(Ae(Vo,e),Ae(Zt,o))}function Tu(e){Vo.current===e&&(Re(Zt),Re(Vo))}var Fe=Tn(0);function gs(e){for(var n=e;n!==null;){if(n.tag===13){var o=n.memoizedState;if(o!==null&&(o=o.dehydrated,o===null||o.data==="$?"||o.data==="$!"))return n}else if(n.tag===19&&n.memoizedProps.revealOrder!==void 0){if((n.flags&128)!==0)return n}else if(n.child!==null){n.child.return=n,n=n.child;continue}if(n===e)break;for(;n.sibling===null;){if(n.return===null||n.return===e)return null;n=n.return}n.sibling.return=n.return,n=n.sibling}return null}var Iu=[];function zu(){for(var e=0;e<Iu.length;e++)Iu[e]._workInProgressVersionPrimary=null;Iu.length=0}var ms=T.ReactCurrentDispatcher,ju=T.ReactCurrentBatchConfig,sr=0,He=null,Ye=null,Qe=null,ys=!1,Bo=!1,Uo=0,o0=0;function nt(){throw Error(i(321))}function Au(e,n){if(n===null)return!1;for(var o=0;o<n.length&&o<e.length;o++)if(!Rt(e[o],n[o]))return!1;return!0}function Lu(e,n,o,s,f,h){if(sr=h,He=n,n.memoizedState=null,n.updateQueue=null,n.lanes=0,ms.current=e===null||e.memoizedState===null?u0:a0,e=o(s,f),Bo){h=0;do{if(Bo=!1,Uo=0,25<=h)throw Error(i(301));h+=1,Qe=Ye=null,n.updateQueue=null,ms.current=c0,e=o(s,f)}while(Bo)}if(ms.current=ws,n=Ye!==null&&Ye.next!==null,sr=0,Qe=Ye=He=null,ys=!1,n)throw Error(i(300));return e}function Ru(){var e=Uo!==0;return Uo=0,e}function Jt(){var e={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};return Qe===null?He.memoizedState=Qe=e:Qe=Qe.next=e,Qe}function jt(){if(Ye===null){var e=He.alternate;e=e!==null?e.memoizedState:null}else e=Ye.next;var n=Qe===null?He.memoizedState:Qe.next;if(n!==null)Qe=n,Ye=e;else{if(e===null)throw Error(i(310));Ye=e,e={memoizedState:Ye.memoizedState,baseState:Ye.baseState,baseQueue:Ye.baseQueue,queue:Ye.queue,next:null},Qe===null?He.memoizedState=Qe=e:Qe=Qe.next=e}return Qe}function Wo(e,n){return typeof n=="function"?n(e):n}function Du(e){var n=jt(),o=n.queue;if(o===null)throw Error(i(311));o.lastRenderedReducer=e;var s=Ye,f=s.baseQueue,h=o.pending;if(h!==null){if(f!==null){var x=f.next;f.next=h.next,h.next=x}s.baseQueue=f=h,o.pending=null}if(f!==null){h=f.next,s=s.baseState;var I=x=null,R=null,K=h;do{var oe=K.lane;if((sr&oe)===oe)R!==null&&(R=R.next={lane:0,action:K.action,hasEagerState:K.hasEagerState,eagerState:K.eagerState,next:null}),s=K.hasEagerState?K.eagerState:e(s,K.action);else{var ie={lane:oe,action:K.action,hasEagerState:K.hasEagerState,eagerState:K.eagerState,next:null};R===null?(I=R=ie,x=s):R=R.next=ie,He.lanes|=oe,lr|=oe}K=K.next}while(K!==null&&K!==h);R===null?x=s:R.next=I,Rt(s,n.memoizedState)||(ht=!0),n.memoizedState=s,n.baseState=x,n.baseQueue=R,o.lastRenderedState=s}if(e=o.interleaved,e!==null){f=e;do h=f.lane,He.lanes|=h,lr|=h,f=f.next;while(f!==e)}else f===null&&(o.lanes=0);return[n.memoizedState,o.dispatch]}function $u(e){var n=jt(),o=n.queue;if(o===null)throw Error(i(311));o.lastRenderedReducer=e;var s=o.dispatch,f=o.pending,h=n.memoizedState;if(f!==null){o.pending=null;var x=f=f.next;do h=e(h,x.action),x=x.next;while(x!==f);Rt(h,n.memoizedState)||(ht=!0),n.memoizedState=h,n.baseQueue===null&&(n.baseState=h),o.lastRenderedState=h}return[h,s]}function Cf(){}function Nf(e,n){var o=He,s=jt(),f=n(),h=!Rt(s.memoizedState,f);if(h&&(s.memoizedState=f,ht=!0),s=s.queue,Ou(Tf.bind(null,o,s,e),[e]),s.getSnapshot!==n||h||Qe!==null&&Qe.memoizedState.tag&1){if(o.flags|=2048,Yo(9,Pf.bind(null,o,s,f,n),void 0,null),Ke===null)throw Error(i(349));(sr&30)!==0||Mf(o,n,f)}return f}function Mf(e,n,o){e.flags|=16384,e={getSnapshot:n,value:o},n=He.updateQueue,n===null?(n={lastEffect:null,stores:null},He.updateQueue=n,n.stores=[e]):(o=n.stores,o===null?n.stores=[e]:o.push(e))}function Pf(e,n,o,s){n.value=o,n.getSnapshot=s,If(n)&&zf(e)}function Tf(e,n,o){return o(function(){If(n)&&zf(e)})}function If(e){var n=e.getSnapshot;e=e.value;try{var o=n();return!Rt(e,o)}catch{return!0}}function zf(e){var n=hn(e,1);n!==null&&Ht(n,e,1,-1)}function jf(e){var n=Jt();return typeof e=="function"&&(e=e()),n.memoizedState=n.baseState=e,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:Wo,lastRenderedState:e},n.queue=e,e=e.dispatch=l0.bind(null,He,e),[n.memoizedState,e]}function Yo(e,n,o,s){return e={tag:e,create:n,destroy:o,deps:s,next:null},n=He.updateQueue,n===null?(n={lastEffect:null,stores:null},He.updateQueue=n,n.lastEffect=e.next=e):(o=n.lastEffect,o===null?n.lastEffect=e.next=e:(s=o.next,o.next=e,e.next=s,n.lastEffect=e)),e}function Af(){return jt().memoizedState}function vs(e,n,o,s){var f=Jt();He.flags|=e,f.memoizedState=Yo(1|n,o,void 0,s===void 0?null:s)}function xs(e,n,o,s){var f=jt();s=s===void 0?null:s;var h=void 0;if(Ye!==null){var x=Ye.memoizedState;if(h=x.destroy,s!==null&&Au(s,x.deps)){f.memoizedState=Yo(n,o,h,s);return}}He.flags|=e,f.memoizedState=Yo(1|n,o,h,s)}function Lf(e,n){return vs(8390656,8,e,n)}function Ou(e,n){return xs(2048,8,e,n)}function Rf(e,n){return xs(4,2,e,n)}function Df(e,n){return xs(4,4,e,n)}function $f(e,n){if(typeof n=="function")return e=e(),n(e),function(){n(null)};if(n!=null)return e=e(),n.current=e,function(){n.current=null}}function Of(e,n,o){return o=o!=null?o.concat([e]):null,xs(4,4,$f.bind(null,n,e),o)}function Fu(){}function Ff(e,n){var o=jt();n=n===void 0?null:n;var s=o.memoizedState;return s!==null&&n!==null&&Au(n,s[1])?s[0]:(o.memoizedState=[e,n],e)}function Hf(e,n){var o=jt();n=n===void 0?null:n;var s=o.memoizedState;return s!==null&&n!==null&&Au(n,s[1])?s[0]:(e=e(),o.memoizedState=[e,n],e)}function Vf(e,n,o){return(sr&21)===0?(e.baseState&&(e.baseState=!1,ht=!0),e.memoizedState=o):(Rt(o,n)||(o=Vi(),He.lanes|=o,lr|=o,e.baseState=!0),n)}function i0(e,n){var o=je;je=o!==0&&4>o?o:4,e(!0);var s=ju.transition;ju.transition={};try{e(!1),n()}finally{je=o,ju.transition=s}}function bf(){return jt().memoizedState}function s0(e,n,o){var s=$n(e);if(o={lane:s,action:o,hasEagerState:!1,eagerState:null,next:null},Bf(e))Uf(n,o);else if(o=wf(e,n,o,s),o!==null){var f=lt();Ht(o,e,s,f),Wf(o,n,s)}}function l0(e,n,o){var s=$n(e),f={lane:s,action:o,hasEagerState:!1,eagerState:null,next:null};if(Bf(e))Uf(n,f);else{var h=e.alternate;if(e.lanes===0&&(h===null||h.lanes===0)&&(h=n.lastRenderedReducer,h!==null))try{var x=n.lastRenderedState,I=h(x,o);if(f.hasEagerState=!0,f.eagerState=I,Rt(I,x)){var R=n.interleaved;R===null?(f.next=f,Nu(n)):(f.next=R.next,R.next=f),n.interleaved=f;return}}catch{}finally{}o=wf(e,n,f,s),o!==null&&(f=lt(),Ht(o,e,s,f),Wf(o,n,s))}}function Bf(e){var n=e.alternate;return e===He||n!==null&&n===He}function Uf(e,n){Bo=ys=!0;var o=e.pending;o===null?n.next=n:(n.next=o.next,o.next=n),e.pending=n}function Wf(e,n,o){if((o&4194240)!==0){var s=n.lanes;s&=e.pendingLanes,o|=s,n.lanes=o,Tr(e,o)}}var ws={readContext:zt,useCallback:nt,useContext:nt,useEffect:nt,useImperativeHandle:nt,useInsertionEffect:nt,useLayoutEffect:nt,useMemo:nt,useReducer:nt,useRef:nt,useState:nt,useDebugValue:nt,useDeferredValue:nt,useTransition:nt,useMutableSource:nt,useSyncExternalStore:nt,useId:nt,unstable_isNewReconciler:!1},u0={readContext:zt,useCallback:function(e,n){return Jt().memoizedState=[e,n===void 0?null:n],e},useContext:zt,useEffect:Lf,useImperativeHandle:function(e,n,o){return o=o!=null?o.concat([e]):null,vs(4194308,4,$f.bind(null,n,e),o)},useLayoutEffect:function(e,n){return vs(4194308,4,e,n)},useInsertionEffect:function(e,n){return vs(4,2,e,n)},useMemo:function(e,n){var o=Jt();return n=n===void 0?null:n,e=e(),o.memoizedState=[e,n],e},useReducer:function(e,n,o){var s=Jt();return n=o!==void 0?o(n):n,s.memoizedState=s.baseState=n,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:n},s.queue=e,e=e.dispatch=s0.bind(null,He,e),[s.memoizedState,e]},useRef:function(e){var n=Jt();return e={current:e},n.memoizedState=e},useState:jf,useDebugValue:Fu,useDeferredValue:function(e){return Jt().memoizedState=e},useTransition:function(){var e=jf(!1),n=e[0];return e=i0.bind(null,e[1]),Jt().memoizedState=e,[n,e]},useMutableSource:function(){},useSyncExternalStore:function(e,n,o){var s=He,f=Jt();if($e){if(o===void 0)throw Error(i(407));o=o()}else{if(o=n(),Ke===null)throw Error(i(349));(sr&30)!==0||Mf(s,n,o)}f.memoizedState=o;var h={value:o,getSnapshot:n};return f.queue=h,Lf(Tf.bind(null,s,h,e),[e]),s.flags|=2048,Yo(9,Pf.bind(null,s,h,o,n),void 0,null),o},useId:function(){var e=Jt(),n=Ke.identifierPrefix;if($e){var o=dn,s=fn;o=(s&~(1<<32-wt(s)-1)).toString(32)+o,n=":"+n+"R"+o,o=Uo++,0<o&&(n+="H"+o.toString(32)),n+=":"}else o=o0++,n=":"+n+"r"+o.toString(32)+":";return e.memoizedState=n},unstable_isNewReconciler:!1},a0={readContext:zt,useCallback:Ff,useContext:zt,useEffect:Ou,useImperativeHandle:Of,useInsertionEffect:Rf,useLayoutEffect:Df,useMemo:Hf,useReducer:Du,useRef:Af,useState:function(){return Du(Wo)},useDebugValue:Fu,useDeferredValue:function(e){var n=jt();return Vf(n,Ye.memoizedState,e)},useTransition:function(){var e=Du(Wo)[0],n=jt().memoizedState;return[e,n]},useMutableSource:Cf,useSyncExternalStore:Nf,useId:bf,unstable_isNewReconciler:!1},c0={readContext:zt,useCallback:Ff,useContext:zt,useEffect:Ou,useImperativeHandle:Of,useInsertionEffect:Rf,useLayoutEffect:Df,useMemo:Hf,useReducer:$u,useRef:Af,useState:function(){return $u(Wo)},useDebugValue:Fu,useDeferredValue:function(e){var n=jt();return Ye===null?n.memoizedState=e:Vf(n,Ye.memoizedState,e)},useTransition:function(){var e=$u(Wo)[0],n=jt().memoizedState;return[e,n]},useMutableSource:Cf,useSyncExternalStore:Nf,useId:bf,unstable_isNewReconciler:!1};function $t(e,n){if(e&&e.defaultProps){n=O({},n),e=e.defaultProps;for(var o in e)n[o]===void 0&&(n[o]=e[o]);return n}return n}function Hu(e,n,o,s){n=e.memoizedState,o=o(s,n),o=o==null?n:O({},n,o),e.memoizedState=o,e.lanes===0&&(e.updateQueue.baseState=o)}var _s={isMounted:function(e){return(e=e._reactInternals)?Gt(e)===e:!1},enqueueSetState:function(e,n,o){e=e._reactInternals;var s=lt(),f=$n(e),h=pn(s,f);h.payload=n,o!=null&&(h.callback=o),n=An(e,h,f),n!==null&&(Ht(n,e,f,s),hs(n,e,f))},enqueueReplaceState:function(e,n,o){e=e._reactInternals;var s=lt(),f=$n(e),h=pn(s,f);h.tag=1,h.payload=n,o!=null&&(h.callback=o),n=An(e,h,f),n!==null&&(Ht(n,e,f,s),hs(n,e,f))},enqueueForceUpdate:function(e,n){e=e._reactInternals;var o=lt(),s=$n(e),f=pn(o,s);f.tag=2,n!=null&&(f.callback=n),n=An(e,f,s),n!==null&&(Ht(n,e,s,o),hs(n,e,s))}};function Yf(e,n,o,s,f,h,x){return e=e.stateNode,typeof e.shouldComponentUpdate=="function"?e.shouldComponentUpdate(s,h,x):n.prototype&&n.prototype.isPureReactComponent?!jo(o,s)||!jo(f,h):!0}function Xf(e,n,o){var s=!1,f=In,h=n.contextType;return typeof h=="object"&&h!==null?h=zt(h):(f=dt(n)?tr:tt.current,s=n.contextTypes,h=(s=s!=null)?Fr(e,f):In),n=new n(o,h),e.memoizedState=n.state!==null&&n.state!==void 0?n.state:null,n.updater=_s,e.stateNode=n,n._reactInternals=e,s&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=f,e.__reactInternalMemoizedMaskedChildContext=h),n}function Qf(e,n,o,s){e=n.state,typeof n.componentWillReceiveProps=="function"&&n.componentWillReceiveProps(o,s),typeof n.UNSAFE_componentWillReceiveProps=="function"&&n.UNSAFE_componentWillReceiveProps(o,s),n.state!==e&&_s.enqueueReplaceState(n,n.state,null)}function Vu(e,n,o,s){var f=e.stateNode;f.props=o,f.state=e.memoizedState,f.refs={},Mu(e);var h=n.contextType;typeof h=="object"&&h!==null?f.context=zt(h):(h=dt(n)?tr:tt.current,f.context=Fr(e,h)),f.state=e.memoizedState,h=n.getDerivedStateFromProps,typeof h=="function"&&(Hu(e,n,h,o),f.state=e.memoizedState),typeof n.getDerivedStateFromProps=="function"||typeof f.getSnapshotBeforeUpdate=="function"||typeof f.UNSAFE_componentWillMount!="function"&&typeof f.componentWillMount!="function"||(n=f.state,typeof f.componentWillMount=="function"&&f.componentWillMount(),typeof f.UNSAFE_componentWillMount=="function"&&f.UNSAFE_componentWillMount(),n!==f.state&&_s.enqueueReplaceState(f,f.state,null),ps(e,o,f,s),f.state=e.memoizedState),typeof f.componentDidMount=="function"&&(e.flags|=4194308)}function Xr(e,n){try{var o="",s=n;do o+=ue(s),s=s.return;while(s);var f=o}catch(h){f=`
+Error generating stack: `+h.message+`
+`+h.stack}return{value:e,source:n,stack:f,digest:null}}function bu(e,n,o){return{value:e,source:null,stack:o??null,digest:n??null}}function Bu(e,n){try{console.error(n.value)}catch(o){setTimeout(function(){throw o})}}var f0=typeof WeakMap=="function"?WeakMap:Map;function Kf(e,n,o){o=pn(-1,o),o.tag=3,o.payload={element:null};var s=n.value;return o.callback=function(){Ps||(Ps=!0,oa=s),Bu(e,n)},o}function Gf(e,n,o){o=pn(-1,o),o.tag=3;var s=e.type.getDerivedStateFromError;if(typeof s=="function"){var f=n.value;o.payload=function(){return s(f)},o.callback=function(){Bu(e,n)}}var h=e.stateNode;return h!==null&&typeof h.componentDidCatch=="function"&&(o.callback=function(){Bu(e,n),typeof s!="function"&&(Rn===null?Rn=new Set([this]):Rn.add(this));var x=n.stack;this.componentDidCatch(n.value,{componentStack:x!==null?x:""})}),o}function qf(e,n,o){var s=e.pingCache;if(s===null){s=e.pingCache=new f0;var f=new Set;s.set(n,f)}else f=s.get(n),f===void 0&&(f=new Set,s.set(n,f));f.has(o)||(f.add(o),e=C0.bind(null,e,n,o),n.then(e,e))}function Zf(e){do{var n;if((n=e.tag===13)&&(n=e.memoizedState,n=n!==null?n.dehydrated!==null:!0),n)return e;e=e.return}while(e!==null);return null}function Jf(e,n,o,s,f){return(e.mode&1)===0?(e===n?e.flags|=65536:(e.flags|=128,o.flags|=131072,o.flags&=-52805,o.tag===1&&(o.alternate===null?o.tag=17:(n=pn(-1,1),n.tag=2,An(o,n,1))),o.lanes|=1),e):(e.flags|=65536,e.lanes=f,e)}var d0=T.ReactCurrentOwner,ht=!1;function st(e,n,o,s){n.child=e===null?xf(n,null,o,s):Br(n,e.child,o,s)}function ed(e,n,o,s,f){o=o.render;var h=n.ref;return Wr(n,f),s=Lu(e,n,o,s,h,f),o=Ru(),e!==null&&!ht?(n.updateQueue=e.updateQueue,n.flags&=-2053,e.lanes&=~f,gn(e,n,f)):($e&&o&&yu(n),n.flags|=1,st(e,n,s,f),n.child)}function td(e,n,o,s,f){if(e===null){var h=o.type;return typeof h=="function"&&!fa(h)&&h.defaultProps===void 0&&o.compare===null&&o.defaultProps===void 0?(n.tag=15,n.type=h,nd(e,n,h,s,f)):(e=Ls(o.type,null,s,n,n.mode,f),e.ref=n.ref,e.return=n,n.child=e)}if(h=e.child,(e.lanes&f)===0){var x=h.memoizedProps;if(o=o.compare,o=o!==null?o:jo,o(x,s)&&e.ref===n.ref)return gn(e,n,f)}return n.flags|=1,e=Fn(h,s),e.ref=n.ref,e.return=n,n.child=e}function nd(e,n,o,s,f){if(e!==null){var h=e.memoizedProps;if(jo(h,s)&&e.ref===n.ref)if(ht=!1,n.pendingProps=s=h,(e.lanes&f)!==0)(e.flags&131072)!==0&&(ht=!0);else return n.lanes=e.lanes,gn(e,n,f)}return Uu(e,n,o,s,f)}function rd(e,n,o){var s=n.pendingProps,f=s.children,h=e!==null?e.memoizedState:null;if(s.mode==="hidden")if((n.mode&1)===0)n.memoizedState={baseLanes:0,cachePool:null,transitions:null},Ae(Kr,Et),Et|=o;else{if((o&1073741824)===0)return e=h!==null?h.baseLanes|o:o,n.lanes=n.childLanes=1073741824,n.memoizedState={baseLanes:e,cachePool:null,transitions:null},n.updateQueue=null,Ae(Kr,Et),Et|=e,null;n.memoizedState={baseLanes:0,cachePool:null,transitions:null},s=h!==null?h.baseLanes:o,Ae(Kr,Et),Et|=s}else h!==null?(s=h.baseLanes|o,n.memoizedState=null):s=o,Ae(Kr,Et),Et|=s;return st(e,n,f,o),n.child}function od(e,n){var o=n.ref;(e===null&&o!==null||e!==null&&e.ref!==o)&&(n.flags|=512,n.flags|=2097152)}function Uu(e,n,o,s,f){var h=dt(o)?tr:tt.current;return h=Fr(n,h),Wr(n,f),o=Lu(e,n,o,s,h,f),s=Ru(),e!==null&&!ht?(n.updateQueue=e.updateQueue,n.flags&=-2053,e.lanes&=~f,gn(e,n,f)):($e&&s&&yu(n),n.flags|=1,st(e,n,o,f),n.child)}function id(e,n,o,s,f){if(dt(o)){var h=!0;is(n)}else h=!1;if(Wr(n,f),n.stateNode===null)ks(e,n),Xf(n,o,s),Vu(n,o,s,f),s=!0;else if(e===null){var x=n.stateNode,I=n.memoizedProps;x.props=I;var R=x.context,K=o.contextType;typeof K=="object"&&K!==null?K=zt(K):(K=dt(o)?tr:tt.current,K=Fr(n,K));var oe=o.getDerivedStateFromProps,ie=typeof oe=="function"||typeof x.getSnapshotBeforeUpdate=="function";ie||typeof x.UNSAFE_componentWillReceiveProps!="function"&&typeof x.componentWillReceiveProps!="function"||(I!==s||R!==K)&&Qf(n,x,s,K),jn=!1;var ne=n.memoizedState;x.state=ne,ps(n,s,x,f),R=n.memoizedState,I!==s||ne!==R||ft.current||jn?(typeof oe=="function"&&(Hu(n,o,oe,s),R=n.memoizedState),(I=jn||Yf(n,o,I,s,ne,R,K))?(ie||typeof x.UNSAFE_componentWillMount!="function"&&typeof x.componentWillMount!="function"||(typeof x.componentWillMount=="function"&&x.componentWillMount(),typeof x.UNSAFE_componentWillMount=="function"&&x.UNSAFE_componentWillMount()),typeof x.componentDidMount=="function"&&(n.flags|=4194308)):(typeof x.componentDidMount=="function"&&(n.flags|=4194308),n.memoizedProps=s,n.memoizedState=R),x.props=s,x.state=R,x.context=K,s=I):(typeof x.componentDidMount=="function"&&(n.flags|=4194308),s=!1)}else{x=n.stateNode,_f(e,n),I=n.memoizedProps,K=n.type===n.elementType?I:$t(n.type,I),x.props=K,ie=n.pendingProps,ne=x.context,R=o.contextType,typeof R=="object"&&R!==null?R=zt(R):(R=dt(o)?tr:tt.current,R=Fr(n,R));var fe=o.getDerivedStateFromProps;(oe=typeof fe=="function"||typeof x.getSnapshotBeforeUpdate=="function")||typeof x.UNSAFE_componentWillReceiveProps!="function"&&typeof x.componentWillReceiveProps!="function"||(I!==ie||ne!==R)&&Qf(n,x,s,R),jn=!1,ne=n.memoizedState,x.state=ne,ps(n,s,x,f);var he=n.memoizedState;I!==ie||ne!==he||ft.current||jn?(typeof fe=="function"&&(Hu(n,o,fe,s),he=n.memoizedState),(K=jn||Yf(n,o,K,s,ne,he,R)||!1)?(oe||typeof x.UNSAFE_componentWillUpdate!="function"&&typeof x.componentWillUpdate!="function"||(typeof x.componentWillUpdate=="function"&&x.componentWillUpdate(s,he,R),typeof x.UNSAFE_componentWillUpdate=="function"&&x.UNSAFE_componentWillUpdate(s,he,R)),typeof x.componentDidUpdate=="function"&&(n.flags|=4),typeof x.getSnapshotBeforeUpdate=="function"&&(n.flags|=1024)):(typeof x.componentDidUpdate!="function"||I===e.memoizedProps&&ne===e.memoizedState||(n.flags|=4),typeof x.getSnapshotBeforeUpdate!="function"||I===e.memoizedProps&&ne===e.memoizedState||(n.flags|=1024),n.memoizedProps=s,n.memoizedState=he),x.props=s,x.state=he,x.context=R,s=K):(typeof x.componentDidUpdate!="function"||I===e.memoizedProps&&ne===e.memoizedState||(n.flags|=4),typeof x.getSnapshotBeforeUpdate!="function"||I===e.memoizedProps&&ne===e.memoizedState||(n.flags|=1024),s=!1)}return Wu(e,n,o,s,h,f)}function Wu(e,n,o,s,f,h){od(e,n);var x=(n.flags&128)!==0;if(!s&&!x)return f&&cf(n,o,!1),gn(e,n,h);s=n.stateNode,d0.current=n;var I=x&&typeof o.getDerivedStateFromError!="function"?null:s.render();return n.flags|=1,e!==null&&x?(n.child=Br(n,e.child,null,h),n.child=Br(n,null,I,h)):st(e,n,I,h),n.memoizedState=s.state,f&&cf(n,o,!0),n.child}function sd(e){var n=e.stateNode;n.pendingContext?uf(e,n.pendingContext,n.pendingContext!==n.context):n.context&&uf(e,n.context,!1),Pu(e,n.containerInfo)}function ld(e,n,o,s,f){return br(),_u(f),n.flags|=256,st(e,n,o,s),n.child}var Yu={dehydrated:null,treeContext:null,retryLane:0};function Xu(e){return{baseLanes:e,cachePool:null,transitions:null}}function ud(e,n,o){var s=n.pendingProps,f=Fe.current,h=!1,x=(n.flags&128)!==0,I;if((I=x)||(I=e!==null&&e.memoizedState===null?!1:(f&2)!==0),I?(h=!0,n.flags&=-129):(e===null||e.memoizedState!==null)&&(f|=1),Ae(Fe,f&1),e===null)return wu(n),e=n.memoizedState,e!==null&&(e=e.dehydrated,e!==null)?((n.mode&1)===0?n.lanes=1:e.data==="$!"?n.lanes=8:n.lanes=1073741824,null):(x=s.children,e=s.fallback,h?(s=n.mode,h=n.child,x={mode:"hidden",children:x},(s&1)===0&&h!==null?(h.childLanes=0,h.pendingProps=x):h=Rs(x,s,0,null),e=fr(e,s,o,null),h.return=n,e.return=n,h.sibling=e,n.child=h,n.child.memoizedState=Xu(o),n.memoizedState=Yu,e):Qu(n,x));if(f=e.memoizedState,f!==null&&(I=f.dehydrated,I!==null))return h0(e,n,x,s,I,f,o);if(h){h=s.fallback,x=n.mode,f=e.child,I=f.sibling;var R={mode:"hidden",children:s.children};return(x&1)===0&&n.child!==f?(s=n.child,s.childLanes=0,s.pendingProps=R,n.deletions=null):(s=Fn(f,R),s.subtreeFlags=f.subtreeFlags&14680064),I!==null?h=Fn(I,h):(h=fr(h,x,o,null),h.flags|=2),h.return=n,s.return=n,s.sibling=h,n.child=s,s=h,h=n.child,x=e.child.memoizedState,x=x===null?Xu(o):{baseLanes:x.baseLanes|o,cachePool:null,transitions:x.transitions},h.memoizedState=x,h.childLanes=e.childLanes&~o,n.memoizedState=Yu,s}return h=e.child,e=h.sibling,s=Fn(h,{mode:"visible",children:s.children}),(n.mode&1)===0&&(s.lanes=o),s.return=n,s.sibling=null,e!==null&&(o=n.deletions,o===null?(n.deletions=[e],n.flags|=16):o.push(e)),n.child=s,n.memoizedState=null,s}function Qu(e,n){return n=Rs({mode:"visible",children:n},e.mode,0,null),n.return=e,e.child=n}function Ss(e,n,o,s){return s!==null&&_u(s),Br(n,e.child,null,o),e=Qu(n,n.pendingProps.children),e.flags|=2,n.memoizedState=null,e}function h0(e,n,o,s,f,h,x){if(o)return n.flags&256?(n.flags&=-257,s=bu(Error(i(422))),Ss(e,n,x,s)):n.memoizedState!==null?(n.child=e.child,n.flags|=128,null):(h=s.fallback,f=n.mode,s=Rs({mode:"visible",children:s.children},f,0,null),h=fr(h,f,x,null),h.flags|=2,s.return=n,h.return=n,s.sibling=h,n.child=s,(n.mode&1)!==0&&Br(n,e.child,null,x),n.child.memoizedState=Xu(x),n.memoizedState=Yu,h);if((n.mode&1)===0)return Ss(e,n,x,null);if(f.data==="$!"){if(s=f.nextSibling&&f.nextSibling.dataset,s)var I=s.dgst;return s=I,h=Error(i(419)),s=bu(h,s,void 0),Ss(e,n,x,s)}if(I=(x&e.childLanes)!==0,ht||I){if(s=Ke,s!==null){switch(x&-x){case 4:f=2;break;case 16:f=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:f=32;break;case 536870912:f=268435456;break;default:f=0}f=(f&(s.suspendedLanes|x))!==0?0:f,f!==0&&f!==h.retryLane&&(h.retryLane=f,hn(e,f),Ht(s,e,f,-1))}return ca(),s=bu(Error(i(421))),Ss(e,n,x,s)}return f.data==="$?"?(n.flags|=128,n.child=e.child,n=N0.bind(null,e),f._reactRetry=n,null):(e=h.treeContext,kt=Pn(f.nextSibling),St=n,$e=!0,Dt=null,e!==null&&(Tt[It++]=fn,Tt[It++]=dn,Tt[It++]=nr,fn=e.id,dn=e.overflow,nr=n),n=Qu(n,s.children),n.flags|=4096,n)}function ad(e,n,o){e.lanes|=n;var s=e.alternate;s!==null&&(s.lanes|=n),Cu(e.return,n,o)}function Ku(e,n,o,s,f){var h=e.memoizedState;h===null?e.memoizedState={isBackwards:n,rendering:null,renderingStartTime:0,last:s,tail:o,tailMode:f}:(h.isBackwards=n,h.rendering=null,h.renderingStartTime=0,h.last=s,h.tail=o,h.tailMode=f)}function cd(e,n,o){var s=n.pendingProps,f=s.revealOrder,h=s.tail;if(st(e,n,s.children,o),s=Fe.current,(s&2)!==0)s=s&1|2,n.flags|=128;else{if(e!==null&&(e.flags&128)!==0)e:for(e=n.child;e!==null;){if(e.tag===13)e.memoizedState!==null&&ad(e,o,n);else if(e.tag===19)ad(e,o,n);else if(e.child!==null){e.child.return=e,e=e.child;continue}if(e===n)break e;for(;e.sibling===null;){if(e.return===null||e.return===n)break e;e=e.return}e.sibling.return=e.return,e=e.sibling}s&=1}if(Ae(Fe,s),(n.mode&1)===0)n.memoizedState=null;else switch(f){case"forwards":for(o=n.child,f=null;o!==null;)e=o.alternate,e!==null&&gs(e)===null&&(f=o),o=o.sibling;o=f,o===null?(f=n.child,n.child=null):(f=o.sibling,o.sibling=null),Ku(n,!1,f,o,h);break;case"backwards":for(o=null,f=n.child,n.child=null;f!==null;){if(e=f.alternate,e!==null&&gs(e)===null){n.child=f;break}e=f.sibling,f.sibling=o,o=f,f=e}Ku(n,!0,o,null,h);break;case"together":Ku(n,!1,null,null,void 0);break;default:n.memoizedState=null}return n.child}function ks(e,n){(n.mode&1)===0&&e!==null&&(e.alternate=null,n.alternate=null,n.flags|=2)}function gn(e,n,o){if(e!==null&&(n.dependencies=e.dependencies),lr|=n.lanes,(o&n.childLanes)===0)return null;if(e!==null&&n.child!==e.child)throw Error(i(153));if(n.child!==null){for(e=n.child,o=Fn(e,e.pendingProps),n.child=o,o.return=n;e.sibling!==null;)e=e.sibling,o=o.sibling=Fn(e,e.pendingProps),o.return=n;o.sibling=null}return n.child}function p0(e,n,o){switch(n.tag){case 3:sd(n),br();break;case 5:Ef(n);break;case 1:dt(n.type)&&is(n);break;case 4:Pu(n,n.stateNode.containerInfo);break;case 10:var s=n.type._context,f=n.memoizedProps.value;Ae(fs,s._currentValue),s._currentValue=f;break;case 13:if(s=n.memoizedState,s!==null)return s.dehydrated!==null?(Ae(Fe,Fe.current&1),n.flags|=128,null):(o&n.child.childLanes)!==0?ud(e,n,o):(Ae(Fe,Fe.current&1),e=gn(e,n,o),e!==null?e.sibling:null);Ae(Fe,Fe.current&1);break;case 19:if(s=(o&n.childLanes)!==0,(e.flags&128)!==0){if(s)return cd(e,n,o);n.flags|=128}if(f=n.memoizedState,f!==null&&(f.rendering=null,f.tail=null,f.lastEffect=null),Ae(Fe,Fe.current),s)break;return null;case 22:case 23:return n.lanes=0,rd(e,n,o)}return gn(e,n,o)}var fd,Gu,dd,hd;fd=function(e,n){for(var o=n.child;o!==null;){if(o.tag===5||o.tag===6)e.appendChild(o.stateNode);else if(o.tag!==4&&o.child!==null){o.child.return=o,o=o.child;continue}if(o===n)break;for(;o.sibling===null;){if(o.return===null||o.return===n)return;o=o.return}o.sibling.return=o.return,o=o.sibling}},Gu=function(){},dd=function(e,n,o,s){var f=e.memoizedProps;if(f!==s){e=n.stateNode,ir(Zt.current);var h=null;switch(o){case"input":f=ke(e,f),s=ke(e,s),h=[];break;case"select":f=O({},f,{value:void 0}),s=O({},s,{value:void 0}),h=[];break;case"textarea":f=sn(e,f),s=sn(e,s),h=[];break;default:typeof f.onClick!="function"&&typeof s.onClick=="function"&&(e.onclick=ns)}ao(o,s);var x;o=null;for(K in f)if(!s.hasOwnProperty(K)&&f.hasOwnProperty(K)&&f[K]!=null)if(K==="style"){var I=f[K];for(x in I)I.hasOwnProperty(x)&&(o||(o={}),o[x]="")}else K!=="dangerouslySetInnerHTML"&&K!=="children"&&K!=="suppressContentEditableWarning"&&K!=="suppressHydrationWarning"&&K!=="autoFocus"&&(u.hasOwnProperty(K)?h||(h=[]):(h=h||[]).push(K,null));for(K in s){var R=s[K];if(I=f!=null?f[K]:void 0,s.hasOwnProperty(K)&&R!==I&&(R!=null||I!=null))if(K==="style")if(I){for(x in I)!I.hasOwnProperty(x)||R&&R.hasOwnProperty(x)||(o||(o={}),o[x]="");for(x in R)R.hasOwnProperty(x)&&I[x]!==R[x]&&(o||(o={}),o[x]=R[x])}else o||(h||(h=[]),h.push(K,o)),o=R;else K==="dangerouslySetInnerHTML"?(R=R?R.__html:void 0,I=I?I.__html:void 0,R!=null&&I!==R&&(h=h||[]).push(K,R)):K==="children"?typeof R!="string"&&typeof R!="number"||(h=h||[]).push(K,""+R):K!=="suppressContentEditableWarning"&&K!=="suppressHydrationWarning"&&(u.hasOwnProperty(K)?(R!=null&&K==="onScroll"&&Le("scroll",e),h||I===R||(h=[])):(h=h||[]).push(K,R))}o&&(h=h||[]).push("style",o);var K=h;(n.updateQueue=K)&&(n.flags|=4)}},hd=function(e,n,o,s){o!==s&&(n.flags|=4)};function Xo(e,n){if(!$e)switch(e.tailMode){case"hidden":n=e.tail;for(var o=null;n!==null;)n.alternate!==null&&(o=n),n=n.sibling;o===null?e.tail=null:o.sibling=null;break;case"collapsed":o=e.tail;for(var s=null;o!==null;)o.alternate!==null&&(s=o),o=o.sibling;s===null?n||e.tail===null?e.tail=null:e.tail.sibling=null:s.sibling=null}}function rt(e){var n=e.alternate!==null&&e.alternate.child===e.child,o=0,s=0;if(n)for(var f=e.child;f!==null;)o|=f.lanes|f.childLanes,s|=f.subtreeFlags&14680064,s|=f.flags&14680064,f.return=e,f=f.sibling;else for(f=e.child;f!==null;)o|=f.lanes|f.childLanes,s|=f.subtreeFlags,s|=f.flags,f.return=e,f=f.sibling;return e.subtreeFlags|=s,e.childLanes=o,n}function g0(e,n,o){var s=n.pendingProps;switch(vu(n),n.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return rt(n),null;case 1:return dt(n.type)&&os(),rt(n),null;case 3:return s=n.stateNode,Yr(),Re(ft),Re(tt),zu(),s.pendingContext&&(s.context=s.pendingContext,s.pendingContext=null),(e===null||e.child===null)&&(as(n)?n.flags|=4:e===null||e.memoizedState.isDehydrated&&(n.flags&256)===0||(n.flags|=1024,Dt!==null&&(la(Dt),Dt=null))),Gu(e,n),rt(n),null;case 5:Tu(n);var f=ir(bo.current);if(o=n.type,e!==null&&n.stateNode!=null)dd(e,n,o,s,f),e.ref!==n.ref&&(n.flags|=512,n.flags|=2097152);else{if(!s){if(n.stateNode===null)throw Error(i(166));return rt(n),null}if(e=ir(Zt.current),as(n)){s=n.stateNode,o=n.type;var h=n.memoizedProps;switch(s[qt]=n,s[$o]=h,e=(n.mode&1)!==0,o){case"dialog":Le("cancel",s),Le("close",s);break;case"iframe":case"object":case"embed":Le("load",s);break;case"video":case"audio":for(f=0;f<Lo.length;f++)Le(Lo[f],s);break;case"source":Le("error",s);break;case"img":case"image":case"link":Le("error",s),Le("load",s);break;case"details":Le("toggle",s);break;case"input":Ce(s,h),Le("invalid",s);break;case"select":s._wrapperState={wasMultiple:!!h.multiple},Le("invalid",s);break;case"textarea":Qt(s,h),Le("invalid",s)}ao(o,h),f=null;for(var x in h)if(h.hasOwnProperty(x)){var I=h[x];x==="children"?typeof I=="string"?s.textContent!==I&&(h.suppressHydrationWarning!==!0&&ts(s.textContent,I,e),f=["children",I]):typeof I=="number"&&s.textContent!==""+I&&(h.suppressHydrationWarning!==!0&&ts(s.textContent,I,e),f=["children",""+I]):u.hasOwnProperty(x)&&I!=null&&x==="onScroll"&&Le("scroll",s)}switch(o){case"input":xe(s),it(s,h,!0);break;case"textarea":xe(s),ct(s);break;case"select":case"option":break;default:typeof h.onClick=="function"&&(s.onclick=ns)}s=f,n.updateQueue=s,s!==null&&(n.flags|=4)}else{x=f.nodeType===9?f:f.ownerDocument,e==="http://www.w3.org/1999/xhtml"&&(e=Mt(o)),e==="http://www.w3.org/1999/xhtml"?o==="script"?(e=x.createElement("div"),e.innerHTML="<script><\/script>",e=e.removeChild(e.firstChild)):typeof s.is=="string"?e=x.createElement(o,{is:s.is}):(e=x.createElement(o),o==="select"&&(x=e,s.multiple?x.multiple=!0:s.size&&(x.size=s.size))):e=x.createElementNS(e,o),e[qt]=n,e[$o]=s,fd(e,n,!1,!1),n.stateNode=e;e:{switch(x=co(o,s),o){case"dialog":Le("cancel",e),Le("close",e),f=s;break;case"iframe":case"object":case"embed":Le("load",e),f=s;break;case"video":case"audio":for(f=0;f<Lo.length;f++)Le(Lo[f],e);f=s;break;case"source":Le("error",e),f=s;break;case"img":case"image":case"link":Le("error",e),Le("load",e),f=s;break;case"details":Le("toggle",e),f=s;break;case"input":Ce(e,s),f=ke(e,s),Le("invalid",e);break;case"option":f=s;break;case"select":e._wrapperState={wasMultiple:!!s.multiple},f=O({},s,{value:void 0}),Le("invalid",e);break;case"textarea":Qt(e,s),f=sn(e,s),Le("invalid",e);break;default:f=s}ao(o,f),I=f;for(h in I)if(I.hasOwnProperty(h)){var R=I[h];h==="style"?Ni(e,R):h==="dangerouslySetInnerHTML"?(R=R?R.__html:void 0,R!=null&&Sr(e,R)):h==="children"?typeof R=="string"?(o!=="textarea"||R!=="")&&ln(e,R):typeof R=="number"&&ln(e,""+R):h!=="suppressContentEditableWarning"&&h!=="suppressHydrationWarning"&&h!=="autoFocus"&&(u.hasOwnProperty(h)?R!=null&&h==="onScroll"&&Le("scroll",e):R!=null&&_(e,h,R,x))}switch(o){case"input":xe(e),it(e,s,!1);break;case"textarea":xe(e),ct(e);break;case"option":s.value!=null&&e.setAttribute("value",""+G(s.value));break;case"select":e.multiple=!!s.multiple,h=s.value,h!=null?et(e,!!s.multiple,h,!1):s.defaultValue!=null&&et(e,!!s.multiple,s.defaultValue,!0);break;default:typeof f.onClick=="function"&&(e.onclick=ns)}switch(o){case"button":case"input":case"select":case"textarea":s=!!s.autoFocus;break e;case"img":s=!0;break e;default:s=!1}}s&&(n.flags|=4)}n.ref!==null&&(n.flags|=512,n.flags|=2097152)}return rt(n),null;case 6:if(e&&n.stateNode!=null)hd(e,n,e.memoizedProps,s);else{if(typeof s!="string"&&n.stateNode===null)throw Error(i(166));if(o=ir(bo.current),ir(Zt.current),as(n)){if(s=n.stateNode,o=n.memoizedProps,s[qt]=n,(h=s.nodeValue!==o)&&(e=St,e!==null))switch(e.tag){case 3:ts(s.nodeValue,o,(e.mode&1)!==0);break;case 5:e.memoizedProps.suppressHydrationWarning!==!0&&ts(s.nodeValue,o,(e.mode&1)!==0)}h&&(n.flags|=4)}else s=(o.nodeType===9?o:o.ownerDocument).createTextNode(s),s[qt]=n,n.stateNode=s}return rt(n),null;case 13:if(Re(Fe),s=n.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if($e&&kt!==null&&(n.mode&1)!==0&&(n.flags&128)===0)mf(),br(),n.flags|=98560,h=!1;else if(h=as(n),s!==null&&s.dehydrated!==null){if(e===null){if(!h)throw Error(i(318));if(h=n.memoizedState,h=h!==null?h.dehydrated:null,!h)throw Error(i(317));h[qt]=n}else br(),(n.flags&128)===0&&(n.memoizedState=null),n.flags|=4;rt(n),h=!1}else Dt!==null&&(la(Dt),Dt=null),h=!0;if(!h)return n.flags&65536?n:null}return(n.flags&128)!==0?(n.lanes=o,n):(s=s!==null,s!==(e!==null&&e.memoizedState!==null)&&s&&(n.child.flags|=8192,(n.mode&1)!==0&&(e===null||(Fe.current&1)!==0?Xe===0&&(Xe=3):ca())),n.updateQueue!==null&&(n.flags|=4),rt(n),null);case 4:return Yr(),Gu(e,n),e===null&&Ro(n.stateNode.containerInfo),rt(n),null;case 10:return Eu(n.type._context),rt(n),null;case 17:return dt(n.type)&&os(),rt(n),null;case 19:if(Re(Fe),h=n.memoizedState,h===null)return rt(n),null;if(s=(n.flags&128)!==0,x=h.rendering,x===null)if(s)Xo(h,!1);else{if(Xe!==0||e!==null&&(e.flags&128)!==0)for(e=n.child;e!==null;){if(x=gs(e),x!==null){for(n.flags|=128,Xo(h,!1),s=x.updateQueue,s!==null&&(n.updateQueue=s,n.flags|=4),n.subtreeFlags=0,s=o,o=n.child;o!==null;)h=o,e=s,h.flags&=14680066,x=h.alternate,x===null?(h.childLanes=0,h.lanes=e,h.child=null,h.subtreeFlags=0,h.memoizedProps=null,h.memoizedState=null,h.updateQueue=null,h.dependencies=null,h.stateNode=null):(h.childLanes=x.childLanes,h.lanes=x.lanes,h.child=x.child,h.subtreeFlags=0,h.deletions=null,h.memoizedProps=x.memoizedProps,h.memoizedState=x.memoizedState,h.updateQueue=x.updateQueue,h.type=x.type,e=x.dependencies,h.dependencies=e===null?null:{lanes:e.lanes,firstContext:e.firstContext}),o=o.sibling;return Ae(Fe,Fe.current&1|2),n.child}e=e.sibling}h.tail!==null&&Oe()>Gr&&(n.flags|=128,s=!0,Xo(h,!1),n.lanes=4194304)}else{if(!s)if(e=gs(x),e!==null){if(n.flags|=128,s=!0,o=e.updateQueue,o!==null&&(n.updateQueue=o,n.flags|=4),Xo(h,!0),h.tail===null&&h.tailMode==="hidden"&&!x.alternate&&!$e)return rt(n),null}else 2*Oe()-h.renderingStartTime>Gr&&o!==1073741824&&(n.flags|=128,s=!0,Xo(h,!1),n.lanes=4194304);h.isBackwards?(x.sibling=n.child,n.child=x):(o=h.last,o!==null?o.sibling=x:n.child=x,h.last=x)}return h.tail!==null?(n=h.tail,h.rendering=n,h.tail=n.sibling,h.renderingStartTime=Oe(),n.sibling=null,o=Fe.current,Ae(Fe,s?o&1|2:o&1),n):(rt(n),null);case 22:case 23:return aa(),s=n.memoizedState!==null,e!==null&&e.memoizedState!==null!==s&&(n.flags|=8192),s&&(n.mode&1)!==0?(Et&1073741824)!==0&&(rt(n),n.subtreeFlags&6&&(n.flags|=8192)):rt(n),null;case 24:return null;case 25:return null}throw Error(i(156,n.tag))}function m0(e,n){switch(vu(n),n.tag){case 1:return dt(n.type)&&os(),e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 3:return Yr(),Re(ft),Re(tt),zu(),e=n.flags,(e&65536)!==0&&(e&128)===0?(n.flags=e&-65537|128,n):null;case 5:return Tu(n),null;case 13:if(Re(Fe),e=n.memoizedState,e!==null&&e.dehydrated!==null){if(n.alternate===null)throw Error(i(340));br()}return e=n.flags,e&65536?(n.flags=e&-65537|128,n):null;case 19:return Re(Fe),null;case 4:return Yr(),null;case 10:return Eu(n.type._context),null;case 22:case 23:return aa(),null;case 24:return null;default:return null}}var Es=!1,ot=!1,y0=typeof WeakSet=="function"?WeakSet:Set,de=null;function Qr(e,n){var o=e.ref;if(o!==null)if(typeof o=="function")try{o(null)}catch(s){be(e,n,s)}else o.current=null}function qu(e,n,o){try{o()}catch(s){be(e,n,s)}}var pd=!1;function v0(e,n){if(au=Ui,e=Wc(),tu(e)){if("selectionStart"in e)var o={start:e.selectionStart,end:e.selectionEnd};else e:{o=(o=e.ownerDocument)&&o.defaultView||window;var s=o.getSelection&&o.getSelection();if(s&&s.rangeCount!==0){o=s.anchorNode;var f=s.anchorOffset,h=s.focusNode;s=s.focusOffset;try{o.nodeType,h.nodeType}catch{o=null;break e}var x=0,I=-1,R=-1,K=0,oe=0,ie=e,ne=null;t:for(;;){for(var fe;ie!==o||f!==0&&ie.nodeType!==3||(I=x+f),ie!==h||s!==0&&ie.nodeType!==3||(R=x+s),ie.nodeType===3&&(x+=ie.nodeValue.length),(fe=ie.firstChild)!==null;)ne=ie,ie=fe;for(;;){if(ie===e)break t;if(ne===o&&++K===f&&(I=x),ne===h&&++oe===s&&(R=x),(fe=ie.nextSibling)!==null)break;ie=ne,ne=ie.parentNode}ie=fe}o=I===-1||R===-1?null:{start:I,end:R}}else o=null}o=o||{start:0,end:0}}else o=null;for(cu={focusedElem:e,selectionRange:o},Ui=!1,de=n;de!==null;)if(n=de,e=n.child,(n.subtreeFlags&1028)!==0&&e!==null)e.return=n,de=e;else for(;de!==null;){n=de;try{var he=n.alternate;if((n.flags&1024)!==0)switch(n.tag){case 0:case 11:case 15:break;case 1:if(he!==null){var ge=he.memoizedProps,Be=he.memoizedState,W=n.stateNode,$=W.getSnapshotBeforeUpdate(n.elementType===n.type?ge:$t(n.type,ge),Be);W.__reactInternalSnapshotBeforeUpdate=$}break;case 3:var X=n.stateNode.containerInfo;X.nodeType===1?X.textContent="":X.nodeType===9&&X.documentElement&&X.removeChild(X.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(i(163))}}catch(le){be(n,n.return,le)}if(e=n.sibling,e!==null){e.return=n.return,de=e;break}de=n.return}return he=pd,pd=!1,he}function Qo(e,n,o){var s=n.updateQueue;if(s=s!==null?s.lastEffect:null,s!==null){var f=s=s.next;do{if((f.tag&e)===e){var h=f.destroy;f.destroy=void 0,h!==void 0&&qu(n,o,h)}f=f.next}while(f!==s)}}function Cs(e,n){if(n=n.updateQueue,n=n!==null?n.lastEffect:null,n!==null){var o=n=n.next;do{if((o.tag&e)===e){var s=o.create;o.destroy=s()}o=o.next}while(o!==n)}}function Zu(e){var n=e.ref;if(n!==null){var o=e.stateNode;switch(e.tag){case 5:e=o;break;default:e=o}typeof n=="function"?n(e):n.current=e}}function gd(e){var n=e.alternate;n!==null&&(e.alternate=null,gd(n)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(n=e.stateNode,n!==null&&(delete n[qt],delete n[$o],delete n[pu],delete n[e0],delete n[t0])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function md(e){return e.tag===5||e.tag===3||e.tag===4}function yd(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||md(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function Ju(e,n,o){var s=e.tag;if(s===5||s===6)e=e.stateNode,n?o.nodeType===8?o.parentNode.insertBefore(e,n):o.insertBefore(e,n):(o.nodeType===8?(n=o.parentNode,n.insertBefore(e,o)):(n=o,n.appendChild(e)),o=o._reactRootContainer,o!=null||n.onclick!==null||(n.onclick=ns));else if(s!==4&&(e=e.child,e!==null))for(Ju(e,n,o),e=e.sibling;e!==null;)Ju(e,n,o),e=e.sibling}function ea(e,n,o){var s=e.tag;if(s===5||s===6)e=e.stateNode,n?o.insertBefore(e,n):o.appendChild(e);else if(s!==4&&(e=e.child,e!==null))for(ea(e,n,o),e=e.sibling;e!==null;)ea(e,n,o),e=e.sibling}var qe=null,Ot=!1;function Ln(e,n,o){for(o=o.child;o!==null;)vd(e,n,o),o=o.sibling}function vd(e,n,o){if(xt&&typeof xt.onCommitFiberUnmount=="function")try{xt.onCommitFiberUnmount(Nr,o)}catch{}switch(o.tag){case 5:ot||Qr(o,n);case 6:var s=qe,f=Ot;qe=null,Ln(e,n,o),qe=s,Ot=f,qe!==null&&(Ot?(e=qe,o=o.stateNode,e.nodeType===8?e.parentNode.removeChild(o):e.removeChild(o)):qe.removeChild(o.stateNode));break;case 18:qe!==null&&(Ot?(e=qe,o=o.stateNode,e.nodeType===8?hu(e.parentNode,o):e.nodeType===1&&hu(e,o),No(e)):hu(qe,o.stateNode));break;case 4:s=qe,f=Ot,qe=o.stateNode.containerInfo,Ot=!0,Ln(e,n,o),qe=s,Ot=f;break;case 0:case 11:case 14:case 15:if(!ot&&(s=o.updateQueue,s!==null&&(s=s.lastEffect,s!==null))){f=s=s.next;do{var h=f,x=h.destroy;h=h.tag,x!==void 0&&((h&2)!==0||(h&4)!==0)&&qu(o,n,x),f=f.next}while(f!==s)}Ln(e,n,o);break;case 1:if(!ot&&(Qr(o,n),s=o.stateNode,typeof s.componentWillUnmount=="function"))try{s.props=o.memoizedProps,s.state=o.memoizedState,s.componentWillUnmount()}catch(I){be(o,n,I)}Ln(e,n,o);break;case 21:Ln(e,n,o);break;case 22:o.mode&1?(ot=(s=ot)||o.memoizedState!==null,Ln(e,n,o),ot=s):Ln(e,n,o);break;default:Ln(e,n,o)}}function xd(e){var n=e.updateQueue;if(n!==null){e.updateQueue=null;var o=e.stateNode;o===null&&(o=e.stateNode=new y0),n.forEach(function(s){var f=M0.bind(null,e,s);o.has(s)||(o.add(s),s.then(f,f))})}}function Ft(e,n){var o=n.deletions;if(o!==null)for(var s=0;s<o.length;s++){var f=o[s];try{var h=e,x=n,I=x;e:for(;I!==null;){switch(I.tag){case 5:qe=I.stateNode,Ot=!1;break e;case 3:qe=I.stateNode.containerInfo,Ot=!0;break e;case 4:qe=I.stateNode.containerInfo,Ot=!0;break e}I=I.return}if(qe===null)throw Error(i(160));vd(h,x,f),qe=null,Ot=!1;var R=f.alternate;R!==null&&(R.return=null),f.return=null}catch(K){be(f,n,K)}}if(n.subtreeFlags&12854)for(n=n.child;n!==null;)wd(n,e),n=n.sibling}function wd(e,n){var o=e.alternate,s=e.flags;switch(e.tag){case 0:case 11:case 14:case 15:if(Ft(n,e),en(e),s&4){try{Qo(3,e,e.return),Cs(3,e)}catch(ge){be(e,e.return,ge)}try{Qo(5,e,e.return)}catch(ge){be(e,e.return,ge)}}break;case 1:Ft(n,e),en(e),s&512&&o!==null&&Qr(o,o.return);break;case 5:if(Ft(n,e),en(e),s&512&&o!==null&&Qr(o,o.return),e.flags&32){var f=e.stateNode;try{ln(f,"")}catch(ge){be(e,e.return,ge)}}if(s&4&&(f=e.stateNode,f!=null)){var h=e.memoizedProps,x=o!==null?o.memoizedProps:h,I=e.type,R=e.updateQueue;if(e.updateQueue=null,R!==null)try{I==="input"&&h.type==="radio"&&h.name!=null&&Me(f,h),co(I,x);var K=co(I,h);for(x=0;x<R.length;x+=2){var oe=R[x],ie=R[x+1];oe==="style"?Ni(f,ie):oe==="dangerouslySetInnerHTML"?Sr(f,ie):oe==="children"?ln(f,ie):_(f,oe,ie,K)}switch(I){case"input":ze(f,h);break;case"textarea":Kt(f,h);break;case"select":var ne=f._wrapperState.wasMultiple;f._wrapperState.wasMultiple=!!h.multiple;var fe=h.value;fe!=null?et(f,!!h.multiple,fe,!1):ne!==!!h.multiple&&(h.defaultValue!=null?et(f,!!h.multiple,h.defaultValue,!0):et(f,!!h.multiple,h.multiple?[]:"",!1))}f[$o]=h}catch(ge){be(e,e.return,ge)}}break;case 6:if(Ft(n,e),en(e),s&4){if(e.stateNode===null)throw Error(i(162));f=e.stateNode,h=e.memoizedProps;try{f.nodeValue=h}catch(ge){be(e,e.return,ge)}}break;case 3:if(Ft(n,e),en(e),s&4&&o!==null&&o.memoizedState.isDehydrated)try{No(n.containerInfo)}catch(ge){be(e,e.return,ge)}break;case 4:Ft(n,e),en(e);break;case 13:Ft(n,e),en(e),f=e.child,f.flags&8192&&(h=f.memoizedState!==null,f.stateNode.isHidden=h,!h||f.alternate!==null&&f.alternate.memoizedState!==null||(ra=Oe())),s&4&&xd(e);break;case 22:if(oe=o!==null&&o.memoizedState!==null,e.mode&1?(ot=(K=ot)||oe,Ft(n,e),ot=K):Ft(n,e),en(e),s&8192){if(K=e.memoizedState!==null,(e.stateNode.isHidden=K)&&!oe&&(e.mode&1)!==0)for(de=e,oe=e.child;oe!==null;){for(ie=de=oe;de!==null;){switch(ne=de,fe=ne.child,ne.tag){case 0:case 11:case 14:case 15:Qo(4,ne,ne.return);break;case 1:Qr(ne,ne.return);var he=ne.stateNode;if(typeof he.componentWillUnmount=="function"){s=ne,o=ne.return;try{n=s,he.props=n.memoizedProps,he.state=n.memoizedState,he.componentWillUnmount()}catch(ge){be(s,o,ge)}}break;case 5:Qr(ne,ne.return);break;case 22:if(ne.memoizedState!==null){kd(ie);continue}}fe!==null?(fe.return=ne,de=fe):kd(ie)}oe=oe.sibling}e:for(oe=null,ie=e;;){if(ie.tag===5){if(oe===null){oe=ie;try{f=ie.stateNode,K?(h=f.style,typeof h.setProperty=="function"?h.setProperty("display","none","important"):h.display="none"):(I=ie.stateNode,R=ie.memoizedProps.style,x=R!=null&&R.hasOwnProperty("display")?R.display:null,I.style.display=Ci("display",x))}catch(ge){be(e,e.return,ge)}}}else if(ie.tag===6){if(oe===null)try{ie.stateNode.nodeValue=K?"":ie.memoizedProps}catch(ge){be(e,e.return,ge)}}else if((ie.tag!==22&&ie.tag!==23||ie.memoizedState===null||ie===e)&&ie.child!==null){ie.child.return=ie,ie=ie.child;continue}if(ie===e)break e;for(;ie.sibling===null;){if(ie.return===null||ie.return===e)break e;oe===ie&&(oe=null),ie=ie.return}oe===ie&&(oe=null),ie.sibling.return=ie.return,ie=ie.sibling}}break;case 19:Ft(n,e),en(e),s&4&&xd(e);break;case 21:break;default:Ft(n,e),en(e)}}function en(e){var n=e.flags;if(n&2){try{e:{for(var o=e.return;o!==null;){if(md(o)){var s=o;break e}o=o.return}throw Error(i(160))}switch(s.tag){case 5:var f=s.stateNode;s.flags&32&&(ln(f,""),s.flags&=-33);var h=yd(e);ea(e,h,f);break;case 3:case 4:var x=s.stateNode.containerInfo,I=yd(e);Ju(e,I,x);break;default:throw Error(i(161))}}catch(R){be(e,e.return,R)}e.flags&=-3}n&4096&&(e.flags&=-4097)}function x0(e,n,o){de=e,_d(e)}function _d(e,n,o){for(var s=(e.mode&1)!==0;de!==null;){var f=de,h=f.child;if(f.tag===22&&s){var x=f.memoizedState!==null||Es;if(!x){var I=f.alternate,R=I!==null&&I.memoizedState!==null||ot;I=Es;var K=ot;if(Es=x,(ot=R)&&!K)for(de=f;de!==null;)x=de,R=x.child,x.tag===22&&x.memoizedState!==null?Ed(f):R!==null?(R.return=x,de=R):Ed(f);for(;h!==null;)de=h,_d(h),h=h.sibling;de=f,Es=I,ot=K}Sd(e)}else(f.subtreeFlags&8772)!==0&&h!==null?(h.return=f,de=h):Sd(e)}}function Sd(e){for(;de!==null;){var n=de;if((n.flags&8772)!==0){var o=n.alternate;try{if((n.flags&8772)!==0)switch(n.tag){case 0:case 11:case 15:ot||Cs(5,n);break;case 1:var s=n.stateNode;if(n.flags&4&&!ot)if(o===null)s.componentDidMount();else{var f=n.elementType===n.type?o.memoizedProps:$t(n.type,o.memoizedProps);s.componentDidUpdate(f,o.memoizedState,s.__reactInternalSnapshotBeforeUpdate)}var h=n.updateQueue;h!==null&&kf(n,h,s);break;case 3:var x=n.updateQueue;if(x!==null){if(o=null,n.child!==null)switch(n.child.tag){case 5:o=n.child.stateNode;break;case 1:o=n.child.stateNode}kf(n,x,o)}break;case 5:var I=n.stateNode;if(o===null&&n.flags&4){o=I;var R=n.memoizedProps;switch(n.type){case"button":case"input":case"select":case"textarea":R.autoFocus&&o.focus();break;case"img":R.src&&(o.src=R.src)}}break;case 6:break;case 4:break;case 12:break;case 13:if(n.memoizedState===null){var K=n.alternate;if(K!==null){var oe=K.memoizedState;if(oe!==null){var ie=oe.dehydrated;ie!==null&&No(ie)}}}break;case 19:case 17:case 21:case 22:case 23:case 25:break;default:throw Error(i(163))}ot||n.flags&512&&Zu(n)}catch(ne){be(n,n.return,ne)}}if(n===e){de=null;break}if(o=n.sibling,o!==null){o.return=n.return,de=o;break}de=n.return}}function kd(e){for(;de!==null;){var n=de;if(n===e){de=null;break}var o=n.sibling;if(o!==null){o.return=n.return,de=o;break}de=n.return}}function Ed(e){for(;de!==null;){var n=de;try{switch(n.tag){case 0:case 11:case 15:var o=n.return;try{Cs(4,n)}catch(R){be(n,o,R)}break;case 1:var s=n.stateNode;if(typeof s.componentDidMount=="function"){var f=n.return;try{s.componentDidMount()}catch(R){be(n,f,R)}}var h=n.return;try{Zu(n)}catch(R){be(n,h,R)}break;case 5:var x=n.return;try{Zu(n)}catch(R){be(n,x,R)}}}catch(R){be(n,n.return,R)}if(n===e){de=null;break}var I=n.sibling;if(I!==null){I.return=n.return,de=I;break}de=n.return}}var w0=Math.ceil,Ns=T.ReactCurrentDispatcher,ta=T.ReactCurrentOwner,At=T.ReactCurrentBatchConfig,Pe=0,Ke=null,Ue=null,Ze=0,Et=0,Kr=Tn(0),Xe=0,Ko=null,lr=0,Ms=0,na=0,Go=null,pt=null,ra=0,Gr=1/0,mn=null,Ps=!1,oa=null,Rn=null,Ts=!1,Dn=null,Is=0,qo=0,ia=null,zs=-1,js=0;function lt(){return(Pe&6)!==0?Oe():zs!==-1?zs:zs=Oe()}function $n(e){return(e.mode&1)===0?1:(Pe&2)!==0&&Ze!==0?Ze&-Ze:r0.transition!==null?(js===0&&(js=Vi()),js):(e=je,e!==0||(e=window.event,e=e===void 0?16:Cc(e.type)),e)}function Ht(e,n,o,s){if(50<qo)throw qo=0,ia=null,Error(i(185));Jn(e,o,s),((Pe&2)===0||e!==Ke)&&(e===Ke&&((Pe&2)===0&&(Ms|=o),Xe===4&&On(e,Ze)),gt(e,s),o===1&&Pe===0&&(n.mode&1)===0&&(Gr=Oe()+500,ss&&zn()))}function gt(e,n){var o=e.callbackNode;Hi(e,n);var s=un(e,e===Ke?Ze:0);if(s===0)o!==null&&Di(o),e.callbackNode=null,e.callbackPriority=0;else if(n=s&-s,e.callbackPriority!==n){if(o!=null&&Di(o),n===1)e.tag===0?n0(Nd.bind(null,e)):ff(Nd.bind(null,e)),Zm(function(){(Pe&6)===0&&zn()}),o=null;else{switch(yc(s)){case 1:o=wo;break;case 4:o=Oi;break;case 16:o=Cr;break;case 536870912:o=Fi;break;default:o=Cr}o=Ld(o,Cd.bind(null,e))}e.callbackPriority=n,e.callbackNode=o}}function Cd(e,n){if(zs=-1,js=0,(Pe&6)!==0)throw Error(i(327));var o=e.callbackNode;if(qr()&&e.callbackNode!==o)return null;var s=un(e,e===Ke?Ze:0);if(s===0)return null;if((s&30)!==0||(s&e.expiredLanes)!==0||n)n=As(e,s);else{n=s;var f=Pe;Pe|=2;var h=Pd();(Ke!==e||Ze!==n)&&(mn=null,Gr=Oe()+500,ar(e,n));do try{k0();break}catch(I){Md(e,I)}while(!0);ku(),Ns.current=h,Pe=f,Ue!==null?n=0:(Ke=null,Ze=0,n=Xe)}if(n!==0){if(n===2&&(f=_o(e),f!==0&&(s=f,n=sa(e,f))),n===1)throw o=Ko,ar(e,0),On(e,s),gt(e,Oe()),o;if(n===6)On(e,s);else{if(f=e.current.alternate,(s&30)===0&&!_0(f)&&(n=As(e,s),n===2&&(h=_o(e),h!==0&&(s=h,n=sa(e,h))),n===1))throw o=Ko,ar(e,0),On(e,s),gt(e,Oe()),o;switch(e.finishedWork=f,e.finishedLanes=s,n){case 0:case 1:throw Error(i(345));case 2:cr(e,pt,mn);break;case 3:if(On(e,s),(s&130023424)===s&&(n=ra+500-Oe(),10<n)){if(un(e,0)!==0)break;if(f=e.suspendedLanes,(f&s)!==s){lt(),e.pingedLanes|=e.suspendedLanes&f;break}e.timeoutHandle=du(cr.bind(null,e,pt,mn),n);break}cr(e,pt,mn);break;case 4:if(On(e,s),(s&4194240)===s)break;for(n=e.eventTimes,f=-1;0<s;){var x=31-wt(s);h=1<<x,x=n[x],x>f&&(f=x),s&=~h}if(s=f,s=Oe()-s,s=(120>s?120:480>s?480:1080>s?1080:1920>s?1920:3e3>s?3e3:4320>s?4320:1960*w0(s/1960))-s,10<s){e.timeoutHandle=du(cr.bind(null,e,pt,mn),s);break}cr(e,pt,mn);break;case 5:cr(e,pt,mn);break;default:throw Error(i(329))}}}return gt(e,Oe()),e.callbackNode===o?Cd.bind(null,e):null}function sa(e,n){var o=Go;return e.current.memoizedState.isDehydrated&&(ar(e,n).flags|=256),e=As(e,n),e!==2&&(n=pt,pt=o,n!==null&&la(n)),e}function la(e){pt===null?pt=e:pt.push.apply(pt,e)}function _0(e){for(var n=e;;){if(n.flags&16384){var o=n.updateQueue;if(o!==null&&(o=o.stores,o!==null))for(var s=0;s<o.length;s++){var f=o[s],h=f.getSnapshot;f=f.value;try{if(!Rt(h(),f))return!1}catch{return!1}}}if(o=n.child,n.subtreeFlags&16384&&o!==null)o.return=n,n=o;else{if(n===e)break;for(;n.sibling===null;){if(n.return===null||n.return===e)return!0;n=n.return}n.sibling.return=n.return,n=n.sibling}}return!0}function On(e,n){for(n&=~na,n&=~Ms,e.suspendedLanes|=n,e.pingedLanes&=~n,e=e.expirationTimes;0<n;){var o=31-wt(n),s=1<<o;e[o]=-1,n&=~s}}function Nd(e){if((Pe&6)!==0)throw Error(i(327));qr();var n=un(e,0);if((n&1)===0)return gt(e,Oe()),null;var o=As(e,n);if(e.tag!==0&&o===2){var s=_o(e);s!==0&&(n=s,o=sa(e,s))}if(o===1)throw o=Ko,ar(e,0),On(e,n),gt(e,Oe()),o;if(o===6)throw Error(i(345));return e.finishedWork=e.current.alternate,e.finishedLanes=n,cr(e,pt,mn),gt(e,Oe()),null}function ua(e,n){var o=Pe;Pe|=1;try{return e(n)}finally{Pe=o,Pe===0&&(Gr=Oe()+500,ss&&zn())}}function ur(e){Dn!==null&&Dn.tag===0&&(Pe&6)===0&&qr();var n=Pe;Pe|=1;var o=At.transition,s=je;try{if(At.transition=null,je=1,e)return e()}finally{je=s,At.transition=o,Pe=n,(Pe&6)===0&&zn()}}function aa(){Et=Kr.current,Re(Kr)}function ar(e,n){e.finishedWork=null,e.finishedLanes=0;var o=e.timeoutHandle;if(o!==-1&&(e.timeoutHandle=-1,qm(o)),Ue!==null)for(o=Ue.return;o!==null;){var s=o;switch(vu(s),s.tag){case 1:s=s.type.childContextTypes,s!=null&&os();break;case 3:Yr(),Re(ft),Re(tt),zu();break;case 5:Tu(s);break;case 4:Yr();break;case 13:Re(Fe);break;case 19:Re(Fe);break;case 10:Eu(s.type._context);break;case 22:case 23:aa()}o=o.return}if(Ke=e,Ue=e=Fn(e.current,null),Ze=Et=n,Xe=0,Ko=null,na=Ms=lr=0,pt=Go=null,or!==null){for(n=0;n<or.length;n++)if(o=or[n],s=o.interleaved,s!==null){o.interleaved=null;var f=s.next,h=o.pending;if(h!==null){var x=h.next;h.next=f,s.next=x}o.pending=s}or=null}return e}function Md(e,n){do{var o=Ue;try{if(ku(),ms.current=ws,ys){for(var s=He.memoizedState;s!==null;){var f=s.queue;f!==null&&(f.pending=null),s=s.next}ys=!1}if(sr=0,Qe=Ye=He=null,Bo=!1,Uo=0,ta.current=null,o===null||o.return===null){Xe=1,Ko=n,Ue=null;break}e:{var h=e,x=o.return,I=o,R=n;if(n=Ze,I.flags|=32768,R!==null&&typeof R=="object"&&typeof R.then=="function"){var K=R,oe=I,ie=oe.tag;if((oe.mode&1)===0&&(ie===0||ie===11||ie===15)){var ne=oe.alternate;ne?(oe.updateQueue=ne.updateQueue,oe.memoizedState=ne.memoizedState,oe.lanes=ne.lanes):(oe.updateQueue=null,oe.memoizedState=null)}var fe=Zf(x);if(fe!==null){fe.flags&=-257,Jf(fe,x,I,h,n),fe.mode&1&&qf(h,K,n),n=fe,R=K;var he=n.updateQueue;if(he===null){var ge=new Set;ge.add(R),n.updateQueue=ge}else he.add(R);break e}else{if((n&1)===0){qf(h,K,n),ca();break e}R=Error(i(426))}}else if($e&&I.mode&1){var Be=Zf(x);if(Be!==null){(Be.flags&65536)===0&&(Be.flags|=256),Jf(Be,x,I,h,n),_u(Xr(R,I));break e}}h=R=Xr(R,I),Xe!==4&&(Xe=2),Go===null?Go=[h]:Go.push(h),h=x;do{switch(h.tag){case 3:h.flags|=65536,n&=-n,h.lanes|=n;var W=Kf(h,R,n);Sf(h,W);break e;case 1:I=R;var $=h.type,X=h.stateNode;if((h.flags&128)===0&&(typeof $.getDerivedStateFromError=="function"||X!==null&&typeof X.componentDidCatch=="function"&&(Rn===null||!Rn.has(X)))){h.flags|=65536,n&=-n,h.lanes|=n;var le=Gf(h,I,n);Sf(h,le);break e}}h=h.return}while(h!==null)}Id(o)}catch(me){n=me,Ue===o&&o!==null&&(Ue=o=o.return);continue}break}while(!0)}function Pd(){var e=Ns.current;return Ns.current=ws,e===null?ws:e}function ca(){(Xe===0||Xe===3||Xe===2)&&(Xe=4),Ke===null||(lr&268435455)===0&&(Ms&268435455)===0||On(Ke,Ze)}function As(e,n){var o=Pe;Pe|=2;var s=Pd();(Ke!==e||Ze!==n)&&(mn=null,ar(e,n));do try{S0();break}catch(f){Md(e,f)}while(!0);if(ku(),Pe=o,Ns.current=s,Ue!==null)throw Error(i(261));return Ke=null,Ze=0,Xe}function S0(){for(;Ue!==null;)Td(Ue)}function k0(){for(;Ue!==null&&!Al();)Td(Ue)}function Td(e){var n=Ad(e.alternate,e,Et);e.memoizedProps=e.pendingProps,n===null?Id(e):Ue=n,ta.current=null}function Id(e){var n=e;do{var o=n.alternate;if(e=n.return,(n.flags&32768)===0){if(o=g0(o,n,Et),o!==null){Ue=o;return}}else{if(o=m0(o,n),o!==null){o.flags&=32767,Ue=o;return}if(e!==null)e.flags|=32768,e.subtreeFlags=0,e.deletions=null;else{Xe=6,Ue=null;return}}if(n=n.sibling,n!==null){Ue=n;return}Ue=n=e}while(n!==null);Xe===0&&(Xe=5)}function cr(e,n,o){var s=je,f=At.transition;try{At.transition=null,je=1,E0(e,n,o,s)}finally{At.transition=f,je=s}return null}function E0(e,n,o,s){do qr();while(Dn!==null);if((Pe&6)!==0)throw Error(i(327));o=e.finishedWork;var f=e.finishedLanes;if(o===null)return null;if(e.finishedWork=null,e.finishedLanes=0,o===e.current)throw Error(i(177));e.callbackNode=null,e.callbackPriority=0;var h=o.lanes|o.childLanes;if(Vl(e,h),e===Ke&&(Ue=Ke=null,Ze=0),(o.subtreeFlags&2064)===0&&(o.flags&2064)===0||Ts||(Ts=!0,Ld(Cr,function(){return qr(),null})),h=(o.flags&15990)!==0,(o.subtreeFlags&15990)!==0||h){h=At.transition,At.transition=null;var x=je;je=1;var I=Pe;Pe|=4,ta.current=null,v0(e,o),wd(o,e),Um(cu),Ui=!!au,cu=au=null,e.current=o,x0(o),$i(),Pe=I,je=x,At.transition=h}else e.current=o;if(Ts&&(Ts=!1,Dn=e,Is=f),h=e.pendingLanes,h===0&&(Rn=null),Dl(o.stateNode),gt(e,Oe()),n!==null)for(s=e.onRecoverableError,o=0;o<n.length;o++)f=n[o],s(f.value,{componentStack:f.stack,digest:f.digest});if(Ps)throw Ps=!1,e=oa,oa=null,e;return(Is&1)!==0&&e.tag!==0&&qr(),h=e.pendingLanes,(h&1)!==0?e===ia?qo++:(qo=0,ia=e):qo=0,zn(),null}function qr(){if(Dn!==null){var e=yc(Is),n=At.transition,o=je;try{if(At.transition=null,je=16>e?16:e,Dn===null)var s=!1;else{if(e=Dn,Dn=null,Is=0,(Pe&6)!==0)throw Error(i(331));var f=Pe;for(Pe|=4,de=e.current;de!==null;){var h=de,x=h.child;if((de.flags&16)!==0){var I=h.deletions;if(I!==null){for(var R=0;R<I.length;R++){var K=I[R];for(de=K;de!==null;){var oe=de;switch(oe.tag){case 0:case 11:case 15:Qo(8,oe,h)}var ie=oe.child;if(ie!==null)ie.return=oe,de=ie;else for(;de!==null;){oe=de;var ne=oe.sibling,fe=oe.return;if(gd(oe),oe===K){de=null;break}if(ne!==null){ne.return=fe,de=ne;break}de=fe}}}var he=h.alternate;if(he!==null){var ge=he.child;if(ge!==null){he.child=null;do{var Be=ge.sibling;ge.sibling=null,ge=Be}while(ge!==null)}}de=h}}if((h.subtreeFlags&2064)!==0&&x!==null)x.return=h,de=x;else e:for(;de!==null;){if(h=de,(h.flags&2048)!==0)switch(h.tag){case 0:case 11:case 15:Qo(9,h,h.return)}var W=h.sibling;if(W!==null){W.return=h.return,de=W;break e}de=h.return}}var $=e.current;for(de=$;de!==null;){x=de;var X=x.child;if((x.subtreeFlags&2064)!==0&&X!==null)X.return=x,de=X;else e:for(x=$;de!==null;){if(I=de,(I.flags&2048)!==0)try{switch(I.tag){case 0:case 11:case 15:Cs(9,I)}}catch(me){be(I,I.return,me)}if(I===x){de=null;break e}var le=I.sibling;if(le!==null){le.return=I.return,de=le;break e}de=I.return}}if(Pe=f,zn(),xt&&typeof xt.onPostCommitFiberRoot=="function")try{xt.onPostCommitFiberRoot(Nr,e)}catch{}s=!0}return s}finally{je=o,At.transition=n}}return!1}function zd(e,n,o){n=Xr(o,n),n=Kf(e,n,1),e=An(e,n,1),n=lt(),e!==null&&(Jn(e,1,n),gt(e,n))}function be(e,n,o){if(e.tag===3)zd(e,e,o);else for(;n!==null;){if(n.tag===3){zd(n,e,o);break}else if(n.tag===1){var s=n.stateNode;if(typeof n.type.getDerivedStateFromError=="function"||typeof s.componentDidCatch=="function"&&(Rn===null||!Rn.has(s))){e=Xr(o,e),e=Gf(n,e,1),n=An(n,e,1),e=lt(),n!==null&&(Jn(n,1,e),gt(n,e));break}}n=n.return}}function C0(e,n,o){var s=e.pingCache;s!==null&&s.delete(n),n=lt(),e.pingedLanes|=e.suspendedLanes&o,Ke===e&&(Ze&o)===o&&(Xe===4||Xe===3&&(Ze&130023424)===Ze&&500>Oe()-ra?ar(e,0):na|=o),gt(e,n)}function jd(e,n){n===0&&((e.mode&1)===0?n=1:(n=Pr,Pr<<=1,(Pr&130023424)===0&&(Pr=4194304)));var o=lt();e=hn(e,n),e!==null&&(Jn(e,n,o),gt(e,o))}function N0(e){var n=e.memoizedState,o=0;n!==null&&(o=n.retryLane),jd(e,o)}function M0(e,n){var o=0;switch(e.tag){case 13:var s=e.stateNode,f=e.memoizedState;f!==null&&(o=f.retryLane);break;case 19:s=e.stateNode;break;default:throw Error(i(314))}s!==null&&s.delete(n),jd(e,o)}var Ad;Ad=function(e,n,o){if(e!==null)if(e.memoizedProps!==n.pendingProps||ft.current)ht=!0;else{if((e.lanes&o)===0&&(n.flags&128)===0)return ht=!1,p0(e,n,o);ht=(e.flags&131072)!==0}else ht=!1,$e&&(n.flags&1048576)!==0&&df(n,us,n.index);switch(n.lanes=0,n.tag){case 2:var s=n.type;ks(e,n),e=n.pendingProps;var f=Fr(n,tt.current);Wr(n,o),f=Lu(null,n,s,e,f,o);var h=Ru();return n.flags|=1,typeof f=="object"&&f!==null&&typeof f.render=="function"&&f.$$typeof===void 0?(n.tag=1,n.memoizedState=null,n.updateQueue=null,dt(s)?(h=!0,is(n)):h=!1,n.memoizedState=f.state!==null&&f.state!==void 0?f.state:null,Mu(n),f.updater=_s,n.stateNode=f,f._reactInternals=n,Vu(n,s,e,o),n=Wu(null,n,s,!0,h,o)):(n.tag=0,$e&&h&&yu(n),st(null,n,f,o),n=n.child),n;case 16:s=n.elementType;e:{switch(ks(e,n),e=n.pendingProps,f=s._init,s=f(s._payload),n.type=s,f=n.tag=T0(s),e=$t(s,e),f){case 0:n=Uu(null,n,s,e,o);break e;case 1:n=id(null,n,s,e,o);break e;case 11:n=ed(null,n,s,e,o);break e;case 14:n=td(null,n,s,$t(s.type,e),o);break e}throw Error(i(306,s,""))}return n;case 0:return s=n.type,f=n.pendingProps,f=n.elementType===s?f:$t(s,f),Uu(e,n,s,f,o);case 1:return s=n.type,f=n.pendingProps,f=n.elementType===s?f:$t(s,f),id(e,n,s,f,o);case 3:e:{if(sd(n),e===null)throw Error(i(387));s=n.pendingProps,h=n.memoizedState,f=h.element,_f(e,n),ps(n,s,null,o);var x=n.memoizedState;if(s=x.element,h.isDehydrated)if(h={element:s,isDehydrated:!1,cache:x.cache,pendingSuspenseBoundaries:x.pendingSuspenseBoundaries,transitions:x.transitions},n.updateQueue.baseState=h,n.memoizedState=h,n.flags&256){f=Xr(Error(i(423)),n),n=ld(e,n,s,o,f);break e}else if(s!==f){f=Xr(Error(i(424)),n),n=ld(e,n,s,o,f);break e}else for(kt=Pn(n.stateNode.containerInfo.firstChild),St=n,$e=!0,Dt=null,o=xf(n,null,s,o),n.child=o;o;)o.flags=o.flags&-3|4096,o=o.sibling;else{if(br(),s===f){n=gn(e,n,o);break e}st(e,n,s,o)}n=n.child}return n;case 5:return Ef(n),e===null&&wu(n),s=n.type,f=n.pendingProps,h=e!==null?e.memoizedProps:null,x=f.children,fu(s,f)?x=null:h!==null&&fu(s,h)&&(n.flags|=32),od(e,n),st(e,n,x,o),n.child;case 6:return e===null&&wu(n),null;case 13:return ud(e,n,o);case 4:return Pu(n,n.stateNode.containerInfo),s=n.pendingProps,e===null?n.child=Br(n,null,s,o):st(e,n,s,o),n.child;case 11:return s=n.type,f=n.pendingProps,f=n.elementType===s?f:$t(s,f),ed(e,n,s,f,o);case 7:return st(e,n,n.pendingProps,o),n.child;case 8:return st(e,n,n.pendingProps.children,o),n.child;case 12:return st(e,n,n.pendingProps.children,o),n.child;case 10:e:{if(s=n.type._context,f=n.pendingProps,h=n.memoizedProps,x=f.value,Ae(fs,s._currentValue),s._currentValue=x,h!==null)if(Rt(h.value,x)){if(h.children===f.children&&!ft.current){n=gn(e,n,o);break e}}else for(h=n.child,h!==null&&(h.return=n);h!==null;){var I=h.dependencies;if(I!==null){x=h.child;for(var R=I.firstContext;R!==null;){if(R.context===s){if(h.tag===1){R=pn(-1,o&-o),R.tag=2;var K=h.updateQueue;if(K!==null){K=K.shared;var oe=K.pending;oe===null?R.next=R:(R.next=oe.next,oe.next=R),K.pending=R}}h.lanes|=o,R=h.alternate,R!==null&&(R.lanes|=o),Cu(h.return,o,n),I.lanes|=o;break}R=R.next}}else if(h.tag===10)x=h.type===n.type?null:h.child;else if(h.tag===18){if(x=h.return,x===null)throw Error(i(341));x.lanes|=o,I=x.alternate,I!==null&&(I.lanes|=o),Cu(x,o,n),x=h.sibling}else x=h.child;if(x!==null)x.return=h;else for(x=h;x!==null;){if(x===n){x=null;break}if(h=x.sibling,h!==null){h.return=x.return,x=h;break}x=x.return}h=x}st(e,n,f.children,o),n=n.child}return n;case 9:return f=n.type,s=n.pendingProps.children,Wr(n,o),f=zt(f),s=s(f),n.flags|=1,st(e,n,s,o),n.child;case 14:return s=n.type,f=$t(s,n.pendingProps),f=$t(s.type,f),td(e,n,s,f,o);case 15:return nd(e,n,n.type,n.pendingProps,o);case 17:return s=n.type,f=n.pendingProps,f=n.elementType===s?f:$t(s,f),ks(e,n),n.tag=1,dt(s)?(e=!0,is(n)):e=!1,Wr(n,o),Xf(n,s,f),Vu(n,s,f,o),Wu(null,n,s,!0,e,o);case 19:return cd(e,n,o);case 22:return rd(e,n,o)}throw Error(i(156,n.tag))};function Ld(e,n){return Ri(e,n)}function P0(e,n,o,s){this.tag=e,this.key=o,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=n,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=s,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Lt(e,n,o,s){return new P0(e,n,o,s)}function fa(e){return e=e.prototype,!(!e||!e.isReactComponent)}function T0(e){if(typeof e=="function")return fa(e)?1:0;if(e!=null){if(e=e.$$typeof,e===J)return 11;if(e===b)return 14}return 2}function Fn(e,n){var o=e.alternate;return o===null?(o=Lt(e.tag,n,e.key,e.mode),o.elementType=e.elementType,o.type=e.type,o.stateNode=e.stateNode,o.alternate=e,e.alternate=o):(o.pendingProps=n,o.type=e.type,o.flags=0,o.subtreeFlags=0,o.deletions=null),o.flags=e.flags&14680064,o.childLanes=e.childLanes,o.lanes=e.lanes,o.child=e.child,o.memoizedProps=e.memoizedProps,o.memoizedState=e.memoizedState,o.updateQueue=e.updateQueue,n=e.dependencies,o.dependencies=n===null?null:{lanes:n.lanes,firstContext:n.firstContext},o.sibling=e.sibling,o.index=e.index,o.ref=e.ref,o}function Ls(e,n,o,s,f,h){var x=2;if(s=e,typeof e=="function")fa(e)&&(x=1);else if(typeof e=="string")x=5;else e:switch(e){case V:return fr(o.children,f,h,n);case Q:x=8,f|=8;break;case Y:return e=Lt(12,o,n,f|2),e.elementType=Y,e.lanes=h,e;case Z:return e=Lt(13,o,n,f),e.elementType=Z,e.lanes=h,e;case N:return e=Lt(19,o,n,f),e.elementType=N,e.lanes=h,e;case B:return Rs(o,f,h,n);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case ee:x=10;break e;case q:x=9;break e;case J:x=11;break e;case b:x=14;break e;case H:x=16,s=null;break e}throw Error(i(130,e==null?e:typeof e,""))}return n=Lt(x,o,n,f),n.elementType=e,n.type=s,n.lanes=h,n}function fr(e,n,o,s){return e=Lt(7,e,s,n),e.lanes=o,e}function Rs(e,n,o,s){return e=Lt(22,e,s,n),e.elementType=B,e.lanes=o,e.stateNode={isHidden:!1},e}function da(e,n,o){return e=Lt(6,e,null,n),e.lanes=o,e}function ha(e,n,o){return n=Lt(4,e.children!==null?e.children:[],e.key,n),n.lanes=o,n.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},n}function I0(e,n,o,s,f){this.tag=n,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Zn(0),this.expirationTimes=Zn(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Zn(0),this.identifierPrefix=s,this.onRecoverableError=f,this.mutableSourceEagerHydrationData=null}function pa(e,n,o,s,f,h,x,I,R){return e=new I0(e,n,o,I,R),n===1?(n=1,h===!0&&(n|=8)):n=0,h=Lt(3,null,null,n),e.current=h,h.stateNode=e,h.memoizedState={element:s,isDehydrated:o,cache:null,transitions:null,pendingSuspenseBoundaries:null},Mu(h),e}function z0(e,n,o){var s=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:D,key:s==null?null:""+s,children:e,containerInfo:n,implementation:o}}function Rd(e){if(!e)return In;e=e._reactInternals;e:{if(Gt(e)!==e||e.tag!==1)throw Error(i(170));var n=e;do{switch(n.tag){case 3:n=n.stateNode.context;break e;case 1:if(dt(n.type)){n=n.stateNode.__reactInternalMemoizedMergedChildContext;break e}}n=n.return}while(n!==null);throw Error(i(171))}if(e.tag===1){var o=e.type;if(dt(o))return af(e,o,n)}return n}function Dd(e,n,o,s,f,h,x,I,R){return e=pa(o,s,!0,e,f,h,x,I,R),e.context=Rd(null),o=e.current,s=lt(),f=$n(o),h=pn(s,f),h.callback=n??null,An(o,h,f),e.current.lanes=f,Jn(e,f,s),gt(e,s),e}function Ds(e,n,o,s){var f=n.current,h=lt(),x=$n(f);return o=Rd(o),n.context===null?n.context=o:n.pendingContext=o,n=pn(h,x),n.payload={element:e},s=s===void 0?null:s,s!==null&&(n.callback=s),e=An(f,n,x),e!==null&&(Ht(e,f,x,h),hs(e,f,x)),x}function $s(e){if(e=e.current,!e.child)return null;switch(e.child.tag){case 5:return e.child.stateNode;default:return e.child.stateNode}}function $d(e,n){if(e=e.memoizedState,e!==null&&e.dehydrated!==null){var o=e.retryLane;e.retryLane=o!==0&&o<n?o:n}}function ga(e,n){$d(e,n),(e=e.alternate)&&$d(e,n)}function j0(){return null}var Od=typeof reportError=="function"?reportError:function(e){console.error(e)};function ma(e){this._internalRoot=e}Os.prototype.render=ma.prototype.render=function(e){var n=this._internalRoot;if(n===null)throw Error(i(409));Ds(e,n,null,null)},Os.prototype.unmount=ma.prototype.unmount=function(){var e=this._internalRoot;if(e!==null){this._internalRoot=null;var n=e.containerInfo;ur(function(){Ds(null,e,null,null)}),n[an]=null}};function Os(e){this._internalRoot=e}Os.prototype.unstable_scheduleHydration=function(e){if(e){var n=wc();e={blockedOn:null,target:e,priority:n};for(var o=0;o<Cn.length&&n!==0&&n<Cn[o].priority;o++);Cn.splice(o,0,e),o===0&&kc(e)}};function ya(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11)}function Fs(e){return!(!e||e.nodeType!==1&&e.nodeType!==9&&e.nodeType!==11&&(e.nodeType!==8||e.nodeValue!==" react-mount-point-unstable "))}function Fd(){}function A0(e,n,o,s,f){if(f){if(typeof s=="function"){var h=s;s=function(){var K=$s(x);h.call(K)}}var x=Dd(n,s,e,0,null,!1,!1,"",Fd);return e._reactRootContainer=x,e[an]=x.current,Ro(e.nodeType===8?e.parentNode:e),ur(),x}for(;f=e.lastChild;)e.removeChild(f);if(typeof s=="function"){var I=s;s=function(){var K=$s(R);I.call(K)}}var R=pa(e,0,!1,null,null,!1,!1,"",Fd);return e._reactRootContainer=R,e[an]=R.current,Ro(e.nodeType===8?e.parentNode:e),ur(function(){Ds(n,R,o,s)}),R}function Hs(e,n,o,s,f){var h=o._reactRootContainer;if(h){var x=h;if(typeof f=="function"){var I=f;f=function(){var R=$s(x);I.call(R)}}Ds(n,x,e,f)}else x=A0(o,n,e,f,s);return $s(x)}vc=function(e){switch(e.tag){case 3:var n=e.stateNode;if(n.current.memoizedState.isDehydrated){var o=qn(n.pendingLanes);o!==0&&(Tr(n,o|1),gt(n,Oe()),(Pe&6)===0&&(Gr=Oe()+500,zn()))}break;case 13:ur(function(){var s=hn(e,1);if(s!==null){var f=lt();Ht(s,e,1,f)}}),ga(e,1)}},bl=function(e){if(e.tag===13){var n=hn(e,134217728);if(n!==null){var o=lt();Ht(n,e,134217728,o)}ga(e,134217728)}},xc=function(e){if(e.tag===13){var n=$n(e),o=hn(e,n);if(o!==null){var s=lt();Ht(o,e,n,s)}ga(e,n)}},wc=function(){return je},_c=function(e,n){var o=je;try{return je=e,n()}finally{je=o}},po=function(e,n,o){switch(n){case"input":if(ze(e,o),n=o.name,o.type==="radio"&&n!=null){for(o=e;o.parentNode;)o=o.parentNode;for(o=o.querySelectorAll("input[name="+JSON.stringify(""+n)+'][type="radio"]'),n=0;n<o.length;n++){var s=o[n];if(s!==e&&s.form===e.form){var f=rs(s);if(!f)throw Error(i(90));ye(s),ze(s,f)}}}break;case"textarea":Kt(e,o);break;case"select":n=o.value,n!=null&&et(e,!!o.multiple,n,!1)}},Ii=ua,zi=ur;var L0={usingClientEntryPoint:!1,Events:[Oo,$r,rs,Pi,Ti,ua]},Zo={findFiberByHostInstance:er,bundleType:0,version:"18.3.1",rendererPackageName:"react-dom"},R0={bundleType:Zo.bundleType,version:Zo.version,rendererPackageName:Zo.rendererPackageName,rendererConfig:Zo.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setErrorHandler:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:T.ReactCurrentDispatcher,findHostInstanceByFiber:function(e){return e=Ai(e),e===null?null:e.stateNode},findFiberByHostInstance:Zo.findFiberByHostInstance||j0,findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null,reconcilerVersion:"18.3.1-next-f1338f8080-20240426"};if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__<"u"){var Vs=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(!Vs.isDisabled&&Vs.supportsFiber)try{Nr=Vs.inject(R0),xt=Vs}catch{}}return mt.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=L0,mt.createPortal=function(e,n){var o=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!ya(n))throw Error(i(200));return z0(e,n,null,o)},mt.createRoot=function(e,n){if(!ya(e))throw Error(i(299));var o=!1,s="",f=Od;return n!=null&&(n.unstable_strictMode===!0&&(o=!0),n.identifierPrefix!==void 0&&(s=n.identifierPrefix),n.onRecoverableError!==void 0&&(f=n.onRecoverableError)),n=pa(e,1,!1,null,null,o,!1,s,f),e[an]=n.current,Ro(e.nodeType===8?e.parentNode:e),new ma(n)},mt.findDOMNode=function(e){if(e==null)return null;if(e.nodeType===1)return e;var n=e._reactInternals;if(n===void 0)throw typeof e.render=="function"?Error(i(188)):(e=Object.keys(e).join(","),Error(i(268,e)));return e=Ai(n),e=e===null?null:e.stateNode,e},mt.flushSync=function(e){return ur(e)},mt.hydrate=function(e,n,o){if(!Fs(n))throw Error(i(200));return Hs(null,e,n,!0,o)},mt.hydrateRoot=function(e,n,o){if(!ya(e))throw Error(i(405));var s=o!=null&&o.hydratedSources||null,f=!1,h="",x=Od;if(o!=null&&(o.unstable_strictMode===!0&&(f=!0),o.identifierPrefix!==void 0&&(h=o.identifierPrefix),o.onRecoverableError!==void 0&&(x=o.onRecoverableError)),n=Dd(n,null,e,1,o??null,f,!1,h,x),e[an]=n.current,Ro(e),s)for(e=0;e<s.length;e++)o=s[e],f=o._getVersion,f=f(o._source),n.mutableSourceEagerHydrationData==null?n.mutableSourceEagerHydrationData=[o,f]:n.mutableSourceEagerHydrationData.push(o,f);return new Os(n)},mt.render=function(e,n,o){if(!Fs(n))throw Error(i(200));return Hs(null,e,n,!1,o)},mt.unmountComponentAtNode=function(e){if(!Fs(e))throw Error(i(40));return e._reactRootContainer?(ur(function(){Hs(null,null,e,!1,function(){e._reactRootContainer=null,e[an]=null})}),!0):!1},mt.unstable_batchedUpdates=ua,mt.unstable_renderSubtreeIntoContainer=function(e,n,o,s){if(!Fs(o))throw Error(i(200));if(e==null||e._reactInternals===void 0)throw Error(i(38));return Hs(e,n,o,!1,s)},mt.version="18.3.1-next-f1338f8080-20240426",mt}var Xd;function cp(){if(Xd)return wa.exports;Xd=1;function t(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(r){console.error(r)}}return t(),wa.exports=b0(),wa.exports}var Qd;function B0(){if(Qd)return bs;Qd=1;var t=cp();return bs.createRoot=t.createRoot,bs.hydrateRoot=t.hydrateRoot,bs}var U0=B0();async function tn(t){if(!t.ok){let r;try{r=(await t.json()).detail}catch{r=t.statusText}throw new rl(t.status,r)}return t.json()}class rl extends Error{constructor(r,i){super(typeof i=="string"?i:JSON.stringify(i)),this.status=r,this.detail=i}}const ut={schema:()=>fetch("/api/step-schema").then(t=>tn(t)),listFlows:()=>fetch("/api/flows").then(t=>tn(t)),getFlow:t=>fetch(`/api/flows/${encodeURIComponent(t)}`).then(r=>tn(r)),saveFlow:(t,r)=>fetch(`/api/flows/${encodeURIComponent(t)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)}).then(i=>tn(i)),deleteFlow:t=>fetch(`/api/flows/${encodeURIComponent(t)}`,{method:"DELETE"}).then(r=>tn(r)),runFlow:(t,r)=>fetch(`/api/flows/${encodeURIComponent(t)}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({close_mode:r})}).then(i=>tn(i)),assist:(t,r,i)=>fetch(`/api/flows/${encodeURIComponent(t)}/assist`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({instruction:r,steps:i.steps,settings:i.settings})}).then(l=>tn(l)),run:t=>fetch(`/api/runs/${t}`).then(r=>tn(r)),closeBrowser:t=>fetch(`/api/runs/${t}/close-browser`,{method:"POST"}).then(r=>tn(r)),showTrace:t=>fetch(`/api/runs/${t}/show-trace`,{method:"POST"}).then(r=>tn(r)),exportUrl:t=>`/api/flows/${encodeURIComponent(t)}/export`},Kd={en:{app_title:"RPA Tool",page_title:"RPA Tool – flow builder",new_flow:"New flow",delete_flow:"Delete flow",save:"Save",saved:"Saved",unsaved:"Unsaved changes",run:"Run flow",close_browser:"Close browser",show_trace:"Show trace",wiki:"Wiki",settings:"⚙ Settings",export:"Export as script",close_always:"Close the browser",close_on_success:"Keep open on error",close_never:"Always keep open",add_step:"+ Add step",add_step_after:"Add step after",add_step_in:"+ Add step",add_branch:"+ Add 'else if' branch",else_toggle:"…else (else)",remove:"Remove",remove_branch:"Remove branch",branch_if:"If",branch_else_if:"…else if",add_field:"+ Add column",column_name:"Column name",label_ph:"Label (optional)",on_error_stop:"On error: stop",on_error_continue:"On error: continue",enabled_title:"Enabled (unchecked = skipped when running)",move_up:"Move up",move_down:"Move down",run_log:"Run log",variables:"Variables",no_vars:"No variables yet.",vars_hint:"Click a name to see its content (filled in once the flow finishes).",assist_title:"AI assistant",assist_hint:"Describe in plain language what the flow should do. The suggestion replaces the canvas (unsaved) so you can review it before saving/running.",assist_ph:"e.g. Go to example.com, wait for login, list all links and save them as CSV",assist_btn:"Suggest changes",assist_working:"Thinking…",assist_applied:"Suggestion applied — review, then Save / Run flow.",assist_unavailable:"Set ANTHROPIC_API_KEY (or run `ant auth login`) and restart the server to use this.",assist_failed:"Could not generate a flow: {msg}",status_running:"Running…",status_done:"Done",status_error:"Error",condition:"Condition",new_flow_prompt:"Name of new flow:",delete_flow_confirm:'Delete the flow "{name}"?',settings_hint:"Saved with the flow, so they travel along when you export it as a standalone script.",cancel:"Cancel",fit_view:"Fit to view",relayout:"Auto-arrange",var_list:"list, {n} item(s)",var_object:"object",var_text:"text, {n} chars",save_failed:"Save failed: {msg}"},sv:{app_title:"RPA-verktyg",page_title:"RPA-verktyg – flödesbyggare",new_flow:"Nytt flöde",delete_flow:"Ta bort flöde",save:"Spara",saved:"Sparat",unsaved:"Osparade ändringar",run:"Kör flöde",close_browser:"Stäng webbläsare",show_trace:"Visa trace",wiki:"Wiki",settings:"⚙ Inställningar",export:"Exportera som script",close_always:"Stäng webbläsaren",close_on_success:"Håll öppen vid fel",close_never:"Håll alltid öppen",add_step:"+ Lägg till steg",add_step_after:"Lägg till steg efter",add_step_in:"+ Lägg till steg",add_branch:"+ Lägg till 'else if'-gren",else_toggle:"…annars (else)",remove:"Ta bort",remove_branch:"Ta bort gren",branch_if:"Om",branch_else_if:"…annars om",add_field:"+ Lägg till kolumn",column_name:"Kolumnnamn",label_ph:"Etikett (valfri)",on_error_stop:"Vid fel: stanna",on_error_continue:"Vid fel: fortsätt",enabled_title:"Aktiverat (avbockad = hoppas över vid körning)",move_up:"Flytta upp",move_down:"Flytta ner",run_log:"Körlogg",variables:"Variabler",no_vars:"Inga variabler ännu.",vars_hint:"Klicka på ett namn för att se innehållet (fylls i när flödet är klart).",assist_title:"AI-assistent",assist_hint:"Beskriv i klartext vad flödet ska göra. Förslaget ersätter canvasen (osparat) så du kan granska det innan du sparar/kör.",assist_ph:"t.ex. Gå till example.com, vänta på inloggning, lista alla länkar och spara som CSV",assist_btn:"Föreslå ändringar",assist_working:"Tänker…",assist_applied:"Förslag infogat — granska, sedan Spara / Kör flöde.",assist_unavailable:"Sätt ANTHROPIC_API_KEY (eller kör `ant auth login`) och starta om servern för att använda detta.",assist_failed:"Kunde inte generera ett flöde: {msg}",status_running:"Kör…",status_done:"Klar",status_error:"Fel",condition:"Villkor",new_flow_prompt:"Namn på nytt flöde:",delete_flow_confirm:'Ta bort flödet "{name}"?',settings_hint:"Sparas med flödet, så de följer med om du exporterar det som ett fristående script.",cancel:"Avbryt",fit_view:"Anpassa till vy",relayout:"Autoplacera",var_list:"lista, {n} post(er)",var_object:"objekt",var_text:"text, {n} tecken",save_failed:"Kunde inte spara: {msg}"}};function W0(t){return(r,i)=>{let l=Kd[t][r]??Kd.en[r]??r;if(i)for(const[u,a]of Object.entries(i))l=l.replace(`{${u}}`,String(a));return l}}function fp(){try{return crypto.randomUUID().slice(0,12)}catch{return Math.random().toString(36).slice(2,14)}}const Y0=150,X0=0;function Wn(t){var r,i,l;return t.id||(t.id=fp()),(r=t.steps)==null||r.forEach(Wn),(i=t.else)==null||i.forEach(Wn),(l=t.branches)==null||l.forEach(u=>u.steps.forEach(Wn)),t}function dp(t){const r=(t.steps??[]).map(c=>Wn({...c})),i=t.layout??{},l=r.map((c,d)=>({id:c.id,step:c,position:i[c.id]??{x:X0,y:d*Y0}})),u=[];for(let c=0;c<r.length-1;c++)u.push({id:`seq-${r[c].id}-${r[c+1].id}`,source:r[c].id,target:r[c+1].id,kind:"seq"});const a=new Map;for(const c of r)c.label&&a.set(c.label,c.id);for(const c of r)if(c.type==="jump_to_label"&&typeof c.target=="string"&&a.has(c.target)){const d=a.get(c.target);u.push({id:`jump-${c.id}-${d}`,source:c.id,target:d,kind:"jump"})}return{nodes:l,edges:u}}function mr(t,r){const i=t.step_types.find(u=>u.type===r),l={type:r,id:fp()};for(const u of(i==null?void 0:i.fields)??[])u.default!==void 0&&u.default!==""&&(l[u.key]=u.default);return r==="loop"&&(l.steps=[]),r==="if"&&(l.branches=[{condition:{kind:"element_exists"},steps:[]}]),r==="extract_row"&&(l.fields=[{name:"",by:"css",value:""}]),l}function hp(t,r,i){const l=mr(t,i);return l.id=r.id,r.label&&(l.label=r.label),r.enabled===!1&&(l.enabled=!1),r.on_error==="continue"&&(l.on_error="continue"),l}function pp(t,r,i){const l=t.slice();return l.splice(r,0,i),l}function gp(t,r){const i=t.slice();return i.splice(r,1),i}function Da(t,r,i){if(i<0||i>=t.length)return t;const l=t.slice(),[u]=l.splice(r,1);return l.splice(i,0,u),l}function ei(t,r,i){const l=t.slice();return l[r]=i,l}function Q0(){return{condition:{kind:"element_exists"},steps:[]}}function K0(t){const{t:r}=t;return E.jsxs("header",{children:[E.jsx("h1",{children:r("app_title")}),E.jsxs("select",{value:t.lang,onChange:i=>t.setLang(i.target.value),children:[E.jsx("option",{value:"en",children:"English"}),E.jsx("option",{value:"sv",children:"Svenska"})]}),E.jsx("select",{value:t.currentName??"",onChange:i=>t.onSelect(i.target.value),children:t.flowNames.map(i=>E.jsx("option",{value:i,children:i},i))}),E.jsx("button",{className:"secondary",onClick:t.onNew,children:r("new_flow")}),E.jsx("button",{className:"danger",onClick:t.onDelete,disabled:!t.currentName,children:r("delete_flow")}),E.jsx("a",{className:"secondary",href:"/wiki",target:"_blank",rel:"noopener",children:r("wiki")}),E.jsx("button",{className:"secondary",onClick:t.onOpenSettings,children:r("settings")}),E.jsx("a",{className:"secondary",href:t.currentName?ut.exportUrl(t.currentName):"#",target:"_blank",rel:"noopener",children:r("export")}),E.jsx("span",{className:"spacer"}),E.jsx("span",{className:"dirty-dot "+(t.dirty?"on":""),title:t.dirty?r("unsaved"):r("saved")}),E.jsxs("select",{value:t.closeMode,onChange:i=>t.setCloseMode(i.target.value),children:[E.jsx("option",{value:"always",children:r("close_always")}),E.jsx("option",{value:"on_success",children:r("close_on_success")}),E.jsx("option",{value:"never",children:r("close_never")})]}),E.jsx("button",{className:"secondary",onClick:t.onSave,children:r("save")}),E.jsx("button",{onClick:t.onRun,disabled:t.running||!t.currentName,children:r("run")})]})}function We(t){if(typeof t=="string"||typeof t=="number")return""+t;let r="";if(Array.isArray(t))for(let i=0,l;i<t.length;i++)(l=We(t[i]))!==""&&(r+=(r&&" ")+l);else for(let i in t)t[i]&&(r+=(r&&" ")+i);return r}var G0={value:()=>{}};function gl(){for(var t=0,r=arguments.length,i={},l;t<r;++t){if(!(l=arguments[t]+"")||l in i||/[\s.]/.test(l))throw new Error("illegal type: "+l);i[l]=[]}return new Zs(i)}function Zs(t){this._=t}function q0(t,r){return t.trim().split(/^|\s+/).map(function(i){var l="",u=i.indexOf(".");if(u>=0&&(l=i.slice(u+1),i=i.slice(0,u)),i&&!r.hasOwnProperty(i))throw new Error("unknown type: "+i);return{type:i,name:l}})}Zs.prototype=gl.prototype={constructor:Zs,on:function(t,r){var i=this._,l=q0(t+"",i),u,a=-1,c=l.length;if(arguments.length<2){for(;++a<c;)if((u=(t=l[a]).type)&&(u=Z0(i[u],t.name)))return u;return}if(r!=null&&typeof r!="function")throw new Error("invalid callback: "+r);for(;++a<c;)if(u=(t=l[a]).type)i[u]=Gd(i[u],t.name,r);else if(r==null)for(u in i)i[u]=Gd(i[u],t.name,null);return this},copy:function(){var t={},r=this._;for(var i in r)t[i]=r[i].slice();return new Zs(t)},call:function(t,r){if((u=arguments.length-2)>0)for(var i=new Array(u),l=0,u,a;l<u;++l)i[l]=arguments[l+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=this._[t],l=0,u=a.length;l<u;++l)a[l].value.apply(r,i)},apply:function(t,r,i){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var l=this._[t],u=0,a=l.length;u<a;++u)l[u].value.apply(r,i)}};function Z0(t,r){for(var i=0,l=t.length,u;i<l;++i)if((u=t[i]).name===r)return u.value}function Gd(t,r,i){for(var l=0,u=t.length;l<u;++l)if(t[l].name===r){t[l]=G0,t=t.slice(0,l).concat(t.slice(l+1));break}return i!=null&&t.push({name:r,value:i}),t}var $a="http://www.w3.org/1999/xhtml";const qd={svg:"http://www.w3.org/2000/svg",xhtml:$a,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function ml(t){var r=t+="",i=r.indexOf(":");return i>=0&&(r=t.slice(0,i))!=="xmlns"&&(t=t.slice(i+1)),qd.hasOwnProperty(r)?{space:qd[r],local:t}:t}function J0(t){return function(){var r=this.ownerDocument,i=this.namespaceURI;return i===$a&&r.documentElement.namespaceURI===$a?r.createElement(t):r.createElementNS(i,t)}}function ey(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function mp(t){var r=ml(t);return(r.local?ey:J0)(r)}function ty(){}function Za(t){return t==null?ty:function(){return this.querySelector(t)}}function ny(t){typeof t!="function"&&(t=Za(t));for(var r=this._groups,i=r.length,l=new Array(i),u=0;u<i;++u)for(var a=r[u],c=a.length,d=l[u]=new Array(c),p,g,v=0;v<c;++v)(p=a[v])&&(g=t.call(p,p.__data__,v,a))&&("__data__"in p&&(g.__data__=p.__data__),d[v]=g);return new Nt(l,this._parents)}function ry(t){return t==null?[]:Array.isArray(t)?t:Array.from(t)}function oy(){return[]}function yp(t){return t==null?oy:function(){return this.querySelectorAll(t)}}function iy(t){return function(){return ry(t.apply(this,arguments))}}function sy(t){typeof t=="function"?t=iy(t):t=yp(t);for(var r=this._groups,i=r.length,l=[],u=[],a=0;a<i;++a)for(var c=r[a],d=c.length,p,g=0;g<d;++g)(p=c[g])&&(l.push(t.call(p,p.__data__,g,c)),u.push(p));return new Nt(l,u)}function vp(t){return function(){return this.matches(t)}}function xp(t){return function(r){return r.matches(t)}}var ly=Array.prototype.find;function uy(t){return function(){return ly.call(this.children,t)}}function ay(){return this.firstElementChild}function cy(t){return this.select(t==null?ay:uy(typeof t=="function"?t:xp(t)))}var fy=Array.prototype.filter;function dy(){return Array.from(this.children)}function hy(t){return function(){return fy.call(this.children,t)}}function py(t){return this.selectAll(t==null?dy:hy(typeof t=="function"?t:xp(t)))}function gy(t){typeof t!="function"&&(t=vp(t));for(var r=this._groups,i=r.length,l=new Array(i),u=0;u<i;++u)for(var a=r[u],c=a.length,d=l[u]=[],p,g=0;g<c;++g)(p=a[g])&&t.call(p,p.__data__,g,a)&&d.push(p);return new Nt(l,this._parents)}function wp(t){return new Array(t.length)}function my(){return new Nt(this._enter||this._groups.map(wp),this._parents)}function ol(t,r){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=r}ol.prototype={constructor:ol,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,r){return this._parent.insertBefore(t,r)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};function yy(t){return function(){return t}}function vy(t,r,i,l,u,a){for(var c=0,d,p=r.length,g=a.length;c<g;++c)(d=r[c])?(d.__data__=a[c],l[c]=d):i[c]=new ol(t,a[c]);for(;c<p;++c)(d=r[c])&&(u[c]=d)}function xy(t,r,i,l,u,a,c){var d,p,g=new Map,v=r.length,y=a.length,m=new Array(v),w;for(d=0;d<v;++d)(p=r[d])&&(m[d]=w=c.call(p,p.__data__,d,r)+"",g.has(w)?u[d]=p:g.set(w,p));for(d=0;d<y;++d)w=c.call(t,a[d],d,a)+"",(p=g.get(w))?(l[d]=p,p.__data__=a[d],g.delete(w)):i[d]=new ol(t,a[d]);for(d=0;d<v;++d)(p=r[d])&&g.get(m[d])===p&&(u[d]=p)}function wy(t){return t.__data__}function _y(t,r){if(!arguments.length)return Array.from(this,wy);var i=r?xy:vy,l=this._parents,u=this._groups;typeof t!="function"&&(t=yy(t));for(var a=u.length,c=new Array(a),d=new Array(a),p=new Array(a),g=0;g<a;++g){var v=l[g],y=u[g],m=y.length,w=Sy(t.call(v,v&&v.__data__,g,l)),S=w.length,P=d[g]=new Array(S),C=c[g]=new Array(S),k=p[g]=new Array(m);i(v,y,P,C,k,w,r);for(var j=0,_=0,T,F;j<S;++j)if(T=P[j]){for(j>=_&&(_=j+1);!(F=C[_])&&++_<S;);T._next=F||null}}return c=new Nt(c,l),c._enter=d,c._exit=p,c}function Sy(t){return typeof t=="object"&&"length"in t?t:Array.from(t)}function ky(){return new Nt(this._exit||this._groups.map(wp),this._parents)}function Ey(t,r,i){var l=this.enter(),u=this,a=this.exit();return typeof t=="function"?(l=t(l),l&&(l=l.selection())):l=l.append(t+""),r!=null&&(u=r(u),u&&(u=u.selection())),i==null?a.remove():i(a),l&&u?l.merge(u).order():u}function Cy(t){for(var r=t.selection?t.selection():t,i=this._groups,l=r._groups,u=i.length,a=l.length,c=Math.min(u,a),d=new Array(u),p=0;p<c;++p)for(var g=i[p],v=l[p],y=g.length,m=d[p]=new Array(y),w,S=0;S<y;++S)(w=g[S]||v[S])&&(m[S]=w);for(;p<u;++p)d[p]=i[p];return new Nt(d,this._parents)}function Ny(){for(var t=this._groups,r=-1,i=t.length;++r<i;)for(var l=t[r],u=l.length-1,a=l[u],c;--u>=0;)(c=l[u])&&(a&&c.compareDocumentPosition(a)^4&&a.parentNode.insertBefore(c,a),a=c);return this}function My(t){t||(t=Py);function r(y,m){return y&&m?t(y.__data__,m.__data__):!y-!m}for(var i=this._groups,l=i.length,u=new Array(l),a=0;a<l;++a){for(var c=i[a],d=c.length,p=u[a]=new Array(d),g,v=0;v<d;++v)(g=c[v])&&(p[v]=g);p.sort(r)}return new Nt(u,this._parents).order()}function Py(t,r){return t<r?-1:t>r?1:t>=r?0:NaN}function Ty(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this}function Iy(){return Array.from(this)}function zy(){for(var t=this._groups,r=0,i=t.length;r<i;++r)for(var l=t[r],u=0,a=l.length;u<a;++u){var c=l[u];if(c)return c}return null}function jy(){let t=0;for(const r of this)++t;return t}function Ay(){return!this.node()}function Ly(t){for(var r=this._groups,i=0,l=r.length;i<l;++i)for(var u=r[i],a=0,c=u.length,d;a<c;++a)(d=u[a])&&t.call(d,d.__data__,a,u);return this}function Ry(t){return function(){this.removeAttribute(t)}}function Dy(t){return function(){this.removeAttributeNS(t.space,t.local)}}function $y(t,r){return function(){this.setAttribute(t,r)}}function Oy(t,r){return function(){this.setAttributeNS(t.space,t.local,r)}}function Fy(t,r){return function(){var i=r.apply(this,arguments);i==null?this.removeAttribute(t):this.setAttribute(t,i)}}function Hy(t,r){return function(){var i=r.apply(this,arguments);i==null?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,i)}}function Vy(t,r){var i=ml(t);if(arguments.length<2){var l=this.node();return i.local?l.getAttributeNS(i.space,i.local):l.getAttribute(i)}return this.each((r==null?i.local?Dy:Ry:typeof r=="function"?i.local?Hy:Fy:i.local?Oy:$y)(i,r))}function _p(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function by(t){return function(){this.style.removeProperty(t)}}function By(t,r,i){return function(){this.style.setProperty(t,r,i)}}function Uy(t,r,i){return function(){var l=r.apply(this,arguments);l==null?this.style.removeProperty(t):this.style.setProperty(t,l,i)}}function Wy(t,r,i){return arguments.length>1?this.each((r==null?by:typeof r=="function"?Uy:By)(t,r,i??"")):no(this.node(),t)}function no(t,r){return t.style.getPropertyValue(r)||_p(t).getComputedStyle(t,null).getPropertyValue(r)}function Yy(t){return function(){delete this[t]}}function Xy(t,r){return function(){this[t]=r}}function Qy(t,r){return function(){var i=r.apply(this,arguments);i==null?delete this[t]:this[t]=i}}function Ky(t,r){return arguments.length>1?this.each((r==null?Yy:typeof r=="function"?Qy:Xy)(t,r)):this.node()[t]}function Sp(t){return t.trim().split(/^|\s+/)}function Ja(t){return t.classList||new kp(t)}function kp(t){this._node=t,this._names=Sp(t.getAttribute("class")||"")}kp.prototype={add:function(t){var r=this._names.indexOf(t);r<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var r=this._names.indexOf(t);r>=0&&(this._names.splice(r,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Ep(t,r){for(var i=Ja(t),l=-1,u=r.length;++l<u;)i.add(r[l])}function Cp(t,r){for(var i=Ja(t),l=-1,u=r.length;++l<u;)i.remove(r[l])}function Gy(t){return function(){Ep(this,t)}}function qy(t){return function(){Cp(this,t)}}function Zy(t,r){return function(){(r.apply(this,arguments)?Ep:Cp)(this,t)}}function Jy(t,r){var i=Sp(t+"");if(arguments.length<2){for(var l=Ja(this.node()),u=-1,a=i.length;++u<a;)if(!l.contains(i[u]))return!1;return!0}return this.each((typeof r=="function"?Zy:r?Gy:qy)(i,r))}function ev(){this.textContent=""}function tv(t){return function(){this.textContent=t}}function nv(t){return function(){var r=t.apply(this,arguments);this.textContent=r??""}}function rv(t){return arguments.length?this.each(t==null?ev:(typeof t=="function"?nv:tv)(t)):this.node().textContent}function ov(){this.innerHTML=""}function iv(t){return function(){this.innerHTML=t}}function sv(t){return function(){var r=t.apply(this,arguments);this.innerHTML=r??""}}function lv(t){return arguments.length?this.each(t==null?ov:(typeof t=="function"?sv:iv)(t)):this.node().innerHTML}function uv(){this.nextSibling&&this.parentNode.appendChild(this)}function av(){return this.each(uv)}function cv(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function fv(){return this.each(cv)}function dv(t){var r=typeof t=="function"?t:mp(t);return this.select(function(){return this.appendChild(r.apply(this,arguments))})}function hv(){return null}function pv(t,r){var i=typeof t=="function"?t:mp(t),l=r==null?hv:typeof r=="function"?r:Za(r);return this.select(function(){return this.insertBefore(i.apply(this,arguments),l.apply(this,arguments)||null)})}function gv(){var t=this.parentNode;t&&t.removeChild(this)}function mv(){return this.each(gv)}function yv(){var t=this.cloneNode(!1),r=this.parentNode;return r?r.insertBefore(t,this.nextSibling):t}function vv(){var t=this.cloneNode(!0),r=this.parentNode;return r?r.insertBefore(t,this.nextSibling):t}function xv(t){return this.select(t?vv:yv)}function wv(t){return arguments.length?this.property("__data__",t):this.node().__data__}function _v(t){return function(r){t.call(this,r,this.__data__)}}function Sv(t){return t.trim().split(/^|\s+/).map(function(r){var i="",l=r.indexOf(".");return l>=0&&(i=r.slice(l+1),r=r.slice(0,l)),{type:r,name:i}})}function kv(t){return function(){var r=this.__on;if(r){for(var i=0,l=-1,u=r.length,a;i<u;++i)a=r[i],(!t.type||a.type===t.type)&&a.name===t.name?this.removeEventListener(a.type,a.listener,a.options):r[++l]=a;++l?r.length=l:delete this.__on}}}function Ev(t,r,i){return function(){var l=this.__on,u,a=_v(r);if(l){for(var c=0,d=l.length;c<d;++c)if((u=l[c]).type===t.type&&u.name===t.name){this.removeEventListener(u.type,u.listener,u.options),this.addEventListener(u.type,u.listener=a,u.options=i),u.value=r;return}}this.addEventListener(t.type,a,i),u={type:t.type,name:t.name,value:r,listener:a,options:i},l?l.push(u):this.__on=[u]}}function Cv(t,r,i){var l=Sv(t+""),u,a=l.length,c;if(arguments.length<2){var d=this.node().__on;if(d){for(var p=0,g=d.length,v;p<g;++p)for(u=0,v=d[p];u<a;++u)if((c=l[u]).type===v.type&&c.name===v.name)return v.value}return}for(d=r?Ev:kv,u=0;u<a;++u)this.each(d(l[u],r,i));return this}function Np(t,r,i){var l=_p(t),u=l.CustomEvent;typeof u=="function"?u=new u(r,i):(u=l.document.createEvent("Event"),i?(u.initEvent(r,i.bubbles,i.cancelable),u.detail=i.detail):u.initEvent(r,!1,!1)),t.dispatchEvent(u)}function Nv(t,r){return function(){return Np(this,t,r)}}function Mv(t,r){return function(){return Np(this,t,r.apply(this,arguments))}}function Pv(t,r){return this.each((typeof r=="function"?Mv:Nv)(t,r))}function*Tv(){for(var t=this._groups,r=0,i=t.length;r<i;++r)for(var l=t[r],u=0,a=l.length,c;u<a;++u)(c=l[u])&&(yield c)}var Mp=[null];function Nt(t,r){this._groups=t,this._parents=r}function yi(){return new Nt([[document.documentElement]],Mp)}function Iv(){return this}Nt.prototype=yi.prototype={constructor:Nt,select:ny,selectAll:sy,selectChild:cy,selectChildren:py,filter:gy,data:_y,enter:my,exit:ky,join:Ey,merge:Cy,selection:Iv,order:Ny,sort:My,call:Ty,nodes:Iy,node:zy,size:jy,empty:Ay,each:Ly,attr:Vy,style:Wy,property:Ky,classed:Jy,text:rv,html:lv,raise:av,lower:fv,append:dv,insert:pv,remove:mv,clone:xv,datum:wv,on:Cv,dispatch:Pv,[Symbol.iterator]:Tv};function Ct(t){return typeof t=="string"?new Nt([[document.querySelector(t)]],[document.documentElement]):new Nt([[t]],Mp)}function zv(t){let r;for(;r=t.sourceEvent;)t=r;return t}function Vt(t,r){if(t=zv(t),r===void 0&&(r=t.currentTarget),r){var i=r.ownerSVGElement||r;if(i.createSVGPoint){var l=i.createSVGPoint();return l.x=t.clientX,l.y=t.clientY,l=l.matrixTransform(r.getScreenCTM().inverse()),[l.x,l.y]}if(r.getBoundingClientRect){var u=r.getBoundingClientRect();return[t.clientX-u.left-r.clientLeft,t.clientY-u.top-r.clientTop]}}return[t.pageX,t.pageY]}const jv={passive:!1},li={capture:!0,passive:!1};function ka(t){t.stopImmediatePropagation()}function eo(t){t.preventDefault(),t.stopImmediatePropagation()}function Pp(t){var r=t.document.documentElement,i=Ct(t).on("dragstart.drag",eo,li);"onselectstart"in r?i.on("selectstart.drag",eo,li):(r.__noselect=r.style.MozUserSelect,r.style.MozUserSelect="none")}function Tp(t,r){var i=t.document.documentElement,l=Ct(t).on("dragstart.drag",null);r&&(l.on("click.drag",eo,li),setTimeout(function(){l.on("click.drag",null)},0)),"onselectstart"in i?l.on("selectstart.drag",null):(i.style.MozUserSelect=i.__noselect,delete i.__noselect)}const Bs=t=>()=>t;function Oa(t,{sourceEvent:r,subject:i,target:l,identifier:u,active:a,x:c,y:d,dx:p,dy:g,dispatch:v}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:r,enumerable:!0,configurable:!0},subject:{value:i,enumerable:!0,configurable:!0},target:{value:l,enumerable:!0,configurable:!0},identifier:{value:u,enumerable:!0,configurable:!0},active:{value:a,enumerable:!0,configurable:!0},x:{value:c,enumerable:!0,configurable:!0},y:{value:d,enumerable:!0,configurable:!0},dx:{value:p,enumerable:!0,configurable:!0},dy:{value:g,enumerable:!0,configurable:!0},_:{value:v}})}Oa.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};function Av(t){return!t.ctrlKey&&!t.button}function Lv(){return this.parentNode}function Rv(t,r){return r??{x:t.x,y:t.y}}function Dv(){return navigator.maxTouchPoints||"ontouchstart"in this}function Ip(){var t=Av,r=Lv,i=Rv,l=Dv,u={},a=gl("start","drag","end"),c=0,d,p,g,v,y=0;function m(T){T.on("mousedown.drag",w).filter(l).on("touchstart.drag",C).on("touchmove.drag",k,jv).on("touchend.drag touchcancel.drag",j).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function w(T,F){if(!(v||!t.call(this,T,F))){var D=_(this,r.call(this,T,F),T,F,"mouse");D&&(Ct(T.view).on("mousemove.drag",S,li).on("mouseup.drag",P,li),Pp(T.view),ka(T),g=!1,d=T.clientX,p=T.clientY,D("start",T))}}function S(T){if(eo(T),!g){var F=T.clientX-d,D=T.clientY-p;g=F*F+D*D>y}u.mouse("drag",T)}function P(T){Ct(T.view).on("mousemove.drag mouseup.drag",null),Tp(T.view,g),eo(T),u.mouse("end",T)}function C(T,F){if(t.call(this,T,F)){var D=T.changedTouches,V=r.call(this,T,F),Q=D.length,Y,ee;for(Y=0;Y<Q;++Y)(ee=_(this,V,T,F,D[Y].identifier,D[Y]))&&(ka(T),ee("start",T,D[Y]))}}function k(T){var F=T.changedTouches,D=F.length,V,Q;for(V=0;V<D;++V)(Q=u[F[V].identifier])&&(eo(T),Q("drag",T,F[V]))}function j(T){var F=T.changedTouches,D=F.length,V,Q;for(v&&clearTimeout(v),v=setTimeout(function(){v=null},500),V=0;V<D;++V)(Q=u[F[V].identifier])&&(ka(T),Q("end",T,F[V]))}function _(T,F,D,V,Q,Y){var ee=a.copy(),q=Vt(Y||D,F),J,Z,N;if((N=i.call(T,new Oa("beforestart",{sourceEvent:D,target:m,identifier:Q,active:c,x:q[0],y:q[1],dx:0,dy:0,dispatch:ee}),V))!=null)return J=N.x-q[0]||0,Z=N.y-q[1]||0,function b(H,B,z){var A=q,O;switch(H){case"start":u[Q]=b,O=c++;break;case"end":delete u[Q],--c;case"drag":q=Vt(z||B,F),O=c;break}ee.call(H,T,new Oa(H,{sourceEvent:B,subject:N,target:m,identifier:Q,active:O,x:q[0]+J,y:q[1]+Z,dx:q[0]-A[0],dy:q[1]-A[1],dispatch:ee}),V)}}return m.filter=function(T){return arguments.length?(t=typeof T=="function"?T:Bs(!!T),m):t},m.container=function(T){return arguments.length?(r=typeof T=="function"?T:Bs(T),m):r},m.subject=function(T){return arguments.length?(i=typeof T=="function"?T:Bs(T),m):i},m.touchable=function(T){return arguments.length?(l=typeof T=="function"?T:Bs(!!T),m):l},m.on=function(){var T=a.on.apply(a,arguments);return T===a?m:T},m.clickDistance=function(T){return arguments.length?(y=(T=+T)*T,m):Math.sqrt(y)},m}function ec(t,r,i){t.prototype=r.prototype=i,i.constructor=t}function zp(t,r){var i=Object.create(t.prototype);for(var l in r)i[l]=r[l];return i}function vi(){}var ui=.7,il=1/ui,to="\\s*([+-]?\\d+)\\s*",ai="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",rn="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",$v=/^#([0-9a-f]{3,8})$/,Ov=new RegExp(`^rgb\\(${to},${to},${to}\\)$`),Fv=new RegExp(`^rgb\\(${rn},${rn},${rn}\\)$`),Hv=new RegExp(`^rgba\\(${to},${to},${to},${ai}\\)$`),Vv=new RegExp(`^rgba\\(${rn},${rn},${rn},${ai}\\)$`),bv=new RegExp(`^hsl\\(${ai},${rn},${rn}\\)$`),Bv=new RegExp(`^hsla\\(${ai},${rn},${rn},${ai}\\)$`),Zd={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};ec(vi,yr,{copy(t){return Object.assign(new this.constructor,this,t)},displayable(){return this.rgb().displayable()},hex:Jd,formatHex:Jd,formatHex8:Uv,formatHsl:Wv,formatRgb:eh,toString:eh});function Jd(){return this.rgb().formatHex()}function Uv(){return this.rgb().formatHex8()}function Wv(){return jp(this).formatHsl()}function eh(){return this.rgb().formatRgb()}function yr(t){var r,i;return t=(t+"").trim().toLowerCase(),(r=$v.exec(t))?(i=r[1].length,r=parseInt(r[1],16),i===6?th(r):i===3?new yt(r>>8&15|r>>4&240,r>>4&15|r&240,(r&15)<<4|r&15,1):i===8?Us(r>>24&255,r>>16&255,r>>8&255,(r&255)/255):i===4?Us(r>>12&15|r>>8&240,r>>8&15|r>>4&240,r>>4&15|r&240,((r&15)<<4|r&15)/255):null):(r=Ov.exec(t))?new yt(r[1],r[2],r[3],1):(r=Fv.exec(t))?new yt(r[1]*255/100,r[2]*255/100,r[3]*255/100,1):(r=Hv.exec(t))?Us(r[1],r[2],r[3],r[4]):(r=Vv.exec(t))?Us(r[1]*255/100,r[2]*255/100,r[3]*255/100,r[4]):(r=bv.exec(t))?oh(r[1],r[2]/100,r[3]/100,1):(r=Bv.exec(t))?oh(r[1],r[2]/100,r[3]/100,r[4]):Zd.hasOwnProperty(t)?th(Zd[t]):t==="transparent"?new yt(NaN,NaN,NaN,0):null}function th(t){return new yt(t>>16&255,t>>8&255,t&255,1)}function Us(t,r,i,l){return l<=0&&(t=r=i=NaN),new yt(t,r,i,l)}function Yv(t){return t instanceof vi||(t=yr(t)),t?(t=t.rgb(),new yt(t.r,t.g,t.b,t.opacity)):new yt}function Fa(t,r,i,l){return arguments.length===1?Yv(t):new yt(t,r,i,l??1)}function yt(t,r,i,l){this.r=+t,this.g=+r,this.b=+i,this.opacity=+l}ec(yt,Fa,zp(vi,{brighter(t){return t=t==null?il:Math.pow(il,t),new yt(this.r*t,this.g*t,this.b*t,this.opacity)},darker(t){return t=t==null?ui:Math.pow(ui,t),new yt(this.r*t,this.g*t,this.b*t,this.opacity)},rgb(){return this},clamp(){return new yt(pr(this.r),pr(this.g),pr(this.b),sl(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:nh,formatHex:nh,formatHex8:Xv,formatRgb:rh,toString:rh}));function nh(){return`#${hr(this.r)}${hr(this.g)}${hr(this.b)}`}function Xv(){return`#${hr(this.r)}${hr(this.g)}${hr(this.b)}${hr((isNaN(this.opacity)?1:this.opacity)*255)}`}function rh(){const t=sl(this.opacity);return`${t===1?"rgb(":"rgba("}${pr(this.r)}, ${pr(this.g)}, ${pr(this.b)}${t===1?")":`, ${t})`}`}function sl(t){return isNaN(t)?1:Math.max(0,Math.min(1,t))}function pr(t){return Math.max(0,Math.min(255,Math.round(t)||0))}function hr(t){return t=pr(t),(t<16?"0":"")+t.toString(16)}function oh(t,r,i,l){return l<=0?t=r=i=NaN:i<=0||i>=1?t=r=NaN:r<=0&&(t=NaN),new bt(t,r,i,l)}function jp(t){if(t instanceof bt)return new bt(t.h,t.s,t.l,t.opacity);if(t instanceof vi||(t=yr(t)),!t)return new bt;if(t instanceof bt)return t;t=t.rgb();var r=t.r/255,i=t.g/255,l=t.b/255,u=Math.min(r,i,l),a=Math.max(r,i,l),c=NaN,d=a-u,p=(a+u)/2;return d?(r===a?c=(i-l)/d+(i<l)*6:i===a?c=(l-r)/d+2:c=(r-i)/d+4,d/=p<.5?a+u:2-a-u,c*=60):d=p>0&&p<1?0:c,new bt(c,d,p,t.opacity)}function Qv(t,r,i,l){return arguments.length===1?jp(t):new bt(t,r,i,l??1)}function bt(t,r,i,l){this.h=+t,this.s=+r,this.l=+i,this.opacity=+l}ec(bt,Qv,zp(vi,{brighter(t){return t=t==null?il:Math.pow(il,t),new bt(this.h,this.s,this.l*t,this.opacity)},darker(t){return t=t==null?ui:Math.pow(ui,t),new bt(this.h,this.s,this.l*t,this.opacity)},rgb(){var t=this.h%360+(this.h<0)*360,r=isNaN(t)||isNaN(this.s)?0:this.s,i=this.l,l=i+(i<.5?i:1-i)*r,u=2*i-l;return new yt(Ea(t>=240?t-240:t+120,u,l),Ea(t,u,l),Ea(t<120?t+240:t-120,u,l),this.opacity)},clamp(){return new bt(ih(this.h),Ws(this.s),Ws(this.l),sl(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const t=sl(this.opacity);return`${t===1?"hsl(":"hsla("}${ih(this.h)}, ${Ws(this.s)*100}%, ${Ws(this.l)*100}%${t===1?")":`, ${t})`}`}}));function ih(t){return t=(t||0)%360,t<0?t+360:t}function Ws(t){return Math.max(0,Math.min(1,t||0))}function Ea(t,r,i){return(t<60?r+(i-r)*t/60:t<180?i:t<240?r+(i-r)*(240-t)/60:r)*255}const tc=t=>()=>t;function Kv(t,r){return function(i){return t+i*r}}function Gv(t,r,i){return t=Math.pow(t,i),r=Math.pow(r,i)-t,i=1/i,function(l){return Math.pow(t+l*r,i)}}function qv(t){return(t=+t)==1?Ap:function(r,i){return i-r?Gv(r,i,t):tc(isNaN(r)?i:r)}}function Ap(t,r){var i=r-t;return i?Kv(t,i):tc(isNaN(t)?r:t)}const ll=(function t(r){var i=qv(r);function l(u,a){var c=i((u=Fa(u)).r,(a=Fa(a)).r),d=i(u.g,a.g),p=i(u.b,a.b),g=Ap(u.opacity,a.opacity);return function(v){return u.r=c(v),u.g=d(v),u.b=p(v),u.opacity=g(v),u+""}}return l.gamma=t,l})(1);function Zv(t,r){r||(r=[]);var i=t?Math.min(r.length,t.length):0,l=r.slice(),u;return function(a){for(u=0;u<i;++u)l[u]=t[u]*(1-a)+r[u]*a;return l}}function Jv(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}function ex(t,r){var i=r?r.length:0,l=t?Math.min(i,t.length):0,u=new Array(l),a=new Array(i),c;for(c=0;c<l;++c)u[c]=ii(t[c],r[c]);for(;c<i;++c)a[c]=r[c];return function(d){for(c=0;c<l;++c)a[c]=u[c](d);return a}}function tx(t,r){var i=new Date;return t=+t,r=+r,function(l){return i.setTime(t*(1-l)+r*l),i}}function nn(t,r){return t=+t,r=+r,function(i){return t*(1-i)+r*i}}function nx(t,r){var i={},l={},u;(t===null||typeof t!="object")&&(t={}),(r===null||typeof r!="object")&&(r={});for(u in r)u in t?i[u]=ii(t[u],r[u]):l[u]=r[u];return function(a){for(u in i)l[u]=i[u](a);return l}}var Ha=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Ca=new RegExp(Ha.source,"g");function rx(t){return function(){return t}}function ox(t){return function(r){return t(r)+""}}function Lp(t,r){var i=Ha.lastIndex=Ca.lastIndex=0,l,u,a,c=-1,d=[],p=[];for(t=t+"",r=r+"";(l=Ha.exec(t))&&(u=Ca.exec(r));)(a=u.index)>i&&(a=r.slice(i,a),d[c]?d[c]+=a:d[++c]=a),(l=l[0])===(u=u[0])?d[c]?d[c]+=u:d[++c]=u:(d[++c]=null,p.push({i:c,x:nn(l,u)})),i=Ca.lastIndex;return i<r.length&&(a=r.slice(i),d[c]?d[c]+=a:d[++c]=a),d.length<2?p[0]?ox(p[0].x):rx(r):(r=p.length,function(g){for(var v=0,y;v<r;++v)d[(y=p[v]).i]=y.x(g);return d.join("")})}function ii(t,r){var i=typeof r,l;return r==null||i==="boolean"?tc(r):(i==="number"?nn:i==="string"?(l=yr(r))?(r=l,ll):Lp:r instanceof yr?ll:r instanceof Date?tx:Jv(r)?Zv:Array.isArray(r)?ex:typeof r.valueOf!="function"&&typeof r.toString!="function"||isNaN(r)?nx:nn)(t,r)}var sh=180/Math.PI,Va={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Rp(t,r,i,l,u,a){var c,d,p;return(c=Math.sqrt(t*t+r*r))&&(t/=c,r/=c),(p=t*i+r*l)&&(i-=t*p,l-=r*p),(d=Math.sqrt(i*i+l*l))&&(i/=d,l/=d,p/=d),t*l<r*i&&(t=-t,r=-r,p=-p,c=-c),{translateX:u,translateY:a,rotate:Math.atan2(r,t)*sh,skewX:Math.atan(p)*sh,scaleX:c,scaleY:d}}var Ys;function ix(t){const r=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(t+"");return r.isIdentity?Va:Rp(r.a,r.b,r.c,r.d,r.e,r.f)}function sx(t){return t==null||(Ys||(Ys=document.createElementNS("http://www.w3.org/2000/svg","g")),Ys.setAttribute("transform",t),!(t=Ys.transform.baseVal.consolidate()))?Va:(t=t.matrix,Rp(t.a,t.b,t.c,t.d,t.e,t.f))}function Dp(t,r,i,l){function u(g){return g.length?g.pop()+" ":""}function a(g,v,y,m,w,S){if(g!==y||v!==m){var P=w.push("translate(",null,r,null,i);S.push({i:P-4,x:nn(g,y)},{i:P-2,x:nn(v,m)})}else(y||m)&&w.push("translate("+y+r+m+i)}function c(g,v,y,m){g!==v?(g-v>180?v+=360:v-g>180&&(g+=360),m.push({i:y.push(u(y)+"rotate(",null,l)-2,x:nn(g,v)})):v&&y.push(u(y)+"rotate("+v+l)}function d(g,v,y,m){g!==v?m.push({i:y.push(u(y)+"skewX(",null,l)-2,x:nn(g,v)}):v&&y.push(u(y)+"skewX("+v+l)}function p(g,v,y,m,w,S){if(g!==y||v!==m){var P=w.push(u(w)+"scale(",null,",",null,")");S.push({i:P-4,x:nn(g,y)},{i:P-2,x:nn(v,m)})}else(y!==1||m!==1)&&w.push(u(w)+"scale("+y+","+m+")")}return function(g,v){var y=[],m=[];return g=t(g),v=t(v),a(g.translateX,g.translateY,v.translateX,v.translateY,y,m),c(g.rotate,v.rotate,y,m),d(g.skewX,v.skewX,y,m),p(g.scaleX,g.scaleY,v.scaleX,v.scaleY,y,m),g=v=null,function(w){for(var S=-1,P=m.length,C;++S<P;)y[(C=m[S]).i]=C.x(w);return y.join("")}}}var lx=Dp(ix,"px, ","px)","deg)"),ux=Dp(sx,", ",")",")"),ax=1e-12;function lh(t){return((t=Math.exp(t))+1/t)/2}function cx(t){return((t=Math.exp(t))-1/t)/2}function fx(t){return((t=Math.exp(2*t))-1)/(t+1)}const Js=(function t(r,i,l){function u(a,c){var d=a[0],p=a[1],g=a[2],v=c[0],y=c[1],m=c[2],w=v-d,S=y-p,P=w*w+S*S,C,k;if(P<ax)k=Math.log(m/g)/r,C=function(V){return[d+V*w,p+V*S,g*Math.exp(r*V*k)]};else{var j=Math.sqrt(P),_=(m*m-g*g+l*P)/(2*g*i*j),T=(m*m-g*g-l*P)/(2*m*i*j),F=Math.log(Math.sqrt(_*_+1)-_),D=Math.log(Math.sqrt(T*T+1)-T);k=(D-F)/r,C=function(V){var Q=V*k,Y=lh(F),ee=g/(i*j)*(Y*fx(r*Q+F)-cx(F));return[d+ee*w,p+ee*S,g*Y/lh(r*Q+F)]}}return C.duration=k*1e3*r/Math.SQRT2,C}return u.rho=function(a){var c=Math.max(.001,+a),d=c*c,p=d*d;return t(c,d,p)},u})(Math.SQRT2,2,4);var ro=0,ri=0,ti=0,$p=1e3,ul,oi,al=0,vr=0,yl=0,ci=typeof performance=="object"&&performance.now?performance:Date,Op=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function nc(){return vr||(Op(dx),vr=ci.now()+yl)}function dx(){vr=0}function cl(){this._call=this._time=this._next=null}cl.prototype=Fp.prototype={constructor:cl,restart:function(t,r,i){if(typeof t!="function")throw new TypeError("callback is not a function");i=(i==null?nc():+i)+(r==null?0:+r),!this._next&&oi!==this&&(oi?oi._next=this:ul=this,oi=this),this._call=t,this._time=i,ba()},stop:function(){this._call&&(this._call=null,this._time=1/0,ba())}};function Fp(t,r,i){var l=new cl;return l.restart(t,r,i),l}function hx(){nc(),++ro;for(var t=ul,r;t;)(r=vr-t._time)>=0&&t._call.call(void 0,r),t=t._next;--ro}function uh(){vr=(al=ci.now())+yl,ro=ri=0;try{hx()}finally{ro=0,gx(),vr=0}}function px(){var t=ci.now(),r=t-al;r>$p&&(yl-=r,al=t)}function gx(){for(var t,r=ul,i,l=1/0;r;)r._call?(l>r._time&&(l=r._time),t=r,r=r._next):(i=r._next,r._next=null,r=t?t._next=i:ul=i);oi=t,ba(l)}function ba(t){if(!ro){ri&&(ri=clearTimeout(ri));var r=t-vr;r>24?(t<1/0&&(ri=setTimeout(uh,t-ci.now()-yl)),ti&&(ti=clearInterval(ti))):(ti||(al=ci.now(),ti=setInterval(px,$p)),ro=1,Op(uh))}}function ah(t,r,i){var l=new cl;return r=r==null?0:+r,l.restart(u=>{l.stop(),t(u+r)},r,i),l}var mx=gl("start","end","cancel","interrupt"),yx=[],Hp=0,ch=1,Ba=2,el=3,fh=4,Ua=5,tl=6;function vl(t,r,i,l,u,a){var c=t.__transition;if(!c)t.__transition={};else if(i in c)return;vx(t,i,{name:r,index:l,group:u,on:mx,tween:yx,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:Hp})}function rc(t,r){var i=Yt(t,r);if(i.state>Hp)throw new Error("too late; already scheduled");return i}function on(t,r){var i=Yt(t,r);if(i.state>el)throw new Error("too late; already running");return i}function Yt(t,r){var i=t.__transition;if(!i||!(i=i[r]))throw new Error("transition not found");return i}function vx(t,r,i){var l=t.__transition,u;l[r]=i,i.timer=Fp(a,0,i.time);function a(g){i.state=ch,i.timer.restart(c,i.delay,i.time),i.delay<=g&&c(g-i.delay)}function c(g){var v,y,m,w;if(i.state!==ch)return p();for(v in l)if(w=l[v],w.name===i.name){if(w.state===el)return ah(c);w.state===fh?(w.state=tl,w.timer.stop(),w.on.call("interrupt",t,t.__data__,w.index,w.group),delete l[v]):+v<r&&(w.state=tl,w.timer.stop(),w.on.call("cancel",t,t.__data__,w.index,w.group),delete l[v])}if(ah(function(){i.state===el&&(i.state=fh,i.timer.restart(d,i.delay,i.time),d(g))}),i.state=Ba,i.on.call("start",t,t.__data__,i.index,i.group),i.state===Ba){for(i.state=el,u=new Array(m=i.tween.length),v=0,y=-1;v<m;++v)(w=i.tween[v].value.call(t,t.__data__,i.index,i.group))&&(u[++y]=w);u.length=y+1}}function d(g){for(var v=g<i.duration?i.ease.call(null,g/i.duration):(i.timer.restart(p),i.state=Ua,1),y=-1,m=u.length;++y<m;)u[y].call(t,v);i.state===Ua&&(i.on.call("end",t,t.__data__,i.index,i.group),p())}function p(){i.state=tl,i.timer.stop(),delete l[r];for(var g in l)return;delete t.__transition}}function nl(t,r){var i=t.__transition,l,u,a=!0,c;if(i){r=r==null?null:r+"";for(c in i){if((l=i[c]).name!==r){a=!1;continue}u=l.state>Ba&&l.state<Ua,l.state=tl,l.timer.stop(),l.on.call(u?"interrupt":"cancel",t,t.__data__,l.index,l.group),delete i[c]}a&&delete t.__transition}}function xx(t){return this.each(function(){nl(this,t)})}function wx(t,r){var i,l;return function(){var u=on(this,t),a=u.tween;if(a!==i){l=i=a;for(var c=0,d=l.length;c<d;++c)if(l[c].name===r){l=l.slice(),l.splice(c,1);break}}u.tween=l}}function _x(t,r,i){var l,u;if(typeof i!="function")throw new Error;return function(){var a=on(this,t),c=a.tween;if(c!==l){u=(l=c).slice();for(var d={name:r,value:i},p=0,g=u.length;p<g;++p)if(u[p].name===r){u[p]=d;break}p===g&&u.push(d)}a.tween=u}}function Sx(t,r){var i=this._id;if(t+="",arguments.length<2){for(var l=Yt(this.node(),i).tween,u=0,a=l.length,c;u<a;++u)if((c=l[u]).name===t)return c.value;return null}return this.each((r==null?wx:_x)(i,t,r))}function oc(t,r,i){var l=t._id;return t.each(function(){var u=on(this,l);(u.value||(u.value={}))[r]=i.apply(this,arguments)}),function(u){return Yt(u,l).value[r]}}function Vp(t,r){var i;return(typeof r=="number"?nn:r instanceof yr?ll:(i=yr(r))?(r=i,ll):Lp)(t,r)}function kx(t){return function(){this.removeAttribute(t)}}function Ex(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Cx(t,r,i){var l,u=i+"",a;return function(){var c=this.getAttribute(t);return c===u?null:c===l?a:a=r(l=c,i)}}function Nx(t,r,i){var l,u=i+"",a;return function(){var c=this.getAttributeNS(t.space,t.local);return c===u?null:c===l?a:a=r(l=c,i)}}function Mx(t,r,i){var l,u,a;return function(){var c,d=i(this),p;return d==null?void this.removeAttribute(t):(c=this.getAttribute(t),p=d+"",c===p?null:c===l&&p===u?a:(u=p,a=r(l=c,d)))}}function Px(t,r,i){var l,u,a;return function(){var c,d=i(this),p;return d==null?void this.removeAttributeNS(t.space,t.local):(c=this.getAttributeNS(t.space,t.local),p=d+"",c===p?null:c===l&&p===u?a:(u=p,a=r(l=c,d)))}}function Tx(t,r){var i=ml(t),l=i==="transform"?ux:Vp;return this.attrTween(t,typeof r=="function"?(i.local?Px:Mx)(i,l,oc(this,"attr."+t,r)):r==null?(i.local?Ex:kx)(i):(i.local?Nx:Cx)(i,l,r))}function Ix(t,r){return function(i){this.setAttribute(t,r.call(this,i))}}function zx(t,r){return function(i){this.setAttributeNS(t.space,t.local,r.call(this,i))}}function jx(t,r){var i,l;function u(){var a=r.apply(this,arguments);return a!==l&&(i=(l=a)&&zx(t,a)),i}return u._value=r,u}function Ax(t,r){var i,l;function u(){var a=r.apply(this,arguments);return a!==l&&(i=(l=a)&&Ix(t,a)),i}return u._value=r,u}function Lx(t,r){var i="attr."+t;if(arguments.length<2)return(i=this.tween(i))&&i._value;if(r==null)return this.tween(i,null);if(typeof r!="function")throw new Error;var l=ml(t);return this.tween(i,(l.local?jx:Ax)(l,r))}function Rx(t,r){return function(){rc(this,t).delay=+r.apply(this,arguments)}}function Dx(t,r){return r=+r,function(){rc(this,t).delay=r}}function $x(t){var r=this._id;return arguments.length?this.each((typeof t=="function"?Rx:Dx)(r,t)):Yt(this.node(),r).delay}function Ox(t,r){return function(){on(this,t).duration=+r.apply(this,arguments)}}function Fx(t,r){return r=+r,function(){on(this,t).duration=r}}function Hx(t){var r=this._id;return arguments.length?this.each((typeof t=="function"?Ox:Fx)(r,t)):Yt(this.node(),r).duration}function Vx(t,r){if(typeof r!="function")throw new Error;return function(){on(this,t).ease=r}}function bx(t){var r=this._id;return arguments.length?this.each(Vx(r,t)):Yt(this.node(),r).ease}function Bx(t,r){return function(){var i=r.apply(this,arguments);if(typeof i!="function")throw new Error;on(this,t).ease=i}}function Ux(t){if(typeof t!="function")throw new Error;return this.each(Bx(this._id,t))}function Wx(t){typeof t!="function"&&(t=vp(t));for(var r=this._groups,i=r.length,l=new Array(i),u=0;u<i;++u)for(var a=r[u],c=a.length,d=l[u]=[],p,g=0;g<c;++g)(p=a[g])&&t.call(p,p.__data__,g,a)&&d.push(p);return new xn(l,this._parents,this._name,this._id)}function Yx(t){if(t._id!==this._id)throw new Error;for(var r=this._groups,i=t._groups,l=r.length,u=i.length,a=Math.min(l,u),c=new Array(l),d=0;d<a;++d)for(var p=r[d],g=i[d],v=p.length,y=c[d]=new Array(v),m,w=0;w<v;++w)(m=p[w]||g[w])&&(y[w]=m);for(;d<l;++d)c[d]=r[d];return new xn(c,this._parents,this._name,this._id)}function Xx(t){return(t+"").trim().split(/^|\s+/).every(function(r){var i=r.indexOf(".");return i>=0&&(r=r.slice(0,i)),!r||r==="start"})}function Qx(t,r,i){var l,u,a=Xx(r)?rc:on;return function(){var c=a(this,t),d=c.on;d!==l&&(u=(l=d).copy()).on(r,i),c.on=u}}function Kx(t,r){var i=this._id;return arguments.length<2?Yt(this.node(),i).on.on(t):this.each(Qx(i,t,r))}function Gx(t){return function(){var r=this.parentNode;for(var i in this.__transition)if(+i!==t)return;r&&r.removeChild(this)}}function qx(){return this.on("end.remove",Gx(this._id))}function Zx(t){var r=this._name,i=this._id;typeof t!="function"&&(t=Za(t));for(var l=this._groups,u=l.length,a=new Array(u),c=0;c<u;++c)for(var d=l[c],p=d.length,g=a[c]=new Array(p),v,y,m=0;m<p;++m)(v=d[m])&&(y=t.call(v,v.__data__,m,d))&&("__data__"in v&&(y.__data__=v.__data__),g[m]=y,vl(g[m],r,i,m,g,Yt(v,i)));return new xn(a,this._parents,r,i)}function Jx(t){var r=this._name,i=this._id;typeof t!="function"&&(t=yp(t));for(var l=this._groups,u=l.length,a=[],c=[],d=0;d<u;++d)for(var p=l[d],g=p.length,v,y=0;y<g;++y)if(v=p[y]){for(var m=t.call(v,v.__data__,y,p),w,S=Yt(v,i),P=0,C=m.length;P<C;++P)(w=m[P])&&vl(w,r,i,P,m,S);a.push(m),c.push(v)}return new xn(a,c,r,i)}var ew=yi.prototype.constructor;function tw(){return new ew(this._groups,this._parents)}function nw(t,r){var i,l,u;return function(){var a=no(this,t),c=(this.style.removeProperty(t),no(this,t));return a===c?null:a===i&&c===l?u:u=r(i=a,l=c)}}function bp(t){return function(){this.style.removeProperty(t)}}function rw(t,r,i){var l,u=i+"",a;return function(){var c=no(this,t);return c===u?null:c===l?a:a=r(l=c,i)}}function ow(t,r,i){var l,u,a;return function(){var c=no(this,t),d=i(this),p=d+"";return d==null&&(p=d=(this.style.removeProperty(t),no(this,t))),c===p?null:c===l&&p===u?a:(u=p,a=r(l=c,d))}}function iw(t,r){var i,l,u,a="style."+r,c="end."+a,d;return function(){var p=on(this,t),g=p.on,v=p.value[a]==null?d||(d=bp(r)):void 0;(g!==i||u!==v)&&(l=(i=g).copy()).on(c,u=v),p.on=l}}function sw(t,r,i){var l=(t+="")=="transform"?lx:Vp;return r==null?this.styleTween(t,nw(t,l)).on("end.style."+t,bp(t)):typeof r=="function"?this.styleTween(t,ow(t,l,oc(this,"style."+t,r))).each(iw(this._id,t)):this.styleTween(t,rw(t,l,r),i).on("end.style."+t,null)}function lw(t,r,i){return function(l){this.style.setProperty(t,r.call(this,l),i)}}function uw(t,r,i){var l,u;function a(){var c=r.apply(this,arguments);return c!==u&&(l=(u=c)&&lw(t,c,i)),l}return a._value=r,a}function aw(t,r,i){var l="style."+(t+="");if(arguments.length<2)return(l=this.tween(l))&&l._value;if(r==null)return this.tween(l,null);if(typeof r!="function")throw new Error;return this.tween(l,uw(t,r,i??""))}function cw(t){return function(){this.textContent=t}}function fw(t){return function(){var r=t(this);this.textContent=r??""}}function dw(t){return this.tween("text",typeof t=="function"?fw(oc(this,"text",t)):cw(t==null?"":t+""))}function hw(t){return function(r){this.textContent=t.call(this,r)}}function pw(t){var r,i;function l(){var u=t.apply(this,arguments);return u!==i&&(r=(i=u)&&hw(u)),r}return l._value=t,l}function gw(t){var r="text";if(arguments.length<1)return(r=this.tween(r))&&r._value;if(t==null)return this.tween(r,null);if(typeof t!="function")throw new Error;return this.tween(r,pw(t))}function mw(){for(var t=this._name,r=this._id,i=Bp(),l=this._groups,u=l.length,a=0;a<u;++a)for(var c=l[a],d=c.length,p,g=0;g<d;++g)if(p=c[g]){var v=Yt(p,r);vl(p,t,i,g,c,{time:v.time+v.delay+v.duration,delay:0,duration:v.duration,ease:v.ease})}return new xn(l,this._parents,t,i)}function yw(){var t,r,i=this,l=i._id,u=i.size();return new Promise(function(a,c){var d={value:c},p={value:function(){--u===0&&a()}};i.each(function(){var g=on(this,l),v=g.on;v!==t&&(r=(t=v).copy(),r._.cancel.push(d),r._.interrupt.push(d),r._.end.push(p)),g.on=r}),u===0&&a()})}var vw=0;function xn(t,r,i,l){this._groups=t,this._parents=r,this._name=i,this._id=l}function Bp(){return++vw}var yn=yi.prototype;xn.prototype={constructor:xn,select:Zx,selectAll:Jx,selectChild:yn.selectChild,selectChildren:yn.selectChildren,filter:Wx,merge:Yx,selection:tw,transition:mw,call:yn.call,nodes:yn.nodes,node:yn.node,size:yn.size,empty:yn.empty,each:yn.each,on:Kx,attr:Tx,attrTween:Lx,style:sw,styleTween:aw,text:dw,textTween:gw,remove:qx,tween:Sx,delay:$x,duration:Hx,ease:bx,easeVarying:Ux,end:yw,[Symbol.iterator]:yn[Symbol.iterator]};function xw(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}var ww={time:null,delay:0,duration:250,ease:xw};function _w(t,r){for(var i;!(i=t.__transition)||!(i=i[r]);)if(!(t=t.parentNode))throw new Error(`transition ${r} not found`);return i}function Sw(t){var r,i;t instanceof xn?(r=t._id,t=t._name):(r=Bp(),(i=ww).time=nc(),t=t==null?null:t+"");for(var l=this._groups,u=l.length,a=0;a<u;++a)for(var c=l[a],d=c.length,p,g=0;g<d;++g)(p=c[g])&&vl(p,t,r,g,c,i||_w(p,r));return new xn(l,this._parents,t,r)}yi.prototype.interrupt=xx;yi.prototype.transition=Sw;const Xs=t=>()=>t;function kw(t,{sourceEvent:r,target:i,transform:l,dispatch:u}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:r,enumerable:!0,configurable:!0},target:{value:i,enumerable:!0,configurable:!0},transform:{value:l,enumerable:!0,configurable:!0},_:{value:u}})}function vn(t,r,i){this.k=t,this.x=r,this.y=i}vn.prototype={constructor:vn,scale:function(t){return t===1?this:new vn(this.k*t,this.x,this.y)},translate:function(t,r){return t===0&r===0?this:new vn(this.k,this.x+this.k*t,this.y+this.k*r)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var xl=new vn(1,0,0);Up.prototype=vn.prototype;function Up(t){for(;!t.__zoom;)if(!(t=t.parentNode))return xl;return t.__zoom}function Na(t){t.stopImmediatePropagation()}function ni(t){t.preventDefault(),t.stopImmediatePropagation()}function Ew(t){return(!t.ctrlKey||t.type==="wheel")&&!t.button}function Cw(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t,t.hasAttribute("viewBox")?(t=t.viewBox.baseVal,[[t.x,t.y],[t.x+t.width,t.y+t.height]]):[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]):[[0,0],[t.clientWidth,t.clientHeight]]}function dh(){return this.__zoom||xl}function Nw(t){return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function Mw(){return navigator.maxTouchPoints||"ontouchstart"in this}function Pw(t,r,i){var l=t.invertX(r[0][0])-i[0][0],u=t.invertX(r[1][0])-i[1][0],a=t.invertY(r[0][1])-i[0][1],c=t.invertY(r[1][1])-i[1][1];return t.translate(u>l?(l+u)/2:Math.min(0,l)||Math.max(0,u),c>a?(a+c)/2:Math.min(0,a)||Math.max(0,c))}function Wp(){var t=Ew,r=Cw,i=Pw,l=Nw,u=Mw,a=[0,1/0],c=[[-1/0,-1/0],[1/0,1/0]],d=250,p=Js,g=gl("start","zoom","end"),v,y,m,w=500,S=150,P=0,C=10;function k(N){N.property("__zoom",dh).on("wheel.zoom",Q,{passive:!1}).on("mousedown.zoom",Y).on("dblclick.zoom",ee).filter(u).on("touchstart.zoom",q).on("touchmove.zoom",J).on("touchend.zoom touchcancel.zoom",Z).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}k.transform=function(N,b,H,B){var z=N.selection?N.selection():N;z.property("__zoom",dh),N!==z?F(N,b,H,B):z.interrupt().each(function(){D(this,arguments).event(B).start().zoom(null,typeof b=="function"?b.apply(this,arguments):b).end()})},k.scaleBy=function(N,b,H,B){k.scaleTo(N,function(){var z=this.__zoom.k,A=typeof b=="function"?b.apply(this,arguments):b;return z*A},H,B)},k.scaleTo=function(N,b,H,B){k.transform(N,function(){var z=r.apply(this,arguments),A=this.__zoom,O=H==null?T(z):typeof H=="function"?H.apply(this,arguments):H,M=A.invert(O),L=typeof b=="function"?b.apply(this,arguments):b;return i(_(j(A,L),O,M),z,c)},H,B)},k.translateBy=function(N,b,H,B){k.transform(N,function(){return i(this.__zoom.translate(typeof b=="function"?b.apply(this,arguments):b,typeof H=="function"?H.apply(this,arguments):H),r.apply(this,arguments),c)},null,B)},k.translateTo=function(N,b,H,B,z){k.transform(N,function(){var A=r.apply(this,arguments),O=this.__zoom,M=B==null?T(A):typeof B=="function"?B.apply(this,arguments):B;return i(xl.translate(M[0],M[1]).scale(O.k).translate(typeof b=="function"?-b.apply(this,arguments):-b,typeof H=="function"?-H.apply(this,arguments):-H),A,c)},B,z)};function j(N,b){return b=Math.max(a[0],Math.min(a[1],b)),b===N.k?N:new vn(b,N.x,N.y)}function _(N,b,H){var B=b[0]-H[0]*N.k,z=b[1]-H[1]*N.k;return B===N.x&&z===N.y?N:new vn(N.k,B,z)}function T(N){return[(+N[0][0]+ +N[1][0])/2,(+N[0][1]+ +N[1][1])/2]}function F(N,b,H,B){N.on("start.zoom",function(){D(this,arguments).event(B).start()}).on("interrupt.zoom end.zoom",function(){D(this,arguments).event(B).end()}).tween("zoom",function(){var z=this,A=arguments,O=D(z,A).event(B),M=r.apply(z,A),L=H==null?T(M):typeof H=="function"?H.apply(z,A):H,te=Math.max(M[1][0]-M[0][0],M[1][1]-M[0][1]),re=z.__zoom,ue=typeof b=="function"?b.apply(z,A):b,ce=p(re.invert(L).concat(te/re.k),ue.invert(L).concat(te/ue.k));return function(ae){if(ae===1)ae=ue;else{var G=ce(ae),se=te/G[2];ae=new vn(se,L[0]-G[0]*se,L[1]-G[1]*se)}O.zoom(null,ae)}})}function D(N,b,H){return!H&&N.__zooming||new V(N,b)}function V(N,b){this.that=N,this.args=b,this.active=0,this.sourceEvent=null,this.extent=r.apply(N,b),this.taps=0}V.prototype={event:function(N){return N&&(this.sourceEvent=N),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(N,b){return this.mouse&&N!=="mouse"&&(this.mouse[1]=b.invert(this.mouse[0])),this.touch0&&N!=="touch"&&(this.touch0[1]=b.invert(this.touch0[0])),this.touch1&&N!=="touch"&&(this.touch1[1]=b.invert(this.touch1[0])),this.that.__zoom=b,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(N){var b=Ct(this.that).datum();g.call(N,this.that,new kw(N,{sourceEvent:this.sourceEvent,target:k,transform:this.that.__zoom,dispatch:g}),b)}};function Q(N,...b){if(!t.apply(this,arguments))return;var H=D(this,b).event(N),B=this.__zoom,z=Math.max(a[0],Math.min(a[1],B.k*Math.pow(2,l.apply(this,arguments)))),A=Vt(N);if(H.wheel)(H.mouse[0][0]!==A[0]||H.mouse[0][1]!==A[1])&&(H.mouse[1]=B.invert(H.mouse[0]=A)),clearTimeout(H.wheel);else{if(B.k===z)return;H.mouse=[A,B.invert(A)],nl(this),H.start()}ni(N),H.wheel=setTimeout(O,S),H.zoom("mouse",i(_(j(B,z),H.mouse[0],H.mouse[1]),H.extent,c));function O(){H.wheel=null,H.end()}}function Y(N,...b){if(m||!t.apply(this,arguments))return;var H=N.currentTarget,B=D(this,b,!0).event(N),z=Ct(N.view).on("mousemove.zoom",L,!0).on("mouseup.zoom",te,!0),A=Vt(N,H),O=N.clientX,M=N.clientY;Pp(N.view),Na(N),B.mouse=[A,this.__zoom.invert(A)],nl(this),B.start();function L(re){if(ni(re),!B.moved){var ue=re.clientX-O,ce=re.clientY-M;B.moved=ue*ue+ce*ce>P}B.event(re).zoom("mouse",i(_(B.that.__zoom,B.mouse[0]=Vt(re,H),B.mouse[1]),B.extent,c))}function te(re){z.on("mousemove.zoom mouseup.zoom",null),Tp(re.view,B.moved),ni(re),B.event(re).end()}}function ee(N,...b){if(t.apply(this,arguments)){var H=this.__zoom,B=Vt(N.changedTouches?N.changedTouches[0]:N,this),z=H.invert(B),A=H.k*(N.shiftKey?.5:2),O=i(_(j(H,A),B,z),r.apply(this,b),c);ni(N),d>0?Ct(this).transition().duration(d).call(F,O,B,N):Ct(this).call(k.transform,O,B,N)}}function q(N,...b){if(t.apply(this,arguments)){var H=N.touches,B=H.length,z=D(this,b,N.changedTouches.length===B).event(N),A,O,M,L;for(Na(N),O=0;O<B;++O)M=H[O],L=Vt(M,this),L=[L,this.__zoom.invert(L),M.identifier],z.touch0?!z.touch1&&z.touch0[2]!==L[2]&&(z.touch1=L,z.taps=0):(z.touch0=L,A=!0,z.taps=1+!!v);v&&(v=clearTimeout(v)),A&&(z.taps<2&&(y=L[0],v=setTimeout(function(){v=null},w)),nl(this),z.start())}}function J(N,...b){if(this.__zooming){var H=D(this,b).event(N),B=N.changedTouches,z=B.length,A,O,M,L;for(ni(N),A=0;A<z;++A)O=B[A],M=Vt(O,this),H.touch0&&H.touch0[2]===O.identifier?H.touch0[0]=M:H.touch1&&H.touch1[2]===O.identifier&&(H.touch1[0]=M);if(O=H.that.__zoom,H.touch1){var te=H.touch0[0],re=H.touch0[1],ue=H.touch1[0],ce=H.touch1[1],ae=(ae=ue[0]-te[0])*ae+(ae=ue[1]-te[1])*ae,G=(G=ce[0]-re[0])*G+(G=ce[1]-re[1])*G;O=j(O,Math.sqrt(ae/G)),M=[(te[0]+ue[0])/2,(te[1]+ue[1])/2],L=[(re[0]+ce[0])/2,(re[1]+ce[1])/2]}else if(H.touch0)M=H.touch0[0],L=H.touch0[1];else return;H.zoom("touch",i(_(O,M,L),H.extent,c))}}function Z(N,...b){if(this.__zooming){var H=D(this,b).event(N),B=N.changedTouches,z=B.length,A,O;for(Na(N),m&&clearTimeout(m),m=setTimeout(function(){m=null},w),A=0;A<z;++A)O=B[A],H.touch0&&H.touch0[2]===O.identifier?delete H.touch0:H.touch1&&H.touch1[2]===O.identifier&&delete H.touch1;if(H.touch1&&!H.touch0&&(H.touch0=H.touch1,delete H.touch1),H.touch0)H.touch0[1]=this.__zoom.invert(H.touch0[0]);else if(H.end(),H.taps===2&&(O=Vt(O,this),Math.hypot(y[0]-O[0],y[1]-O[1])<C)){var M=Ct(this).on("dblclick.zoom");M&&M.apply(this,arguments)}}}return k.wheelDelta=function(N){return arguments.length?(l=typeof N=="function"?N:Xs(+N),k):l},k.filter=function(N){return arguments.length?(t=typeof N=="function"?N:Xs(!!N),k):t},k.touchable=function(N){return arguments.length?(u=typeof N=="function"?N:Xs(!!N),k):u},k.extent=function(N){return arguments.length?(r=typeof N=="function"?N:Xs([[+N[0][0],+N[0][1]],[+N[1][0],+N[1][1]]]),k):r},k.scaleExtent=function(N){return arguments.length?(a[0]=+N[0],a[1]=+N[1],k):[a[0],a[1]]},k.translateExtent=function(N){return arguments.length?(c[0][0]=+N[0][0],c[1][0]=+N[1][0],c[0][1]=+N[0][1],c[1][1]=+N[1][1],k):[[c[0][0],c[0][1]],[c[1][0],c[1][1]]]},k.constrain=function(N){return arguments.length?(i=N,k):i},k.duration=function(N){return arguments.length?(d=+N,k):d},k.interpolate=function(N){return arguments.length?(p=N,k):p},k.on=function(){var N=g.on.apply(g,arguments);return N===g?k:N},k.clickDistance=function(N){return arguments.length?(P=(N=+N)*N,k):Math.sqrt(P)},k.tapDistance=function(N){return arguments.length?(C=+N,k):C},k}const Wt={error001:(t="react")=>`Seems like you have not used ${t==="svelte"?"SvelteFlowProvider":"ReactFlowProvider"} as an ancestor. Help: https://${t}flow.dev/error#001`,error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:t=>`Node type "${t}" not found. Using fallback type "default".`,error004:()=>"The parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:t=>`The old edge with id=${t} does not exist.`,error009:t=>`Marker type "${t}" doesn't exist.`,error008:(t,{id:r,sourceHandle:i,targetHandle:l})=>`Couldn't create edge for ${t} handle id: "${t==="source"?i:l}", edge id: ${r}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:t=>`Edge type "${t}" not found. Using fallback type "default".`,error012:t=>`Node with id "${t}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(t="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${t}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs.",error016:t=>`Edge with id "${t}" does not exist, it may have been removed. This can happen when an edge is deleted before the "onEdgeClick" handler is called.`},fi=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Yp=["Enter"," ","Escape"],Xp={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:t,x:r,y:i})=>`Moved selected node ${t}. New position, x: ${r}, y: ${i}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"};var oo;(function(t){t.Strict="strict",t.Loose="loose"})(oo||(oo={}));var gr;(function(t){t.Free="free",t.Vertical="vertical",t.Horizontal="horizontal"})(gr||(gr={}));var di;(function(t){t.Partial="partial",t.Full="full"})(di||(di={}));const Qp={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null};var Un;(function(t){t.Bezier="default",t.Straight="straight",t.Step="step",t.SmoothStep="smoothstep",t.SimpleBezier="simplebezier"})(Un||(Un={}));var fl;(function(t){t.Arrow="arrow",t.ArrowClosed="arrowclosed"})(fl||(fl={}));var ve;(function(t){t.Left="left",t.Top="top",t.Right="right",t.Bottom="bottom"})(ve||(ve={}));const hh={[ve.Left]:ve.Right,[ve.Right]:ve.Left,[ve.Top]:ve.Bottom,[ve.Bottom]:ve.Top},Kp=t=>!!t&&typeof t=="object"&&"id"in t&&"source"in t&&"target"in t,Tw=t=>!!t&&typeof t=="object"&&"id"in t&&"position"in t&&!("source"in t)&&!("target"in t),ic=t=>!!t&&typeof t=="object"&&"id"in t&&"internals"in t&&!("source"in t)&&!("target"in t),xi=(t,r=[0,0])=>{const{width:i,height:l}=Xt(t),u=t.origin??r,a=i*u[0],c=l*u[1];return{x:t.position.x-a,y:t.position.y-c}},Iw=(t,r={nodeOrigin:[0,0]})=>{if(t.length===0)return{x:0,y:0,width:0,height:0};let i=!1;const l=t.reduce((u,a)=>{const c=typeof a=="string";let d=!r.nodeLookup&&!c?a:void 0;return r.nodeLookup&&(d=c?r.nodeLookup.get(a):ic(a)?a:r.nodeLookup.get(a.id)),d?(i=!0,wl(u,dl(d,r.nodeOrigin))):u},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return i?_l(l):{x:0,y:0,width:0,height:0}},wi=(t,r={})=>{let i={x:1/0,y:1/0,x2:-1/0,y2:-1/0},l=!1;return t.forEach(u=>{(r.filter===void 0||r.filter(u))&&(i=wl(i,dl(u)),l=!0)}),l?_l(i):{x:0,y:0,width:0,height:0}},sc=(t,r,[i,l,u]=[0,0,1],a=!1,c=!1)=>{const d=(r.x-i)/u,p=(r.y-l)/u,g=r.width/u,v=r.height/u,y=[];for(const m of t.values()){const{measured:w,selectable:S=!0,hidden:P=!1}=m;if(c&&!S||P)continue;const C=w.width??m.width??m.initialWidth??0,k=w.height??m.height??m.initialHeight??0,{x:j,y:_}=m.internals.positionAbsolute,T=Jp(d,p,g,v,j,_,C,k),F=C*k,D=a&&T>0;(!m.internals.handleBounds||D||T>=F||m.dragging)&&y.push(m)}return y},zw=(t,r)=>{const i=new Set;return t.forEach(l=>{i.add(l.id)}),r.filter(l=>i.has(l.source)||i.has(l.target))};function jw(t,r){const i=new Map,l=r!=null&&r.nodes?new Set(r.nodes.map(u=>u.id)):null;return t.forEach(u=>{let a;if(r!=null&&r.includeHiddenNodes){const{width:c,height:d}=Xt(u);a=c>0&&d>0}else a=!!(u.measured.width&&u.measured.height&&!u.hidden);a&&(!l||l.has(u.id))&&i.set(u.id,u)}),i}async function Aw({nodes:t,width:r,height:i,panZoom:l,minZoom:u,maxZoom:a},c){if(t.size===0)return!0;const d=jw(t,c),p=wi(d),g=uc(p,r,i,(c==null?void 0:c.minZoom)??u,(c==null?void 0:c.maxZoom)??a,(c==null?void 0:c.padding)??.1);return await l.setViewport(g,{duration:c==null?void 0:c.duration,ease:c==null?void 0:c.ease,interpolate:c==null?void 0:c.interpolate}),!0}function Gp({nodeId:t,nextPosition:r,nodeLookup:i,nodeOrigin:l=[0,0],nodeExtent:u,onError:a}){const c=i.get(t),d=c.parentId?i.get(c.parentId):void 0,{x:p,y:g}=d?d.internals.positionAbsolute:{x:0,y:0},v=c.origin??l;let y=c.extent||u;if(c.extent==="parent"&&!c.expandParent)if(!d)a==null||a("005",Wt.error005());else{const{width:w,height:S}=Xt(d);w&&S&&(y=[[p,g],[p+w,g+S]])}else d&&wr(c.extent)&&(y=[[c.extent[0][0]+p,c.extent[0][1]+g],[c.extent[1][0]+p,c.extent[1][1]+g]]);const m=wr(y)?xr(r,y,c.measured):r;return(c.measured.width===void 0||c.measured.height===void 0)&&(a==null||a("015",Wt.error015())),{position:{x:m.x-p+(c.measured.width??0)*v[0],y:m.y-g+(c.measured.height??0)*v[1]},positionAbsolute:m}}async function Lw({nodesToRemove:t=[],edgesToRemove:r=[],nodes:i,edges:l,onBeforeDelete:u}){const a=new Set(t.map(m=>m.id)),c=[];for(const m of i){if(m.deletable===!1)continue;const w=a.has(m.id),S=!w&&m.parentId&&c.find(P=>P.id===m.parentId);(w||S)&&c.push(m)}const d=new Set(r.map(m=>m.id)),p=l.filter(m=>m.deletable!==!1),v=zw(c,p);for(const m of p)d.has(m.id)&&!v.find(S=>S.id===m.id)&&v.push(m);if(!u)return{edges:v,nodes:c};const y=await u({nodes:c,edges:v});return typeof y=="boolean"?y?{edges:v,nodes:c}:{edges:[],nodes:[]}:y}const io=(t,r=0,i=1)=>Math.min(Math.max(t,r),i),xr=(t={x:0,y:0},r,i)=>({x:io(t.x,r[0][0],r[1][0]-((i==null?void 0:i.width)??0)),y:io(t.y,r[0][1],r[1][1]-((i==null?void 0:i.height)??0))});function qp(t,r,i){const{width:l,height:u}=Xt(i),{x:a,y:c}=i.internals.positionAbsolute;return xr(t,[[a,c],[a+l,c+u]],r)}const ph=(t,r,i)=>t<r?io(Math.abs(t-r),1,r)/r:t>i?-io(Math.abs(t-i),1,r)/r:0,lc=(t,r,i=15,l=40)=>{const u=ph(t.x,l,r.width-l)*i,a=ph(t.y,l,r.height-l)*i;return[u,a]},wl=(t,r)=>({x:Math.min(t.x,r.x),y:Math.min(t.y,r.y),x2:Math.max(t.x2,r.x2),y2:Math.max(t.y2,r.y2)}),Wa=({x:t,y:r,width:i,height:l})=>({x:t,y:r,x2:t+i,y2:r+l}),_l=({x:t,y:r,x2:i,y2:l})=>({x:t,y:r,width:i-t,height:l-r}),hi=(t,r=[0,0])=>{var u,a;const{x:i,y:l}=ic(t)?t.internals.positionAbsolute:xi(t,r);return{x:i,y:l,width:((u=t.measured)==null?void 0:u.width)??t.width??t.initialWidth??0,height:((a=t.measured)==null?void 0:a.height)??t.height??t.initialHeight??0}},dl=(t,r=[0,0])=>{var u,a;const{x:i,y:l}=ic(t)?t.internals.positionAbsolute:xi(t,r);return{x:i,y:l,x2:i+(((u=t.measured)==null?void 0:u.width)??t.width??t.initialWidth??0),y2:l+(((a=t.measured)==null?void 0:a.height)??t.height??t.initialHeight??0)}},Zp=(t,r)=>_l(wl(Wa(t),Wa(r))),Jp=(t,r,i,l,u,a,c,d)=>{const p=Math.max(0,Math.min(t+i,u+c)-Math.max(t,u)),g=Math.max(0,Math.min(r+l,a+d)-Math.max(r,a));return Math.ceil(p*g)},hl=(t,r)=>Jp(t.x,t.y,t.width,t.height,r.x,r.y,r.width,r.height),gh=t=>Bt(t.width)&&Bt(t.height)&&Bt(t.x)&&Bt(t.y),Bt=t=>!isNaN(t)&&isFinite(t),eg=(t,r)=>(i,l)=>{},_i=(t,r=[1,1])=>({x:r[0]*Math.round(t.x/r[0]),y:r[1]*Math.round(t.y/r[1])}),Si=({x:t,y:r},[i,l,u],a=!1,c=[1,1])=>{const d={x:(t-i)/u,y:(r-l)/u};return a?_i(d,c):d},so=({x:t,y:r},[i,l,u])=>({x:t*u+i,y:r*u+l});function Zr(t,r){if(typeof t=="number")return Math.floor((r-r/(1+t))*.5);if(typeof t=="string"&&t.endsWith("px")){const i=parseFloat(t);if(!Number.isNaN(i))return Math.floor(i)}if(typeof t=="string"&&t.endsWith("%")){const i=parseFloat(t);if(!Number.isNaN(i))return Math.floor(r*i*.01)}return console.error(`The padding value "${t}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function Rw(t,r,i){if(typeof t=="string"||typeof t=="number"){const l=Zr(t,i),u=Zr(t,r);return{top:l,right:u,bottom:l,left:u,x:u*2,y:l*2}}if(typeof t=="object"){const l=Zr(t.top??t.y??0,i),u=Zr(t.bottom??t.y??0,i),a=Zr(t.left??t.x??0,r),c=Zr(t.right??t.x??0,r);return{top:l,right:c,bottom:u,left:a,x:a+c,y:l+u}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function Dw(t,r,i,l,u,a){const{x:c,y:d}=so(t,[r,i,l]),{x:p,y:g}=so({x:t.x+t.width,y:t.y+t.height},[r,i,l]),v=u-p,y=a-g;return{left:Math.floor(c),top:Math.floor(d),right:Math.floor(v),bottom:Math.floor(y)}}const uc=(t,r,i,l,u,a)=>{const c=Rw(a,r,i),d=(r-c.x)/t.width,p=(i-c.y)/t.height,g=Math.min(d,p),v=io(g,l,u),y=t.x+t.width/2,m=t.y+t.height/2,w=r/2-y*v,S=i/2-m*v,P=Dw(t,w,S,v,r,i),C={left:Math.min(P.left-c.left,0),top:Math.min(P.top-c.top,0),right:Math.min(P.right-c.right,0),bottom:Math.min(P.bottom-c.bottom,0)};return{x:w-C.left+C.right,y:S-C.top+C.bottom,zoom:v}},pi=()=>{var t;return typeof navigator<"u"&&((t=navigator==null?void 0:navigator.userAgent)==null?void 0:t.indexOf("Mac"))>=0};function wr(t){return t!=null&&t!=="parent"}function Xt(t){var r,i;return{width:((r=t.measured)==null?void 0:r.width)??t.width??t.initialWidth??0,height:((i=t.measured)==null?void 0:i.height)??t.height??t.initialHeight??0}}function tg(t){var r,i;return(((r=t.measured)==null?void 0:r.width)??t.width??t.initialWidth)!==void 0&&(((i=t.measured)==null?void 0:i.height)??t.height??t.initialHeight)!==void 0}function ng(t,r={width:0,height:0},i,l,u){const a={...t},c=l.get(i);if(c){const d=c.origin||u;a.x+=c.internals.positionAbsolute.x-(r.width??0)*d[0],a.y+=c.internals.positionAbsolute.y-(r.height??0)*d[1]}return a}function mh(t,r){if(t.size!==r.size)return!1;for(const i of t)if(!r.has(i))return!1;return!0}function $w(){let t,r;return{promise:new Promise((l,u)=>{t=l,r=u}),resolve:t,reject:r}}function Ow(t){return{...Xp,...t||{}}}function rg(t){return t===null?null:t?"valid":"invalid"}function si(t,{snapGrid:r=[0,0],snapToGrid:i=!1,transform:l,containerBounds:u}){const{x:a,y:c}=Ut(t),d=Si({x:a-((u==null?void 0:u.left)??0),y:c-((u==null?void 0:u.top)??0)},l),{x:p,y:g}=i?_i(d,r):d;return{xSnapped:p,ySnapped:g,...d}}const ac=t=>({width:t.offsetWidth,height:t.offsetHeight}),og=t=>{var r;return((r=t==null?void 0:t.getRootNode)==null?void 0:r.call(t))||(window==null?void 0:window.document)},Fw=["INPUT","SELECT","TEXTAREA"];function ig(t){var l,u;const r=((u=(l=t.composedPath)==null?void 0:l.call(t))==null?void 0:u[0])||t.target;return(r==null?void 0:r.nodeType)!==1?!1:Fw.includes(r.nodeName)||r.hasAttribute("contenteditable")||!!r.closest(".nokey")}const sg=t=>"clientX"in t,Ut=(t,r)=>{var a,c;const i=sg(t),l=i?t.clientX:(a=t.touches)==null?void 0:a[0].clientX,u=i?t.clientY:(c=t.touches)==null?void 0:c[0].clientY;return{x:l-((r==null?void 0:r.left)??0),y:u-((r==null?void 0:r.top)??0)}},yh=(t,r,i,l,u)=>{const a=r.querySelectorAll(`.${t}`);return!a||!a.length?null:Array.from(a).map(c=>{const d=c.getBoundingClientRect();return{id:c.getAttribute("data-handleid"),type:t,nodeId:u,position:c.getAttribute("data-handlepos"),x:(d.left-i.left)/l,y:(d.top-i.top)/l,...ac(c)}})};function lg({sourceX:t,sourceY:r,targetX:i,targetY:l,sourceControlX:u,sourceControlY:a,targetControlX:c,targetControlY:d}){const p=t*.125+u*.375+c*.375+i*.125,g=r*.125+a*.375+d*.375+l*.125,v=Math.abs(p-t),y=Math.abs(g-r);return[p,g,v,y]}function Qs(t,r){return t>=0?.5*t:r*25*Math.sqrt(-t)}function vh({pos:t,x1:r,y1:i,x2:l,y2:u,c:a}){switch(t){case ve.Left:return[r-Qs(r-l,a),i];case ve.Right:return[r+Qs(l-r,a),i];case ve.Top:return[r,i-Qs(i-u,a)];case ve.Bottom:return[r,i+Qs(u-i,a)]}}function ug({sourceX:t,sourceY:r,sourcePosition:i=ve.Bottom,targetX:l,targetY:u,targetPosition:a=ve.Top,curvature:c=.25}){const[d,p]=vh({pos:i,x1:t,y1:r,x2:l,y2:u,c}),[g,v]=vh({pos:a,x1:l,y1:u,x2:t,y2:r,c}),[y,m,w,S]=lg({sourceX:t,sourceY:r,targetX:l,targetY:u,sourceControlX:d,sourceControlY:p,targetControlX:g,targetControlY:v});return[`M${t},${r} C${d},${p} ${g},${v} ${l},${u}`,y,m,w,S]}function ag({sourceX:t,sourceY:r,targetX:i,targetY:l}){const u=Math.abs(i-t)/2,a=i<t?i+u:i-u,c=Math.abs(l-r)/2,d=l<r?l+c:l-c;return[a,d,u,c]}function Hw({sourceNode:t,targetNode:r,selected:i=!1,zIndex:l=0,elevateOnSelect:u=!1,zIndexMode:a="basic"}){if(a==="manual")return l;const c=u&&i?l+1e3:l,d=Math.max(t.parentId||u&&t.selected?t.internals.z:0,r.parentId||u&&r.selected?r.internals.z:0);return c+d}function Vw({sourceNode:t,targetNode:r,width:i,height:l,transform:u}){const a=wl(dl(t),dl(r));a.x===a.x2&&(a.x2+=1),a.y===a.y2&&(a.y2+=1);const c={x:-u[0]/u[2],y:-u[1]/u[2],width:i/u[2],height:l/u[2]};return hl(c,_l(a))>0}const bw=({source:t,sourceHandle:r,target:i,targetHandle:l})=>`xy-edge__${t}${r||""}-${i}${l||""}`,Bw=(t,r)=>r.some(i=>i.source===t.source&&i.target===t.target&&(i.sourceHandle===t.sourceHandle||!i.sourceHandle&&!t.sourceHandle)&&(i.targetHandle===t.targetHandle||!i.targetHandle&&!t.targetHandle)),Uw=(t,r,i={})=>{var a;if(!t.source||!t.target)return(a=i.onError)==null||a.call(i,"006",Wt.error006()),r;const l=i.getEdgeId||bw;let u;return Kp(t)?u={...t}:u={...t,id:l(t)},Bw(u,r)?r:(u.sourceHandle===null&&delete u.sourceHandle,u.targetHandle===null&&delete u.targetHandle,r.concat(u))};function cg({sourceX:t,sourceY:r,targetX:i,targetY:l}){const[u,a,c,d]=ag({sourceX:t,sourceY:r,targetX:i,targetY:l});return[`M ${t},${r}L ${i},${l}`,u,a,c,d]}const xh={[ve.Left]:{x:-1,y:0},[ve.Right]:{x:1,y:0},[ve.Top]:{x:0,y:-1},[ve.Bottom]:{x:0,y:1}},Ww=({source:t,sourcePosition:r=ve.Bottom,target:i})=>r===ve.Left||r===ve.Right?t.x<i.x?{x:1,y:0}:{x:-1,y:0}:t.y<i.y?{x:0,y:1}:{x:0,y:-1},wh=(t,r)=>Math.sqrt(Math.pow(r.x-t.x,2)+Math.pow(r.y-t.y,2));function Yw({source:t,sourcePosition:r=ve.Bottom,target:i,targetPosition:l=ve.Top,center:u,offset:a,stepPosition:c}){const d=xh[r],p=xh[l],g={x:t.x+d.x*a,y:t.y+d.y*a},v={x:i.x+p.x*a,y:i.y+p.y*a},y=Ww({source:g,sourcePosition:r,target:v}),m=y.x!==0?"x":"y",w=y[m];let S=[],P,C;const k={x:0,y:0},j={x:0,y:0},[,,_,T]=ag({sourceX:t.x,sourceY:t.y,targetX:i.x,targetY:i.y});if(d[m]*p[m]===-1){m==="x"?(P=u.x??g.x+(v.x-g.x)*c,C=u.y??(g.y+v.y)/2):(P=u.x??(g.x+v.x)/2,C=u.y??g.y+(v.y-g.y)*c);const Q=[{x:P,y:g.y},{x:P,y:v.y}],Y=[{x:g.x,y:C},{x:v.x,y:C}];d[m]===w?S=m==="x"?Q:Y:S=m==="x"?Y:Q}else{const Q=[{x:g.x,y:v.y}],Y=[{x:v.x,y:g.y}];if(m==="x"?S=d.x===w?Y:Q:S=d.y===w?Q:Y,r===l){const N=Math.abs(t[m]-i[m]);if(N<=a){const b=Math.min(a-1,a-N);d[m]===w?k[m]=(g[m]>t[m]?-1:1)*b:j[m]=(v[m]>i[m]?-1:1)*b}}if(r!==l){const N=m==="x"?"y":"x",b=d[m]===p[N],H=g[N]>v[N],B=g[N]<v[N];(d[m]===1&&(!b&&H||b&&B)||d[m]!==1&&(!b&&B||b&&H))&&(S=m==="x"?Q:Y)}const ee={x:g.x+k.x,y:g.y+k.y},q={x:v.x+j.x,y:v.y+j.y},J=Math.max(Math.abs(ee.x-S[0].x),Math.abs(q.x-S[0].x)),Z=Math.max(Math.abs(ee.y-S[0].y),Math.abs(q.y-S[0].y));J>=Z?(P=(ee.x+q.x)/2,C=S[0].y):(P=S[0].x,C=(ee.y+q.y)/2)}const F={x:g.x+k.x,y:g.y+k.y},D={x:v.x+j.x,y:v.y+j.y};return[[t,...F.x!==S[0].x||F.y!==S[0].y?[F]:[],...S,...D.x!==S[S.length-1].x||D.y!==S[S.length-1].y?[D]:[],i],P,C,_,T]}function Xw(t,r,i,l){const u=Math.min(wh(t,r)/2,wh(r,i)/2,l),{x:a,y:c}=r;if(t.x===a&&a===i.x||t.y===c&&c===i.y)return`L${a} ${c}`;if(t.y===c){const g=t.x<i.x?-1:1,v=t.y<i.y?1:-1;return`L ${a+u*g},${c}Q ${a},${c} ${a},${c+u*v}`}const d=t.x<i.x?1:-1,p=t.y<i.y?-1:1;return`L ${a},${c+u*p}Q ${a},${c} ${a+u*d},${c}`}function Ya({sourceX:t,sourceY:r,sourcePosition:i=ve.Bottom,targetX:l,targetY:u,targetPosition:a=ve.Top,borderRadius:c=5,centerX:d,centerY:p,offset:g=20,stepPosition:v=.5}){const[y,m,w,S,P]=Yw({source:{x:t,y:r},sourcePosition:i,target:{x:l,y:u},targetPosition:a,center:{x:d,y:p},offset:g,stepPosition:v});let C=`M${y[0].x} ${y[0].y}`;for(let k=1;k<y.length-1;k++)C+=Xw(y[k-1],y[k],y[k+1],c);return C+=`L${y[y.length-1].x} ${y[y.length-1].y}`,[C,m,w,S,P]}function _h(t){var r;return t&&!!(t.internals.handleBounds||(r=t.handles)!=null&&r.length)&&!!(t.measured.width||t.width||t.initialWidth)}function Qw(t){var y;const{sourceNode:r,targetNode:i}=t;if(!_h(r)||!_h(i))return null;const l=r.internals.handleBounds||Sh(r.handles),u=i.internals.handleBounds||Sh(i.handles),a=kh((l==null?void 0:l.source)??[],t.sourceHandle),c=kh(t.connectionMode===oo.Strict?(u==null?void 0:u.target)??[]:((u==null?void 0:u.target)??[]).concat((u==null?void 0:u.source)??[]),t.targetHandle);if(!a||!c)return(y=t.onError)==null||y.call(t,"008",Wt.error008(a?"target":"source",{id:t.id,sourceHandle:t.sourceHandle,targetHandle:t.targetHandle})),null;const d=(a==null?void 0:a.position)||ve.Bottom,p=(c==null?void 0:c.position)||ve.Top,g=_r(r,a,d),v=_r(i,c,p);return{sourceX:g.x,sourceY:g.y,targetX:v.x,targetY:v.y,sourcePosition:d,targetPosition:p}}function Sh(t){if(!t)return null;const r=[],i=[];for(const l of t)l.width=l.width??1,l.height=l.height??1,l.type==="source"?r.push(l):l.type==="target"&&i.push(l);return{source:r,target:i}}function _r(t,r,i=ve.Left,l=!1){const u=((r==null?void 0:r.x)??0)+t.internals.positionAbsolute.x,a=((r==null?void 0:r.y)??0)+t.internals.positionAbsolute.y,{width:c,height:d}=r??Xt(t);if(l)return{x:u+c/2,y:a+d/2};switch((r==null?void 0:r.position)??i){case ve.Top:return{x:u+c/2,y:a};case ve.Right:return{x:u+c,y:a+d/2};case ve.Bottom:return{x:u+c/2,y:a+d};case ve.Left:return{x:u,y:a+d/2}}}function kh(t,r){return t&&(r?t.find(i=>i.id===r):t[0])||null}function Xa(t,r){return t?typeof t=="string"?t:`${r?`${r}__`:""}${Object.keys(t).sort().map(l=>`${l}=${t[l]}`).join("&")}`:""}function Kw(t,{id:r,defaultColor:i,defaultMarkerStart:l,defaultMarkerEnd:u}){const a=new Set;return t.reduce((c,d)=>([d.markerStart||l,d.markerEnd||u].forEach(p=>{if(p&&typeof p=="object"){const g=Xa(p,r);a.has(g)||(c.push({id:g,color:p.color||i,...p}),a.add(g))}}),c),[]).sort((c,d)=>c.id.localeCompare(d.id))}const fg=1e3,Gw=10,cc={nodeOrigin:[0,0],nodeExtent:fi,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},qw={...cc,checkEquality:!0};function fc(t,r){const i={...t};for(const l in r)r[l]!==void 0&&(i[l]=r[l]);return i}function Zw(t,r,i){const l=fc(cc,i);for(const u of t.values())if(u.parentId)hc(u,t,r,l);else{const a=xi(u,l.nodeOrigin),c=wr(u.extent)?u.extent:l.nodeExtent,d=xr(a,c,Xt(u));u.internals.positionAbsolute=d}}function Jw(t,r){if(!t.handles)return t.measured?r==null?void 0:r.internals.handleBounds:void 0;const i=[],l=[];for(const u of t.handles){const a={id:u.id,width:u.width??1,height:u.height??1,nodeId:t.id,x:u.x,y:u.y,position:u.position,type:u.type};u.type==="source"?i.push(a):u.type==="target"&&l.push(a)}return{source:i,target:l}}function dc(t){return t==="manual"}function Qa(t,r,i,l={}){var v,y;const u=fc(qw,l),a={i:0},c=new Map(r),d=u!=null&&u.elevateNodesOnSelect&&!dc(u.zIndexMode)?fg:0;let p=t.length>0,g=!1;r.clear(),i.clear();for(const m of t){let w=c.get(m.id);if(u.checkEquality&&m===(w==null?void 0:w.internals.userNode))r.set(m.id,w);else{const S=xi(m,u.nodeOrigin),P=wr(m.extent)?m.extent:u.nodeExtent,C=xr(S,P,Xt(m));w={...u.defaults,...m,measured:{width:(v=m.measured)==null?void 0:v.width,height:(y=m.measured)==null?void 0:y.height},internals:{positionAbsolute:C,handleBounds:Jw(m,w),z:dg(m,d,u.zIndexMode),userNode:m}},r.set(m.id,w)}(w.measured===void 0||w.measured.width===void 0||w.measured.height===void 0)&&!w.hidden&&(p=!1),m.parentId&&hc(w,r,i,l,a),g||(g=m.selected??!1)}return{nodesInitialized:p,hasSelectedNodes:g}}function e1(t,r){if(!t.parentId)return;const i=r.get(t.parentId);i?i.set(t.id,t):r.set(t.parentId,new Map([[t.id,t]]))}function hc(t,r,i,l,u){const{elevateNodesOnSelect:a,nodeOrigin:c,nodeExtent:d,zIndexMode:p}=fc(cc,l),g=t.parentId,v=r.get(g);if(!v){console.warn(`Parent node ${g} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}e1(t,i),u&&!v.parentId&&v.internals.rootParentIndex===void 0&&p==="auto"&&(v.internals.rootParentIndex=++u.i,v.internals.z=v.internals.z+u.i*Gw),u&&v.internals.rootParentIndex!==void 0&&(u.i=v.internals.rootParentIndex);const y=a&&!dc(p)?fg:0,{x:m,y:w,z:S}=t1(t,v,c,d,y,p),{positionAbsolute:P}=t.internals,C=m!==P.x||w!==P.y;(C||S!==t.internals.z)&&r.set(t.id,{...t,internals:{...t.internals,positionAbsolute:C?{x:m,y:w}:P,z:S}})}function dg(t,r,i){const l=Bt(t.zIndex)?t.zIndex:0;return dc(i)?l:l+(t.selected?r:0)}function t1(t,r,i,l,u,a){const{x:c,y:d}=r.internals.positionAbsolute,p=Xt(t),g=xi(t,i),v=wr(t.extent)?xr(g,t.extent,p):g;let y=xr({x:c+v.x,y:d+v.y},l,p);t.extent==="parent"&&(y=qp(y,p,r));const m=dg(t,u,a),w=r.internals.z??0;return{x:y.x,y:y.y,z:w>=m?w+1:m}}function pc(t,r,i,l=[0,0]){var c;const u=[],a=new Map;for(const d of t){const p=r.get(d.parentId);if(!p)continue;const g=((c=a.get(d.parentId))==null?void 0:c.expandedRect)??hi(p),v=Zp(g,d.rect);a.set(d.parentId,{expandedRect:v,parent:p})}return a.size>0&&a.forEach(({expandedRect:d,parent:p},g)=>{var _;const v=p.internals.positionAbsolute,y=Xt(p),m=p.origin??l,w=d.x<v.x?Math.round(Math.abs(v.x-d.x)):0,S=d.y<v.y?Math.round(Math.abs(v.y-d.y)):0,P=Math.max(y.width,Math.round(d.width)),C=Math.max(y.height,Math.round(d.height)),k=(P-y.width)*m[0],j=(C-y.height)*m[1];(w>0||S>0||k||j)&&(u.push({id:g,type:"position",position:{x:p.position.x-w+k,y:p.position.y-S+j}}),(_=i.get(g))==null||_.forEach(T=>{t.some(F=>F.id===T.id)||u.push({id:T.id,type:"position",position:{x:T.position.x+w,y:T.position.y+S}})})),(y.width<d.width||y.height<d.height||w||S)&&u.push({id:g,type:"dimensions",setAttributes:!0,dimensions:{width:P+(w?m[0]*w-k:0),height:C+(S?m[1]*S-j:0)}})}),u}function n1(t,r,i,l,u,a,c){const d=l==null?void 0:l.querySelector(".xyflow__viewport");let p=!1;if(!d)return{changes:[],updatedInternals:p};const g=[],v=window.getComputedStyle(d),{m22:y}=new window.DOMMatrixReadOnly(v.transform),m=[];for(const w of t.values()){const S=r.get(w.id);if(!S)continue;if(S.hidden){r.set(S.id,{...S,internals:{...S.internals,handleBounds:void 0}}),p=!0;continue}const P=ac(w.nodeElement),C=S.measured.width!==P.width||S.measured.height!==P.height;if(!!(P.width&&P.height&&(C||!S.internals.handleBounds||w.force))){const j=w.nodeElement.getBoundingClientRect(),_=wr(S.extent)?S.extent:a;let{positionAbsolute:T}=S.internals;if(S.parentId&&S.extent==="parent"){const D=r.get(S.parentId);D&&(T=qp(T,P,D))}else _&&(T=xr(T,_,P));const F={...S,measured:P,internals:{...S.internals,positionAbsolute:T,handleBounds:{source:yh("source",w.nodeElement,j,y,S.id),target:yh("target",w.nodeElement,j,y,S.id)}}};r.set(S.id,F),S.parentId&&hc(F,r,i,{nodeOrigin:u,zIndexMode:c}),p=!0,C&&(g.push({id:S.id,type:"dimensions",dimensions:P}),S.expandParent&&S.parentId&&m.push({id:S.id,parentId:S.parentId,rect:hi(F,u)}))}}if(m.length>0){const w=pc(m,r,i,u);g.push(...w)}return{changes:g,updatedInternals:p}}async function r1({delta:t,panZoom:r,transform:i,translateExtent:l,width:u,height:a}){if(!r||!t.x&&!t.y)return!1;const c=await r.setViewportConstrained({x:i[0]+t.x,y:i[1]+t.y,zoom:i[2]},[[0,0],[u,a]],l);return!!c&&(c.x!==i[0]||c.y!==i[1]||c.k!==i[2])}function Eh(t,r,i,l,u,a){let c=u;const d=l.get(c)||new Map;l.set(c,d.set(i,r)),c=`${u}-${t}`;const p=l.get(c)||new Map;if(l.set(c,p.set(i,r)),a){c=`${u}-${t}-${a}`;const g=l.get(c)||new Map;l.set(c,g.set(i,r))}}function hg(t,r,i){t.clear(),r.clear();for(const l of i){const{source:u,target:a,sourceHandle:c=null,targetHandle:d=null}=l,p={edgeId:l.id,source:u,target:a,sourceHandle:c,targetHandle:d},g=`${u}-${c}--${a}-${d}`,v=`${a}-${d}--${u}-${c}`;Eh("source",p,v,t,u,c),Eh("target",p,g,t,a,d),r.set(l.id,l)}}function pg(t,r){if(!t.parentId)return!1;const i=r.get(t.parentId);return i?i.selected?!0:pg(i,r):!1}function Ch(t,r,i){var u;let l=t;do{if((u=l==null?void 0:l.matches)!=null&&u.call(l,r))return!0;if(l===i)return!1;l=l==null?void 0:l.parentElement}while(l);return!1}function o1(t,r,i,l){const u=new Map;for(const[a,c]of t)if((c.selected||c.id===l)&&(!c.parentId||!pg(c,t))&&(c.draggable||r&&typeof c.draggable>"u")){const d=t.get(a);d&&u.set(a,{id:a,position:d.position||{x:0,y:0},distance:{x:i.x-d.internals.positionAbsolute.x,y:i.y-d.internals.positionAbsolute.y},extent:d.extent,parentId:d.parentId,origin:d.origin,expandParent:d.expandParent,internals:{positionAbsolute:d.internals.positionAbsolute||{x:0,y:0}},measured:{width:d.measured.width??0,height:d.measured.height??0}})}return u}function Ma({nodeId:t,dragItems:r,nodeLookup:i,dragging:l=!0}){var c,d,p;const u=[];for(const[g,v]of r){const y=(c=i.get(g))==null?void 0:c.internals.userNode;y&&u.push({...y,position:v.position,dragging:l})}if(!t)return[u[0],u];const a=(d=i.get(t))==null?void 0:d.internals.userNode;return[a?{...a,position:((p=r.get(t))==null?void 0:p.position)||a.position,dragging:l}:u[0],u]}function i1({dragItems:t,snapGrid:r,x:i,y:l}){const u=t.values().next().value;if(!u)return null;const a={x:i-u.distance.x,y:l-u.distance.y},c=_i(a,r);return{x:c.x-a.x,y:c.y-a.y}}function s1({onNodeMouseDown:t,getStoreItems:r,onDragStart:i,onDrag:l,onDragStop:u}){let a={x:null,y:null},c=0,d=new Map,p=!1,g={x:0,y:0},v=null,y=!1,m=null,w=!1,S=!1,P=null;function C({noDragClassName:j,handleSelector:_,domNode:T,isSelectable:F,nodeId:D,nodeClickDistance:V=0}){m=Ct(T);function Q({x:J,y:Z}){const{nodeLookup:N,nodeExtent:b,snapGrid:H,snapToGrid:B,nodeOrigin:z,onNodeDrag:A,onSelectionDrag:O,onError:M,updateNodePositions:L}=r();a={x:J,y:Z};let te=!1;const re=d.size>1,ue=re&&b?Wa(wi(d)):null,ce=re&&B?i1({dragItems:d,snapGrid:H,x:J,y:Z}):null;for(const[ae,G]of d){if(!N.has(ae))continue;let se={x:J-G.distance.x,y:Z-G.distance.y};B&&(se=ce?{x:Math.round(se.x+ce.x),y:Math.round(se.y+ce.y)}:_i(se,H));let pe=null;if(re&&b&&!G.extent&&ue){const{positionAbsolute:we}=G.internals,ke=we.x-ue.x+b[0][0],Ce=we.x+G.measured.width-ue.x2+b[1][0],Me=we.y-ue.y+b[0][1],ze=we.y+G.measured.height-ue.y2+b[1][1];pe=[[ke,Me],[Ce,ze]]}const{position:xe,positionAbsolute:ye}=Gp({nodeId:ae,nextPosition:se,nodeLookup:N,nodeExtent:pe||b,nodeOrigin:z,onError:M});te=te||G.position.x!==xe.x||G.position.y!==xe.y,G.position=xe,G.internals.positionAbsolute=ye}if(S=S||te,!!te&&(L(d,!0),P&&(l||A||!D&&O))){const[ae,G]=Ma({nodeId:D,dragItems:d,nodeLookup:N});l==null||l(P,d,ae,G),A==null||A(P,ae,G),D||O==null||O(P,G)}}async function Y(){if(!v)return;const{transform:J,panBy:Z,autoPanSpeed:N,autoPanOnNodeDrag:b}=r();if(!b){p=!1,cancelAnimationFrame(c);return}const[H,B]=lc(g,v,N);(H!==0||B!==0)&&(a.x=(a.x??0)-H/J[2],a.y=(a.y??0)-B/J[2],await Z({x:H,y:B})&&Q(a)),c=requestAnimationFrame(Y)}function ee(J){var re;const{nodeLookup:Z,multiSelectionActive:N,nodesDraggable:b,transform:H,snapGrid:B,snapToGrid:z,selectNodesOnDrag:A,onNodeDragStart:O,onSelectionDragStart:M,unselectNodesAndEdges:L}=r();y=!0,(!A||!F)&&!N&&D&&((re=Z.get(D))!=null&&re.selected||L()),F&&A&&D&&(t==null||t(D));const te=si(J.sourceEvent,{transform:H,snapGrid:B,snapToGrid:z,containerBounds:v});if(a=te,d=o1(Z,b,te,D),d.size>0&&(i||O||!D&&M)){const[ue,ce]=Ma({nodeId:D,dragItems:d,nodeLookup:Z});i==null||i(J.sourceEvent,d,ue,ce),O==null||O(J.sourceEvent,ue,ce),D||M==null||M(J.sourceEvent,ce)}}const q=Ip().clickDistance(V).on("start",J=>{const{domNode:Z,nodeDragThreshold:N,transform:b,snapGrid:H,snapToGrid:B}=r();v=(Z==null?void 0:Z.getBoundingClientRect())||null,w=!1,S=!1,P=J.sourceEvent,N===0&&ee(J),a=si(J.sourceEvent,{transform:b,snapGrid:H,snapToGrid:B,containerBounds:v}),g=Ut(J.sourceEvent,v)}).on("drag",J=>{const{autoPanOnNodeDrag:Z,transform:N,snapGrid:b,snapToGrid:H,nodeDragThreshold:B,nodeLookup:z}=r(),A=si(J.sourceEvent,{transform:N,snapGrid:b,snapToGrid:H,containerBounds:v});if(P=J.sourceEvent,(J.sourceEvent.type==="touchmove"&&J.sourceEvent.touches.length>1||D&&!z.has(D))&&(w=!0),!w){if(!p&&Z&&y&&(p=!0,Y()),!y){const O=Ut(J.sourceEvent,v),M=O.x-g.x,L=O.y-g.y;Math.sqrt(M*M+L*L)>B&&ee(J)}(a.x!==A.xSnapped||a.y!==A.ySnapped)&&d&&y&&(g=Ut(J.sourceEvent,v),Q(A))}}).on("end",J=>{if(!y||w){w&&d.size>0&&r().updateNodePositions(d,!1);return}if(p=!1,y=!1,cancelAnimationFrame(c),d.size>0){const{nodeLookup:Z,updateNodePositions:N,onNodeDragStop:b,onSelectionDragStop:H}=r();if(S&&(N(d,!1),S=!1),u||b||!D&&H){const[B,z]=Ma({nodeId:D,dragItems:d,nodeLookup:Z,dragging:!1});u==null||u(J.sourceEvent,d,B,z),b==null||b(J.sourceEvent,B,z),D||H==null||H(J.sourceEvent,z)}}}).filter(J=>{const Z=J.target;return!J.button&&(!j||!Ch(Z,`.${j}`,T))&&(!_||Ch(Z,_,T))});m.call(q)}function k(){m==null||m.on(".drag",null)}return{update:C,destroy:k}}function l1(t,r,i){const l=[],u={x:t.x-i,y:t.y-i,width:i*2,height:i*2};for(const a of r.values())hl(u,hi(a))>0&&l.push(a);return l}const u1=250;function a1(t,r,i,l){var d,p;let u=[],a=1/0;const c=l1(t,i,r+u1);for(const g of c){const v=[...((d=g.internals.handleBounds)==null?void 0:d.source)??[],...((p=g.internals.handleBounds)==null?void 0:p.target)??[]];for(const y of v){if(l.nodeId===y.nodeId&&l.type===y.type&&l.id===y.id)continue;const{x:m,y:w}=_r(g,y,y.position,!0),S=Math.sqrt(Math.pow(m-t.x,2)+Math.pow(w-t.y,2));S>r||(S<a?(u=[{...y,x:m,y:w}],a=S):S===a&&u.push({...y,x:m,y:w}))}}if(!u.length)return null;if(u.length>1){const g=l.type==="source"?"target":"source";return u.find(v=>v.type===g)??u[0]}return u[0]}function gg(t,r,i,l,u,a=!1){var g,v,y;const c=l.get(t);if(!c)return null;const d=u==="strict"?(g=c.internals.handleBounds)==null?void 0:g[r]:[...((v=c.internals.handleBounds)==null?void 0:v.source)??[],...((y=c.internals.handleBounds)==null?void 0:y.target)??[]],p=(i?d==null?void 0:d.find(m=>m.id===i):d==null?void 0:d[0])??null;return p&&a?{...p,..._r(c,p,p.position,!0)}:p}function mg(t,r){return t||(r!=null&&r.classList.contains("target")?"target":r!=null&&r.classList.contains("source")?"source":null)}function c1(t,r){let i=null;return r?i=!0:t&&!r&&(i=!1),i}const yg=()=>!0;function f1(t,{connectionMode:r,connectionRadius:i,handleId:l,nodeId:u,edgeUpdaterType:a,isTarget:c,domNode:d,nodeLookup:p,lib:g,autoPanOnConnect:v,flowId:y,panBy:m,cancelConnection:w,onConnectStart:S,onConnect:P,onConnectEnd:C,isValidConnection:k=yg,onReconnectEnd:j,updateConnection:_,getTransform:T,getFromHandle:F,autoPanSpeed:D,dragThreshold:V=1,handleDomNode:Q}){const Y=og(t.target);let ee=0,q;const{x:J,y:Z}=Ut(t),N=mg(a,Q),b=d==null?void 0:d.getBoundingClientRect();let H=!1;if(!b||!N)return;const B=gg(u,N,l,p,r);if(!B)return;let z=Ut(t,b),A=!1,O=null,M=!1,L=null;function te(){if(!v||!b)return;const[xe,ye]=lc(z,b,D);m({x:xe,y:ye}),ee=requestAnimationFrame(te)}const re={...B,nodeId:u,type:N,position:B.position},ue=p.get(u);let ae={inProgress:!0,isValid:null,from:_r(ue,re,ve.Left,!0),fromHandle:re,fromPosition:re.position,fromNode:ue,to:z,toHandle:null,toPosition:hh[re.position],toNode:null,pointer:z};function G(){H=!0,_(ae),S==null||S(t,{nodeId:u,handleId:l,handleType:N})}V===0&&G();function se(xe){if(!H){const{x:ze,y:it}=Ut(xe),at=ze-J,Je=it-Z;if(!(at*at+Je*Je>V*V))return;G()}if(!F()||!re){pe(xe);return}const ye=T();z=Ut(xe,b),q=a1(Si(z,ye,!1,[1,1]),i,p,re),A||(te(),A=!0);const we=vg(xe,{handle:q,connectionMode:r,fromNodeId:u,fromHandleId:l,fromType:c?"target":"source",isValidConnection:k,doc:Y,lib:g,flowId:y,nodeLookup:p});L=we.handleDomNode,O=we.connection,M=c1(!!q,we.isValid);const ke=p.get(u),Ce=ke?_r(ke,re,ve.Left,!0):ae.from,Me={...ae,from:Ce,isValid:M,to:we.toHandle&&M?so({x:we.toHandle.x,y:we.toHandle.y},ye):z,toHandle:we.toHandle,toPosition:M&&we.toHandle?we.toHandle.position:hh[re.position],toNode:we.toHandle?p.get(we.toHandle.nodeId):null,pointer:z};_(Me),ae=Me}function pe(xe){if(!("touches"in xe&&xe.touches.length>0)){if(H){(q||L)&&O&&M&&(P==null||P(O));const{inProgress:ye,...we}=ae,ke={...we,toPosition:ae.toHandle?ae.toPosition:null};C==null||C(xe,ke),a&&(j==null||j(xe,ke))}w(),cancelAnimationFrame(ee),A=!1,M=!1,O=null,L=null,Y.removeEventListener("mousemove",se),Y.removeEventListener("mouseup",pe),Y.removeEventListener("touchmove",se),Y.removeEventListener("touchend",pe)}}Y.addEventListener("mousemove",se),Y.addEventListener("mouseup",pe),Y.addEventListener("touchmove",se),Y.addEventListener("touchend",pe)}function vg(t,{handle:r,connectionMode:i,fromNodeId:l,fromHandleId:u,fromType:a,doc:c,lib:d,flowId:p,isValidConnection:g=yg,nodeLookup:v}){const y=a==="target",m=r?c.querySelector(`.${d}-flow__handle[data-id="${p}-${r==null?void 0:r.nodeId}-${r==null?void 0:r.id}-${r==null?void 0:r.type}"]`):null,{x:w,y:S}=Ut(t),P=c.elementFromPoint(w,S),C=P!=null&&P.classList.contains(`${d}-flow__handle`)?P:m,k={handleDomNode:C,isValid:!1,connection:null,toHandle:null};if(C){const j=mg(void 0,C),_=C.getAttribute("data-nodeid"),T=C.getAttribute("data-handleid"),F=C.classList.contains("connectable"),D=C.classList.contains("connectableend");if(!_||!j)return k;const V={source:y?_:l,sourceHandle:y?T:u,target:y?l:_,targetHandle:y?u:T};k.connection=V;const Y=F&&D&&(i===oo.Strict?y&&j==="source"||!y&&j==="target":_!==l||T!==u);k.isValid=Y&&g(V),k.toHandle=gg(_,j,T,v,i,!0)}return k}const Ka={onPointerDown:f1,isValid:vg};function d1({domNode:t,panZoom:r,getTransform:i,getViewScale:l}){const u=Ct(t);function a({translateExtent:d,width:p,height:g,zoomStep:v=1,pannable:y=!0,zoomable:m=!0,inversePan:w=!1}){const S=_=>{if(_.sourceEvent.type!=="wheel"||!r)return;const T=i(),F=_.sourceEvent.ctrlKey&&pi()?10:1,D=-_.sourceEvent.deltaY*(_.sourceEvent.deltaMode===1?.05:_.sourceEvent.deltaMode?1:.002)*v,V=T[2]*Math.pow(2,D*F);r.scaleTo(V)};let P=[0,0];const C=_=>{(_.sourceEvent.type==="mousedown"||_.sourceEvent.type==="touchstart")&&(P=[_.sourceEvent.clientX??_.sourceEvent.touches[0].clientX,_.sourceEvent.clientY??_.sourceEvent.touches[0].clientY])},k=_=>{const T=i();if(_.sourceEvent.type!=="mousemove"&&_.sourceEvent.type!=="touchmove"||!r)return;const F=[_.sourceEvent.clientX??_.sourceEvent.touches[0].clientX,_.sourceEvent.clientY??_.sourceEvent.touches[0].clientY],D=[F[0]-P[0],F[1]-P[1]];P=F;const V=l()*Math.max(T[2],Math.log(T[2]))*(w?-1:1),Q={x:T[0]-D[0]*V,y:T[1]-D[1]*V},Y=[[0,0],[p,g]];r.setViewportConstrained({x:Q.x,y:Q.y,zoom:T[2]},Y,d)},j=Wp().on("start",C).on("zoom",y?k:null).on("zoom.wheel",m?S:null);u.call(j,{})}function c(){u.on("zoom",null)}return{update:a,destroy:c,pointer:Vt}}const Sl=t=>({x:t.x,y:t.y,zoom:t.k}),Pa=({x:t,y:r,zoom:i})=>xl.translate(t,r).scale(i),Bn=(t,r)=>t.target.closest(`.${r}`),xg=(t,r)=>r===2&&Array.isArray(t)&&t.includes(2),h1=t=>((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2,Ta=(t,r=0,i=h1,l=()=>{})=>{const u=typeof r=="number"&&r>0;return u||l(),u?t.transition().duration(r).ease(i).on("end",l):t},wg=t=>{const r=t.ctrlKey&&pi()?10:1;return-t.deltaY*(t.deltaMode===1?.05:t.deltaMode?1:.002)*r};function p1({zoomPanValues:t,noWheelClassName:r,d3Selection:i,d3Zoom:l,panOnScrollMode:u,panOnScrollSpeed:a,zoomOnPinch:c,onPanZoomStart:d,onPanZoom:p,onPanZoomEnd:g}){return v=>{if(Bn(v,r))return v.ctrlKey&&v.preventDefault(),!1;v.preventDefault(),v.stopImmediatePropagation();const y=i.property("__zoom").k||1;if(v.ctrlKey&&c){const C=Vt(v),k=wg(v),j=y*Math.pow(2,k);l.scaleTo(i,j,C,v);return}const m=v.deltaMode===1?20:1;let w=u===gr.Vertical?0:v.deltaX*m,S=u===gr.Horizontal?0:v.deltaY*m;!pi()&&v.shiftKey&&u!==gr.Vertical&&(w=v.deltaY*m,S=0),l.translateBy(i,-(w/y)*a,-(S/y)*a,{internal:!0});const P=Sl(i.property("__zoom"));clearTimeout(t.panScrollTimeout),t.isPanScrolling?p==null||p(v,P):(t.isPanScrolling=!0,d==null||d(v,P)),t.panScrollTimeout=setTimeout(()=>{g==null||g(v,P),t.isPanScrolling=!1},150)}}function g1({noWheelClassName:t,preventScrolling:r,d3ZoomHandler:i}){return function(l,u){const a=l.type==="wheel",c=!r&&a&&!l.ctrlKey,d=Bn(l,t);if(l.ctrlKey&&a&&d&&l.preventDefault(),c||d)return null;l.preventDefault(),i.call(this,l,u)}}function m1({zoomPanValues:t,onDraggingChange:r,onPanZoomStart:i}){return l=>{var a,c,d;if((a=l.sourceEvent)!=null&&a.internal)return;const u=Sl(l.transform);t.mouseButton=((c=l.sourceEvent)==null?void 0:c.button)||0,t.isZoomingOrPanning=!0,t.prevViewport=u,((d=l.sourceEvent)==null?void 0:d.type)==="mousedown"&&r(!0),i&&(i==null||i(l.sourceEvent,u))}}function y1({zoomPanValues:t,panOnDrag:r,onPaneContextMenu:i,onTransformChange:l,onPanZoom:u}){return a=>{var c,d;t.usedRightMouseButton=!!(i&&xg(r,t.mouseButton??0)),(c=a.sourceEvent)!=null&&c.sync||l([a.transform.x,a.transform.y,a.transform.k]),u&&!((d=a.sourceEvent)!=null&&d.internal)&&(u==null||u(a.sourceEvent,Sl(a.transform)))}}function v1({zoomPanValues:t,panOnDrag:r,panOnScroll:i,onDraggingChange:l,onPanZoomEnd:u,onPaneContextMenu:a}){return c=>{var d;if(!((d=c.sourceEvent)!=null&&d.internal)&&(t.isZoomingOrPanning=!1,a&&xg(r,t.mouseButton??0)&&!t.usedRightMouseButton&&c.sourceEvent&&a(c.sourceEvent),t.usedRightMouseButton=!1,l(!1),u)){const p=Sl(c.transform);t.prevViewport=p,clearTimeout(t.timerId),t.timerId=setTimeout(()=>{u==null||u(c.sourceEvent,p)},i?150:0)}}}function x1({panActivationKeyPressed:t,zoomActivationKeyPressed:r,zoomOnScroll:i,zoomOnPinch:l,panOnDrag:u,panOnScroll:a,zoomOnDoubleClick:c,userSelectionActive:d,noWheelClassName:p,noPanClassName:g,lib:v,connectionInProgress:y}){return m=>{var k;const w=r||i,S=l&&m.ctrlKey,P=m.type==="wheel";if(m.button===1&&m.type==="mousedown"&&(Bn(m,`${v}-flow__node`)||Bn(m,`${v}-flow__edge`)||Bn(m,`${v}-flow__selection`)||Bn(m,`${v}-flow__nodesselection`)))return!0;if(!u&&!w&&!a&&!c&&!l||d||y&&!P||Bn(m,p)&&P||Bn(m,g)&&(!P||a&&P&&!r)||!l&&m.ctrlKey&&P)return!1;if(!l&&m.type==="touchstart"&&((k=m.touches)==null?void 0:k.length)>1)return m.preventDefault(),!1;if(!w&&!a&&!S&&P||!u&&(m.type==="mousedown"||m.type==="touchstart")||Array.isArray(u)&&!u.includes(m.button)&&m.type==="mousedown")return!1;const C=Array.isArray(u)&&u.includes(m.button)||!m.button||m.button<=1;return(!m.ctrlKey||P||t)&&C}}function w1({domNode:t,minZoom:r,maxZoom:i,translateExtent:l,viewport:u,onPanZoom:a,onPanZoomStart:c,onPanZoomEnd:d,onDraggingChange:p}){const g={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},v=t.getBoundingClientRect();let y=[[0,0],[v.width,v.height]];const m=typeof ResizeObserver<"u"?new ResizeObserver(Z=>{const N=Z[0];N&&(y=[[0,0],[N.contentRect.width,N.contentRect.height]])}):null;m==null||m.observe(t);const w=Wp().extent(()=>y).scaleExtent([r,i]).translateExtent(l),S=Ct(t).call(w);T({x:u.x,y:u.y,zoom:io(u.zoom,r,i)},[[0,0],[v.width,v.height]],l);const P=S.on("wheel.zoom"),C=S.on("dblclick.zoom");w.wheelDelta(wg);async function k(Z,N){return S?new Promise(b=>{w==null||w.interpolate((N==null?void 0:N.interpolate)==="linear"?ii:Js).transform(Ta(S,N==null?void 0:N.duration,N==null?void 0:N.ease,()=>b(!0)),Z)}):!1}function j({noWheelClassName:Z,noPanClassName:N,onPaneContextMenu:b,userSelectionActive:H,panOnScroll:B,panOnDrag:z,panOnScrollMode:A,panOnScrollSpeed:O,preventScrolling:M,zoomOnPinch:L,zoomOnScroll:te,zoomOnDoubleClick:re,panActivationKeyPressed:ue=!1,zoomActivationKeyPressed:ce,lib:ae,onTransformChange:G,connectionInProgress:se,paneClickDistance:pe,selectionOnDrag:xe}){H&&!g.isZoomingOrPanning&&_();const ye=B&&!ce&&!H;w.clickDistance(xe?1/0:!Bt(pe)||pe<0?0:pe);const we=ye?p1({zoomPanValues:g,noWheelClassName:Z,d3Selection:S,d3Zoom:w,panOnScrollMode:A,panOnScrollSpeed:O,zoomOnPinch:L,onPanZoomStart:c,onPanZoom:a,onPanZoomEnd:d}):g1({noWheelClassName:Z,preventScrolling:M,d3ZoomHandler:P});S.on("wheel.zoom",we,{passive:!1});const ke=m1({zoomPanValues:g,onDraggingChange:p,onPanZoomStart:c});w.on("start",ke);const Ce=y1({zoomPanValues:g,panOnDrag:z,onPaneContextMenu:!!b,onPanZoom:a,onTransformChange:G});w.on("zoom",Ce);const Me=v1({zoomPanValues:g,panOnDrag:z,panOnScroll:B,onPaneContextMenu:b,onPanZoomEnd:d,onDraggingChange:p});w.on("end",Me);const ze=x1({panActivationKeyPressed:ue,zoomActivationKeyPressed:ce,panOnDrag:z,zoomOnScroll:te,panOnScroll:B,zoomOnDoubleClick:re,zoomOnPinch:L,userSelectionActive:H,noPanClassName:N,noWheelClassName:Z,lib:ae,connectionInProgress:se});w.filter(ze),re?S.on("dblclick.zoom",C):S.on("dblclick.zoom",null)}function _(){w.on("zoom",null)}async function T(Z,N,b){const H=Pa(Z),B=w==null?void 0:w.constrain()(H,N,b);return B&&await k(B),B}async function F(Z,N){const b=Pa(Z);return await k(b,N),b}function D(Z){if(S){const N=Pa(Z),b=S.property("__zoom");(b.k!==Z.zoom||b.x!==Z.x||b.y!==Z.y)&&(w==null||w.transform(S,N,null,{sync:!0}))}}function V(){const Z=S?Up(S.node()):{x:0,y:0,k:1};return{x:Z.x,y:Z.y,zoom:Z.k}}async function Q(Z,N){return S?new Promise(b=>{w==null||w.interpolate((N==null?void 0:N.interpolate)==="linear"?ii:Js).scaleTo(Ta(S,N==null?void 0:N.duration,N==null?void 0:N.ease,()=>b(!0)),Z)}):!1}async function Y(Z,N){return S?new Promise(b=>{w==null||w.interpolate((N==null?void 0:N.interpolate)==="linear"?ii:Js).scaleBy(Ta(S,N==null?void 0:N.duration,N==null?void 0:N.ease,()=>b(!0)),Z)}):!1}function ee(Z){w==null||w.scaleExtent(Z)}function q(Z){w==null||w.translateExtent(Z)}function J(Z){const N=!Bt(Z)||Z<0?0:Z;w==null||w.clickDistance(N)}return{update:j,destroy:_,setViewport:F,setViewportConstrained:T,getViewport:V,scaleTo:Q,scaleBy:Y,setScaleExtent:ee,setTranslateExtent:q,syncViewport:D,setClickDistance:J}}var lo;(function(t){t.Line="line",t.Handle="handle"})(lo||(lo={}));function _1({width:t,prevWidth:r,height:i,prevHeight:l,affectsX:u,affectsY:a}){const c=t-r,d=i-l,p=[c>0?1:c<0?-1:0,d>0?1:d<0?-1:0];return c&&u&&(p[0]=p[0]*-1),d&&a&&(p[1]=p[1]*-1),p}function Nh(t){const r=t.includes("right")||t.includes("left"),i=t.includes("bottom")||t.includes("top"),l=t.includes("left"),u=t.includes("top");return{isHorizontal:r,isVertical:i,affectsX:l,affectsY:u}}function Vn(t,r){return Math.max(0,r-t)}function bn(t,r){return Math.max(0,t-r)}function Ks(t,r,i){return Math.max(0,r-t,t-i)}function Mh(t,r){return t?!r:r}function S1(t,r,i,l,u,a,c,d){let{affectsX:p,affectsY:g}=r;const{isHorizontal:v,isVertical:y}=r,m=v&&y,{xSnapped:w,ySnapped:S}=i,{minWidth:P,maxWidth:C,minHeight:k,maxHeight:j}=l,{x:_,y:T,width:F,height:D,aspectRatio:V}=t;let Q=Math.floor(v?w-t.pointerX:0),Y=Math.floor(y?S-t.pointerY:0);const ee=F+(p?-Q:Q),q=D+(g?-Y:Y),J=-a[0]*F,Z=-a[1]*D;let N=Ks(ee,P,C),b=Ks(q,k,j);if(c){let z=0,A=0;p&&Q<0?z=Vn(_+Q+J,c[0][0]):!p&&Q>0&&(z=bn(_+ee+J,c[1][0])),g&&Y<0?A=Vn(T+Y+Z,c[0][1]):!g&&Y>0&&(A=bn(T+q+Z,c[1][1])),N=Math.max(N,z),b=Math.max(b,A)}if(d){let z=0,A=0;p&&Q>0?z=bn(_+Q,d[0][0]):!p&&Q<0&&(z=Vn(_+ee,d[1][0])),g&&Y>0?A=bn(T+Y,d[0][1]):!g&&Y<0&&(A=Vn(T+q,d[1][1])),N=Math.max(N,z),b=Math.max(b,A)}if(u){if(v){const z=Ks(ee/V,k,j)*V;if(N=Math.max(N,z),c){let A=0;!p&&!g||p&&!g&&m?A=bn(T+Z+ee/V,c[1][1])*V:A=Vn(T+Z+(p?Q:-Q)/V,c[0][1])*V,N=Math.max(N,A)}if(d){let A=0;!p&&!g||p&&!g&&m?A=Vn(T+ee/V,d[1][1])*V:A=bn(T+(p?Q:-Q)/V,d[0][1])*V,N=Math.max(N,A)}}if(y){const z=Ks(q*V,P,C)/V;if(b=Math.max(b,z),c){let A=0;!p&&!g||g&&!p&&m?A=bn(_+q*V+J,c[1][0])/V:A=Vn(_+(g?Y:-Y)*V+J,c[0][0])/V,b=Math.max(b,A)}if(d){let A=0;!p&&!g||g&&!p&&m?A=Vn(_+q*V,d[1][0])/V:A=bn(_+(g?Y:-Y)*V,d[0][0])/V,b=Math.max(b,A)}}}Y=Y+(Y<0?b:-b),Q=Q+(Q<0?N:-N),u&&(m?ee>q*V?Y=(Mh(p,g)?-Q:Q)/V:Q=(Mh(p,g)?-Y:Y)*V:v?(Y=Q/V,g=p):(Q=Y*V,p=g));const H=p?_+Q:_,B=g?T+Y:T;return{width:F+(p?-Q:Q),height:D+(g?-Y:Y),x:a[0]*Q*(p?-1:1)+H,y:a[1]*Y*(g?-1:1)+B}}const _g={width:0,height:0,x:0,y:0},k1={..._g,pointerX:0,pointerY:0,aspectRatio:1};function E1(t,r,i){const l=r.position.x+t.position.x,u=r.position.y+t.position.y,a=t.measured.width??0,c=t.measured.height??0,d=i[0]*a,p=i[1]*c;return[[l-d,u-p],[l+a-d,u+c-p]]}function C1({domNode:t,nodeId:r,getStoreItems:i,onChange:l,onEnd:u}){const a=Ct(t);let c={controlDirection:Nh("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function d({controlPosition:g,boundaries:v,keepAspectRatio:y,resizeDirection:m,onResizeStart:w,onResize:S,onResizeEnd:P,shouldResize:C}){let k={..._g},j={...k1};c={boundaries:v,resizeDirection:m,keepAspectRatio:y,controlDirection:Nh(g)};let _,T=null,F=[],D,V,Q,Y=!1;const ee=Ip().on("start",q=>{const{nodeLookup:J,transform:Z,snapGrid:N,snapToGrid:b,nodeOrigin:H,paneDomNode:B}=i();if(_=J.get(r),!_)return;T=(B==null?void 0:B.getBoundingClientRect())??null;const{xSnapped:z,ySnapped:A}=si(q.sourceEvent,{transform:Z,snapGrid:N,snapToGrid:b,containerBounds:T});k={width:_.measured.width??0,height:_.measured.height??0,x:_.position.x??0,y:_.position.y??0},j={...k,pointerX:z,pointerY:A,aspectRatio:k.width/k.height},D=void 0,V=wr(_.extent)?_.extent:void 0,_.parentId&&(_.extent==="parent"||_.expandParent)&&(D=J.get(_.parentId)),D&&_.extent==="parent"&&(V=[[0,0],[D.measured.width,D.measured.height]]),F=[],Q=void 0;for(const[O,M]of J)if(M.parentId===r&&(F.push({id:O,position:{...M.position},extent:M.extent}),M.extent==="parent"||M.expandParent)){const L=E1(M,_,M.origin??H);Q?Q=[[Math.min(L[0][0],Q[0][0]),Math.min(L[0][1],Q[0][1])],[Math.max(L[1][0],Q[1][0]),Math.max(L[1][1],Q[1][1])]]:Q=L}w==null||w(q,{...k})}).on("drag",q=>{const{transform:J,snapGrid:Z,snapToGrid:N,nodeOrigin:b}=i(),H=si(q.sourceEvent,{transform:J,snapGrid:Z,snapToGrid:N,containerBounds:T}),B=[];if(!_)return;const{x:z,y:A,width:O,height:M}=k,L={},te=_.origin??b,{width:re,height:ue,x:ce,y:ae}=S1(j,c.controlDirection,H,c.boundaries,c.keepAspectRatio,te,V,Q),G=re!==O,se=ue!==M,pe=ce!==z&&G,xe=ae!==A&&se;if(!pe&&!xe&&!G&&!se)return;if((pe||xe||te[0]===1||te[1]===1)&&(L.x=pe?ce:k.x,L.y=xe?ae:k.y,k.x=L.x,k.y=L.y,F.length>0)){const Ce=ce-z,Me=ae-A;for(const ze of F)ze.position={x:ze.position.x-Ce+te[0]*(re-O),y:ze.position.y-Me+te[1]*(ue-M)},B.push(ze)}if((G||se)&&(L.width=G&&(!c.resizeDirection||c.resizeDirection==="horizontal")?re:k.width,L.height=se&&(!c.resizeDirection||c.resizeDirection==="vertical")?ue:k.height,k.width=L.width,k.height=L.height),D&&_.expandParent){const Ce=te[0]*(L.width??0);L.x&&L.x<Ce&&(k.x=Ce,j.x=j.x-(L.x-Ce));const Me=te[1]*(L.height??0);L.y&&L.y<Me&&(k.y=Me,j.y=j.y-(L.y-Me))}const ye=_1({width:k.width,prevWidth:O,height:k.height,prevHeight:M,affectsX:c.controlDirection.affectsX,affectsY:c.controlDirection.affectsY}),we={...k,direction:ye};(C==null?void 0:C(q,we))!==!1&&(Y=!0,S==null||S(q,we),l(L,B))}).on("end",q=>{Y&&(P==null||P(q,{...k}),u==null||u({...k}),Y=!1)});a.call(ee)}function p(){a.on(".drag",null)}return{update:d,destroy:p}}var Ia={exports:{}},za={},ja={exports:{}},Aa={};/**
+ * @license React
+ * use-sync-external-store-shim.production.js
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var Ph;function N1(){if(Ph)return Aa;Ph=1;var t=mi();function r(y,m){return y===m&&(y!==0||1/y===1/m)||y!==y&&m!==m}var i=typeof Object.is=="function"?Object.is:r,l=t.useState,u=t.useEffect,a=t.useLayoutEffect,c=t.useDebugValue;function d(y,m){var w=m(),S=l({inst:{value:w,getSnapshot:m}}),P=S[0].inst,C=S[1];return a(function(){P.value=w,P.getSnapshot=m,p(P)&&C({inst:P})},[y,w,m]),u(function(){return p(P)&&C({inst:P}),y(function(){p(P)&&C({inst:P})})},[y]),c(w),w}function p(y){var m=y.getSnapshot;y=y.value;try{var w=m();return!i(y,w)}catch{return!0}}function g(y,m){return m()}var v=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?g:d;return Aa.useSyncExternalStore=t.useSyncExternalStore!==void 0?t.useSyncExternalStore:v,Aa}var Th;function M1(){return Th||(Th=1,ja.exports=N1()),ja.exports}/**
+ * @license React
+ * use-sync-external-store-shim/with-selector.production.js
+ *
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */var Ih;function P1(){if(Ih)return za;Ih=1;var t=mi(),r=M1();function i(g,v){return g===v&&(g!==0||1/g===1/v)||g!==g&&v!==v}var l=typeof Object.is=="function"?Object.is:i,u=r.useSyncExternalStore,a=t.useRef,c=t.useEffect,d=t.useMemo,p=t.useDebugValue;return za.useSyncExternalStoreWithSelector=function(g,v,y,m,w){var S=a(null);if(S.current===null){var P={hasValue:!1,value:null};S.current=P}else P=S.current;S=d(function(){function k(D){if(!j){if(j=!0,_=D,D=m(D),w!==void 0&&P.hasValue){var V=P.value;if(w(V,D))return T=V}return T=D}if(V=T,l(_,D))return V;var Q=m(D);return w!==void 0&&w(V,Q)?(_=D,V):(_=D,T=Q)}var j=!1,_,T,F=y===void 0?null:y;return[function(){return k(v())},F===null?void 0:function(){return k(F())}]},[v,y,m,w]);var C=u(g,S[0],S[1]);return c(function(){P.hasValue=!0,P.value=C},[C]),p(C),C},za}var zh;function T1(){return zh||(zh=1,Ia.exports=P1()),Ia.exports}var I1=T1();const z1=up(I1),j1={},jh=t=>{let r;const i=new Set,l=(v,y)=>{const m=typeof v=="function"?v(r):v;if(!Object.is(m,r)){const w=r;r=y??(typeof m!="object"||m===null)?m:Object.assign({},r,m),i.forEach(S=>S(r,w))}},u=()=>r,p={setState:l,getState:u,getInitialState:()=>g,subscribe:v=>(i.add(v),()=>i.delete(v)),destroy:()=>{(j1?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),i.clear()}},g=r=t(l,u,p);return p},A1=t=>t?jh(t):jh,{useDebugValue:L1}=ap,{useSyncExternalStoreWithSelector:R1}=z1,D1=t=>t;function Sg(t,r=D1,i){const l=R1(t.subscribe,t.getState,t.getServerState||t.getInitialState,r,i);return L1(l),l}const Ah=(t,r)=>{const i=A1(t),l=(u,a=r)=>Sg(i,u,a);return Object.assign(l,i),l},$1=(t,r)=>t?Ah(t,r):Ah;function Ve(t,r){if(Object.is(t,r))return!0;if(typeof t!="object"||t===null||typeof r!="object"||r===null)return!1;if(t instanceof Map&&r instanceof Map){if(t.size!==r.size)return!1;for(const[l,u]of t)if(!Object.is(u,r.get(l)))return!1;return!0}if(t instanceof Set&&r instanceof Set){if(t.size!==r.size)return!1;for(const l of t)if(!r.has(l))return!1;return!0}const i=Object.keys(t);if(i.length!==Object.keys(r).length)return!1;for(const l of i)if(!Object.prototype.hasOwnProperty.call(r,l)||!Object.is(t[l],r[l]))return!1;return!0}cp();const kl=U.createContext(null),O1=kl.Provider,kg=Wt.error001("react");function Te(t,r){const i=U.useContext(kl);if(i===null)throw new Error(kg);return Sg(i,t,r)}function De(){const t=U.useContext(kl);if(t===null)throw new Error(kg);return U.useMemo(()=>({getState:t.getState,setState:t.setState,subscribe:t.subscribe}),[t])}const Lh={display:"none"},F1={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},Eg="react-flow__node-desc",Cg="react-flow__edge-desc",H1="react-flow__aria-live",V1=t=>t.ariaLiveMessage,b1=t=>t.ariaLabelConfig;function B1({rfId:t}){const r=Te(V1);return E.jsx("div",{id:`${H1}-${t}`,"aria-live":"assertive","aria-atomic":"true",style:F1,children:r})}function U1({rfId:t,disableKeyboardA11y:r}){const i=Te(b1);return E.jsxs(E.Fragment,{children:[E.jsx("div",{id:`${Eg}-${t}`,style:Lh,children:r?i["node.a11yDescription.default"]:i["node.a11yDescription.keyboardDisabled"]}),E.jsx("div",{id:`${Cg}-${t}`,style:Lh,children:i["edge.a11yDescription.default"]}),!r&&E.jsx(B1,{rfId:t})]})}const ki=U.forwardRef(({position:t="top-left",children:r,className:i,style:l,...u},a)=>{const c=`${t}`.split("-");return E.jsx("div",{className:We(["react-flow__panel",i,...c]),style:l,ref:a,...u,children:r})});ki.displayName="Panel";const Rh="https://reactflow.dev?utm_source=attribution";function W1({proOptions:t,position:r="bottom-right"}){return U.useEffect(()=>{},[]),t!=null&&t.hideAttribution?null:E.jsx(ki,{position:r,className:"react-flow__attribution","data-message":`Please only hide this attribution when you are subscribed to React Flow Pro: ${Rh}`,children:E.jsx("a",{href:Rh,target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}const Y1=t=>{const r=[],i=[];for(const[,l]of t.nodeLookup)l.selected&&r.push(l.internals.userNode);for(const[,l]of t.edgeLookup)l.selected&&i.push(l);return{selectedNodes:r,selectedEdges:i}},Gs=t=>t.id;function X1(t,r){return Ve(t.selectedNodes.map(Gs),r.selectedNodes.map(Gs))&&Ve(t.selectedEdges.map(Gs),r.selectedEdges.map(Gs))}function Q1({onSelectionChange:t}){const r=De(),{selectedNodes:i,selectedEdges:l}=Te(Y1,X1);return U.useEffect(()=>{const u={nodes:i,edges:l};t==null||t(u),r.getState().onSelectionChangeHandlers.forEach(a=>a(u))},[i,l,t]),null}const K1=t=>!!t.onSelectionChangeHandlers;function G1({onSelectionChange:t}){const r=Te(K1);return t||r?E.jsx(Q1,{onSelectionChange:t}):null}const Ng=[0,0],q1={x:0,y:0,zoom:1},Z1=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],Dh=[...Z1,"rfId"],J1=t=>({setNodes:t.setNodes,setEdges:t.setEdges,setMinZoom:t.setMinZoom,setMaxZoom:t.setMaxZoom,setTranslateExtent:t.setTranslateExtent,setNodeExtent:t.setNodeExtent,reset:t.reset,setDefaultNodesAndEdges:t.setDefaultNodesAndEdges}),$h={translateExtent:fi,nodeOrigin:Ng,minZoom:.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function e_(t){const{setNodes:r,setEdges:i,setMinZoom:l,setMaxZoom:u,setTranslateExtent:a,setNodeExtent:c,reset:d,setDefaultNodesAndEdges:p}=Te(J1,Ve),g=De();U.useEffect(()=>(p(t.defaultNodes,t.defaultEdges),()=>{v.current=$h,d()}),[]);const v=U.useRef($h);return U.useEffect(()=>{for(const y of Dh){const m=t[y],w=v.current[y];m!==w&&(typeof t[y]>"u"||(y==="nodes"?r(m):y==="edges"?i(m):y==="minZoom"?l(m):y==="maxZoom"?u(m):y==="translateExtent"?a(m):y==="nodeExtent"?c(m):y==="ariaLabelConfig"?g.setState({ariaLabelConfig:Ow(m)}):y==="fitView"?g.setState({fitViewQueued:m}):y==="fitViewOptions"?g.setState({fitViewOptions:m}):g.setState({[y]:m})))}v.current=t},Dh.map(y=>t[y])),null}function Oh(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function t_(t){var l;const[r,i]=U.useState(t==="system"?null:t);return U.useEffect(()=>{if(t!=="system"){i(t);return}const u=Oh(),a=()=>i(u!=null&&u.matches?"dark":"light");return a(),u==null||u.addEventListener("change",a),()=>{u==null||u.removeEventListener("change",a)}},[t]),r!==null?r:(l=Oh())!=null&&l.matches?"dark":"light"}const Fh=typeof document<"u"?document:null;function gi(t=null,r={target:Fh,actInsideInputWithModifier:!0}){const[i,l]=U.useState(!1),u=U.useRef(!1),a=U.useRef(new Set([])),[c,d]=U.useMemo(()=>{if(t!==null){const g=(Array.isArray(t)?t:[t]).filter(y=>typeof y=="string").map(y=>y.replace(/\+/g,`
+`).replace(`
+
+`,`
++`).split(`
+`)),v=g.reduce((y,m)=>y.concat(...m),[]);return[g,v]}return[[],[]]},[t]);return U.useEffect(()=>{const p=(r==null?void 0:r.target)??Fh,g=(r==null?void 0:r.actInsideInputWithModifier)??!0;if(t!==null){const v=w=>{var C,k;if(u.current=w.ctrlKey||w.metaKey||w.shiftKey||w.altKey,(!u.current||u.current&&!g)&&ig(w))return!1;const P=Vh(w.code,d);if(a.current.add(w[P]),Hh(c,a.current,!1)){const j=((k=(C=w.composedPath)==null?void 0:C.call(w))==null?void 0:k[0])||w.target,_=(j==null?void 0:j.nodeName)==="BUTTON"||(j==null?void 0:j.nodeName)==="A";r.preventDefault!==!1&&(u.current||!_)&&w.preventDefault(),l(!0)}},y=w=>{const S=Vh(w.code,d);Hh(c,a.current,!0)?(l(!1),a.current.clear()):a.current.delete(w[S]),w.key==="Meta"&&a.current.clear(),u.current=!1},m=()=>{a.current.clear(),l(!1)};return p==null||p.addEventListener("keydown",v),p==null||p.addEventListener("keyup",y),window.addEventListener("blur",m),window.addEventListener("contextmenu",m),()=>{p==null||p.removeEventListener("keydown",v),p==null||p.removeEventListener("keyup",y),window.removeEventListener("blur",m),window.removeEventListener("contextmenu",m)}}},[t,l]),i}function Hh(t,r,i){return t.filter(l=>i||l.length===r.size).some(l=>l.every(u=>r.has(u)))}function Vh(t,r){return r.includes(t)?"code":"key"}const n_=()=>{const t=De();return U.useMemo(()=>({zoomIn:async r=>{const{panZoom:i}=t.getState();return i?i.scaleBy(1.2,r):!1},zoomOut:async r=>{const{panZoom:i}=t.getState();return i?i.scaleBy(1/1.2,r):!1},zoomTo:async(r,i)=>{const{panZoom:l}=t.getState();return l?l.scaleTo(r,i):!1},getZoom:()=>t.getState().transform[2],setViewport:async(r,i)=>{const{transform:[l,u,a],panZoom:c}=t.getState();return c?(await c.setViewport({x:r.x??l,y:r.y??u,zoom:r.zoom??a},i),!0):!1},getViewport:()=>{const[r,i,l]=t.getState().transform;return{x:r,y:i,zoom:l}},setCenter:async(r,i,l)=>t.getState().setCenter(r,i,l),fitBounds:async(r,i)=>{const{width:l,height:u,minZoom:a,maxZoom:c,panZoom:d}=t.getState(),p=uc(r,l,u,a,c,(i==null?void 0:i.padding)??.1);return d?(await d.setViewport(p,{duration:i==null?void 0:i.duration,ease:i==null?void 0:i.ease,interpolate:i==null?void 0:i.interpolate}),!0):!1},screenToFlowPosition:(r,i={})=>{const{transform:l,snapGrid:u,snapToGrid:a,domNode:c}=t.getState();if(!c)return r;const{x:d,y:p}=c.getBoundingClientRect(),g={x:r.x-d,y:r.y-p},v=i.snapGrid??u,y=i.snapToGrid??a;return Si(g,l,y,v)},flowToScreenPosition:r=>{const{transform:i,domNode:l}=t.getState();if(!l)return r;const{x:u,y:a}=l.getBoundingClientRect(),c=so(r,i);return{x:c.x+u,y:c.y+a}}}),[])};function Mg(t,r){const i=[],l=new Map,u=[];for(const a of t)if(a.type==="add"){u.push(a);continue}else if(a.type==="remove"||a.type==="replace")l.set(a.id,[a]);else{const c=l.get(a.id);c?c.push(a):l.set(a.id,[a])}for(const a of r){const c=l.get(a.id);if(!c){i.push(a);continue}if(c[0].type==="remove")continue;if(c[0].type==="replace"){i.push({...c[0].item});continue}const d={...a};for(const p of c)r_(p,d);i.push(d)}return u.length&&u.forEach(a=>{a.index!==void 0?i.splice(a.index,0,{...a.item}):i.push({...a.item})}),i}function r_(t,r){switch(t.type){case"select":{r.selected=t.selected;break}case"position":{typeof t.position<"u"&&(r.position=t.position),typeof t.dragging<"u"&&(r.dragging=t.dragging);break}case"dimensions":{typeof t.dimensions<"u"&&(r.measured={...t.dimensions},t.setAttributes&&((t.setAttributes===!0||t.setAttributes==="width")&&(r.width=t.dimensions.width),(t.setAttributes===!0||t.setAttributes==="height")&&(r.height=t.dimensions.height))),typeof t.resizing=="boolean"&&(r.resizing=t.resizing);break}}}function o_(t,r){return Mg(t,r)}function i_(t,r){return Mg(t,r)}function dr(t,r){return{id:t,type:"select",selected:r}}function Jr(t,r=new Set,i=!1){const l=[];for(const[u,a]of t){const c=r.has(u);!(a.selected===void 0&&!c)&&a.selected!==c&&(i&&(a.selected=c),l.push(dr(a.id,c)))}return l}function bh({items:t=[],lookup:r}){var u;const i=[],l=new Map(t.map(a=>[a.id,a]));for(const[a,c]of t.entries()){const d=r.get(c.id),p=((u=d==null?void 0:d.internals)==null?void 0:u.userNode)??d;p!==void 0&&p!==c&&i.push({id:c.id,item:c,type:"replace"}),p===void 0&&i.push({item:c,type:"add",index:a})}for(const[a]of r)l.get(a)===void 0&&i.push({id:a,type:"remove"});return i}function Bh(t){return{id:t.id,type:"remove"}}const s_=eg();function l_(t,r,i={}){return Uw(t,r,{...i,onError:i.onError??s_})}const Uh=t=>Tw(t),u_=t=>Kp(t);function Pg(t){return U.forwardRef(t)}const Tg=typeof window<"u"?U.useLayoutEffect:U.useEffect;function Wh(t){const[r,i]=U.useState(BigInt(0)),[l]=U.useState(()=>a_(()=>i(u=>u+BigInt(1))));return Tg(()=>{const u=l.get();u.length&&(t(u),l.reset())},[r]),l}function a_(t){let r=[];return{get:()=>r,reset:()=>{r=[]},push:i=>{r.push(i),t()}}}const Ig=U.createContext(null);function c_({children:t}){const r=De(),i=U.useCallback(d=>{const{nodes:p=[],setNodes:g,hasDefaultNodes:v,onNodesChange:y,nodeLookup:m,fitViewQueued:w,onNodesChangeMiddlewareMap:S}=r.getState();let P=p;for(const k of d)P=typeof k=="function"?k(P):k;let C=bh({items:P,lookup:m});for(const k of S.values())C=k(C);v&&g(P),C.length>0?y==null||y(C):w&&window.requestAnimationFrame(()=>{const{fitViewQueued:k,nodes:j,setNodes:_}=r.getState();k&&_(j)})},[]),l=Wh(i),u=U.useCallback(d=>{const{edges:p=[],setEdges:g,hasDefaultEdges:v,onEdgesChange:y,edgeLookup:m}=r.getState();let w=p;for(const S of d)w=typeof S=="function"?S(w):S;v?g(w):y&&y(bh({items:w,lookup:m}))},[]),a=Wh(u),c=U.useMemo(()=>({nodeQueue:l,edgeQueue:a}),[]);return E.jsx(Ig.Provider,{value:c,children:t})}function f_(){const t=U.useContext(Ig);if(!t)throw new Error("useBatchContext must be used within a BatchProvider");return t}const d_=t=>!!t.panZoom;function gc(){const t=n_(),r=De(),i=f_(),l=Te(d_),u=U.useMemo(()=>{const a=y=>r.getState().nodeLookup.get(y),c=y=>{i.nodeQueue.push(y)},d=y=>{i.edgeQueue.push(y)},p=y=>{var k,j;const{nodeLookup:m,nodeOrigin:w}=r.getState(),S=Uh(y)?y:m.get(y.id),P=S.parentId?ng(S.position,S.measured,S.parentId,m,w):S.position,C={...S,position:P,width:((k=S.measured)==null?void 0:k.width)??S.width,height:((j=S.measured)==null?void 0:j.height)??S.height};return hi(C)},g=(y,m,w={replace:!1})=>{c(S=>S.map(P=>{if(P.id===y){const C=typeof m=="function"?m(P):m;return w.replace&&Uh(C)?C:{...P,...C}}return P}))},v=(y,m,w={replace:!1})=>{d(S=>S.map(P=>{if(P.id===y){const C=typeof m=="function"?m(P):m;return w.replace&&u_(C)?C:{...P,...C}}return P}))};return{getNodes:()=>r.getState().nodes.map(y=>({...y})),getNode:y=>{var m;return(m=a(y))==null?void 0:m.internals.userNode},getInternalNode:a,getEdges:()=>{const{edges:y=[]}=r.getState();return y.map(m=>({...m}))},getEdge:y=>r.getState().edgeLookup.get(y),setNodes:c,setEdges:d,addNodes:y=>{const m=Array.isArray(y)?y:[y];i.nodeQueue.push(w=>[...w,...m])},addEdges:y=>{const m=Array.isArray(y)?y:[y];i.edgeQueue.push(w=>[...w,...m])},toObject:()=>{const{nodes:y=[],edges:m=[],transform:w}=r.getState(),[S,P,C]=w;return{nodes:y.map(k=>({...k})),edges:m.map(k=>({...k})),viewport:{x:S,y:P,zoom:C}}},deleteElements:async({nodes:y=[],edges:m=[]})=>{const{nodes:w,edges:S,onNodesDelete:P,onEdgesDelete:C,triggerNodeChanges:k,triggerEdgeChanges:j,onDelete:_,onBeforeDelete:T}=r.getState(),{nodes:F,edges:D}=await Lw({nodesToRemove:y,edgesToRemove:m,nodes:w,edges:S,onBeforeDelete:T}),V=D.length>0,Q=F.length>0;if(V){const Y=D.map(Bh);C==null||C(D),j(Y)}if(Q){const Y=F.map(Bh);P==null||P(F),k(Y)}return(Q||V)&&(_==null||_({nodes:F,edges:D})),{deletedNodes:F,deletedEdges:D}},getIntersectingNodes:(y,m=!0,w)=>{const S=gh(y),P=S?y:p(y),C=w!==void 0;return P?(w||r.getState().nodes).filter(k=>{const j=r.getState().nodeLookup.get(k.id);if(j&&!S&&(k.id===y.id||!j.internals.positionAbsolute))return!1;const _=hi(C?k:j),T=hl(_,P);return m&&T>0||T>=_.width*_.height||T>=P.width*P.height}):[]},isNodeIntersecting:(y,m,w=!0)=>{const P=gh(y)?y:p(y);if(!P)return!1;const C=hl(P,m);return w&&C>0||C>=m.width*m.height||C>=P.width*P.height},updateNode:g,updateNodeData:(y,m,w={replace:!1})=>{g(y,S=>{const P=typeof m=="function"?m(S):m;return w.replace?{...S,data:P}:{...S,data:{...S.data,...P}}},w)},updateEdge:v,updateEdgeData:(y,m,w={replace:!1})=>{v(y,S=>{const P=typeof m=="function"?m(S):m;return w.replace?{...S,data:P}:{...S,data:{...S.data,...P}}},w)},getNodesBounds:y=>{const{nodeLookup:m,nodeOrigin:w}=r.getState();return Iw(y,{nodeLookup:m,nodeOrigin:w})},getHandleConnections:({type:y,id:m,nodeId:w})=>{var S;return Array.from(((S=r.getState().connectionLookup.get(`${w}-${y}${m?`-${m}`:""}`))==null?void 0:S.values())??[])},getNodeConnections:({type:y,handleId:m,nodeId:w})=>{var S;return Array.from(((S=r.getState().connectionLookup.get(`${w}${y?m?`-${y}-${m}`:`-${y}`:""}`))==null?void 0:S.values())??[])},fitView:async y=>{const m=r.getState().fitViewResolver??$w();return r.setState({fitViewQueued:!0,fitViewOptions:y,fitViewResolver:m}),i.nodeQueue.push(w=>[...w]),m.promise}}},[]);return U.useMemo(()=>({...u,...t,viewportInitialized:l}),[l])}const Yh=t=>t.selected,h_=typeof window<"u"?window:void 0;function p_({deleteKeyCode:t,multiSelectionKeyCode:r}){const i=De(),{deleteElements:l}=gc(),u=gi(t,{actInsideInputWithModifier:!1}),a=gi(r,{target:h_});U.useEffect(()=>{if(u){const{edges:c,nodes:d}=i.getState();l({nodes:d.filter(Yh),edges:c.filter(Yh)}),i.setState({nodesSelectionActive:!1})}},[u]),U.useEffect(()=>{i.setState({multiSelectionActive:a})},[a])}function g_(t){const r=De();U.useEffect(()=>{const i=()=>{var u,a,c,d;if(!t.current||!(((a=(u=t.current).checkVisibility)==null?void 0:a.call(u))??!0))return!1;const l=ac(t.current);(l.height===0||l.width===0)&&((d=(c=r.getState()).onError)==null||d.call(c,"004",Wt.error004())),r.setState({width:l.width||500,height:l.height||500})};if(t.current){i(),window.addEventListener("resize",i);const l=new ResizeObserver(()=>i());return l.observe(t.current),()=>{window.removeEventListener("resize",i),l&&t.current&&l.unobserve(t.current)}}},[])}const El={position:"absolute",width:"100%",height:"100%",top:0,left:0},m_=t=>({userSelectionActive:t.userSelectionActive,lib:t.lib,connectionInProgress:t.connection.inProgress});function y_({onPaneContextMenu:t,zoomOnScroll:r=!0,zoomOnPinch:i=!0,panOnScroll:l=!1,panActivationKeyPressed:u,panOnScrollSpeed:a=.5,panOnScrollMode:c=gr.Free,zoomOnDoubleClick:d=!0,panOnDrag:p=!0,defaultViewport:g,translateExtent:v,minZoom:y,maxZoom:m,zoomActivationKeyCode:w,preventScrolling:S=!0,children:P,noWheelClassName:C,noPanClassName:k,onViewportChange:j,isControlledViewport:_,paneClickDistance:T,selectionOnDrag:F}){const D=De(),V=U.useRef(null),{userSelectionActive:Q,lib:Y,connectionInProgress:ee}=Te(m_,Ve),q=gi(w),J=U.useRef();g_(V);const Z=U.useCallback(N=>{j==null||j({x:N[0],y:N[1],zoom:N[2]}),_||D.setState({transform:N})},[j,_]);return U.useEffect(()=>{if(V.current){J.current=w1({domNode:V.current,minZoom:y,maxZoom:m,translateExtent:v,viewport:g,onDraggingChange:B=>D.setState(z=>z.paneDragging===B?z:{paneDragging:B}),onPanZoomStart:(B,z)=>{const{onViewportChangeStart:A,onMoveStart:O}=D.getState();O==null||O(B,z),A==null||A(z)},onPanZoom:(B,z)=>{const{onViewportChange:A,onMove:O}=D.getState();O==null||O(B,z),A==null||A(z)},onPanZoomEnd:(B,z)=>{const{onViewportChangeEnd:A,onMoveEnd:O}=D.getState();O==null||O(B,z),A==null||A(z)}});const{x:N,y:b,zoom:H}=J.current.getViewport();return D.setState({panZoom:J.current,transform:[N,b,H],domNode:V.current.closest(".react-flow")}),()=>{var B;(B=J.current)==null||B.destroy()}}},[]),U.useEffect(()=>{var N;(N=J.current)==null||N.update({onPaneContextMenu:t,zoomOnScroll:r,zoomOnPinch:i,panOnScroll:l,panActivationKeyPressed:u,panOnScrollSpeed:a,panOnScrollMode:c,zoomOnDoubleClick:d,panOnDrag:p,zoomActivationKeyPressed:q,preventScrolling:S,noPanClassName:k,userSelectionActive:Q,noWheelClassName:C,lib:Y,onTransformChange:Z,connectionInProgress:ee,selectionOnDrag:F,paneClickDistance:T})},[t,r,i,l,u,a,c,d,p,q,S,k,Q,C,Y,Z,ee,F,T]),E.jsx("div",{className:"react-flow__renderer",ref:V,style:El,children:P})}const v_=t=>({userSelectionActive:t.userSelectionActive,userSelectionRect:t.userSelectionRect});function x_(){const{userSelectionActive:t,userSelectionRect:r}=Te(v_,Ve);return t&&r?E.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:r.width,height:r.height,transform:`translate(${r.x}px, ${r.y}px)`}}):null}const La=(t,r)=>i=>{i.target===r.current&&(t==null||t(i))},w_=t=>({userSelectionActive:t.userSelectionActive,elementsSelectable:t.elementsSelectable,dragging:t.paneDragging,panBy:t.panBy,autoPanSpeed:t.autoPanSpeed});function __({isSelecting:t,selectionKeyPressed:r,selectionMode:i=di.Full,panOnDrag:l,autoPanOnSelection:u,paneClickDistance:a,selectionOnDrag:c,onSelectionStart:d,onSelectionEnd:p,onPaneClick:g,onPaneContextMenu:v,onPaneScroll:y,onPaneMouseEnter:m,onPaneMouseMove:w,onPaneMouseLeave:S,children:P}){const C=U.useRef(0),k=De(),{userSelectionActive:j,elementsSelectable:_,dragging:T,panBy:F,autoPanSpeed:D}=Te(w_,Ve),V=_&&(t||j),Q=U.useRef(null),Y=U.useRef(),ee=U.useRef(new Set),q=U.useRef(new Set),J=U.useRef(!1),Z=U.useRef(!1),N=U.useRef({x:0,y:0}),b=U.useRef(!1),H=G=>{if(Z.current||J.current||k.getState().connection.inProgress){Z.current=!1,J.current=!1;return}g==null||g(G),k.getState().resetSelectedElements(),k.setState({nodesSelectionActive:!1})},B=G=>{if(Array.isArray(l)&&(l!=null&&l.includes(2))){G.preventDefault();return}v==null||v(G)},z=y?G=>y(G):void 0,A=G=>{Z.current&&(G.stopPropagation(),Z.current=!1)},O=G=>{var ze,it;if(G.pointerType==="touch"&&l!==!1&&!r)return;const{domNode:se,transform:pe}=k.getState();if(Y.current=se==null?void 0:se.getBoundingClientRect(),!Y.current)return;const xe=G.target===Q.current;if(!xe&&!!G.target.closest(".nokey")||!t||!(c&&xe||r)||G.button!==0||!G.isPrimary)return;(it=(ze=G.target)==null?void 0:ze.setPointerCapture)==null||it.call(ze,G.pointerId),Z.current=!1;const{x:ke,y:Ce}=Ut(G.nativeEvent,Y.current),Me=Si({x:ke,y:Ce},pe);k.setState({userSelectionRect:{width:0,height:0,startX:Me.x,startY:Me.y,x:ke,y:Ce}}),xe||(G.stopPropagation(),G.preventDefault())};function M(G,se){const{userSelectionRect:pe}=k.getState();if(!pe)return;const{transform:xe,nodeLookup:ye,edgeLookup:we,connectionLookup:ke,triggerNodeChanges:Ce,triggerEdgeChanges:Me,defaultEdgeOptions:ze}=k.getState(),it={x:pe.startX,y:pe.startY},{x:at,y:Je}=so(it,xe),et={startX:it.x,startY:it.y,x:G<at?G:at,y:se<Je?se:Je,width:Math.abs(G-at),height:Math.abs(se-Je)},sn=ee.current,Qt=q.current;ee.current=new Set(sc(ye,et,xe,i===di.Partial,!0).map(ct=>ct.id)),q.current=new Set;const Kt=(ze==null?void 0:ze.selectable)??!0;for(const ct of ee.current){const Mt=ke.get(ct);if(Mt)for(const{edgeId:vt}of Mt.values()){const Pt=we.get(vt);Pt&&(Pt.selectable??Kt)&&q.current.add(vt)}}if(!mh(sn,ee.current)){const ct=Jr(ye,ee.current,!0);Ce(ct)}if(!mh(Qt,q.current)){const ct=Jr(we,q.current);Me(ct)}k.setState({userSelectionRect:et,userSelectionActive:!0,nodesSelectionActive:!1})}function L(){if(!u||!Y.current)return;const[G,se]=lc(N.current,Y.current,D);F({x:G,y:se}).then(pe=>{if(!Z.current||!pe){C.current=requestAnimationFrame(L);return}const{x:xe,y:ye}=N.current;M(xe,ye),C.current=requestAnimationFrame(L)})}const te=()=>{cancelAnimationFrame(C.current),C.current=0,b.current=!1};U.useEffect(()=>()=>te(),[]);const re=G=>{const{userSelectionRect:se,transform:pe,resetSelectedElements:xe}=k.getState();if(!Y.current||!se)return;const{x:ye,y:we}=Ut(G.nativeEvent,Y.current);N.current={x:ye,y:we};const ke=so({x:se.startX,y:se.startY},pe);if(!Z.current){const Ce=r?0:a;if(Math.hypot(ye-ke.x,we-ke.y)<=Ce)return;xe(),d==null||d(G)}Z.current=!0,b.current||(L(),b.current=!0),M(ye,we)},ue=G=>{var se,pe;if(!V){G.target===Q.current&&k.getState().connection.inProgress&&(J.current=!0);return}G.button===0&&((pe=(se=G.target)==null?void 0:se.releasePointerCapture)==null||pe.call(se,G.pointerId),!j&&G.target===Q.current&&k.getState().userSelectionRect&&(H==null||H(G)),k.setState({userSelectionActive:!1,userSelectionRect:null}),Z.current&&(p==null||p(G),k.setState({nodesSelectionActive:ee.current.size>0})),te())},ce=G=>{var se,pe;(pe=(se=G.target)==null?void 0:se.releasePointerCapture)==null||pe.call(se,G.pointerId),te()},ae=l===!0||Array.isArray(l)&&l.includes(0);return E.jsxs("div",{className:We(["react-flow__pane",{draggable:ae,dragging:T,selection:t}]),onClick:V?void 0:La(H,Q),onContextMenu:La(B,Q),onWheel:La(z,Q),onPointerEnter:V?void 0:m,onPointerMove:V?re:w,onPointerUp:ue,onPointerCancel:V?ce:void 0,onPointerDownCapture:V?O:void 0,onClickCapture:V?A:void 0,onPointerLeave:S,ref:Q,style:El,children:[P,E.jsx(x_,{})]})}function Ga({id:t,store:r,unselect:i=!1,nodeRef:l}){const{addSelectedNodes:u,unselectNodesAndEdges:a,multiSelectionActive:c,nodeLookup:d,onError:p}=r.getState(),g=d.get(t);if(!g){p==null||p("012",Wt.error012(t));return}r.setState({nodesSelectionActive:!1}),g.selected?(i||g.selected&&c)&&(a({nodes:[g],edges:[]}),requestAnimationFrame(()=>{var v;return(v=l==null?void 0:l.current)==null?void 0:v.blur()})):u([t])}function zg({nodeRef:t,disabled:r=!1,noDragClassName:i,handleSelector:l,nodeId:u,isSelectable:a,nodeClickDistance:c}){const d=De(),[p,g]=U.useState(!1),v=U.useRef();return U.useEffect(()=>{if(!r)return v.current=s1({getStoreItems:()=>d.getState(),onNodeMouseDown:y=>{Ga({id:y,store:d,nodeRef:t})},onDragStart:()=>{g(!0)},onDragStop:()=>{g(!1)}}),()=>{var y;(y=v.current)==null||y.destroy(),v.current=void 0}},[r,d,t]),U.useEffect(()=>{r||!t.current||!v.current||v.current.update({noDragClassName:i,handleSelector:l,domNode:t.current,isSelectable:a,nodeId:u,nodeClickDistance:c})},[i,l,r,a,t,u,c]),p}const S_=t=>r=>r.selected&&(r.draggable||t&&typeof r.draggable>"u");function jg(){const t=De();return U.useCallback(i=>{const{nodeExtent:l,snapToGrid:u,snapGrid:a,nodesDraggable:c,onError:d,updateNodePositions:p,nodeLookup:g,nodeOrigin:v}=t.getState(),y=new Map,m=S_(c),w=u?a[0]:5,S=u?a[1]:5,P=i.direction.x*w*i.factor,C=i.direction.y*S*i.factor;for(const[,k]of g){if(!m(k))continue;let j={x:k.internals.positionAbsolute.x+P,y:k.internals.positionAbsolute.y+C};u&&(j=_i(j,a));const{position:_,positionAbsolute:T}=Gp({nodeId:k.id,nextPosition:j,nodeLookup:g,nodeExtent:l,nodeOrigin:v,onError:d});k.position=_,k.internals.positionAbsolute=T,y.set(k.id,k)}p(y)},[])}const mc=U.createContext(null),k_=mc.Provider;mc.Consumer;const Ag=()=>U.useContext(mc),E_=t=>({connectOnClick:t.connectOnClick,noPanClassName:t.noPanClassName,rfId:t.rfId}),Lg=U.createContext(null);function C_({children:t}){const r=Te(E_,Ve);return E.jsx(Lg.Provider,{value:r,children:t})}function N_(){const t=U.useContext(Lg);if(!t)throw new Error("useHandleConfig must be used within a HandleConfigProvider");return t}const M_={connectingFrom:!1,connectingTo:!1,clickConnecting:!1,isPossibleEndHandle:!0,connectionInProcess:!1,clickConnectionInProcess:!1,valid:!1},P_=(t,r,i)=>l=>{const{connectionClickStartHandle:u,connectionMode:a,connection:c}=l,{fromHandle:d,toHandle:p,isValid:g}=c;if(!d&&!u)return M_;const v=(p==null?void 0:p.nodeId)===t&&(p==null?void 0:p.id)===r&&(p==null?void 0:p.type)===i;return{connectingFrom:(d==null?void 0:d.nodeId)===t&&(d==null?void 0:d.id)===r&&(d==null?void 0:d.type)===i,connectingTo:v,clickConnecting:(u==null?void 0:u.nodeId)===t&&(u==null?void 0:u.id)===r&&(u==null?void 0:u.type)===i,isPossibleEndHandle:a===oo.Strict?(d==null?void 0:d.type)!==i:t!==(d==null?void 0:d.nodeId)||r!==(d==null?void 0:d.id),connectionInProcess:!!d,clickConnectionInProcess:!!u,valid:v&&g}};function T_({type:t="source",position:r=ve.Top,isValidConnection:i,isConnectable:l=!0,isConnectableStart:u=!0,isConnectableEnd:a=!0,id:c,onConnect:d,children:p,className:g,onMouseDown:v,onTouchStart:y,...m},w){var b,H;const S=c||null,P=t==="target",C=De(),k=Ag(),{connectOnClick:j,noPanClassName:_,rfId:T}=N_(),{connectingFrom:F,connectingTo:D,clickConnecting:V,isPossibleEndHandle:Q,connectionInProcess:Y,clickConnectionInProcess:ee,valid:q}=Te(P_(k,S,t),Ve);k||(H=(b=C.getState()).onError)==null||H.call(b,"010",Wt.error010());const J=B=>{const{defaultEdgeOptions:z,onConnect:A,hasDefaultEdges:O}=C.getState(),M={...z,...B};if(O){const{edges:L,setEdges:te,onError:re}=C.getState();te(l_(M,L,{onError:re}))}A==null||A(M),d==null||d(M)},Z=B=>{if(!k)return;const z=sg(B.nativeEvent);if(u&&(z&&B.button===0||!z)){const A=C.getState();Ka.onPointerDown(B.nativeEvent,{handleDomNode:B.currentTarget,autoPanOnConnect:A.autoPanOnConnect,connectionMode:A.connectionMode,connectionRadius:A.connectionRadius,domNode:A.domNode,nodeLookup:A.nodeLookup,lib:A.lib,isTarget:P,handleId:S,nodeId:k,flowId:A.rfId,panBy:A.panBy,cancelConnection:A.cancelConnection,onConnectStart:A.onConnectStart,onConnectEnd:(...O)=>{var M,L;return(L=(M=C.getState()).onConnectEnd)==null?void 0:L.call(M,...O)},updateConnection:A.updateConnection,onConnect:J,isValidConnection:i||((...O)=>{var M,L;return((L=(M=C.getState()).isValidConnection)==null?void 0:L.call(M,...O))??!0}),getTransform:()=>C.getState().transform,getFromHandle:()=>C.getState().connection.fromHandle,autoPanSpeed:A.autoPanSpeed,dragThreshold:A.connectionDragThreshold})}z?v==null||v(B):y==null||y(B)},N=B=>{const{onClickConnectStart:z,onClickConnectEnd:A,connectionClickStartHandle:O,connectionMode:M,isValidConnection:L,lib:te,rfId:re,nodeLookup:ue,connection:ce}=C.getState();if(!k||!O&&!u)return;if(!O){z==null||z(B.nativeEvent,{nodeId:k,handleId:S,handleType:t}),C.setState({connectionClickStartHandle:{nodeId:k,type:t,id:S}});return}const ae=og(B.target),G=i||L,{connection:se,isValid:pe}=Ka.isValid(B.nativeEvent,{handle:{nodeId:k,id:S,type:t},connectionMode:M,fromNodeId:O.nodeId,fromHandleId:O.id||null,fromType:O.type,isValidConnection:G,flowId:re,doc:ae,lib:te,nodeLookup:ue});pe&&se&&J(se);const xe=structuredClone(ce);delete xe.inProgress,xe.toPosition=xe.toHandle?xe.toHandle.position:null,A==null||A(B,xe),C.setState({connectionClickStartHandle:null})};return E.jsx("div",{"data-handleid":S,"data-nodeid":k,"data-handlepos":r,"data-id":`${T}-${k}-${S}-${t}`,className:We(["react-flow__handle",`react-flow__handle-${r}`,"nodrag",_,g,{source:!P,target:P,connectable:l,connectablestart:u,connectableend:a,clickconnecting:V,connectingfrom:F,connectingto:D,valid:q,connectionindicator:l&&(!Y||Q)&&(Y||ee?a:u)}]),onMouseDown:Z,onTouchStart:Z,onClick:j?N:void 0,ref:w,...m,children:p})}const uo=U.memo(Pg(T_));function I_({data:t,isConnectable:r,sourcePosition:i=ve.Bottom}){return E.jsxs(E.Fragment,{children:[t==null?void 0:t.label,E.jsx(uo,{type:"source",position:i,isConnectable:r})]})}function z_({data:t,isConnectable:r,targetPosition:i=ve.Top,sourcePosition:l=ve.Bottom}){return E.jsxs(E.Fragment,{children:[E.jsx(uo,{type:"target",position:i,isConnectable:r}),t==null?void 0:t.label,E.jsx(uo,{type:"source",position:l,isConnectable:r})]})}function j_(){return null}function A_({data:t,isConnectable:r,targetPosition:i=ve.Top}){return E.jsxs(E.Fragment,{children:[E.jsx(uo,{type:"target",position:i,isConnectable:r}),t==null?void 0:t.label]})}const pl={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},Xh={input:I_,default:z_,output:A_,group:j_};function L_(t){var r,i,l,u;return t.internals.handleBounds===void 0?{width:t.width??t.initialWidth??((r=t.style)==null?void 0:r.width),height:t.height??t.initialHeight??((i=t.style)==null?void 0:i.height)}:{width:t.width??((l=t.style)==null?void 0:l.width),height:t.height??((u=t.style)==null?void 0:u.height)}}const R_=t=>{const{width:r,height:i,x:l,y:u}=wi(t.nodeLookup,{filter:a=>!!a.selected});return{width:Bt(r)?r:null,height:Bt(i)?i:null,userSelectionActive:t.userSelectionActive,transformString:`translate(${t.transform[0]}px,${t.transform[1]}px) scale(${t.transform[2]}) translate(${l}px,${u}px)`}};function D_({onSelectionContextMenu:t,noPanClassName:r,disableKeyboardA11y:i}){const l=De(),{width:u,height:a,transformString:c,userSelectionActive:d}=Te(R_,Ve),p=jg(),g=U.useRef(null);U.useEffect(()=>{var w;i||(w=g.current)==null||w.focus({preventScroll:!0})},[i]);const v=!d&&u!==null&&a!==null;if(zg({nodeRef:g,disabled:!v}),!v)return null;const y=t?w=>{const S=l.getState().nodes.filter(P=>P.selected);t(w,S)}:void 0,m=w=>{Object.prototype.hasOwnProperty.call(pl,w.key)&&(w.preventDefault(),p({direction:pl[w.key],factor:w.shiftKey?4:1}))};return E.jsx("div",{className:We(["react-flow__nodesselection","react-flow__container",r]),style:{transform:c},children:E.jsx("div",{ref:g,className:"react-flow__nodesselection-rect",onContextMenu:y,tabIndex:i?void 0:-1,onKeyDown:i?void 0:m,style:{width:u,height:a}})})}const Qh=typeof window<"u"?window:void 0,$_=t=>({nodesSelectionActive:t.nodesSelectionActive,userSelectionActive:t.userSelectionActive});function Rg({children:t,onPaneClick:r,onPaneMouseEnter:i,onPaneMouseMove:l,onPaneMouseLeave:u,onPaneContextMenu:a,onPaneScroll:c,paneClickDistance:d,deleteKeyCode:p,selectionKeyCode:g,selectionOnDrag:v,selectionMode:y,onSelectionStart:m,onSelectionEnd:w,multiSelectionKeyCode:S,panActivationKeyCode:P,zoomActivationKeyCode:C,elementsSelectable:k,zoomOnScroll:j,zoomOnPinch:_,panOnScroll:T,panOnScrollSpeed:F,panOnScrollMode:D,zoomOnDoubleClick:V,panOnDrag:Q,autoPanOnSelection:Y,defaultViewport:ee,translateExtent:q,minZoom:J,maxZoom:Z,preventScrolling:N,onSelectionContextMenu:b,noWheelClassName:H,noPanClassName:B,disableKeyboardA11y:z,onViewportChange:A,isControlledViewport:O}){const{nodesSelectionActive:M,userSelectionActive:L}=Te($_,Ve),te=gi(g,{target:Qh}),re=gi(P,{target:Qh}),ue=re||Q,ce=re||T,ae=v&&ue!==!0,G=te||L||ae;return p_({deleteKeyCode:p,multiSelectionKeyCode:S}),E.jsx(y_,{onPaneContextMenu:a,elementsSelectable:k,zoomOnScroll:j,zoomOnPinch:_,panOnScroll:ce,panActivationKeyPressed:re,panOnScrollSpeed:F,panOnScrollMode:D,zoomOnDoubleClick:V,panOnDrag:!te&&ue,defaultViewport:ee,translateExtent:q,minZoom:J,maxZoom:Z,zoomActivationKeyCode:C,preventScrolling:N,noWheelClassName:H,noPanClassName:B,onViewportChange:A,isControlledViewport:O,paneClickDistance:d,selectionOnDrag:ae,children:E.jsxs(__,{onSelectionStart:m,onSelectionEnd:w,onPaneClick:r,onPaneMouseEnter:i,onPaneMouseMove:l,onPaneMouseLeave:u,onPaneContextMenu:a,onPaneScroll:c,panOnDrag:ue,autoPanOnSelection:Y,isSelecting:!!G,selectionMode:y,selectionKeyPressed:te,paneClickDistance:d,selectionOnDrag:ae,children:[t,M&&E.jsx(D_,{onSelectionContextMenu:b,noPanClassName:B,disableKeyboardA11y:z})]})})}Rg.displayName="FlowRenderer";const O_=U.memo(Rg),F_=t=>r=>t?sc(r.nodeLookup,{x:0,y:0,width:r.width,height:r.height},r.transform,!0).map(i=>i.id):Array.from(r.nodeLookup.keys());function H_(t){return Te(U.useCallback(F_(t),[t]),Ve)}const V_=t=>t.updateNodeInternals;function b_(){const t=Te(V_),[r]=U.useState(()=>typeof ResizeObserver>"u"?null:new ResizeObserver(i=>{const l=new Map;i.forEach(u=>{const a=u.target.getAttribute("data-id");l.set(a,{id:a,nodeElement:u.target,force:!0})}),t(l)}));return U.useEffect(()=>()=>{r==null||r.disconnect()},[r]),r}function B_({node:t,nodeType:r,hasDimensions:i,resizeObserver:l}){const u=De(),a=U.useRef(null),c=U.useRef(null),d=U.useRef(t.sourcePosition),p=U.useRef(t.targetPosition),g=U.useRef(r),v=i&&!!t.internals.handleBounds;return U.useEffect(()=>{a.current&&!t.hidden&&(!v||c.current!==a.current)&&(c.current&&(l==null||l.unobserve(c.current)),l==null||l.observe(a.current),c.current=a.current)},[v,t.hidden]),U.useEffect(()=>()=>{c.current&&(l==null||l.unobserve(c.current),c.current=null)},[]),U.useEffect(()=>{if(a.current){const y=g.current!==r,m=d.current!==t.sourcePosition,w=p.current!==t.targetPosition;(y||m||w)&&(g.current=r,d.current=t.sourcePosition,p.current=t.targetPosition,u.getState().updateNodeInternals(new Map([[t.id,{id:t.id,nodeElement:a.current,force:!0}]])))}},[t.id,r,t.sourcePosition,t.targetPosition]),a}function U_({id:t,onClick:r,onMouseEnter:i,onMouseMove:l,onMouseLeave:u,onContextMenu:a,onDoubleClick:c,nodesDraggable:d,elementsSelectable:p,nodesConnectable:g,nodesFocusable:v,resizeObserver:y,noDragClassName:m,noPanClassName:w,disableKeyboardA11y:S,rfId:P,nodeTypes:C,nodeClickDistance:k,onError:j}){const{node:_,internals:T,isParent:F}=Te(G=>{const se=G.nodeLookup.get(t),pe=G.parentLookup.has(t);return{node:se,internals:se.internals,isParent:pe}},Ve);let D=_.type||"default",V=(C==null?void 0:C[D])||Xh[D];V===void 0&&(j==null||j("003",Wt.error003(D)),D="default",V=(C==null?void 0:C.default)||Xh.default);const Q=!!(_.draggable||d&&typeof _.draggable>"u"),Y=!!(_.selectable||p&&typeof _.selectable>"u"),ee=!!(_.connectable||g&&typeof _.connectable>"u"),q=!!(_.focusable||v&&typeof _.focusable>"u"),J=De(),Z=tg(_),N=B_({node:_,nodeType:D,hasDimensions:Z,resizeObserver:y}),b=zg({nodeRef:N,disabled:_.hidden||!Q,noDragClassName:m,handleSelector:_.dragHandle,nodeId:t,isSelectable:Y,nodeClickDistance:k}),H=jg();if(_.hidden)return null;const B=Xt(_),z=L_(_),A=Y||Q||r||i||l||u,O=i?G=>i(G,{...T.userNode}):void 0,M=l?G=>l(G,{...T.userNode}):void 0,L=u?G=>u(G,{...T.userNode}):void 0,te=a?G=>a(G,{...T.userNode}):void 0,re=c?G=>c(G,{...T.userNode}):void 0,ue=G=>{const{selectNodesOnDrag:se,nodeDragThreshold:pe}=J.getState();Y&&(!se||!Q||pe>0)&&Ga({id:t,store:J,nodeRef:N}),r&&r(G,{...T.userNode})},ce=G=>{if(!(ig(G.nativeEvent)||S)){if(Yp.includes(G.key)&&Y){const se=G.key==="Escape";Ga({id:t,store:J,unselect:se,nodeRef:N})}else if(Q&&_.selected&&Object.prototype.hasOwnProperty.call(pl,G.key)){G.preventDefault();const{ariaLabelConfig:se}=J.getState();J.setState({ariaLiveMessage:se["node.a11yDescription.ariaLiveMessage"]({direction:G.key.replace("Arrow","").toLowerCase(),x:~~T.positionAbsolute.x,y:~~T.positionAbsolute.y})}),H({direction:pl[G.key],factor:G.shiftKey?4:1})}}},ae=()=>{var ke;if(S||!((ke=N.current)!=null&&ke.matches(":focus-visible")))return;const{transform:G,width:se,height:pe,autoPanOnNodeFocus:xe,setCenter:ye}=J.getState();if(!xe)return;sc(new Map([[t,_]]),{x:0,y:0,width:se,height:pe},G,!0).length>0||ye(_.position.x+B.width/2,_.position.y+B.height/2,{zoom:G[2]})};return E.jsx("div",{className:We(["react-flow__node",`react-flow__node-${D}`,{[w]:Q},_.className,{selected:_.selected,selectable:Y,parent:F,draggable:Q,dragging:b}]),ref:N,style:{zIndex:T.z,transform:`translate(${T.positionAbsolute.x}px,${T.positionAbsolute.y}px)`,pointerEvents:A?"all":"none",visibility:Z?"visible":"hidden",..._.style,...z},"data-id":t,"data-testid":`rf__node-${t}`,onMouseEnter:O,onMouseMove:M,onMouseLeave:L,onContextMenu:te,onClick:ue,onDoubleClick:re,onKeyDown:q?ce:void 0,tabIndex:q?0:void 0,onFocus:q?ae:void 0,role:_.ariaRole??(q?"group":void 0),"aria-roledescription":"node","aria-describedby":S?void 0:`${Eg}-${P}`,"aria-label":_.ariaLabel,..._.domAttributes,children:E.jsx(k_,{value:t,children:E.jsx(V,{id:t,data:_.data,type:D,positionAbsoluteX:T.positionAbsolute.x,positionAbsoluteY:T.positionAbsolute.y,selected:_.selected??!1,selectable:Y,draggable:Q,deletable:_.deletable??!0,isConnectable:ee,sourcePosition:_.sourcePosition,targetPosition:_.targetPosition,dragging:b,dragHandle:_.dragHandle,zIndex:T.z,parentId:_.parentId,...B})})})}var W_=U.memo(U_);const Y_=t=>({nodesConnectable:t.nodesConnectable,nodesFocusable:t.nodesFocusable,elementsSelectable:t.elementsSelectable,onError:t.onError});function Dg(t){const{nodesConnectable:r,nodesFocusable:i,elementsSelectable:l,onError:u}=Te(Y_,Ve),a=H_(t.onlyRenderVisibleElements),c=b_();return E.jsx("div",{className:"react-flow__nodes",style:El,children:a.map(d=>E.jsx(W_,{id:d,nodeTypes:t.nodeTypes,nodeExtent:t.nodeExtent,onClick:t.onNodeClick,onMouseEnter:t.onNodeMouseEnter,onMouseMove:t.onNodeMouseMove,onMouseLeave:t.onNodeMouseLeave,onContextMenu:t.onNodeContextMenu,onDoubleClick:t.onNodeDoubleClick,noDragClassName:t.noDragClassName,noPanClassName:t.noPanClassName,rfId:t.rfId,disableKeyboardA11y:t.disableKeyboardA11y,resizeObserver:c,nodesDraggable:t.nodesDraggable??!0,nodesConnectable:r,nodesFocusable:i,elementsSelectable:l,nodeClickDistance:t.nodeClickDistance,onError:u},d))})}Dg.displayName="NodeRenderer";const X_=U.memo(Dg);function Q_(t){return Te(U.useCallback(i=>{if(!t)return i.edges.map(u=>u.id);const l=[];if(i.width&&i.height)for(const u of i.edges){const a=i.nodeLookup.get(u.source),c=i.nodeLookup.get(u.target);a&&c&&Vw({sourceNode:a,targetNode:c,width:i.width,height:i.height,transform:i.transform})&&l.push(u.id)}return l},[t]),Ve)}const K_=({color:t="none",strokeWidth:r=1})=>{const i={strokeWidth:r,...t&&{stroke:t}};return E.jsx("polyline",{className:"arrow",style:i,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},G_=({color:t="none",strokeWidth:r=1})=>{const i={strokeWidth:r,...t&&{stroke:t,fill:t}};return E.jsx("polyline",{className:"arrowclosed",style:i,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},Kh={[fl.Arrow]:K_,[fl.ArrowClosed]:G_};function q_(t){const r=De();return U.useMemo(()=>{var u,a;return Object.prototype.hasOwnProperty.call(Kh,t)?Kh[t]:((a=(u=r.getState()).onError)==null||a.call(u,"009",Wt.error009(t)),null)},[t])}const Z_=({id:t,type:r,color:i,width:l=12.5,height:u=12.5,markerUnits:a="strokeWidth",strokeWidth:c,orient:d="auto-start-reverse"})=>{const p=q_(r);return p?E.jsx("marker",{className:"react-flow__arrowhead",id:t,markerWidth:`${l}`,markerHeight:`${u}`,viewBox:"-10 -10 20 20",markerUnits:a,orient:d,refX:"0",refY:"0",children:E.jsx(p,{color:i,strokeWidth:c})}):null},$g=({defaultColor:t,rfId:r})=>{const i=Te(a=>a.edges),l=Te(a=>a.defaultEdgeOptions),u=U.useMemo(()=>Kw(i,{id:r,defaultColor:t,defaultMarkerStart:l==null?void 0:l.markerStart,defaultMarkerEnd:l==null?void 0:l.markerEnd}),[i,l,r,t]);return u.length?E.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:E.jsx("defs",{children:u.map(a=>E.jsx(Z_,{id:a.id,type:a.type,color:a.color,width:a.width,height:a.height,markerUnits:a.markerUnits,strokeWidth:a.strokeWidth,orient:a.orient},a.id))})}):null};$g.displayName="MarkerDefinitions";var J_=U.memo($g);function Og({x:t,y:r,label:i,labelStyle:l,labelShowBg:u=!0,labelBgStyle:a,labelBgPadding:c=[2,4],labelBgBorderRadius:d=2,children:p,className:g,...v}){const[y,m]=U.useState({x:1,y:0,width:0,height:0}),w=We(["react-flow__edge-textwrapper",g]),S=U.useRef(null);return U.useEffect(()=>{if(S.current){const P=S.current.getBBox();m({x:P.x,y:P.y,width:P.width,height:P.height})}},[i]),i?E.jsxs("g",{transform:`translate(${t-y.width/2} ${r-y.height/2})`,className:w,visibility:y.width?"visible":"hidden",...v,children:[u&&E.jsx("rect",{width:y.width+2*c[0],x:-c[0],y:-c[1],height:y.height+2*c[1],className:"react-flow__edge-textbg",style:a,rx:d,ry:d}),E.jsx("text",{className:"react-flow__edge-text",y:y.height/2,dy:"0.3em",ref:S,style:l,children:i}),p]}):null}Og.displayName="EdgeText";const eS=U.memo(Og);function Cl({path:t,labelX:r,labelY:i,label:l,labelStyle:u,labelShowBg:a,labelBgStyle:c,labelBgPadding:d,labelBgBorderRadius:p,interactionWidth:g=20,...v}){return E.jsxs(E.Fragment,{children:[E.jsx("path",{...v,d:t,fill:"none",className:We(["react-flow__edge-path",v.className])}),g?E.jsx("path",{d:t,fill:"none",strokeOpacity:0,strokeWidth:g,className:"react-flow__edge-interaction"}):null,l&&Bt(r)&&Bt(i)?E.jsx(eS,{x:r,y:i,label:l,labelStyle:u,labelShowBg:a,labelBgStyle:c,labelBgPadding:d,labelBgBorderRadius:p}):null]})}function Gh({pos:t,x1:r,y1:i,x2:l,y2:u}){return t===ve.Left||t===ve.Right?[.5*(r+l),i]:[r,.5*(i+u)]}function Fg({sourceX:t,sourceY:r,sourcePosition:i=ve.Bottom,targetX:l,targetY:u,targetPosition:a=ve.Top}){const[c,d]=Gh({pos:i,x1:t,y1:r,x2:l,y2:u}),[p,g]=Gh({pos:a,x1:l,y1:u,x2:t,y2:r}),[v,y,m,w]=lg({sourceX:t,sourceY:r,targetX:l,targetY:u,sourceControlX:c,sourceControlY:d,targetControlX:p,targetControlY:g});return[`M${t},${r} C${c},${d} ${p},${g} ${l},${u}`,v,y,m,w]}function Hg(t){return U.memo(({id:r,sourceX:i,sourceY:l,targetX:u,targetY:a,sourcePosition:c,targetPosition:d,label:p,labelStyle:g,labelShowBg:v,labelBgStyle:y,labelBgPadding:m,labelBgBorderRadius:w,style:S,markerEnd:P,markerStart:C,interactionWidth:k})=>{const[j,_,T]=Fg({sourceX:i,sourceY:l,sourcePosition:c,targetX:u,targetY:a,targetPosition:d}),F=t.isInternal?void 0:r;return E.jsx(Cl,{id:F,path:j,labelX:_,labelY:T,label:p,labelStyle:g,labelShowBg:v,labelBgStyle:y,labelBgPadding:m,labelBgBorderRadius:w,style:S,markerEnd:P,markerStart:C,interactionWidth:k})})}const tS=Hg({isInternal:!1}),Vg=Hg({isInternal:!0});tS.displayName="SimpleBezierEdge";Vg.displayName="SimpleBezierEdgeInternal";function bg(t){return U.memo(({id:r,sourceX:i,sourceY:l,targetX:u,targetY:a,label:c,labelStyle:d,labelShowBg:p,labelBgStyle:g,labelBgPadding:v,labelBgBorderRadius:y,style:m,sourcePosition:w=ve.Bottom,targetPosition:S=ve.Top,markerEnd:P,markerStart:C,pathOptions:k,interactionWidth:j})=>{const[_,T,F]=Ya({sourceX:i,sourceY:l,sourcePosition:w,targetX:u,targetY:a,targetPosition:S,borderRadius:k==null?void 0:k.borderRadius,offset:k==null?void 0:k.offset,stepPosition:k==null?void 0:k.stepPosition}),D=t.isInternal?void 0:r;return E.jsx(Cl,{id:D,path:_,labelX:T,labelY:F,label:c,labelStyle:d,labelShowBg:p,labelBgStyle:g,labelBgPadding:v,labelBgBorderRadius:y,style:m,markerEnd:P,markerStart:C,interactionWidth:j})})}const Bg=bg({isInternal:!1}),Ug=bg({isInternal:!0});Bg.displayName="SmoothStepEdge";Ug.displayName="SmoothStepEdgeInternal";function Wg(t){return U.memo(({id:r,...i})=>{var u;const l=t.isInternal?void 0:r;return E.jsx(Bg,{...i,id:l,pathOptions:U.useMemo(()=>{var a;return{borderRadius:0,offset:(a=i.pathOptions)==null?void 0:a.offset}},[(u=i.pathOptions)==null?void 0:u.offset])})})}const nS=Wg({isInternal:!1}),Yg=Wg({isInternal:!0});nS.displayName="StepEdge";Yg.displayName="StepEdgeInternal";function Xg(t){return U.memo(({id:r,sourceX:i,sourceY:l,targetX:u,targetY:a,label:c,labelStyle:d,labelShowBg:p,labelBgStyle:g,labelBgPadding:v,labelBgBorderRadius:y,style:m,markerEnd:w,markerStart:S,interactionWidth:P})=>{const[C,k,j]=cg({sourceX:i,sourceY:l,targetX:u,targetY:a}),_=t.isInternal?void 0:r;return E.jsx(Cl,{id:_,path:C,labelX:k,labelY:j,label:c,labelStyle:d,labelShowBg:p,labelBgStyle:g,labelBgPadding:v,labelBgBorderRadius:y,style:m,markerEnd:w,markerStart:S,interactionWidth:P})})}const rS=Xg({isInternal:!1}),Qg=Xg({isInternal:!0});rS.displayName="StraightEdge";Qg.displayName="StraightEdgeInternal";function Kg(t){return U.memo(({id:r,sourceX:i,sourceY:l,targetX:u,targetY:a,sourcePosition:c=ve.Bottom,targetPosition:d=ve.Top,label:p,labelStyle:g,labelShowBg:v,labelBgStyle:y,labelBgPadding:m,labelBgBorderRadius:w,style:S,markerEnd:P,markerStart:C,pathOptions:k,interactionWidth:j})=>{const[_,T,F]=ug({sourceX:i,sourceY:l,sourcePosition:c,targetX:u,targetY:a,targetPosition:d,curvature:k==null?void 0:k.curvature}),D=t.isInternal?void 0:r;return E.jsx(Cl,{id:D,path:_,labelX:T,labelY:F,label:p,labelStyle:g,labelShowBg:v,labelBgStyle:y,labelBgPadding:m,labelBgBorderRadius:w,style:S,markerEnd:P,markerStart:C,interactionWidth:j})})}const oS=Kg({isInternal:!1}),Gg=Kg({isInternal:!0});oS.displayName="BezierEdge";Gg.displayName="BezierEdgeInternal";const qh={default:Gg,straight:Qg,step:Yg,smoothstep:Ug,simplebezier:Vg},Zh={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null,zIndex:void 0},iS=(t,r,i)=>i===ve.Left?t-r:i===ve.Right?t+r:t,sS=(t,r,i)=>i===ve.Top?t-r:i===ve.Bottom?t+r:t,Jh="react-flow__edgeupdater";function ep({position:t,centerX:r,centerY:i,radius:l=10,onMouseDown:u,onMouseEnter:a,onMouseOut:c,type:d}){return E.jsx("circle",{onMouseDown:u,onMouseEnter:a,onMouseOut:c,className:We([Jh,`${Jh}-${d}`]),cx:iS(r,l,t),cy:sS(i,l,t),r:l,stroke:"transparent",fill:"transparent"})}function lS({isReconnectable:t,reconnectRadius:r,edge:i,sourceX:l,sourceY:u,targetX:a,targetY:c,sourcePosition:d,targetPosition:p,onReconnect:g,onReconnectStart:v,onReconnectEnd:y,setReconnecting:m,setUpdateHover:w}){const S=De(),P=(T,F)=>{if(T.button!==0)return;const{autoPanOnConnect:D,domNode:V,connectionMode:Q,connectionRadius:Y,lib:ee,onConnectStart:q,cancelConnection:J,nodeLookup:Z,rfId:N,panBy:b,updateConnection:H}=S.getState(),B=F.type==="target",z=(M,L)=>{m(!1),y==null||y(M,i,F.type,L)},A=M=>g==null?void 0:g(i,M),O=(M,L)=>{m(!0),v==null||v(T,i,F.type),q==null||q(M,L)};Ka.onPointerDown(T.nativeEvent,{autoPanOnConnect:D,connectionMode:Q,connectionRadius:Y,domNode:V,handleId:F.id,nodeId:F.nodeId,nodeLookup:Z,isTarget:B,edgeUpdaterType:F.type,lib:ee,flowId:N,cancelConnection:J,panBy:b,isValidConnection:(...M)=>{var L,te;return((te=(L=S.getState()).isValidConnection)==null?void 0:te.call(L,...M))??!0},onConnect:A,onConnectStart:O,onConnectEnd:(...M)=>{var L,te;return(te=(L=S.getState()).onConnectEnd)==null?void 0:te.call(L,...M)},onReconnectEnd:z,updateConnection:H,getTransform:()=>S.getState().transform,getFromHandle:()=>S.getState().connection.fromHandle,dragThreshold:S.getState().connectionDragThreshold,handleDomNode:T.currentTarget})},C=T=>P(T,{nodeId:i.target,id:i.targetHandle??null,type:"target"}),k=T=>P(T,{nodeId:i.source,id:i.sourceHandle??null,type:"source"}),j=()=>w(!0),_=()=>w(!1);return E.jsxs(E.Fragment,{children:[(t===!0||t==="source")&&E.jsx(ep,{position:d,centerX:l,centerY:u,radius:r,onMouseDown:C,onMouseEnter:j,onMouseOut:_,type:"source"}),(t===!0||t==="target")&&E.jsx(ep,{position:p,centerX:a,centerY:c,radius:r,onMouseDown:k,onMouseEnter:j,onMouseOut:_,type:"target"})]})}function uS({id:t,edgesFocusable:r,edgesReconnectable:i,elementsSelectable:l,onClick:u,onDoubleClick:a,onContextMenu:c,onMouseEnter:d,onMouseMove:p,onMouseLeave:g,reconnectRadius:v,onReconnect:y,onReconnectStart:m,onReconnectEnd:w,rfId:S,edgeTypes:P,noPanClassName:C,onError:k,disableKeyboardA11y:j}){let _=Te(ye=>ye.edgeLookup.get(t));const T=Te(ye=>ye.defaultEdgeOptions);_=T?{...T,..._}:_;let F=_.type||"default",D=(P==null?void 0:P[F])||qh[F];D===void 0&&(k==null||k("011",Wt.error011(F)),F="default",D=(P==null?void 0:P.default)||qh.default);const V=!!(_.focusable||r&&typeof _.focusable>"u"),Q=typeof y<"u"&&(_.reconnectable||i&&typeof _.reconnectable>"u"),Y=!!(_.selectable||l&&typeof _.selectable>"u"),ee=U.useRef(null),[q,J]=U.useState(!1),[Z,N]=U.useState(!1),b=De(),{zIndex:H=_.zIndex,sourceX:B,sourceY:z,targetX:A,targetY:O,sourcePosition:M,targetPosition:L}=Te(U.useCallback(ye=>{const we=ye.nodeLookup.get(_.source),ke=ye.nodeLookup.get(_.target);if(!we||!ke)return Zh;const Ce=Qw({id:t,sourceNode:we,targetNode:ke,sourceHandle:_.sourceHandle||null,targetHandle:_.targetHandle||null,connectionMode:ye.connectionMode,onError:k}),Me=Hw({selected:_.selected,zIndex:_.zIndex,sourceNode:we,targetNode:ke,elevateOnSelect:ye.elevateEdgesOnSelect,zIndexMode:ye.zIndexMode});return{...Ce||Zh,zIndex:Me}},[_.source,_.target,_.sourceHandle,_.targetHandle,_.selected,_.zIndex,k]),Ve),te=U.useMemo(()=>_.markerStart?`url('#${Xa(_.markerStart,S)}')`:void 0,[_.markerStart,S]),re=U.useMemo(()=>_.markerEnd?`url('#${Xa(_.markerEnd,S)}')`:void 0,[_.markerEnd,S]);if(_.hidden||B===null||z===null||A===null||O===null)return null;const ue=ye=>{var Me;const{addSelectedEdges:we,unselectNodesAndEdges:ke,multiSelectionActive:Ce}=b.getState();Y&&(b.setState({nodesSelectionActive:!1}),_.selected&&Ce?(ke({nodes:[],edges:[_]}),(Me=ee.current)==null||Me.blur()):we([t])),u&&u(ye,_)},ce=a?ye=>{a(ye,{..._})}:void 0,ae=c?ye=>{c(ye,{..._})}:void 0,G=d?ye=>{d(ye,{..._})}:void 0,se=p?ye=>{p(ye,{..._})}:void 0,pe=g?ye=>{g(ye,{..._})}:void 0,xe=ye=>{var we;if(!j&&Yp.includes(ye.key)&&Y){const{unselectNodesAndEdges:ke,addSelectedEdges:Ce}=b.getState();ye.key==="Escape"?((we=ee.current)==null||we.blur(),ke({edges:[_]})):Ce([t])}};return E.jsx("svg",{style:{zIndex:H},children:E.jsxs("g",{className:We(["react-flow__edge",`react-flow__edge-${F}`,_.className,C,{selected:_.selected,animated:_.animated,inactive:!Y&&!u,updating:q,selectable:Y}]),onClick:ue,onDoubleClick:ce,onContextMenu:ae,onMouseEnter:G,onMouseMove:se,onMouseLeave:pe,onKeyDown:V?xe:void 0,tabIndex:V?0:void 0,role:_.ariaRole??(V?"group":"img"),"aria-roledescription":"edge","data-id":t,"data-testid":`rf__edge-${t}`,"aria-label":_.ariaLabel===null?void 0:_.ariaLabel||`Edge from ${_.source} to ${_.target}`,"aria-describedby":V?`${Cg}-${S}`:void 0,ref:ee,..._.domAttributes,children:[!Z&&E.jsx(D,{id:t,source:_.source,target:_.target,type:_.type,selected:_.selected,animated:_.animated,selectable:Y,deletable:_.deletable??!0,label:_.label,labelStyle:_.labelStyle,labelShowBg:_.labelShowBg,labelBgStyle:_.labelBgStyle,labelBgPadding:_.labelBgPadding,labelBgBorderRadius:_.labelBgBorderRadius,sourceX:B,sourceY:z,targetX:A,targetY:O,sourcePosition:M,targetPosition:L,data:_.data,style:_.style,sourceHandleId:_.sourceHandle,targetHandleId:_.targetHandle,markerStart:te,markerEnd:re,pathOptions:"pathOptions"in _?_.pathOptions:void 0,interactionWidth:_.interactionWidth}),Q&&E.jsx(lS,{edge:_,isReconnectable:Q,reconnectRadius:v,onReconnect:y,onReconnectStart:m,onReconnectEnd:w,sourceX:B,sourceY:z,targetX:A,targetY:O,sourcePosition:M,targetPosition:L,setUpdateHover:J,setReconnecting:N})]})})}var aS=U.memo(uS);const cS=t=>({edgesFocusable:t.edgesFocusable,edgesReconnectable:t.edgesReconnectable,elementsSelectable:t.elementsSelectable,connectionMode:t.connectionMode,onError:t.onError});function qg({defaultMarkerColor:t,onlyRenderVisibleElements:r,rfId:i,edgeTypes:l,noPanClassName:u,onReconnect:a,onEdgeContextMenu:c,onEdgeMouseEnter:d,onEdgeMouseMove:p,onEdgeMouseLeave:g,onEdgeClick:v,reconnectRadius:y,onEdgeDoubleClick:m,onReconnectStart:w,onReconnectEnd:S,disableKeyboardA11y:P}){const{edgesFocusable:C,edgesReconnectable:k,elementsSelectable:j,onError:_}=Te(cS,Ve),T=Q_(r);return E.jsxs("div",{className:"react-flow__edges",children:[E.jsx(J_,{defaultColor:t,rfId:i}),T.map(F=>E.jsx(aS,{id:F,edgesFocusable:C,edgesReconnectable:k,elementsSelectable:j,noPanClassName:u,onReconnect:a,onContextMenu:c,onMouseEnter:d,onMouseMove:p,onMouseLeave:g,onClick:v,reconnectRadius:y,onDoubleClick:m,onReconnectStart:w,onReconnectEnd:S,rfId:i,onError:_,edgeTypes:l,disableKeyboardA11y:P},F))]})}qg.displayName="EdgeRenderer";const fS=U.memo(qg),tp=t=>`translate(${t[0]}px,${t[1]}px) scale(${t[2]})`;function dS({children:t}){const r=De(),i=U.useRef(null),[l]=U.useState(()=>r.getState().transform);return Tg(()=>{let u=null;const a=()=>{const c=r.getState().transform;u&&c[0]===u[0]&&c[1]===u[1]&&c[2]===u[2]||(u=c,i.current&&(i.current.style.transform=tp(c)))};return a(),r.subscribe(a)},[r]),E.jsx("div",{ref:i,className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:tp(l)},children:t})}function hS(t){const r=gc(),i=U.useRef(!1);U.useEffect(()=>{!i.current&&r.viewportInitialized&&t&&(setTimeout(()=>t(r),1),i.current=!0)},[t,r.viewportInitialized])}const pS=t=>{var r;return(r=t.panZoom)==null?void 0:r.syncViewport};function gS(t){const r=Te(pS),i=De();return U.useEffect(()=>{t&&(r==null||r(t),i.setState({transform:[t.x,t.y,t.zoom]}))},[t,r]),null}function mS(t){return t.connection.inProgress?{...t.connection,to:Si(t.connection.to,t.transform)}:{...t.connection}}function yS(t){return mS}function vS(t){const r=yS();return Te(r,Ve)}const xS=t=>({nodesConnectable:t.nodesConnectable,isValid:t.connection.isValid,inProgress:t.connection.inProgress,width:t.width,height:t.height});function wS({containerStyle:t,style:r,type:i,component:l}){const{nodesConnectable:u,width:a,height:c,isValid:d,inProgress:p}=Te(xS,Ve);return!(a&&u&&p)?null:E.jsx("svg",{style:t,width:a,height:c,className:"react-flow__connectionline react-flow__container",children:E.jsx("g",{className:We(["react-flow__connection",rg(d)]),children:E.jsx(Zg,{style:r,type:i,CustomComponent:l,isValid:d})})})}const Zg=({style:t,type:r=Un.Bezier,CustomComponent:i,isValid:l})=>{const{inProgress:u,from:a,fromNode:c,fromHandle:d,fromPosition:p,to:g,toNode:v,toHandle:y,toPosition:m,pointer:w}=vS();if(!u)return;if(i)return E.jsx(i,{connectionLineType:r,connectionLineStyle:t,fromNode:c,fromHandle:d,fromX:a.x,fromY:a.y,toX:g.x,toY:g.y,fromPosition:p,toPosition:m,connectionStatus:rg(l),toNode:v,toHandle:y,pointer:w});let S="";const P={sourceX:a.x,sourceY:a.y,sourcePosition:p,targetX:g.x,targetY:g.y,targetPosition:m};switch(r){case Un.Bezier:[S]=ug(P);break;case Un.SimpleBezier:[S]=Fg(P);break;case Un.Step:[S]=Ya({...P,borderRadius:0});break;case Un.SmoothStep:[S]=Ya(P);break;default:[S]=cg(P)}return E.jsx("path",{d:S,fill:"none",className:"react-flow__connection-path",style:t})};Zg.displayName="ConnectionLine";const _S={};function np(t=_S){U.useRef(t),De(),U.useEffect(()=>{},[t])}function SS(){De(),U.useRef(!1),U.useEffect(()=>{},[])}function Jg({nodeTypes:t,edgeTypes:r,onInit:i,onNodeClick:l,onEdgeClick:u,onNodeDoubleClick:a,onEdgeDoubleClick:c,onNodeMouseEnter:d,onNodeMouseMove:p,onNodeMouseLeave:g,onNodeContextMenu:v,onSelectionContextMenu:y,onSelectionStart:m,onSelectionEnd:w,connectionLineType:S,connectionLineStyle:P,connectionLineComponent:C,connectionLineContainerStyle:k,selectionKeyCode:j,selectionOnDrag:_,selectionMode:T,multiSelectionKeyCode:F,panActivationKeyCode:D,zoomActivationKeyCode:V,deleteKeyCode:Q,onlyRenderVisibleElements:Y,elementsSelectable:ee,defaultViewport:q,translateExtent:J,minZoom:Z,maxZoom:N,preventScrolling:b,defaultMarkerColor:H,zoomOnScroll:B,zoomOnPinch:z,panOnScroll:A,panOnScrollSpeed:O,panOnScrollMode:M,zoomOnDoubleClick:L,panOnDrag:te,autoPanOnSelection:re,onPaneClick:ue,onPaneMouseEnter:ce,onPaneMouseMove:ae,onPaneMouseLeave:G,onPaneScroll:se,onPaneContextMenu:pe,paneClickDistance:xe,nodeClickDistance:ye,onEdgeContextMenu:we,onEdgeMouseEnter:ke,onEdgeMouseMove:Ce,onEdgeMouseLeave:Me,reconnectRadius:ze,onReconnect:it,onReconnectStart:at,onReconnectEnd:Je,noDragClassName:et,noWheelClassName:sn,noPanClassName:Qt,disableKeyboardA11y:Kt,nodeExtent:ct,rfId:Mt,viewport:vt,onViewportChange:Pt,nodesDraggable:Sr}){return np(t),np(r),SS(),hS(i),gS(vt),E.jsx(O_,{onPaneClick:ue,onPaneMouseEnter:ce,onPaneMouseMove:ae,onPaneMouseLeave:G,onPaneContextMenu:pe,onPaneScroll:se,paneClickDistance:xe,deleteKeyCode:Q,selectionKeyCode:j,selectionOnDrag:_,selectionMode:T,onSelectionStart:m,onSelectionEnd:w,multiSelectionKeyCode:F,panActivationKeyCode:D,zoomActivationKeyCode:V,elementsSelectable:ee,zoomOnScroll:B,zoomOnPinch:z,zoomOnDoubleClick:L,panOnScroll:A,panOnScrollSpeed:O,panOnScrollMode:M,panOnDrag:te,autoPanOnSelection:re,defaultViewport:q,translateExtent:J,minZoom:Z,maxZoom:N,onSelectionContextMenu:y,preventScrolling:b,noDragClassName:et,noWheelClassName:sn,noPanClassName:Qt,disableKeyboardA11y:Kt,onViewportChange:Pt,isControlledViewport:!!vt,children:E.jsxs(dS,{children:[E.jsx(fS,{edgeTypes:r,onEdgeClick:u,onEdgeDoubleClick:c,onReconnect:it,onReconnectStart:at,onReconnectEnd:Je,onlyRenderVisibleElements:Y,onEdgeContextMenu:we,onEdgeMouseEnter:ke,onEdgeMouseMove:Ce,onEdgeMouseLeave:Me,reconnectRadius:ze,defaultMarkerColor:H,noPanClassName:Qt,disableKeyboardA11y:Kt,rfId:Mt}),E.jsx(wS,{style:P,type:S,component:C,containerStyle:k}),E.jsx("div",{className:"react-flow__edgelabel-renderer"}),E.jsx(X_,{nodeTypes:t,onNodeClick:l,onNodeDoubleClick:a,onNodeMouseEnter:d,onNodeMouseMove:p,onNodeMouseLeave:g,onNodeContextMenu:v,nodeClickDistance:ye,onlyRenderVisibleElements:Y,noPanClassName:Qt,noDragClassName:et,disableKeyboardA11y:Kt,nodeExtent:ct,rfId:Mt,nodesDraggable:Sr}),E.jsx("div",{className:"react-flow__viewport-portal"})]})})}Jg.displayName="GraphView";const kS=U.memo(Jg),ES=eg(),rp=({nodes:t,edges:r,defaultNodes:i,defaultEdges:l,width:u,height:a,fitView:c,fitViewOptions:d,minZoom:p=.5,maxZoom:g=2,nodeOrigin:v,nodeExtent:y,zIndexMode:m="basic"}={})=>{const w=new Map,S=new Map,P=new Map,C=new Map,k=l??r??[],j=i??t??[],_=v??[0,0],T=y??fi;hg(P,C,k);const{nodesInitialized:F}=Qa(j,w,S,{nodeOrigin:_,nodeExtent:T,zIndexMode:m});let D=[0,0,1];if(c&&u&&a){const V=wi(w,{filter:q=>!!((q.width||q.initialWidth)&&(q.height||q.initialHeight))}),{x:Q,y:Y,zoom:ee}=uc(V,u,a,p,g,(d==null?void 0:d.padding)??.1);D=[Q,Y,ee]}return{rfId:"1",width:u??0,height:a??0,transform:D,nodes:j,nodesInitialized:F,nodeLookup:w,parentLookup:S,edges:k,edgeLookup:C,connectionLookup:P,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:i!==void 0,hasDefaultEdges:l!==void 0,panZoom:null,minZoom:p,maxZoom:g,translateExtent:fi,nodeExtent:T,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:oo.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:_,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:c??!1,fitViewOptions:d,fitViewResolver:null,connection:{...Qp},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:ES,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:Xp,zIndexMode:m,defaultEdgeOptions:void 0,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map,onNodesDelete:void 0,onEdgesDelete:void 0,onDelete:void 0,onBeforeDelete:void 0,onViewportChangeStart:void 0,onViewportChange:void 0,onViewportChangeEnd:void 0,onNodeDragStart:void 0,onNodeDrag:void 0,onNodeDragStop:void 0,onSelectionDragStart:void 0,onSelectionDrag:void 0,onSelectionDragStop:void 0,onMoveStart:void 0,onMove:void 0,onMoveEnd:void 0,onConnect:void 0,onConnectStart:void 0,onConnectEnd:void 0,onClickConnectStart:void 0,onClickConnectEnd:void 0}},CS=({nodes:t,edges:r,defaultNodes:i,defaultEdges:l,width:u,height:a,fitView:c,fitViewOptions:d,minZoom:p,maxZoom:g,nodeOrigin:v,nodeExtent:y,zIndexMode:m})=>$1((w,S)=>{async function P(){const{nodeLookup:C,panZoom:k,fitViewOptions:j,fitViewResolver:_,width:T,height:F,minZoom:D,maxZoom:V}=S();k&&(await Aw({nodes:C,width:T,height:F,panZoom:k,minZoom:D,maxZoom:V},j),_==null||_.resolve(!0),w({fitViewResolver:null}))}return{...rp({nodes:t,edges:r,width:u,height:a,fitView:c,fitViewOptions:d,minZoom:p,maxZoom:g,nodeOrigin:v,nodeExtent:y,defaultNodes:i,defaultEdges:l,zIndexMode:m}),setNodes:C=>{const{nodeLookup:k,parentLookup:j,nodeOrigin:_,nodeExtent:T,elevateNodesOnSelect:F,fitViewQueued:D,zIndexMode:V,nodesSelectionActive:Q}=S(),{nodesInitialized:Y,hasSelectedNodes:ee}=Qa(C,k,j,{nodeOrigin:_,nodeExtent:T,elevateNodesOnSelect:F,checkEquality:!0,zIndexMode:V}),q=Q&&ee;D&&Y?(P(),w({nodes:C,nodesInitialized:Y,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:q})):w({nodes:C,nodesInitialized:Y,nodesSelectionActive:q})},setEdges:C=>{const{connectionLookup:k,edgeLookup:j}=S();hg(k,j,C),w({edges:C})},setDefaultNodesAndEdges:(C,k)=>{if(C){const{setNodes:j}=S();j(C),w({hasDefaultNodes:!0})}if(k){const{setEdges:j}=S();j(k),w({hasDefaultEdges:!0})}},updateNodeInternals:C=>{const{triggerNodeChanges:k,nodeLookup:j,parentLookup:_,domNode:T,nodeOrigin:F,nodeExtent:D,debug:V,fitViewQueued:Q,zIndexMode:Y}=S(),{changes:ee,updatedInternals:q}=n1(C,j,_,T,F,D,Y);q&&(Zw(j,_,{nodeOrigin:F,nodeExtent:D,zIndexMode:Y}),Q?(P(),w({fitViewQueued:!1,fitViewOptions:void 0})):w({}),(ee==null?void 0:ee.length)>0&&(V&&console.log("React Flow: trigger node changes",ee),k==null||k(ee)))},updateNodePositions:(C,k=!1)=>{const j=[];let _=[];const{nodeLookup:T,triggerNodeChanges:F,connection:D,updateConnection:V,onNodesChangeMiddlewareMap:Q}=S();for(const[Y,ee]of C){const q=T.get(Y),J=!!(q!=null&&q.expandParent&&(q!=null&&q.parentId)&&(ee!=null&&ee.position)),Z={id:Y,type:"position",position:J?{x:Math.max(0,ee.position.x),y:Math.max(0,ee.position.y)}:ee.position,dragging:k};if(q&&D.inProgress&&D.fromNode.id===q.id){const N=_r(q,D.fromHandle,ve.Left,!0);V({...D,from:N})}J&&q.parentId&&j.push({id:Y,parentId:q.parentId,rect:{...ee.internals.positionAbsolute,width:ee.measured.width??0,height:ee.measured.height??0}}),_.push(Z)}if(j.length>0){const{parentLookup:Y,nodeOrigin:ee}=S(),q=pc(j,T,Y,ee);_.push(...q)}for(const Y of Q.values())_=Y(_);F(_)},triggerNodeChanges:C=>{const{onNodesChange:k,setNodes:j,nodes:_,hasDefaultNodes:T,debug:F}=S();if(C!=null&&C.length){if(T){const D=o_(C,_);j(D)}F&&console.log("React Flow: trigger node changes",C),k==null||k(C)}},triggerEdgeChanges:C=>{const{onEdgesChange:k,setEdges:j,edges:_,hasDefaultEdges:T,debug:F}=S();if(C!=null&&C.length){if(T){const D=i_(C,_);j(D)}F&&console.log("React Flow: trigger edge changes",C),k==null||k(C)}},addSelectedNodes:C=>{const{multiSelectionActive:k,edgeLookup:j,nodeLookup:_,triggerNodeChanges:T,triggerEdgeChanges:F}=S();if(k){const D=C.map(V=>dr(V,!0));T(D);return}T(Jr(_,new Set([...C]),!0)),F(Jr(j))},addSelectedEdges:C=>{const{multiSelectionActive:k,edgeLookup:j,nodeLookup:_,triggerNodeChanges:T,triggerEdgeChanges:F}=S();if(k){const D=C.map(V=>dr(V,!0));F(D);return}F(Jr(j,new Set([...C]))),T(Jr(_,new Set,!0))},unselectNodesAndEdges:({nodes:C,edges:k}={})=>{const{edges:j,nodes:_,nodeLookup:T,triggerNodeChanges:F,triggerEdgeChanges:D}=S(),V=C||_,Q=k||j,Y=[];for(const q of V){if(!q.selected)continue;const J=T.get(q.id);J&&(J.selected=!1),Y.push(dr(q.id,!1))}const ee=[];for(const q of Q)q.selected&&ee.push(dr(q.id,!1));F(Y),D(ee)},setMinZoom:C=>{const{panZoom:k,maxZoom:j}=S();k==null||k.setScaleExtent([C,j]),w({minZoom:C})},setMaxZoom:C=>{const{panZoom:k,minZoom:j}=S();k==null||k.setScaleExtent([j,C]),w({maxZoom:C})},setTranslateExtent:C=>{var k;(k=S().panZoom)==null||k.setTranslateExtent(C),w({translateExtent:C})},resetSelectedElements:()=>{const{edges:C,nodes:k,triggerNodeChanges:j,triggerEdgeChanges:_,elementsSelectable:T}=S();if(!T)return;const F=k.reduce((V,Q)=>Q.selected?[...V,dr(Q.id,!1)]:V,[]),D=C.reduce((V,Q)=>Q.selected?[...V,dr(Q.id,!1)]:V,[]);j(F),_(D)},setNodeExtent:C=>{const{nodes:k,nodeLookup:j,parentLookup:_,nodeOrigin:T,elevateNodesOnSelect:F,nodeExtent:D,zIndexMode:V}=S();C[0][0]===D[0][0]&&C[0][1]===D[0][1]&&C[1][0]===D[1][0]&&C[1][1]===D[1][1]||(Qa(k,j,_,{nodeOrigin:T,nodeExtent:C,elevateNodesOnSelect:F,checkEquality:!1,zIndexMode:V}),w({nodeExtent:C}))},panBy:C=>{const{transform:k,width:j,height:_,panZoom:T,translateExtent:F}=S();return r1({delta:C,panZoom:T,transform:k,translateExtent:F,width:j,height:_})},setCenter:async(C,k,j)=>{const{width:_,height:T,maxZoom:F,panZoom:D}=S();if(!D)return!1;const V=typeof(j==null?void 0:j.zoom)<"u"?j.zoom:F;return await D.setViewport({x:_/2-C*V,y:T/2-k*V,zoom:V},{duration:j==null?void 0:j.duration,ease:j==null?void 0:j.ease,interpolate:j==null?void 0:j.interpolate}),!0},cancelConnection:()=>{w({connection:{...Qp}})},updateConnection:C=>{w({connection:C})},reset:()=>w({...rp()})}},Object.is);function NS({initialNodes:t,initialEdges:r,defaultNodes:i,defaultEdges:l,initialWidth:u,initialHeight:a,initialMinZoom:c,initialMaxZoom:d,initialFitViewOptions:p,fitView:g,nodeOrigin:v,nodeExtent:y,zIndexMode:m,children:w}){const[S]=U.useState(()=>CS({nodes:t,edges:r,defaultNodes:i,defaultEdges:l,width:u,height:a,fitView:g,minZoom:c,maxZoom:d,fitViewOptions:p,nodeOrigin:v,nodeExtent:y,zIndexMode:m}));return E.jsx(O1,{value:S,children:E.jsx(c_,{children:E.jsx(C_,{children:w})})})}function MS({children:t,nodes:r,edges:i,defaultNodes:l,defaultEdges:u,width:a,height:c,fitView:d,fitViewOptions:p,minZoom:g,maxZoom:v,nodeOrigin:y,nodeExtent:m,zIndexMode:w}){return U.useContext(kl)?E.jsx(E.Fragment,{children:t}):E.jsx(NS,{initialNodes:r,initialEdges:i,defaultNodes:l,defaultEdges:u,initialWidth:a,initialHeight:c,fitView:d,initialFitViewOptions:p,initialMinZoom:g,initialMaxZoom:v,nodeOrigin:y,nodeExtent:m,zIndexMode:w,children:t})}const PS={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function TS({nodes:t,edges:r,defaultNodes:i,defaultEdges:l,className:u,nodeTypes:a,edgeTypes:c,onNodeClick:d,onEdgeClick:p,onInit:g,onMove:v,onMoveStart:y,onMoveEnd:m,onConnect:w,onConnectStart:S,onConnectEnd:P,onClickConnectStart:C,onClickConnectEnd:k,onNodeMouseEnter:j,onNodeMouseMove:_,onNodeMouseLeave:T,onNodeContextMenu:F,onNodeDoubleClick:D,onNodeDragStart:V,onNodeDrag:Q,onNodeDragStop:Y,onNodesDelete:ee,onEdgesDelete:q,onDelete:J,onSelectionChange:Z,onSelectionDragStart:N,onSelectionDrag:b,onSelectionDragStop:H,onSelectionContextMenu:B,onSelectionStart:z,onSelectionEnd:A,onBeforeDelete:O,connectionMode:M,connectionLineType:L=Un.Bezier,connectionLineStyle:te,connectionLineComponent:re,connectionLineContainerStyle:ue,deleteKeyCode:ce="Backspace",selectionKeyCode:ae="Shift",selectionOnDrag:G=!1,selectionMode:se=di.Full,panActivationKeyCode:pe="Space",multiSelectionKeyCode:xe=pi()?"Meta":"Control",zoomActivationKeyCode:ye=pi()?"Meta":"Control",snapToGrid:we,snapGrid:ke,onlyRenderVisibleElements:Ce=!1,selectNodesOnDrag:Me,nodesDraggable:ze,autoPanOnNodeFocus:it,nodesConnectable:at,nodesFocusable:Je,nodeOrigin:et=Ng,edgesFocusable:sn,edgesReconnectable:Qt,elementsSelectable:Kt=!0,defaultViewport:ct=q1,minZoom:Mt=.5,maxZoom:vt=2,translateExtent:Pt=fi,preventScrolling:Sr=!0,nodeExtent:ln,defaultMarkerColor:Xn="#b1b1b7",zoomOnScroll:Nl=!0,zoomOnPinch:Ci=!0,panOnScroll:Ni=!1,panOnScrollSpeed:Ml=.5,panOnScrollMode:ao=gr.Free,zoomOnDoubleClick:co=!0,panOnDrag:fo=!0,onPaneClick:ho,onPaneMouseEnter:po,onPaneMouseMove:wn,onPaneMouseLeave:_n,onPaneScroll:Mi,onPaneContextMenu:Pi,paneClickDistance:Ti=1,nodeClickDistance:Ii=0,children:zi,onReconnect:go,onReconnectStart:ji,onReconnectEnd:Qn,onEdgeContextMenu:mo,onEdgeDoubleClick:Kn,onEdgeMouseEnter:Pl,onEdgeMouseMove:Gn,onEdgeMouseLeave:kr,reconnectRadius:Er=10,onNodesChange:yo,onEdgesChange:Tl,noDragClassName:Il="nodrag",noWheelClassName:zl="nowheel",noPanClassName:Gt="nopan",fitView:vo,fitViewOptions:xo,connectOnClick:jl,attributionPosition:Ai,proOptions:Li,defaultEdgeOptions:Ri,elevateNodesOnSelect:Di=!0,elevateEdgesOnSelect:Al=!1,disableKeyboardA11y:$i=!1,autoPanOnConnect:Oe,autoPanOnNodeDrag:Ll,autoPanOnSelection:wo=!0,autoPanSpeed:Oi,connectionRadius:Cr,isValidConnection:Rl,onError:Fi,style:Nr,id:xt,nodeDragThreshold:Dl,connectionDragThreshold:wt,viewport:$l,onViewportChange:Ol,width:Fl,height:Mr,colorMode:Pr="light",debug:qn,onScroll:un,ariaLabelConfig:Hl,zIndexMode:Hi="basic",..._o},Vi){const Zn=xt||"1",Jn=t_(Pr),Vl=U.useCallback(Tr=>{Tr.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),un==null||un(Tr)},[un]);return E.jsx("div",{"data-testid":"rf__wrapper",..._o,onScroll:Vl,style:{...Nr,...PS},ref:Vi,className:We(["react-flow",u,Jn]),id:xt,role:"application",children:E.jsxs(MS,{nodes:t,edges:r,width:Fl,height:Mr,fitView:vo,fitViewOptions:xo,minZoom:Mt,maxZoom:vt,nodeOrigin:et,nodeExtent:ln,zIndexMode:Hi,children:[E.jsx(e_,{nodes:t,edges:r,defaultNodes:i,defaultEdges:l,onConnect:w,onConnectStart:S,onConnectEnd:P,onClickConnectStart:C,onClickConnectEnd:k,nodesDraggable:ze,autoPanOnNodeFocus:it,nodesConnectable:at,nodesFocusable:Je,edgesFocusable:sn,edgesReconnectable:Qt,elementsSelectable:Kt,elevateNodesOnSelect:Di,elevateEdgesOnSelect:Al,minZoom:Mt,maxZoom:vt,nodeExtent:ln,onNodesChange:yo,onEdgesChange:Tl,snapToGrid:we,snapGrid:ke,connectionMode:M,translateExtent:Pt,connectOnClick:jl,defaultEdgeOptions:Ri,fitView:vo,fitViewOptions:xo,onNodesDelete:ee,onEdgesDelete:q,onDelete:J,onNodeDragStart:V,onNodeDrag:Q,onNodeDragStop:Y,onSelectionDrag:b,onSelectionDragStart:N,onSelectionDragStop:H,onMove:v,onMoveStart:y,onMoveEnd:m,noPanClassName:Gt,nodeOrigin:et,rfId:Zn,autoPanOnConnect:Oe,autoPanOnNodeDrag:Ll,autoPanSpeed:Oi,onError:Fi,connectionRadius:Cr,isValidConnection:Rl,selectNodesOnDrag:Me,nodeDragThreshold:Dl,connectionDragThreshold:wt,onBeforeDelete:O,debug:qn,ariaLabelConfig:Hl,zIndexMode:Hi}),E.jsx(kS,{onInit:g,onNodeClick:d,onEdgeClick:p,onNodeMouseEnter:j,onNodeMouseMove:_,onNodeMouseLeave:T,onNodeContextMenu:F,onNodeDoubleClick:D,nodeTypes:a,edgeTypes:c,connectionLineType:L,connectionLineStyle:te,connectionLineComponent:re,connectionLineContainerStyle:ue,selectionKeyCode:ae,selectionOnDrag:G,selectionMode:se,deleteKeyCode:ce,multiSelectionKeyCode:xe,panActivationKeyCode:pe,zoomActivationKeyCode:ye,onlyRenderVisibleElements:Ce,defaultViewport:ct,translateExtent:Pt,minZoom:Mt,maxZoom:vt,preventScrolling:Sr,zoomOnScroll:Nl,zoomOnPinch:Ci,zoomOnDoubleClick:co,panOnScroll:Ni,panOnScrollSpeed:Ml,panOnScrollMode:ao,panOnDrag:fo,autoPanOnSelection:wo,onPaneClick:ho,onPaneMouseEnter:po,onPaneMouseMove:wn,onPaneMouseLeave:_n,onPaneScroll:Mi,onPaneContextMenu:Pi,paneClickDistance:Ti,nodeClickDistance:Ii,onSelectionContextMenu:B,onSelectionStart:z,onSelectionEnd:A,onReconnect:go,onReconnectStart:ji,onReconnectEnd:Qn,onEdgeContextMenu:mo,onEdgeDoubleClick:Kn,onEdgeMouseEnter:Pl,onEdgeMouseMove:Gn,onEdgeMouseLeave:kr,reconnectRadius:Er,defaultMarkerColor:Xn,noDragClassName:Il,noWheelClassName:zl,noPanClassName:Gt,rfId:Zn,disableKeyboardA11y:$i,nodeExtent:ln,viewport:$l,onViewportChange:Ol,nodesDraggable:ze}),E.jsx(G1,{onSelectionChange:Z}),zi,E.jsx(W1,{proOptions:Li,position:Ai}),E.jsx(U1,{rfId:Zn,disableKeyboardA11y:$i})]})})}var IS=Pg(TS);function zS({dimensions:t,lineWidth:r,variant:i,className:l}){return E.jsx("path",{strokeWidth:r,d:`M${t[0]/2} 0 V${t[1]} M0 ${t[1]/2} H${t[0]}`,className:We(["react-flow__background-pattern",i,l])})}function jS({radius:t,className:r}){return E.jsx("circle",{cx:t,cy:t,r:t,className:We(["react-flow__background-pattern","dots",r])})}var Yn;(function(t){t.Lines="lines",t.Dots="dots",t.Cross="cross"})(Yn||(Yn={}));const AS={[Yn.Dots]:1,[Yn.Lines]:1,[Yn.Cross]:6},LS=t=>({transform:t.transform,patternId:`pattern-${t.rfId}`});function em({id:t,variant:r=Yn.Dots,gap:i=20,size:l,lineWidth:u=1,offset:a=0,color:c,bgColor:d,style:p,className:g,patternClassName:v}){const y=U.useRef(null),{transform:m,patternId:w}=Te(LS,Ve),S=l||AS[r],P=r===Yn.Dots,C=r===Yn.Cross,k=Array.isArray(i)?i:[i,i],j=[k[0]*m[2]||1,k[1]*m[2]||1],_=S*m[2],T=Array.isArray(a)?a:[a,a],F=C?[_,_]:j,D=[T[0]*m[2]+F[0]/2,T[1]*m[2]+F[1]/2],V=`${w}${t||""}`;return E.jsxs("svg",{className:We(["react-flow__background",g]),style:{...p,...El,"--xy-background-color-props":d,"--xy-background-pattern-color-props":c},ref:y,"data-testid":"rf__background",children:[E.jsx("pattern",{id:V,x:m[0]%j[0],y:m[1]%j[1],width:j[0],height:j[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${D[0]},-${D[1]})`,children:P?E.jsx(jS,{radius:_/2,className:v}):E.jsx(zS,{dimensions:F,lineWidth:u,variant:r,className:v})}),E.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${V})`})]})}em.displayName="Background";const RS=U.memo(em);function DS(){return E.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:E.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function $S(){return E.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:E.jsx("path",{d:"M0 0h32v4.2H0z"})})}function OS(){return E.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:E.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function FS(){return E.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:E.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function HS(){return E.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:E.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function qs({children:t,className:r,...i}){return E.jsx("button",{type:"button",className:We(["react-flow__controls-button",r]),...i,children:t})}const VS=t=>({isInteractive:t.nodesDraggable||t.nodesConnectable||t.elementsSelectable,minZoomReached:t.transform[2]<=t.minZoom,maxZoomReached:t.transform[2]>=t.maxZoom,ariaLabelConfig:t.ariaLabelConfig});function tm({style:t,showZoom:r=!0,showFitView:i=!0,showInteractive:l=!0,fitViewOptions:u,onZoomIn:a,onZoomOut:c,onFitView:d,onInteractiveChange:p,className:g,children:v,position:y="bottom-left",orientation:m="vertical","aria-label":w}){const S=De(),{isInteractive:P,minZoomReached:C,maxZoomReached:k,ariaLabelConfig:j}=Te(VS,Ve),{zoomIn:_,zoomOut:T,fitView:F}=gc(),D=()=>{_(),a==null||a()},V=()=>{T(),c==null||c()},Q=()=>{F(u),d==null||d()},Y=()=>{S.setState({nodesDraggable:!P,nodesConnectable:!P,elementsSelectable:!P}),p==null||p(!P)},ee=m==="horizontal"?"horizontal":"vertical";return E.jsxs(ki,{className:We(["react-flow__controls",ee,g]),position:y,style:t,"data-testid":"rf__controls","aria-label":w??j["controls.ariaLabel"],children:[r&&E.jsxs(E.Fragment,{children:[E.jsx(qs,{onClick:D,className:"react-flow__controls-zoomin",title:j["controls.zoomIn.ariaLabel"],"aria-label":j["controls.zoomIn.ariaLabel"],disabled:k,children:E.jsx(DS,{})}),E.jsx(qs,{onClick:V,className:"react-flow__controls-zoomout",title:j["controls.zoomOut.ariaLabel"],"aria-label":j["controls.zoomOut.ariaLabel"],disabled:C,children:E.jsx($S,{})})]}),i&&E.jsx(qs,{className:"react-flow__controls-fitview",onClick:Q,title:j["controls.fitView.ariaLabel"],"aria-label":j["controls.fitView.ariaLabel"],children:E.jsx(OS,{})}),l&&E.jsx(qs,{className:"react-flow__controls-interactive",onClick:Y,title:j["controls.interactive.ariaLabel"],"aria-label":j["controls.interactive.ariaLabel"],children:P?E.jsx(HS,{}):E.jsx(FS,{})}),v]})}tm.displayName="Controls";const bS=U.memo(tm);function BS({id:t,x:r,y:i,width:l,height:u,style:a,color:c,strokeColor:d,strokeWidth:p,className:g,borderRadius:v,shapeRendering:y,selected:m,onClick:w}){const{background:S,backgroundColor:P}=a||{},C=c||S||P;return E.jsx("rect",{className:We(["react-flow__minimap-node",{selected:m},g]),x:r,y:i,rx:v,ry:v,width:l,height:u,style:{fill:C,stroke:d,strokeWidth:p},shapeRendering:y,onClick:w?k=>w(k,t):void 0})}const US=U.memo(BS),WS=t=>t.nodes.map(r=>r.id),Ra=t=>t instanceof Function?t:()=>t;function YS({nodeStrokeColor:t,nodeColor:r,nodeClassName:i="",nodeBorderRadius:l=5,nodeStrokeWidth:u,nodeComponent:a=US,onClick:c}){const d=Te(WS,Ve),p=Ra(r),g=Ra(t),v=Ra(i),y=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return E.jsx(E.Fragment,{children:d.map(m=>E.jsx(QS,{id:m,nodeColorFunc:p,nodeStrokeColorFunc:g,nodeClassNameFunc:v,nodeBorderRadius:l,nodeStrokeWidth:u,NodeComponent:a,onClick:c,shapeRendering:y},m))})}function XS({id:t,nodeColorFunc:r,nodeStrokeColorFunc:i,nodeClassNameFunc:l,nodeBorderRadius:u,nodeStrokeWidth:a,shapeRendering:c,NodeComponent:d,onClick:p}){const{node:g,x:v,y,width:m,height:w}=Te(S=>{const P=S.nodeLookup.get(t);if(!P)return{node:void 0,x:0,y:0,width:0,height:0};const C=P.internals.userNode,{x:k,y:j}=P.internals.positionAbsolute,{width:_,height:T}=Xt(C);return{node:C,x:k,y:j,width:_,height:T}},Ve);return!g||g.hidden||!tg(g)?null:E.jsx(d,{x:v,y,width:m,height:w,style:g.style,selected:!!g.selected,className:l(g),color:r(g),borderRadius:u,strokeColor:i(g),strokeWidth:a,shapeRendering:c,onClick:p,id:g.id})}const QS=U.memo(XS);var KS=U.memo(YS);const GS=200,qS=150,ZS=t=>!t.hidden,JS=t=>{const r={x:-t.transform[0]/t.transform[2],y:-t.transform[1]/t.transform[2],width:t.width/t.transform[2],height:t.height/t.transform[2]};let i=!1;for(const l of t.nodeLookup.values())if(!l.hidden){i=!0;break}return{viewBB:r,boundingRect:i?Zp(wi(t.nodeLookup,{filter:ZS}),r):r,rfId:t.rfId,panZoom:t.panZoom,translateExtent:t.translateExtent,flowWidth:t.width,flowHeight:t.height,ariaLabelConfig:t.ariaLabelConfig}},op=(t,r)=>t.x===r.x&&t.y===r.y&&t.width===r.width&&t.height===r.height,ek=(t,r)=>op(t.viewBB,r.viewBB)&&op(t.boundingRect,r.boundingRect)&&t.rfId===r.rfId&&t.panZoom===r.panZoom&&t.translateExtent===r.translateExtent&&t.flowWidth===r.flowWidth&&t.flowHeight===r.flowHeight&&t.ariaLabelConfig===r.ariaLabelConfig,tk="react-flow__minimap-desc";function nm({style:t,className:r,nodeStrokeColor:i,nodeColor:l,nodeClassName:u="",nodeBorderRadius:a=5,nodeStrokeWidth:c,nodeComponent:d,bgColor:p,maskColor:g,maskStrokeColor:v,maskStrokeWidth:y,position:m="bottom-right",onClick:w,onNodeClick:S,pannable:P=!1,zoomable:C=!1,ariaLabel:k,inversePan:j,zoomStep:_=1,offsetScale:T=5}){const F=De(),D=U.useRef(null),{boundingRect:V,panZoom:Q,viewBB:Y,rfId:ee,translateExtent:q,flowWidth:J,flowHeight:Z,ariaLabelConfig:N}=Te(JS,ek),b=(t==null?void 0:t.width)??GS,H=(t==null?void 0:t.height)??qS,B=V.width/b,z=V.height/H,A=Math.max(B,z),O=A*b,M=A*H,L=T*A,te=V.x-(O-V.width)/2-L,re=V.y-(M-V.height)/2-L,ue=O+L*2,ce=M+L*2,ae=`${tk}-${ee}`,G=U.useRef(0),se=U.useRef();G.current=A,U.useEffect(()=>{const ke=F.getState().panZoom;if(D.current&&ke)return se.current=d1({domNode:D.current,panZoom:ke,getTransform:()=>F.getState().transform,getViewScale:()=>G.current}),()=>{var Ce;(Ce=se.current)==null||Ce.destroy()}},[Q]),U.useEffect(()=>{var ke;(ke=se.current)==null||ke.update({translateExtent:q,width:J,height:Z,inversePan:j,pannable:P,zoomStep:_,zoomable:C})},[P,C,j,_,q,J,Z]);const pe=w?ke=>{var ze;const[Ce,Me]=((ze=se.current)==null?void 0:ze.pointer(ke))||[0,0];w(ke,{x:Ce,y:Me})}:void 0,xe=U.useCallback((ke,Ce)=>{const Me=F.getState().nodeLookup.get(Ce).internals.userNode;S==null||S(ke,Me)},[S]),ye=S?xe:void 0,we=k??N["minimap.ariaLabel"];return E.jsx(ki,{position:m,style:{...t,"--xy-minimap-background-color-props":typeof p=="string"?p:void 0,"--xy-minimap-mask-background-color-props":typeof g=="string"?g:void 0,"--xy-minimap-mask-stroke-color-props":typeof v=="string"?v:void 0,"--xy-minimap-mask-stroke-width-props":typeof y=="number"?y*A:void 0,"--xy-minimap-node-background-color-props":typeof l=="string"?l:void 0,"--xy-minimap-node-stroke-color-props":typeof i=="string"?i:void 0,"--xy-minimap-node-stroke-width-props":typeof c=="number"?c:void 0},className:We(["react-flow__minimap",r]),"data-testid":"rf__minimap",children:E.jsxs("svg",{width:b,height:H,viewBox:`${te} ${re} ${ue} ${ce}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":ae,ref:D,onClick:pe,children:[we&&E.jsx("title",{id:ae,children:we}),E.jsx(KS,{onClick:ye,nodeColor:l,nodeStrokeColor:i,nodeBorderRadius:a,nodeClassName:u,nodeStrokeWidth:c,nodeComponent:d}),E.jsx("path",{className:"react-flow__minimap-mask",d:`M${te-L},${re-L}h${ue+L*2}v${ce+L*2}h${-ue-L*2}z
+        M${Y.x},${Y.y}h${Y.width}v${Y.height}h${-Y.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}nm.displayName="MiniMap";const nk=U.memo(nm),rk=t=>r=>t?`${Math.max(1/r.transform[2],1)}`:void 0,ok={[lo.Line]:"right",[lo.Handle]:"bottom-right"};function ik({nodeId:t,position:r,variant:i=lo.Handle,className:l,style:u=void 0,children:a,color:c,minWidth:d=10,minHeight:p=10,maxWidth:g=Number.MAX_VALUE,maxHeight:v=Number.MAX_VALUE,keepAspectRatio:y=!1,resizeDirection:m,autoScale:w=!0,shouldResize:S,onResizeStart:P,onResize:C,onResizeEnd:k}){const j=Ag(),_=typeof t=="string"?t:j,T=De(),F=U.useRef(null),D=i===lo.Handle,V=Te(U.useCallback(rk(D&&w),[D,w]),Ve),Q=U.useRef(null),Y=r??ok[i];U.useEffect(()=>{if(!(!F.current||!_))return Q.current||(Q.current=C1({domNode:F.current,nodeId:_,getStoreItems:()=>{const{nodeLookup:q,transform:J,snapGrid:Z,snapToGrid:N,nodeOrigin:b,domNode:H}=T.getState();return{nodeLookup:q,transform:J,snapGrid:Z,snapToGrid:N,nodeOrigin:b,paneDomNode:H}},onChange:(q,J)=>{const{triggerNodeChanges:Z,nodeLookup:N,parentLookup:b,nodeOrigin:H}=T.getState(),B=[],z={x:q.x,y:q.y},A=N.get(_);if(A&&A.expandParent&&A.parentId){const O=A.origin??H,M=q.width??A.measured.width??0,L=q.height??A.measured.height??0,te={id:A.id,parentId:A.parentId,rect:{width:M,height:L,...ng({x:q.x??A.position.x,y:q.y??A.position.y},{width:M,height:L},A.parentId,N,O)}},re=pc([te],N,b,H);B.push(...re),z.x=q.x?Math.max(O[0]*M,q.x):void 0,z.y=q.y?Math.max(O[1]*L,q.y):void 0}if(z.x!==void 0&&z.y!==void 0){const O={id:_,type:"position",position:{...z}};B.push(O)}if(q.width!==void 0&&q.height!==void 0){const M={id:_,type:"dimensions",resizing:!0,setAttributes:m?m==="horizontal"?"width":"height":!0,dimensions:{width:q.width,height:q.height}};B.push(M)}for(const O of J){const M={...O,type:"position"};B.push(M)}Z(B)},onEnd:({width:q,height:J})=>{const Z={id:_,type:"dimensions",resizing:!1,dimensions:{width:q,height:J}};T.getState().triggerNodeChanges([Z])}})),Q.current.update({controlPosition:Y,boundaries:{minWidth:d,minHeight:p,maxWidth:g,maxHeight:v},keepAspectRatio:y,resizeDirection:m,onResizeStart:P,onResize:C,onResizeEnd:k,shouldResize:S}),()=>{var q;(q=Q.current)==null||q.destroy()}},[Y,d,p,g,v,y,P,C,k,S]);const ee=Y.split("-");return E.jsx("div",{className:We(["react-flow__resize-control","nodrag",...ee,i,l]),ref:F,style:{...u,scale:V,...c&&{[D?"backgroundColor":"borderColor"]:c}},children:a})}U.memo(ik);const ip=40,sp=180,sk=0;function lk(t,r,i={}){var a,c;const l={};let u=0;for(const d of t){if(i[d]){l[d]=i[d];const p=((a=r.get(d))==null?void 0:a.height)??sp;u=Math.max(u,i[d].y+p+ip);continue}l[d]={x:sk,y:u},u+=(((c=r.get(d))==null?void 0:c.height)??sp)+ip}return l}function uk(t,r){return t.condition_kinds.find(i=>i.kind===r)}function Ei(t,r){return r==="sv"?t.label_sv:t.label_en}function rm({values:t,fields:r,lang:i,showScope:l,onChange:u}){const a=r.filter(c=>c.kind==="field_list"?!1:c.key==="scope"?!!l:!0);return a.length===0?null:E.jsx("div",{className:"fields",children:a.map(c=>E.jsx(ak,{field:c,value:t[c.key],lang:i,onChange:d=>u({[c.key]:d})},c.key))})}function ak({field:t,value:r,lang:i,onChange:l}){var d;const u=Ei(t,i),a=t.help??u;let c;return t.kind==="select"?c=E.jsx("select",{value:String(r??t.default??((d=t.options)==null?void 0:d[0])??""),title:a,onChange:p=>l(p.target.value),children:(t.options??[]).map(p=>E.jsx("option",{value:p,children:p===""?"—":p},p))}):t.kind==="bool"?c=E.jsx("input",{type:"checkbox",checked:r===!0||r==="true",title:a,onChange:p=>l(p.target.checked)}):t.kind==="number"?c=E.jsx("input",{type:"number",value:r==null?"":String(r),title:a,onChange:p=>l(p.target.value===""?void 0:Number(p.target.value))}):c=E.jsx("input",{type:"text",value:r==null?"":String(r),title:a,onChange:p=>l(p.target.value)}),E.jsxs("label",{className:"field"+(t.kind==="bool"?" field-bool":""),children:[E.jsx("span",{children:u}),c]})}function ck({condition:t,schema:r,lang:i,t:l,showScope:u,onChange:a}){const c=uk(r,t.kind);return E.jsxs("div",{className:"condition",children:[E.jsxs("label",{className:"field",children:[E.jsx("span",{children:l("condition")}),E.jsx("select",{value:t.kind,onChange:d=>a({kind:d.target.value}),children:r.condition_kinds.map(d=>E.jsx("option",{value:d.kind,children:Ei(d,i)},d.kind))})]}),c&&E.jsx(rm,{values:t,fields:c.fields,lang:i,showScope:u,onChange:d=>a({...t,...d})})]})}function fk({fields:t,t:r,onChange:i}){const l=(u,a)=>i(t.map((c,d)=>d===u?{...c,...a}:c));return E.jsxs("div",{className:"row-fields",children:[t.map((u,a)=>E.jsxs("div",{className:"row-field",children:[E.jsxs("div",{className:"row-field-head",children:[E.jsx("input",{type:"text",placeholder:r("column_name"),value:u.name,onChange:c=>l(a,{name:c.target.value})}),E.jsx("button",{className:"danger",onClick:()=>i(t.filter((c,d)=>d!==a)),children:r("remove")})]}),E.jsxs("div",{className:"fields",children:[E.jsxs("label",{className:"field",children:[E.jsx("span",{children:"by"}),E.jsx("select",{value:u.by??"css",onChange:c=>l(a,{by:c.target.value}),children:["text","css","role"].map(c=>E.jsx("option",{children:c},c))})]}),E.jsxs("label",{className:"field",children:[E.jsx("span",{children:"value"}),E.jsx("input",{type:"text",value:u.value??"",onChange:c=>l(a,{value:c.target.value})})]}),E.jsxs("label",{className:"field",children:[E.jsx("span",{children:"role"}),E.jsx("input",{type:"text",value:u.role??"",onChange:c=>l(a,{role:c.target.value})})]})]})]},a)),E.jsx("button",{className:"secondary",onClick:()=>i([...t,{name:"",by:"css",value:""}]),children:r("add_field")})]})}function qa({steps:t,schema:r,lang:i,t:l,inLoop:u,onChange:a}){const c=()=>a([...t,mr(r,"click")]);return E.jsxs("div",{className:"nested-steps",children:[t.map((d,p)=>E.jsxs("div",{className:"mini-card"+(d.enabled===!1?" disabled":""),children:[E.jsxs("div",{className:"mini-head",children:[E.jsx("input",{type:"checkbox",checked:d.enabled!==!1,title:l("enabled_title"),onChange:g=>a(ei(t,p,{...d,enabled:g.target.checked?void 0:!1}))}),E.jsx("select",{value:d.type,onChange:g=>a(ei(t,p,hp(r,d,g.target.value))),children:r.step_types.map(g=>E.jsx("option",{value:g.type,children:Ei(g,i)},g.type))}),E.jsx("input",{type:"text",className:"label-input",placeholder:l("label_ph"),value:d.label??"",onChange:g=>a(ei(t,p,{...d,label:g.target.value||void 0}))}),E.jsxs("select",{value:d.on_error==="continue"?"continue":"stop",onChange:g=>a(ei(t,p,{...d,on_error:g.target.value==="continue"?"continue":void 0})),children:[E.jsx("option",{value:"stop",children:l("on_error_stop")}),E.jsx("option",{value:"continue",children:l("on_error_continue")})]}),E.jsx("span",{className:"spacer"}),E.jsx("button",{className:"icon",title:l("move_up"),disabled:p===0,onClick:()=>a(Da(t,p,p-1)),children:"↑"}),E.jsx("button",{className:"icon",title:l("move_down"),disabled:p===t.length-1,onClick:()=>a(Da(t,p,p+1)),children:"↓"}),E.jsx("button",{className:"danger",onClick:()=>a(gp(t,p)),children:l("remove")})]}),E.jsx(om,{step:d,schema:r,lang:i,t:l,inLoop:u||d.type==="loop",onChange:g=>a(ei(t,p,g))}),E.jsx("button",{className:"secondary tiny",onClick:()=>a(pp(t,p+1,mr(r,"click"))),children:"+"})]},d.id??p)),E.jsx("button",{className:"secondary",onClick:c,children:l("add_step_in")})]})}function om({step:t,schema:r,lang:i,t:l,inLoop:u,onChange:a}){const c=r.step_types.find(g=>g.type===t.type);if(!c)return E.jsxs("div",{className:"hint",children:["Okänd stegtyp: ",t.type]});const d=g=>{const v={...t};for(const[y,m]of Object.entries(g))m===void 0||m===""?delete v[y]:v[y]=m;a(v)},p=u||t.type==="loop"||t.type==="extract_row";return E.jsxs(E.Fragment,{children:[E.jsx(rm,{values:t,fields:c.fields,lang:i,showScope:p,onChange:d}),t.type==="extract_row"&&E.jsx(fk,{fields:t.fields??[],lang:i,t:l,onChange:g=>a({...t,fields:g})}),t.type==="loop"&&E.jsx("div",{className:"nested",children:E.jsx(qa,{steps:t.steps??[],schema:r,lang:i,t:l,inLoop:!0,onChange:g=>a({...t,steps:g})})}),t.type==="if"&&E.jsx(dk,{step:t,schema:r,lang:i,t:l,inLoop:u,onChange:a})]})}function dk({step:t,schema:r,lang:i,t:l,inLoop:u,onChange:a}){const c=t.branches??[{condition:{kind:"element_exists"},steps:[]}],d=(p,g)=>a({...t,branches:c.map((v,y)=>y===p?g:v)});return E.jsxs("div",{className:"nested",children:[c.map((p,g)=>E.jsxs("div",{className:"branch",children:[E.jsxs("div",{className:"branch-head",children:[E.jsx("strong",{children:l(g===0?"branch_if":"branch_else_if")}),g>0&&E.jsx("button",{className:"danger",onClick:()=>a({...t,branches:c.filter((v,y)=>y!==g)}),children:l("remove_branch")})]}),E.jsx(ck,{condition:p.condition,schema:r,lang:i,t:l,showScope:u,onChange:v=>d(g,{...p,condition:v})}),E.jsx(qa,{steps:p.steps,schema:r,lang:i,t:l,inLoop:u,onChange:v=>d(g,{...p,steps:v})})]},g)),E.jsx("button",{className:"secondary",onClick:()=>a({...t,branches:[...c,Q0()]}),children:l("add_branch")}),E.jsxs("label",{className:"branch else-toggle",children:[E.jsx("input",{type:"checkbox",checked:Array.isArray(t.else),onChange:p=>a({...t,else:p.target.checked?[]:void 0})}),E.jsx("span",{children:l("else_toggle")})]}),Array.isArray(t.else)&&E.jsx("div",{className:"branch",children:E.jsx(qa,{steps:t.else,schema:r,lang:i,t:l,inLoop:u,onChange:p=>a({...t,else:p})})})]})}function hk({data:t}){const r=t,{step:i,schema:l,lang:u,t:a}=r,c=l.step_types.find(d=>d.type===i.type);return E.jsxs("div",{className:"step-node cat-"+((c==null?void 0:c.category)??"x")+(i.enabled===!1?" disabled":""),children:[E.jsx(uo,{type:"target",position:ve.Top}),E.jsxs("div",{className:"node-head",children:[E.jsx("input",{type:"checkbox",className:"nodrag",checked:i.enabled!==!1,title:a("enabled_title"),onChange:d=>r.onChange({...i,enabled:d.target.checked?void 0:!1})}),E.jsx("select",{className:"nodrag type-select",value:i.type,onChange:d=>r.onChange(hp(l,i,d.target.value)),children:l.step_types.map(d=>E.jsx("option",{value:d.type,children:Ei(d,u)},d.type))}),E.jsx("span",{className:"spacer"}),E.jsx("button",{className:"icon nodrag",title:a("move_up"),disabled:r.index===0,onClick:()=>r.onMove(-1),children:"↑"}),E.jsx("button",{className:"icon nodrag",title:a("move_down"),disabled:r.index===r.count-1,onClick:()=>r.onMove(1),children:"↓"}),E.jsx("button",{className:"danger nodrag",onClick:r.onDelete,children:"✕"})]}),E.jsxs("div",{className:"node-subhead",children:[E.jsx("input",{type:"text",className:"nodrag label-input",placeholder:a("label_ph"),value:i.label??"",onChange:d=>r.onChange({...i,label:d.target.value||void 0})}),E.jsxs("select",{className:"nodrag",value:i.on_error==="continue"?"continue":"stop",onChange:d=>r.onChange({...i,on_error:d.target.value==="continue"?"continue":void 0}),children:[E.jsx("option",{value:"stop",children:a("on_error_stop")}),E.jsx("option",{value:"continue",children:a("on_error_continue")})]})]}),E.jsx("div",{className:"node-body nodrag",children:E.jsx(om,{step:i,schema:l,lang:u,t:a,inLoop:!1,onChange:r.onChange})}),E.jsx("button",{className:"add-after nodrag",title:a("add_step_after"),onClick:r.onAddAfter,children:"+"}),E.jsx(uo,{type:"source",position:ve.Bottom})]})}const pk={step:hk};function gk(t){var i,l;let r=150;if(t.type==="loop"&&(r+=70+(((i=t.steps)==null?void 0:i.length)??0)*120),t.type==="if"){for(const u of t.branches??[])r+=150+u.steps.length*120;Array.isArray(t.else)&&(r+=90+t.else.length*120)}return t.type==="extract_row"&&(r+=60+(((l=t.fields)==null?void 0:l.length)??0)*130),r}function mk({flow:t,schema:r,lang:i,t:l,onChange:u}){const[a,c]=U.useState(()=>dp(t).nodes.map(k=>k.step)),[d,p]=U.useState(()=>({...t.layout??{}})),g=U.useRef(new Map),v=U.useRef(!0),y=U.useMemo(()=>{const k=new Map;for(const j of a)k.set(j.id,g.current.get(j.id)??{width:340,height:gk(j)});return lk(a.map(j=>j.id),k,d)},[a,d]);U.useEffect(()=>{if(v.current){v.current=!1;return}u({steps:a,layout:d})},[a,d,u]);const m=U.useCallback((k,j)=>c(_=>_.map(T=>T.id===k?j:T)),[]),w=U.useMemo(()=>a.map((k,j)=>({id:k.id,type:"step",position:y[k.id]??{x:0,y:j*200},data:{step:k,schema:r,lang:i,t:l,index:j,count:a.length,onChange:_=>m(k.id,_),onDelete:()=>c(_=>gp(_,_.findIndex(T=>T.id===k.id))),onMove:_=>c(T=>{const F=T.findIndex(D=>D.id===k.id);return Da(T,F,F+_)}),onAddAfter:()=>c(_=>{const T=_.findIndex(F=>F.id===k.id);return pp(_,T+1,mr(r,"click"))})}})),[a,y,r,i,l,m]),S=U.useMemo(()=>{const k=[];for(let _=0;_<a.length-1;_++)k.push({id:`seq-${a[_].id}-${a[_+1].id}`,source:a[_].id,target:a[_+1].id,animated:!1});const j=new Map;for(const _ of a)_.label&&j.set(_.label,_.id);for(const _ of a)_.type==="jump_to_label"&&typeof _.target=="string"&&j.has(_.target)&&k.push({id:`jump-${_.id}`,source:_.id,target:j.get(_.target),style:{stroke:"#d1453b",strokeDasharray:"5 5"},label:"goto"});return k},[a]),P=U.useCallback(k=>{for(const j of k)j.type==="dimensions"&&j.dimensions&&g.current.set(j.id,{width:j.dimensions.width,height:j.dimensions.height});p(j=>{let _=j;for(const T of k)T.type==="position"&&T.position&&T.dragging&&(_===j&&(_={...j}),_[T.id]={x:Math.round(T.position.x),y:Math.round(T.position.y)});return _})},[]),C=U.useCallback(()=>{c(k=>[...k].sort((j,_)=>{var T,F;return(((T=y[j.id])==null?void 0:T.y)??0)-(((F=y[_.id])==null?void 0:F.y)??0)}))},[y]);return E.jsx("div",{className:"canvas",children:E.jsxs(IS,{nodes:w,edges:S,nodeTypes:pk,onNodesChange:P,onNodeDragStop:C,nodesConnectable:!1,fitView:!0,minZoom:.2,proOptions:{hideAttribution:!0},children:[E.jsx(RS,{}),E.jsx(nk,{pannable:!0,zoomable:!0}),E.jsx(bS,{}),E.jsxs(ki,{position:"top-left",className:"canvas-panel",children:[E.jsx("button",{className:"secondary",onClick:()=>c(k=>[...k,mr(r,"goto")]),children:l("add_step")}),E.jsx("button",{className:"secondary",onClick:()=>p({}),children:l("relayout")})]})]})})}function yk({t,runId:r,run:i}){const l=U.useRef(null);U.useEffect(()=>{l.current&&(l.current.scrollTop=l.current.scrollHeight)},[i==null?void 0:i.logs]);const u=i==null?void 0:i.status;return E.jsxs("div",{className:"panel",children:[E.jsxs("div",{className:"panel-head",children:[E.jsx("strong",{children:t("run_log")}),u&&E.jsx("span",{className:"badge badge-"+u,children:t(u==="running"?"status_running":u==="done"?"status_done":"status_error")}),E.jsx("span",{className:"spacer"}),(i==null?void 0:i.browser_open)&&r&&E.jsx("button",{className:"secondary",onClick:()=>ut.closeBrowser(r),children:t("close_browser")}),(i==null?void 0:i.trace_ready)&&r&&E.jsx("button",{className:"secondary",onClick:()=>ut.showTrace(r),children:t("show_trace")})]}),E.jsx("pre",{className:"log",ref:l,children:((i==null?void 0:i.logs)??[]).join(`
+`)})]})}function vk(t,r){return Array.isArray(r)?t("var_list",{n:r.length}):r&&typeof r=="object"?t("var_object"):typeof r=="string"?t("var_text",{n:r.length}):typeof r}function xk({t,vars:r}){const[i,l]=U.useState(new Set),u=Object.keys(r??{});return E.jsxs("div",{className:"panel",children:[E.jsx("strong",{children:t("variables")}),E.jsx("p",{className:"hint",children:t("vars_hint")}),u.length===0&&E.jsx("p",{className:"hint",children:t("no_vars")}),u.map(a=>{const c=i.has(a),d=r[a];return E.jsxs("div",{className:"var-row",children:[E.jsxs("div",{className:"var-head",onClick:()=>l(p=>{const g=new Set(p);return g.has(a)?g.delete(a):g.add(a),g}),children:[a," ",E.jsxs("span",{className:"hint",children:["(",vk(t,d),")"]})]}),c&&E.jsx("pre",{className:"var-value",children:typeof d=="string"?d:JSON.stringify(d,null,2)})]},a)})]})}function wk({t,currentName:r,flow:i,onApply:l}){const[u,a]=U.useState(""),[c,d]=U.useState(!1),[p,g]=U.useState(null);async function v(){if(!(!u.trim()||!r)){d(!0),g(null);try{const y=await ut.assist(r,u.trim(),i);l({steps:y.steps,settings:y.settings,layout:{}}),g({text:t("assist_applied")+(y.explanation?`
+
+`+y.explanation:""),error:!1})}catch(y){if(y instanceof rl&&y.status===501)g({text:t("assist_unavailable"),error:!0});else{const m=y instanceof rl?String(y.detail):String(y);g({text:t("assist_failed",{msg:m}),error:!0})}}finally{d(!1)}}}return E.jsxs("div",{className:"panel",children:[E.jsx("strong",{children:t("assist_title")}),E.jsx("p",{className:"hint",children:t("assist_hint")}),E.jsx("textarea",{rows:3,placeholder:t("assist_ph"),value:u,onChange:y=>a(y.target.value)}),E.jsx("button",{onClick:v,disabled:c||!r,children:t(c?"assist_working":"assist_btn")}),p&&E.jsx("p",{className:"assist-msg"+(p.error?" error":""),children:p.text})]})}function _k({open:t,schema:r,lang:i,t:l,settings:u,onClose:a,onSave:c}){const d=U.useRef(null);U.useEffect(()=>{const g=d.current;g&&(t&&!g.open&&g.showModal(),!t&&g.open&&g.close())},[t]);function p(g){g.preventDefault();const v=g.currentTarget,y={};for(const m of r.settings){const w=v.elements.namedItem(m.key);w&&(m.kind==="bool"?y[m.key]=w.checked:m.kind==="number"?y[m.key]=Number(w.value)||Number(m.default)||0:y[m.key]=w.value||String(m.default))}c(y)}return E.jsx("dialog",{ref:d,onClose:a,children:E.jsxs("form",{onSubmit:p,method:"dialog",children:[E.jsx("h2",{children:l("settings")}),E.jsx("p",{className:"hint",children:l("settings_hint")}),E.jsx("div",{className:"fields",children:r.settings.map(g=>E.jsxs("label",{className:"field"+(g.kind==="bool"?" field-bool":""),children:[E.jsx("span",{children:Ei(g,i)}),g.kind==="bool"?E.jsx("input",{type:"checkbox",name:g.key,defaultChecked:u[g.key]===!0}):E.jsx("input",{type:g.kind==="number"?"number":"text",name:g.key,defaultValue:String(u[g.key]??g.default)})]},g.key))}),E.jsxs("div",{className:"dialog-actions",children:[E.jsx("button",{type:"button",className:"secondary",onClick:a,children:l("cancel")}),E.jsx("button",{type:"submit",children:l("save")})]})]})})}const lp={steps:[],settings:{},layout:{}};function Sk(){const[t,r]=U.useState(null),[i,l]=U.useState(()=>localStorage.getItem("rpa_lang")||"en"),[u,a]=U.useState(()=>localStorage.getItem("rpa_close_mode")||"always"),[c,d]=U.useState([]),[p,g]=U.useState(null),[v,y]=U.useState(lp),[m,w]=U.useState(!1),[S,P]=U.useState(0),[C,k]=U.useState(!1),[j,_]=U.useState(null),[T,F]=U.useState(null),[D,V]=U.useState(null),Q=U.useRef(null),Y=W0(i);U.useEffect(()=>{localStorage.setItem("rpa_lang",i),document.title=Y("page_title")},[i]),U.useEffect(()=>localStorage.setItem("rpa_close_mode",u),[u]),U.useEffect(()=>{(async()=>{const z=await ut.schema();r(z);const A=await ut.listFlows();if(d(A),A.length)await ee(A[0]);else{const O={steps:[Wn(mr(z,"goto")),Wn(mr(z,"wait_for_login"))],settings:{},layout:{}};O.steps[0].url="https://www.1177.se/",g("standard"),y(O),w(!0),P(M=>M+1)}})()},[]);const ee=U.useCallback(async z=>{const A=await ut.getFlow(z);A.steps=(A.steps??[]).map(O=>Wn({...O})),A.settings=A.settings??{},A.layout=A.layout??{},g(z),y(A),w(!1),P(O=>O+1)},[]),q=U.useCallback(z=>{y(A=>({...A,steps:z.steps,layout:z.layout})),w(!0)},[]),J=U.useCallback(async()=>{if(!p)return!1;try{await ut.saveFlow(p,v),w(!1);const z=await ut.listFlows();return d(z),!0}catch(z){const A=z instanceof rl?JSON.stringify(z.detail):String(z);return _(Y("save_failed",{msg:A})),!1}},[p,v,Y]);async function Z(){const z=prompt(Y("new_flow_prompt"));if(!z||!t)return;const A={steps:[],settings:{},layout:{}};g(z),y(A),P(O=>O+1);try{await ut.saveFlow(z,A),d(await ut.listFlows()),w(!1)}catch{w(!0)}}async function N(){if(!p||!confirm(Y("delete_flow_confirm",{name:p})))return;await ut.deleteFlow(p);const z=await ut.listFlows();d(z),z.length?await ee(z[0]):(g(null),y(lp),P(A=>A+1))}const b=()=>{Q.current&&window.clearTimeout(Q.current),Q.current=null},H=U.useCallback(z=>{ut.run(z).then(A=>{V(A),(A.status==="running"||A.browser_open)&&(Q.current=window.setTimeout(()=>H(z),1e3))})},[]);async function B(){if(!p)return;await J(),b(),V(null);const{run_id:z}=await ut.runFlow(p,u);F(z),H(z)}return U.useEffect(()=>b,[]),t?E.jsxs("div",{className:"app",children:[E.jsx(K0,{t:Y,lang:i,setLang:l,schema:t,flowNames:c,currentName:p,onSelect:ee,onNew:Z,onDelete:N,onSave:J,onRun:B,dirty:m,running:(D==null?void 0:D.status)==="running",closeMode:u,setCloseMode:a,onOpenSettings:()=>k(!0)}),E.jsxs("main",{children:[E.jsx(mk,{flow:v,schema:t,lang:i,t:Y,onChange:q},S),E.jsxs("aside",{className:"side",children:[E.jsx(wk,{t:Y,currentName:p,flow:v,onApply:z=>{z.steps=z.steps.map(A=>Wn({...A})),z.layout=z.layout??{},dp(z),y(z),w(!0),P(A=>A+1)}}),E.jsx(yk,{t:Y,runId:T,run:D}),E.jsx(xk,{t:Y,vars:(D==null?void 0:D.vars)??{}})]})]}),E.jsx(_k,{open:C,schema:t,lang:i,t:Y,settings:v.settings,onClose:()=>k(!1),onSave:z=>{y(A=>({...A,settings:z})),w(!0),k(!1)}}),j&&E.jsx("div",{className:"toast",onClick:()=>_(null),children:j})]}):E.jsx("div",{className:"loading",children:"…"})}U0.createRoot(document.getElementById("root")).render(E.jsx(ap.StrictMode,{children:E.jsx(Sk,{})}));
diff --git a/webui/static/index.html b/webui/static/index.html
new file mode 100644
index 0000000..b7b76bc
--- /dev/null
+++ b/webui/static/index.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>RPA Tool – flow builder</title>
+    <script type="module" crossorigin src="/assets/index-ClN1xAxw.js"></script>
+    <link rel="stylesheet" crossorigin href="/assets/index-CYM_2KrD.css">
+  </head>
+  <body>
+    <div id="root"></div>
+  </body>
+</html>
diff --git a/webui/templates/index.html b/webui/templates/index.html
deleted file mode 100644
index f0a11d4..0000000
--- a/webui/templates/index.html
+++ /dev/null
@@ -1,1428 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-<head>
-<meta charset="UTF-8">
-<title>RPA Tool – flow builder</title>
-<style>
-  :root {
-    color-scheme: light dark;
-    --bg: #f5f5f7;
-    --panel: #ffffff;
-    --border: #d8d8dd;
-    --text: #1c1c1e;
-    --muted: #6b6b70;
-    --accent: #2f6fed;
-    --accent-text: #ffffff;
-    --danger: #d1453b;
-    --loop-bg: #eef2fb;
-  }
-  @media (prefers-color-scheme: dark) {
-    :root {
-      --bg: #1c1c1e;
-      --panel: #2a2a2d;
-      --border: #444448;
-      --text: #f2f2f3;
-      --muted: #9a9a9e;
-      --loop-bg: #232b3d;
-    }
-  }
-  * { box-sizing: border-box; }
-  body {
-    margin: 0;
-    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
-    background: var(--bg);
-    color: var(--text);
-  }
-  header {
-    display: flex;
-    align-items: center;
-    gap: 12px;
-    padding: 14px 20px;
-    background: var(--panel);
-    border-bottom: 1px solid var(--border);
-    flex-wrap: wrap;
-  }
-  header h1 { font-size: 16px; margin: 0 16px 0 0; white-space: nowrap; }
-  select, input, button, a.secondary {
-    font: inherit;
-    padding: 6px 10px;
-    border-radius: 6px;
-    border: 1px solid var(--border);
-    background: var(--panel);
-    color: var(--text);
-  }
-  button {
-    cursor: pointer;
-    background: var(--accent);
-    color: var(--accent-text);
-    border: none;
-  }
-  button.secondary, a.secondary {
-    background: transparent;
-    color: var(--text);
-    border: 1px solid var(--border);
-  }
-  a.secondary {
-    display: inline-block;
-    text-decoration: none;
-    cursor: pointer;
-    line-height: 1.2;
-  }
-  a.secondary:hover { background: var(--loop-bg); }
-  button.danger { background: var(--danger); }
-  button:disabled { opacity: 0.5; cursor: not-allowed; }
-  main {
-    display: grid;
-    grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
-    gap: 16px;
-    padding: 16px 20px;
-    align-items: start;
-  }
-  .panel {
-    background: var(--panel);
-    border: 1px solid var(--border);
-    border-radius: 10px;
-    padding: 14px;
-  }
-  .steps { display: flex; flex-direction: column; gap: 10px; }
-  .step-card {
-    border: 1px solid var(--border);
-    border-radius: 8px;
-    padding: 10px 12px;
-    background: var(--panel);
-  }
-  .step-card.loop { background: var(--loop-bg); }
-  .step-card.step-disabled { opacity: 0.5; }
-  .step-head {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    margin-bottom: 8px;
-  }
-  .step-head .enabled-toggle { flex: 0 0 auto; cursor: pointer; }
-  .step-head .type-select { font-weight: 600; }
-  .step-head .spacer { flex: 1; }
-  .step-head button { padding: 3px 8px; font-size: 12px; }
-  .insert-divider {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    position: relative;
-    height: 14px;
-    cursor: pointer;
-  }
-  .insert-divider .line {
-    position: absolute;
-    left: 0;
-    right: 0;
-    top: 50%;
-    border-top: 1px dashed transparent;
-  }
-  .insert-divider:hover .line { border-top: 1px dashed var(--accent); }
-  .insert-divider .plus {
-    position: relative;
-    width: 16px;
-    height: 16px;
-    line-height: 14px;
-    text-align: center;
-    border-radius: 50%;
-    border: 1px solid var(--border);
-    background: var(--panel);
-    color: var(--muted);
-    font-size: 12px;
-    opacity: 0;
-    transition: opacity 0.1s;
-  }
-  .insert-divider:hover .plus {
-    opacity: 1;
-    color: var(--accent);
-    border-color: var(--accent);
-  }
-  .fields {
-    display: grid;
-    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
-    gap: 8px;
-  }
-  .field label {
-    display: block;
-    font-size: 11px;
-    color: var(--muted);
-    margin-bottom: 2px;
-  }
-  .field input, .field select { width: 100%; }
-  .nested {
-    margin-top: 10px;
-    padding: 10px;
-    border-left: 3px solid var(--accent);
-    border-radius: 6px;
-  }
-  .nested .steps { margin-bottom: 8px; }
-  .add-step-row { margin-top: 4px; }
-  .branch {
-    border: 1px dashed var(--border);
-    border-radius: 6px;
-    padding: 8px;
-    margin-bottom: 8px;
-  }
-  .branch .step-head { margin-bottom: 6px; }
-  .branch.else-branch { border-style: solid; }
-  #log {
-    background: #111114;
-    color: #d6d6d8;
-    border-radius: 8px;
-    padding: 10px;
-    font-family: ui-monospace, Consolas, monospace;
-    font-size: 12px;
-    height: 360px;
-    overflow-y: auto;
-    white-space: pre-wrap;
-  }
-  .status-badge {
-    display: inline-block;
-    padding: 2px 8px;
-    border-radius: 999px;
-    font-size: 12px;
-    margin-left: 8px;
-  }
-  .status-running { background: #f5b942; color: #222; }
-  .status-done { background: #3aa757; color: #fff; }
-  .status-error { background: var(--danger); color: #fff; }
-  .hint { color: var(--muted); font-size: 12px; margin: 0 0 10px; }
-  .side-col { display: flex; flex-direction: column; gap: 16px; }
-  .var-row {
-    border: 1px solid var(--border);
-    border-radius: 6px;
-    margin-bottom: 6px;
-    overflow: hidden;
-  }
-  .var-header {
-    padding: 6px 10px;
-    cursor: pointer;
-    font-family: ui-monospace, Consolas, monospace;
-    font-size: 12px;
-    background: var(--bg);
-  }
-  .var-header:hover { background: var(--loop-bg); }
-  .var-header .var-type { color: var(--muted); font-weight: normal; }
-  .var-value {
-    margin: 0;
-    padding: 8px 10px;
-    background: #111114;
-    color: #d6d6d8;
-    font-family: ui-monospace, Consolas, monospace;
-    font-size: 12px;
-    max-height: 240px;
-    overflow: auto;
-    white-space: pre-wrap;
-  }
-  dialog#settingsDialog {
-    background: var(--panel);
-    color: var(--text);
-    border: 1px solid var(--border);
-    border-radius: 10px;
-    padding: 20px 22px;
-    width: min(520px, 90vw);
-  }
-  dialog#settingsDialog::backdrop { background: rgba(0, 0, 0, 0.4); }
-  dialog#settingsDialog h2 { margin: 0 0 4px; font-size: 16px; }
-  dialog#settingsDialog .field input[type="checkbox"] { width: auto; }
-</style>
-</head>
-<body>
-
-<header>
-  <h1 id="appTitle">RPA Tool</h1>
-  <select id="langSelect">
-    <option value="en">English</option>
-    <option value="sv">Svenska</option>
-  </select>
-  <select id="flowSelect"></select>
-  <button class="secondary" id="newFlowBtn">New flow</button>
-  <button class="danger" id="deleteFlowBtn">Delete flow</button>
-  <a class="secondary" id="wikiLink" href="/wiki" target="_blank" rel="noopener">Wiki</a>
-  <button class="secondary" id="settingsBtn">⚙ Settings</button>
-  <a class="secondary" id="exportLink" href="#" target="_blank" rel="noopener">Export as script</a>
-  <span class="spacer" style="flex:1"></span>
-  <select id="closeModeSelect">
-    <option value="always">Close the browser</option>
-    <option value="on_success">Keep open on error</option>
-    <option value="never">Always keep open</option>
-  </select>
-  <button class="secondary" id="saveBtn">Save</button>
-  <button id="runBtn">Run flow</button>
-  <button class="secondary" id="closeBrowserBtn" style="display:none">Close browser</button>
-  <button class="secondary" id="showTraceBtn" style="display:none">Show trace</button>
-  <span id="statusBadge"></span>
-</header>
-
-<dialog id="settingsDialog">
-  <h2 id="settingsTitle">Settings</h2>
-  <p class="hint" id="settingsHint"></p>
-  <div class="fields">
-    <div class="field">
-      <label id="settingsHeadlessLabel">Headless</label>
-      <input type="checkbox" id="settingsHeadless" style="width:auto">
-    </div>
-    <div class="field">
-      <label id="settingsViewportWidthLabel">Viewport width</label>
-      <input type="number" id="settingsViewportWidth" value="1280">
-    </div>
-    <div class="field">
-      <label id="settingsViewportHeightLabel">Viewport height</label>
-      <input type="number" id="settingsViewportHeight" value="900">
-    </div>
-    <div class="field">
-      <label id="settingsLocaleLabel">Locale</label>
-      <input type="text" id="settingsLocale" value="sv-SE">
-    </div>
-    <div class="field">
-      <label id="settingsSlowMoLabel">Slow motion (ms)</label>
-      <input type="number" id="settingsSlowMo" value="0">
-    </div>
-    <div class="field">
-      <label id="settingsNotifyLabel">Notify on completion</label>
-      <input type="checkbox" id="settingsNotify" style="width:auto">
-    </div>
-  </div>
-  <div class="add-step-row" style="display:flex; gap:8px; justify-content:flex-end;">
-    <button class="secondary" id="settingsCancelBtn">Cancel</button>
-    <button id="settingsSaveBtn">Save</button>
-  </div>
-</dialog>
-
-<main>
-  <div class="panel">
-    <p class="hint" id="stepsHint"></p>
-    <div id="rootSteps" class="steps"></div>
-    <div class="add-step-row">
-      <button class="secondary" id="addRootStepBtn">+ Add step</button>
-    </div>
-  </div>
-
-  <div class="side-col">
-    <div class="panel">
-      <strong id="logTitle">Run log</strong>
-      <div id="log"></div>
-    </div>
-    <div class="panel">
-      <strong id="varsTitle">Variables</strong>
-      <p class="hint" id="varsHint"></p>
-      <div id="varsPanel"></div>
-    </div>
-  </div>
-</main>
-
-<script>
-// ---------------------------------------------------------------------
-// i18n: English is the default UI language; Swedish is a full translation
-// selectable via the language dropdown (persisted in localStorage). Only
-// the browser UI is translated here - the run log is produced by the
-// Python backend and stays in Swedish regardless of the UI language.
-// ---------------------------------------------------------------------
-
-const I18N = {
-  en: {
-    ui_title: "RPA Tool",
-    ui_page_title: "RPA Tool – flow builder",
-    ui_new_flow: "New flow",
-    ui_delete_flow: "Delete flow",
-    ui_close_mode_title: "What should happen to the browser when the flow finishes?",
-    ui_close_always: "Close the browser",
-    ui_close_on_success: "Keep open on error",
-    ui_close_never: "Always keep open",
-    ui_save: "Save",
-    ui_run: "Run flow",
-    ui_close_browser_btn: "Close browser",
-    ui_show_trace: "Show trace",
-    ui_wiki_link: "Wiki",
-    ui_wiki_title: "Open the step/field reference in a new tab",
-    ui_settings_btn: "⚙ Settings",
-    ui_export_link: "Export as script",
-    ui_export_title: "Download a standalone Python script for this flow",
-    ui_settings_title: "Settings",
-    ui_settings_hint: "Saved with this flow, so they travel along when you export it as a standalone script.",
-    ui_settings_headless: "Headless (no visible browser window)",
-    ui_settings_viewport_width: "Viewport width",
-    ui_settings_viewport_height: "Viewport height",
-    ui_settings_locale: "Locale",
-    ui_settings_slow_mo: "Slow motion (ms between actions)",
-    ui_settings_notify: "Desktop notification when the run finishes",
-    ui_settings_cancel: "Cancel",
-    ui_settings_save: "Save",
-    ui_steps_hint: 'Build the flow step by step. "List items" finds elements via a CSS selector and saves them in a variable; "Loop" iterates over that variable - either DOM results (Search scope "item" searches only within the current item) or a plain list (e.g. from "Append to list"/"Load variable"), where the current value is available as {item} in "Find by: text/role" fields.',
-    ui_add_step: "+ Add step",
-    ui_run_log: "Run log",
-    ui_variables: "Variables",
-    ui_variables_hint: "Click a variable name to see its content (filled in once the flow finishes).",
-    ui_no_vars_yet: "No variables yet.",
-    ui_insert_step_here: "Insert step here",
-    ui_label_placeholder: "Label (optional)",
-    ui_label_title: "Name this step so a 'Jump to label' step can jump here",
-    ui_enabled_title: "Enabled (unchecked = step is skipped when running)",
-    ui_on_error_title: "What happens if this step fails / times out",
-    ui_on_error_stop: "On error: stop",
-    ui_on_error_continue: "On error: continue",
-    ui_remove: "Remove",
-    ui_add_step_in_loop: "+ Add step in loop",
-    ui_branch_if: "If",
-    ui_branch_else_if: "...else if",
-    ui_remove_branch: "Remove branch",
-    ui_add_else_if: "+ Add 'else if' branch",
-    ui_else_toggle: "...else",
-    ui_condition_label: "Condition",
-    ui_status_running: "Running…",
-    ui_status_done: "Done",
-    ui_status_error: "Error",
-    ui_var_list: "list, {n} item(s)",
-    ui_var_object: "object",
-    ui_var_text: "text, {n} characters",
-    ui_new_flow_prompt: "Name of new flow:",
-    ui_delete_flow_confirm: 'Delete the flow "{name}"?',
-    ui_field_name_placeholder: "Column name",
-    ui_add_field: "+ Add field",
-
-    step_goto: "Go to URL",
-    step_wait: "Wait (seconds)",
-    step_wait_for_load: "Wait for page to load",
-    step_go_back: "Go back (browser history)",
-    step_wait_for_login: "Wait for login",
-    step_wait_for_selector: "Wait for element",
-    step_click: "Click",
-    step_type: "Type in field",
-    step_press_key: "Press key",
-    step_select_option: "Select in dropdown",
-    step_check: "Check checkbox/radio",
-    step_extract_text: "Read text into variable",
-    step_extract_row: "Extract row (multiple columns)",
-    step_set_var: "Set variable",
-    step_increment: "Increment variable",
-    step_clear_var: "Clear variable",
-    step_list_append: "Append to list",
-    step_save_var: "Save variable (JSON/CSV)",
-    step_load_var: "Load variable (JSON/CSV)",
-    step_run_script: "Run Python script",
-    step_screenshot: "Take screenshot",
-    step_list: "List items",
-    step_if: "If / else if / else",
-    step_jump_to_label: "Jump to label",
-    step_loop: "Loop (for each item)",
-    step_download: "Download",
-    step_save_attachment: "Download attachment",
-    step_capture_pdf: "Capture PDF (print button)",
-
-    cond_element_exists: "Element exists",
-    cond_element_not_exists: "Element does not exist",
-    cond_var_equals: "Variable equals",
-    cond_var_not_equals: "Variable does NOT equal",
-    cond_var_contains: "Variable contains",
-
-    f_by: "Find by",
-    f_value: "Text / CSS selector / role name",
-    f_role: "Role (if by=role, e.g. link, button)",
-    f_scope: "Search scope",
-    f_nth: "Index (0 = first match)",
-
-    condf_var_name: "Variable name",
-    condf_compare_text: "Compare with",
-    condf_contains_text: "Should contain",
-
-    fd_goto_url: "URL",
-    fd_wait_seconds: "Seconds",
-    fd_timeout_seconds: "Timeout (seconds)",
-    fd_login_indicators: "Logged-in text (comma-separated, empty = default)",
-    fd_type_text: "Text to type",
-    fd_press_key_key: "Key (e.g. Enter, Tab, Escape)",
-    fd_press_key_value: "Text / CSS selector / role name (empty = whole page)",
-    fd_by_conditional: "Find by (if the above is filled in)",
-    fd_role_short: "Role (if by=role)",
-    fd_select_option_option: "Option (visible text)",
-    fd_check_checked: "Check (unchecked = uncheck)",
-    fd_save_as_var: "Save as variable",
-    fd_var_name: "Variable name",
-    fd_set_var_text: "Value",
-    fd_increment_by: "Increase by (negative = decrease)",
-    fd_loop_index_var: "Iteration counter variable (1, 2, 3 … each round)",
-    fd_list_append_var: "List variable",
-    fd_list_append_text: "Literal value (used if the selector below is empty)",
-    fd_list_append_value: "Or: Text / CSS selector / role name (reads from the page)",
-    fd_save_var_var: "Variable to save",
-    fd_export_filename: "Filename in data/exports/ (.json or .csv)",
-    fd_save_var_format: "Format (empty = guess from filename)",
-    fd_save_var_mode: "Mode",
-    fd_run_script_path: "Path to Python script",
-    fd_run_script_args: "Arguments (space-separated)",
-    fd_run_script_var: "Save stdout as variable (optional, JSON if possible)",
-    fd_screenshot_name: "Filename (without .png)",
-    fd_list_value: "CSS selector",
-    fd_loop_over: "Loop over variable",
-    fd_loop_item_var: "Current item variable (only used when looping over a plain list)",
-    fd_jump_target: "Jump to label",
-    fd_capture_pdf_filename: "PDF filename in data/downloads/ (supports {variable})",
-    fd_save_attachment_filename: "Filename in data/downloads/ (blank = from the file's URL; supports {variable})",
-    fd_save_attachment_var: "Append saved path to this list variable (optional)",
-  },
-  sv: {
-    ui_title: "RPA-verktyg",
-    ui_page_title: "RPA-verktyg – flödesbyggare",
-    ui_new_flow: "Nytt flöde",
-    ui_delete_flow: "Ta bort flöde",
-    ui_close_mode_title: "Vad ska hända med webbläsaren när flödet är klart?",
-    ui_close_always: "Stäng webbläsaren",
-    ui_close_on_success: "Håll öppen vid fel",
-    ui_close_never: "Håll alltid öppen",
-    ui_save: "Spara",
-    ui_run: "Kör flöde",
-    ui_close_browser_btn: "Stäng webbläsare",
-    ui_show_trace: "Visa trace",
-    ui_wiki_link: "Wiki",
-    ui_wiki_title: "Öppna steg-/fältreferensen i en ny flik",
-    ui_settings_btn: "⚙ Inställningar",
-    ui_export_link: "Exportera som script",
-    ui_export_title: "Ladda ner ett fristående Python-script för det här flödet",
-    ui_settings_title: "Inställningar",
-    ui_settings_hint: "Sparas tillsammans med flödet, så de följer med om du exporterar det som ett fristående script.",
-    ui_settings_headless: "Headless (inget synligt webbläsarfönster)",
-    ui_settings_viewport_width: "Fönsterbredd (viewport)",
-    ui_settings_viewport_height: "Fönsterhöjd (viewport)",
-    ui_settings_locale: "Locale",
-    ui_settings_slow_mo: "Slow motion (ms mellan åtgärder)",
-    ui_settings_notify: "Skrivbordsnotis när körningen är klar",
-    ui_settings_cancel: "Avbryt",
-    ui_settings_save: "Spara",
-    ui_steps_hint: 'Bygg flödet steg för steg. "Lista poster" hittar element via en CSS-selector och sparar dem i en variabel; "Loop" itererar över den variabeln - antingen DOM-resultat (Sökområde "item" söker bara inom aktuell post) eller en vanlig lista (t.ex. från "Lägg till i lista"/"Läs in variabel"), där aktuellt värde finns som {item} i "Hitta via: text/role"-fält.',
-    ui_add_step: "+ Lägg till steg",
-    ui_run_log: "Körlogg",
-    ui_variables: "Variabler",
-    ui_variables_hint: "Klicka på ett variabelnamn för att se dess innehåll (fylls i när flödet är klart).",
-    ui_no_vars_yet: "Inga variabler ännu.",
-    ui_insert_step_here: "Infoga steg här",
-    ui_label_placeholder: "Etikett (valfri)",
-    ui_label_title: "Namnge steget så ett 'Hoppa till etikett'-steg kan hoppa hit",
-    ui_enabled_title: "Aktiverat (avbockad = steget hoppas över vid körning)",
-    ui_on_error_title: "Vad som händer om steget misslyckas / får timeout",
-    ui_on_error_stop: "Vid fel: stanna",
-    ui_on_error_continue: "Vid fel: fortsätt",
-    ui_remove: "Ta bort",
-    ui_add_step_in_loop: "+ Lägg till steg i loop",
-    ui_branch_if: "Om",
-    ui_branch_else_if: "...annars om",
-    ui_remove_branch: "Ta bort gren",
-    ui_add_else_if: "+ Lägg till 'else if'-gren",
-    ui_else_toggle: "...annars (else)",
-    ui_condition_label: "Villkor",
-    ui_status_running: "Kör...",
-    ui_status_done: "Klar",
-    ui_status_error: "Fel",
-    ui_var_list: "lista, {n} post(er)",
-    ui_var_object: "objekt",
-    ui_var_text: "text, {n} tecken",
-    ui_new_flow_prompt: "Namn på nytt flöde:",
-    ui_delete_flow_confirm: 'Ta bort flödet "{name}"?',
-    ui_field_name_placeholder: "Kolumnnamn",
-    ui_add_field: "+ Lägg till fält",
-
-    step_goto: "Gå till URL",
-    step_wait: "Vänta (sekunder)",
-    step_wait_for_load: "Vänta på att sidan laddas",
-    step_go_back: "Gå tillbaka (webbläsarhistorik)",
-    step_wait_for_login: "Vänta på inloggning",
-    step_wait_for_selector: "Vänta på element",
-    step_click: "Klicka",
-    step_type: "Skriv i textfält",
-    step_press_key: "Tryck tangent",
-    step_select_option: "Välj i dropdown",
-    step_check: "Kryssa checkbox/radio",
-    step_extract_text: "Läs text till variabel",
-    step_extract_row: "Extrahera rad (flera kolumner)",
-    step_set_var: "Sätt variabel",
-    step_increment: "Öka variabel",
-    step_clear_var: "Nollställ variabel",
-    step_list_append: "Lägg till i lista",
-    step_save_var: "Spara variabel (JSON/CSV)",
-    step_load_var: "Läs in variabel (JSON/CSV)",
-    step_run_script: "Kör Python-script",
-    step_screenshot: "Ta skärmdump",
-    step_list: "Lista poster",
-    step_if: "Om / annars om / annars",
-    step_jump_to_label: "Hoppa till etikett",
-    step_loop: "Loop (för varje post)",
-    step_download: "Ladda ner",
-    step_save_attachment: "Ladda ner bilaga",
-    step_capture_pdf: "Fånga PDF (skriv ut-knapp)",
-
-    cond_element_exists: "Element finns",
-    cond_element_not_exists: "Element finns inte",
-    cond_var_equals: "Variabel är lika med",
-    cond_var_not_equals: "Variabel är INTE lika med",
-    cond_var_contains: "Variabel innehåller",
-
-    f_by: "Hitta via",
-    f_value: "Text / CSS-selector / rollnamn",
-    f_role: "Roll (om by=role, ex. link, button)",
-    f_scope: "Sökområde",
-    f_nth: "Index (0 = första matchning)",
-
-    condf_var_name: "Variabelnamn",
-    condf_compare_text: "Jämför med",
-    condf_contains_text: "Ska innehålla",
-
-    fd_goto_url: "URL",
-    fd_wait_seconds: "Sekunder",
-    fd_timeout_seconds: "Timeout (sekunder)",
-    fd_login_indicators: "Text som visar inloggad (komma-separerad, tomt = standard)",
-    fd_type_text: "Text att skriva",
-    fd_press_key_key: "Tangent (ex. Enter, Tab, Escape)",
-    fd_press_key_value: "Text / CSS-selector / rollnamn (tomt = hela sidan)",
-    fd_by_conditional: "Hitta via (om ovan ifylld)",
-    fd_role_short: "Roll (om by=role)",
-    fd_select_option_option: "Alternativ (synlig text)",
-    fd_check_checked: "Kryssa i (av = kryssa ur)",
-    fd_save_as_var: "Spara som variabel",
-    fd_var_name: "Variabelnamn",
-    fd_set_var_text: "Värde",
-    fd_increment_by: "Öka med (negativt = minska)",
-    fd_loop_index_var: "Variabel för varvräknare (1, 2, 3 … varje varv)",
-    fd_list_append_var: "Listvariabel",
-    fd_list_append_text: "Literalt värde (används om selector nedan är tom)",
-    fd_list_append_value: "Eller: Text / CSS-selector / rollnamn (hämtar från sidan)",
-    fd_save_var_var: "Variabel att spara",
-    fd_export_filename: "Filnamn i data/exports/ (.json eller .csv)",
-    fd_save_var_format: "Format (tomt = gissa från filnamn)",
-    fd_save_var_mode: "Läge",
-    fd_run_script_path: "Sökväg till Python-script",
-    fd_run_script_args: "Argument (mellanslagsseparerade)",
-    fd_run_script_var: "Spara stdout som variabel (valfritt, JSON om möjligt)",
-    fd_screenshot_name: "Filnamn (utan .png)",
-    fd_list_value: "CSS-selector",
-    fd_loop_over: "Loopa över variabel",
-    fd_loop_item_var: "Variabel för aktuell post (används bara vid loop över en vanlig lista)",
-    fd_jump_target: "Hoppa till etikett",
-    fd_capture_pdf_filename: "PDF-filnamn i data/downloads/ (stödjer {variabel})",
-    fd_save_attachment_filename: "Filnamn i data/downloads/ (tomt = från filens URL; stödjer {variabel})",
-    fd_save_attachment_var: "Lägg sparad sökväg i den här listvariabeln (valfritt)",
-  },
-};
-
-let LANG = localStorage.getItem("rpa_lang") || "en";
-
-function t(key) {
-  return (I18N[LANG] && I18N[LANG][key]) ?? (I18N.en && I18N.en[key]) ?? key;
-}
-
-function tf(key, params) {
-  let s = t(key);
-  for (const [k, v] of Object.entries(params || {})) {
-    s = s.replace(`{${k}}`, v);
-  }
-  return s;
-}
-
-const STEP_TYPES = [
-  "goto", "wait", "wait_for_load", "go_back", "wait_for_login", "wait_for_selector",
-  "click", "type", "press_key", "select_option", "check",
-  "extract_text", "extract_row", "set_var", "increment", "clear_var", "list_append", "save_var", "load_var", "run_script",
-  "screenshot", "list", "if", "jump_to_label", "loop", "download", "save_attachment", "capture_pdf",
-];
-
-const SELECTOR_FIELDS = [
-  { key: "by", labelKey: "f_by", type: "select", options: ["text", "css", "role"], default: "text" },
-  { key: "value", labelKey: "f_value", type: "text" },
-  { key: "role", labelKey: "f_role", type: "text" },
-  { key: "scope", labelKey: "f_scope", type: "select", options: ["page", "item"], default: "page" },
-  { key: "nth", labelKey: "f_nth", type: "number", default: 0 },
-];
-
-const CONDITION_KINDS = ["element_exists", "element_not_exists", "var_equals", "var_not_equals", "var_contains"];
-
-const CONDITION_FIELD_DEFS = {
-  element_exists: SELECTOR_FIELDS,
-  element_not_exists: SELECTOR_FIELDS,
-  var_equals: [
-    { key: "var", labelKey: "condf_var_name", type: "text" },
-    { key: "text", labelKey: "condf_compare_text", type: "text" },
-  ],
-  var_not_equals: [
-    { key: "var", labelKey: "condf_var_name", type: "text" },
-    { key: "text", labelKey: "condf_compare_text", type: "text" },
-  ],
-  var_contains: [
-    { key: "var", labelKey: "condf_var_name", type: "text" },
-    { key: "text", labelKey: "condf_contains_text", type: "text" },
-  ],
-};
-
-const FIELD_DEFS = {
-  goto: [{ key: "url", labelKey: "fd_goto_url", type: "text" }],
-  wait: [{ key: "seconds", labelKey: "fd_wait_seconds", type: "number", default: 1 }],
-  wait_for_load: [],
-  go_back: [],
-  wait_for_login: [
-    { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 180 },
-    { key: "indicators", labelKey: "fd_login_indicators", type: "text" },
-  ],
-  wait_for_selector: [
-    ...SELECTOR_FIELDS,
-    { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 15 },
-  ],
-  click: SELECTOR_FIELDS,
-  type: [...SELECTOR_FIELDS, { key: "text", labelKey: "fd_type_text", type: "text" }],
-  press_key: [
-    { key: "key", labelKey: "fd_press_key_key", type: "text", default: "Enter" },
-    { key: "value", labelKey: "fd_press_key_value", type: "text" },
-    { key: "by", labelKey: "fd_by_conditional", type: "select", options: ["text", "css", "role"], default: "text" },
-    { key: "role", labelKey: "fd_role_short", type: "text" },
-    { key: "scope", labelKey: "f_scope", type: "select", options: ["page", "item"], default: "page" },
-    { key: "nth", labelKey: "f_nth", type: "number", default: 0 },
-  ],
-  select_option: [...SELECTOR_FIELDS, { key: "option", labelKey: "fd_select_option_option", type: "text" }],
-  check: [
-    ...SELECTOR_FIELDS,
-    { key: "checked", labelKey: "fd_check_checked", type: "select", options: ["true", "false"], default: "true" },
-  ],
-  extract_text: [...SELECTOR_FIELDS, { key: "var", labelKey: "fd_save_as_var", type: "text" }],
-  extract_row: [
-    { key: "var", labelKey: "fd_save_as_var", type: "text" },
-    { key: "scope", labelKey: "f_scope", type: "select", options: ["page", "item"], default: "item" },
-  ],
-  set_var: [
-    { key: "var", labelKey: "fd_var_name", type: "text" },
-    { key: "text", labelKey: "fd_set_var_text", type: "text" },
-  ],
-  increment: [
-    { key: "var", labelKey: "fd_var_name", type: "text" },
-    { key: "by", labelKey: "fd_increment_by", type: "number", default: 1 },
-  ],
-  clear_var: [
-    { key: "var", labelKey: "fd_var_name", type: "text" },
-  ],
-  list_append: [
-    { key: "var", labelKey: "fd_list_append_var", type: "text" },
-    { key: "text", labelKey: "fd_list_append_text", type: "text" },
-    { key: "value", labelKey: "fd_list_append_value", type: "text" },
-    { key: "by", labelKey: "fd_by_conditional", type: "select", options: ["text", "css", "role"], default: "text" },
-    { key: "role", labelKey: "fd_role_short", type: "text" },
-    { key: "scope", labelKey: "f_scope", type: "select", options: ["page", "item"], default: "page" },
-    { key: "nth", labelKey: "f_nth", type: "number", default: 0 },
-  ],
-  save_var: [
-    { key: "var", labelKey: "fd_save_var_var", type: "text" },
-    { key: "filename", labelKey: "fd_export_filename", type: "text" },
-    { key: "format", labelKey: "fd_save_var_format", type: "select", options: ["", "json", "csv"], default: "" },
-    { key: "mode", labelKey: "fd_save_var_mode", type: "select", options: ["overwrite", "append"], default: "overwrite" },
-  ],
-  load_var: [
-    { key: "var", labelKey: "fd_save_as_var", type: "text" },
-    { key: "filename", labelKey: "fd_export_filename", type: "text" },
-  ],
-  run_script: [
-    { key: "path", labelKey: "fd_run_script_path", type: "text" },
-    { key: "args", labelKey: "fd_run_script_args", type: "text" },
-    { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 60 },
-    { key: "var", labelKey: "fd_run_script_var", type: "text" },
-  ],
-  screenshot: [{ key: "name", labelKey: "fd_screenshot_name", type: "text", default: "screenshot" }],
-  list: [
-    { key: "value", labelKey: "fd_list_value", type: "text" },
-    { key: "var", labelKey: "fd_save_as_var", type: "text" },
-  ],
-  loop: [
-    { key: "over", labelKey: "fd_loop_over", type: "text" },
-    { key: "item_var", labelKey: "fd_loop_item_var", type: "text", default: "item" },
-    { key: "index_var", labelKey: "fd_loop_index_var", type: "text", default: "index" },
-  ],
-  download: SELECTOR_FIELDS,
-  save_attachment: [
-    ...SELECTOR_FIELDS,
-    { key: "filename", labelKey: "fd_save_attachment_filename", type: "text" },
-    { key: "var", labelKey: "fd_save_attachment_var", type: "text" },
-    { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 15 },
-  ],
-  capture_pdf: [
-    ...SELECTOR_FIELDS,
-    { key: "filename", labelKey: "fd_capture_pdf_filename", type: "text", default: "utskrift.pdf" },
-    { key: "var", labelKey: "fd_save_as_var", type: "text" },
-    { key: "timeout", labelKey: "fd_timeout_seconds", type: "number", default: 15 },
-  ],
-  if: [],
-  jump_to_label: [{ key: "target", labelKey: "fd_jump_target", type: "text" }],
-};
-
-let currentFlowName = null;
-let currentRunId = null;
-let flow = { steps: [] };
-
-function newStep(type) {
-  const step = { type };
-  for (const f of FIELD_DEFS[type] || []) {
-    if (f.default !== undefined) step[f.key] = f.default;
-  }
-  if (type === "loop") step.steps = [];
-  if (type === "if") step.branches = [{ condition: { kind: "element_exists" }, steps: [] }];
-  if (type === "extract_row") step.fields = [{ name: "", by: "css", value: "" }];
-  return step;
-}
-
-function fieldRow(step, def, onStructuralChange) {
-  const wrap = document.createElement("div");
-  wrap.className = "field";
-  const label = document.createElement("label");
-  label.textContent = t(def.labelKey);
-  wrap.appendChild(label);
-
-  let input;
-  if (def.type === "select") {
-    input = document.createElement("select");
-    for (const opt of def.options) {
-      const o = document.createElement("option");
-      o.value = opt;
-      o.textContent = opt;
-      input.appendChild(o);
-    }
-    input.value = step[def.key] ?? def.default ?? def.options[0];
-  } else {
-    input = document.createElement("input");
-    input.type = def.type;
-    input.value = step[def.key] ?? def.default ?? "";
-  }
-  input.addEventListener("input", () => {
-    step[def.key] = def.type === "number" ? Number(input.value) : input.value;
-  });
-  wrap.appendChild(input);
-  return wrap;
-}
-
-function renderCondition(condition, onStructuralChange) {
-  const wrap = document.createElement("div");
-  wrap.className = "fields";
-
-  const kindWrap = document.createElement("div");
-  kindWrap.className = "field";
-  const kindLabel = document.createElement("label");
-  kindLabel.textContent = t("ui_condition_label");
-  kindWrap.appendChild(kindLabel);
-  const kindSelect = document.createElement("select");
-  for (const k of CONDITION_KINDS) {
-    const o = document.createElement("option");
-    o.value = k;
-    o.textContent = t("cond_" + k);
-    kindSelect.appendChild(o);
-  }
-  kindSelect.value = condition.kind || "element_exists";
-  kindSelect.addEventListener("change", () => {
-    for (const key of Object.keys(condition)) delete condition[key];
-    condition.kind = kindSelect.value;
-    onStructuralChange();
-  });
-  kindWrap.appendChild(kindSelect);
-  wrap.appendChild(kindWrap);
-
-  for (const def of CONDITION_FIELD_DEFS[condition.kind || "element_exists"] || []) {
-    wrap.appendChild(fieldRow(condition, def, onStructuralChange));
-  }
-  return wrap;
-}
-
-function renderStepCard(step, arr, idx, isItemScope, onStructuralChange) {
-  const card = document.createElement("div");
-  card.className = "step-card" + (step.type === "loop" ? " loop" : "") + (step.enabled === false ? " step-disabled" : "");
-
-  const head = document.createElement("div");
-  head.className = "step-head";
-
-  const enabledToggle = document.createElement("input");
-  enabledToggle.type = "checkbox";
-  enabledToggle.className = "enabled-toggle";
-  enabledToggle.title = t("ui_enabled_title");
-  enabledToggle.checked = step.enabled !== false;
-  enabledToggle.addEventListener("change", () => {
-    step.enabled = enabledToggle.checked;
-    card.classList.toggle("step-disabled", !enabledToggle.checked);
-  });
-  head.appendChild(enabledToggle);
-
-  const typeSelect = document.createElement("select");
-  typeSelect.className = "type-select";
-  for (const stepType of STEP_TYPES) {
-    const o = document.createElement("option");
-    o.value = stepType;
-    o.textContent = t("step_" + stepType);
-    typeSelect.appendChild(o);
-  }
-  typeSelect.value = step.type;
-  typeSelect.addEventListener("change", () => {
-    const replaced = newStep(typeSelect.value);
-    arr[idx] = replaced;
-    onStructuralChange();
-  });
-  head.appendChild(typeSelect);
-
-  const labelInput = document.createElement("input");
-  labelInput.type = "text";
-  labelInput.placeholder = t("ui_label_placeholder");
-  labelInput.title = t("ui_label_title");
-  labelInput.style.width = "120px";
-  labelInput.value = step.label || "";
-  labelInput.addEventListener("input", () => {
-    step.label = labelInput.value || undefined;
-  });
-  head.appendChild(labelInput);
-
-  const onErrorSelect = document.createElement("select");
-  onErrorSelect.title = t("ui_on_error_title");
-  onErrorSelect.style.width = "130px";
-  for (const [val, key] of [["stop", "ui_on_error_stop"], ["continue", "ui_on_error_continue"]]) {
-    const o = document.createElement("option");
-    o.value = val;
-    o.textContent = t(key);
-    onErrorSelect.appendChild(o);
-  }
-  onErrorSelect.value = step.on_error === "continue" ? "continue" : "stop";
-  onErrorSelect.addEventListener("change", () => {
-    step.on_error = onErrorSelect.value === "continue" ? "continue" : undefined;
-  });
-  head.appendChild(onErrorSelect);
-
-  head.appendChild(document.createElement("span")).className = "spacer";
-
-  const upBtn = document.createElement("button");
-  upBtn.className = "secondary";
-  upBtn.textContent = "↑";
-  upBtn.disabled = idx === 0;
-  upBtn.addEventListener("click", () => {
-    [arr[idx - 1], arr[idx]] = [arr[idx], arr[idx - 1]];
-    onStructuralChange();
-  });
-
-  const downBtn = document.createElement("button");
-  downBtn.className = "secondary";
-  downBtn.textContent = "↓";
-  downBtn.disabled = idx === arr.length - 1;
-  downBtn.addEventListener("click", () => {
-    [arr[idx + 1], arr[idx]] = [arr[idx], arr[idx + 1]];
-    onStructuralChange();
-  });
-
-  const delBtn = document.createElement("button");
-  delBtn.className = "danger";
-  delBtn.textContent = t("ui_remove");
-  delBtn.addEventListener("click", () => {
-    arr.splice(idx, 1);
-    onStructuralChange();
-  });
-
-  head.appendChild(upBtn);
-  head.appendChild(downBtn);
-  head.appendChild(delBtn);
-  card.appendChild(head);
-
-  const fields = document.createElement("div");
-  fields.className = "fields";
-  let defs = FIELD_DEFS[step.type] || [];
-  if (!isItemScope) {
-    defs = defs.filter((d) => d.key !== "scope" || step.type === "loop" || step.type === "extract_row");
-  }
-  for (const def of defs) {
-    fields.appendChild(fieldRow(step, def, onStructuralChange));
-  }
-  card.appendChild(fields);
-
-  if (step.type === "loop") {
-    const nested = document.createElement("div");
-    nested.className = "nested";
-    const nestedSteps = document.createElement("div");
-    nestedSteps.className = "steps";
-    nested.appendChild(nestedSteps);
-    renderStepsInto(nestedSteps, step.steps, true, onStructuralChange);
-
-    const addBtn = document.createElement("button");
-    addBtn.className = "secondary add-step-row";
-    addBtn.textContent = t("ui_add_step_in_loop");
-    addBtn.addEventListener("click", () => {
-      step.steps.push(newStep("click"));
-      onStructuralChange();
-    });
-    nested.appendChild(addBtn);
-    card.appendChild(nested);
-  }
-
-  if (step.type === "extract_row") {
-    if (!Array.isArray(step.fields)) step.fields = [];
-
-    const rowBlock = document.createElement("div");
-    rowBlock.className = "nested";
-
-    step.fields.forEach((field, fIdx) => {
-      const fieldWrap = document.createElement("div");
-      fieldWrap.className = "branch";
-
-      const fieldHead = document.createElement("div");
-      fieldHead.className = "step-head";
-      const nameInput = document.createElement("input");
-      nameInput.type = "text";
-      nameInput.placeholder = t("ui_field_name_placeholder");
-      nameInput.style.width = "160px";
-      nameInput.value = field.name || "";
-      nameInput.addEventListener("input", () => { field.name = nameInput.value; });
-      fieldHead.appendChild(nameInput);
-      fieldHead.appendChild(document.createElement("span")).className = "spacer";
-      const removeFieldBtn = document.createElement("button");
-      removeFieldBtn.className = "danger";
-      removeFieldBtn.textContent = t("ui_remove");
-      removeFieldBtn.addEventListener("click", () => {
-        step.fields.splice(fIdx, 1);
-        onStructuralChange();
-      });
-      fieldHead.appendChild(removeFieldBtn);
-      fieldWrap.appendChild(fieldHead);
-
-      const fieldFields = document.createElement("div");
-      fieldFields.className = "fields";
-      for (const def of [
-        { key: "by", labelKey: "f_by", type: "select", options: ["text", "css", "role"], default: "css" },
-        { key: "value", labelKey: "f_value", type: "text" },
-        { key: "role", labelKey: "f_role", type: "text" },
-      ]) {
-        fieldFields.appendChild(fieldRow(field, def, onStructuralChange));
-      }
-      fieldWrap.appendChild(fieldFields);
-
-      rowBlock.appendChild(fieldWrap);
-    });
-
-    const addFieldBtn = document.createElement("button");
-    addFieldBtn.className = "secondary add-step-row";
-    addFieldBtn.textContent = t("ui_add_field");
-    addFieldBtn.addEventListener("click", () => {
-      step.fields.push({ name: "", by: "css", value: "" });
-      onStructuralChange();
-    });
-    rowBlock.appendChild(addFieldBtn);
-
-    card.appendChild(rowBlock);
-  }
-
-  if (step.type === "if") {
-    if (!Array.isArray(step.branches) || step.branches.length === 0) {
-      step.branches = [{ condition: { kind: "element_exists" }, steps: [] }];
-    }
-
-    const ifBlock = document.createElement("div");
-    ifBlock.className = "nested";
-
-    step.branches.forEach((branch, bIdx) => {
-      if (!branch.condition) branch.condition = { kind: "element_exists" };
-      if (!Array.isArray(branch.steps)) branch.steps = [];
-
-      const branchWrap = document.createElement("div");
-      branchWrap.className = "branch";
-
-      const branchHead = document.createElement("div");
-      branchHead.className = "step-head";
-      const branchTitle = document.createElement("strong");
-      branchTitle.textContent = bIdx === 0 ? t("ui_branch_if") : t("ui_branch_else_if");
-      branchHead.appendChild(branchTitle);
-      branchHead.appendChild(document.createElement("span")).className = "spacer";
-      if (bIdx > 0) {
-        const removeBranchBtn = document.createElement("button");
-        removeBranchBtn.className = "danger";
-        removeBranchBtn.textContent = t("ui_remove_branch");
-        removeBranchBtn.addEventListener("click", () => {
-          step.branches.splice(bIdx, 1);
-          onStructuralChange();
-        });
-        branchHead.appendChild(removeBranchBtn);
-      }
-      branchWrap.appendChild(branchHead);
-
-      branchWrap.appendChild(renderCondition(branch.condition, onStructuralChange));
-
-      const branchSteps = document.createElement("div");
-      branchSteps.className = "steps";
-      renderStepsInto(branchSteps, branch.steps, isItemScope, onStructuralChange);
-      branchWrap.appendChild(branchSteps);
-
-      ifBlock.appendChild(branchWrap);
-    });
-
-    const addBranchBtn = document.createElement("button");
-    addBranchBtn.className = "secondary add-step-row";
-    addBranchBtn.textContent = t("ui_add_else_if");
-    addBranchBtn.addEventListener("click", () => {
-      step.branches.push({ condition: { kind: "element_exists" }, steps: [] });
-      onStructuralChange();
-    });
-    ifBlock.appendChild(addBranchBtn);
-
-    const elseWrap = document.createElement("div");
-    elseWrap.className = "branch else-branch";
-    const elseHead = document.createElement("div");
-    elseHead.className = "step-head";
-    const elseToggleLabel = document.createElement("label");
-    elseToggleLabel.style.fontSize = "13px";
-    elseToggleLabel.style.display = "flex";
-    elseToggleLabel.style.alignItems = "center";
-    elseToggleLabel.style.gap = "6px";
-    const elseCheckbox = document.createElement("input");
-    elseCheckbox.type = "checkbox";
-    elseCheckbox.checked = Array.isArray(step.else);
-    elseCheckbox.addEventListener("change", () => {
-      step.else = elseCheckbox.checked ? [] : undefined;
-      onStructuralChange();
-    });
-    elseToggleLabel.appendChild(elseCheckbox);
-    elseToggleLabel.appendChild(document.createTextNode(t("ui_else_toggle")));
-    elseHead.appendChild(elseToggleLabel);
-    elseWrap.appendChild(elseHead);
-
-    if (Array.isArray(step.else)) {
-      const elseSteps = document.createElement("div");
-      elseSteps.className = "steps";
-      renderStepsInto(elseSteps, step.else, isItemScope, onStructuralChange);
-      elseWrap.appendChild(elseSteps);
-    }
-    ifBlock.appendChild(elseWrap);
-
-    card.appendChild(ifBlock);
-  }
-
-  return card;
-}
-
-function insertDivider(arr, index, onStructuralChange) {
-  const div = document.createElement("div");
-  div.className = "insert-divider";
-  div.title = t("ui_insert_step_here");
-  const line = document.createElement("div");
-  line.className = "line";
-  div.appendChild(line);
-  const plus = document.createElement("div");
-  plus.className = "plus";
-  plus.textContent = "+";
-  div.appendChild(plus);
-  div.addEventListener("click", () => {
-    arr.splice(index, 0, newStep("click"));
-    onStructuralChange();
-  });
-  return div;
-}
-
-function renderStepsInto(container, arr, isItemScope, onStructuralChange) {
-  container.innerHTML = "";
-  container.appendChild(insertDivider(arr, 0, onStructuralChange));
-  arr.forEach((step, idx) => {
-    container.appendChild(renderStepCard(step, arr, idx, isItemScope, onStructuralChange));
-    container.appendChild(insertDivider(arr, idx + 1, onStructuralChange));
-  });
-}
-
-function render() {
-  const root = document.getElementById("rootSteps");
-  renderStepsInto(root, flow.steps, false, render);
-}
-
-document.getElementById("addRootStepBtn").addEventListener("click", () => {
-  flow.steps.push(newStep("goto"));
-  render();
-});
-
-async function loadFlowList() {
-  const res = await fetch("/api/flows");
-  const names = await res.json();
-  const select = document.getElementById("flowSelect");
-  select.innerHTML = "";
-  for (const name of names) {
-    const o = document.createElement("option");
-    o.value = name;
-    o.textContent = name;
-    select.appendChild(o);
-  }
-  if (names.length && !names.includes(currentFlowName)) {
-    currentFlowName = names[0];
-  }
-  if (currentFlowName) select.value = currentFlowName;
-  return names;
-}
-
-function updateExportLink() {
-  const link = document.getElementById("exportLink");
-  if (currentFlowName) {
-    link.href = `/api/flows/${encodeURIComponent(currentFlowName)}/export`;
-    link.removeAttribute("aria-disabled");
-  } else {
-    link.href = "#";
-    link.setAttribute("aria-disabled", "true");
-  }
-}
-
-async function loadFlow(name) {
-  const res = await fetch(`/api/flows/${encodeURIComponent(name)}`);
-  flow = await res.json();
-  if (!flow.steps) flow.steps = [];
-  if (!flow.settings) flow.settings = {};
-  currentFlowName = name;
-  updateExportLink();
-  render();
-}
-
-async function saveFlow() {
-  if (!currentFlowName) return;
-  await fetch(`/api/flows/${encodeURIComponent(currentFlowName)}`, {
-    method: "PUT",
-    headers: { "Content-Type": "application/json" },
-    body: JSON.stringify({ steps: flow.steps, settings: flow.settings || {} }),
-  });
-  await loadFlowList();
-}
-
-document.getElementById("flowSelect").addEventListener("change", (e) => {
-  loadFlow(e.target.value);
-});
-
-document.getElementById("newFlowBtn").addEventListener("click", async () => {
-  const name = prompt(t("ui_new_flow_prompt"));
-  if (!name) return;
-  currentFlowName = name;
-  flow = { steps: [], settings: {} };
-  await saveFlow();
-  await loadFlowList();
-  updateExportLink();
-  render();
-});
-
-document.getElementById("deleteFlowBtn").addEventListener("click", async () => {
-  if (!currentFlowName) return;
-  if (!confirm(tf("ui_delete_flow_confirm", { name: currentFlowName }))) return;
-  await fetch(`/api/flows/${encodeURIComponent(currentFlowName)}`, { method: "DELETE" });
-  currentFlowName = null;
-  const names = await loadFlowList();
-  if (names.length) await loadFlow(names[0]);
-  else { flow = { steps: [], settings: {} }; updateExportLink(); render(); }
-});
-
-document.getElementById("saveBtn").addEventListener("click", saveFlow);
-
-const settingsDialog = document.getElementById("settingsDialog");
-
-function openSettingsDialog() {
-  const s = flow.settings || {};
-  document.getElementById("settingsHeadless").checked = !!s.headless;
-  document.getElementById("settingsViewportWidth").value = s.viewport_width ?? 1280;
-  document.getElementById("settingsViewportHeight").value = s.viewport_height ?? 900;
-  document.getElementById("settingsLocale").value = s.locale ?? "sv-SE";
-  document.getElementById("settingsSlowMo").value = s.slow_mo ?? 0;
-  document.getElementById("settingsNotify").checked = !!s.notify_on_complete;
-  settingsDialog.showModal();
-}
-
-document.getElementById("settingsBtn").addEventListener("click", openSettingsDialog);
-document.getElementById("settingsCancelBtn").addEventListener("click", () => settingsDialog.close());
-
-document.getElementById("settingsSaveBtn").addEventListener("click", async (e) => {
-  e.preventDefault();
-  flow.settings = {
-    headless: document.getElementById("settingsHeadless").checked,
-    viewport_width: Number(document.getElementById("settingsViewportWidth").value) || 1280,
-    viewport_height: Number(document.getElementById("settingsViewportHeight").value) || 900,
-    locale: document.getElementById("settingsLocale").value || "sv-SE",
-    slow_mo: Number(document.getElementById("settingsSlowMo").value) || 0,
-    notify_on_complete: document.getElementById("settingsNotify").checked,
-  };
-  await saveFlow();
-  settingsDialog.close();
-});
-
-const logEl = document.getElementById("log");
-const statusBadge = document.getElementById("statusBadge");
-const runBtn = document.getElementById("runBtn");
-const closeModeSelect = document.getElementById("closeModeSelect");
-const closeBrowserBtn = document.getElementById("closeBrowserBtn");
-const showTraceBtn = document.getElementById("showTraceBtn");
-const varsPanel = document.getElementById("varsPanel");
-const langSelect = document.getElementById("langSelect");
-const expandedVars = new Set();
-let lastStatus = null;
-
-function describeVarType(value) {
-  if (Array.isArray(value)) return tf("ui_var_list", { n: value.length });
-  if (value && typeof value === "object") return t("ui_var_object");
-  if (typeof value === "string") return tf("ui_var_text", { n: value.length });
-  return typeof value;
-}
-
-function renderVars(vars) {
-  varsPanel.innerHTML = "";
-  const names = Object.keys(vars || {});
-  if (!names.length) {
-    const p = document.createElement("p");
-    p.className = "hint";
-    p.textContent = t("ui_no_vars_yet");
-    varsPanel.appendChild(p);
-    return;
-  }
-  for (const name of names) {
-    const row = document.createElement("div");
-    row.className = "var-row";
-
-    const header = document.createElement("div");
-    header.className = "var-header";
-    header.innerHTML = `${name} <span class="var-type">(${describeVarType(vars[name])})</span>`;
-
-    const valueEl = document.createElement("pre");
-    valueEl.className = "var-value";
-    const value = vars[name];
-    valueEl.textContent = typeof value === "string" ? value : JSON.stringify(value, null, 2);
-    valueEl.style.display = expandedVars.has(name) ? "block" : "none";
-
-    header.addEventListener("click", () => {
-      const show = valueEl.style.display === "none";
-      valueEl.style.display = show ? "block" : "none";
-      if (show) expandedVars.add(name);
-      else expandedVars.delete(name);
-    });
-
-    row.appendChild(header);
-    row.appendChild(valueEl);
-    varsPanel.appendChild(row);
-  }
-}
-
-const savedCloseMode = localStorage.getItem("rpa_close_mode");
-if (savedCloseMode) closeModeSelect.value = savedCloseMode;
-closeModeSelect.addEventListener("change", () => {
-  localStorage.setItem("rpa_close_mode", closeModeSelect.value);
-});
-
-function setStatus(status) {
-  lastStatus = status;
-  statusBadge.className = "status-badge status-" + status;
-  statusBadge.textContent = { running: t("ui_status_running"), done: t("ui_status_done"), error: t("ui_status_error") }[status] || "";
-}
-
-async function pollRun(runId) {
-  const res = await fetch(`/api/runs/${runId}`);
-  const data = await res.json();
-  logEl.textContent = data.logs.join("\n");
-  logEl.scrollTop = logEl.scrollHeight;
-
-  if (data.status !== "running") {
-    setStatus(data.status);
-    runBtn.disabled = false;
-  }
-
-  closeBrowserBtn.style.display = data.browser_open ? "inline-block" : "none";
-  closeBrowserBtn.disabled = false;
-
-  showTraceBtn.style.display = data.trace_ready ? "inline-block" : "none";
-
-  if (data.status !== "running") {
-    renderVars(data.vars);
-  }
-
-  if (data.status === "running" || data.browser_open) {
-    setTimeout(() => pollRun(runId), 1000);
-  }
-}
-
-document.getElementById("runBtn").addEventListener("click", async () => {
-  await saveFlow();
-  logEl.textContent = "";
-  runBtn.disabled = true;
-  closeBrowserBtn.style.display = "none";
-  showTraceBtn.style.display = "none";
-  expandedVars.clear();
-  renderVars({});
-  setStatus("running");
-  const res = await fetch(`/api/flows/${encodeURIComponent(currentFlowName)}/run`, {
-    method: "POST",
-    headers: { "Content-Type": "application/json" },
-    body: JSON.stringify({ close_mode: closeModeSelect.value }),
-  });
-  const data = await res.json();
-  currentRunId = data.run_id;
-  pollRun(currentRunId);
-});
-
-closeBrowserBtn.addEventListener("click", async () => {
-  closeBrowserBtn.disabled = true;
-  await fetch(`/api/runs/${encodeURIComponent(currentRunId)}/close-browser`, { method: "POST" });
-});
-
-showTraceBtn.addEventListener("click", async () => {
-  await fetch(`/api/runs/${encodeURIComponent(currentRunId)}/show-trace`, { method: "POST" });
-});
-
-function applyStaticTranslations() {
-  document.title = t("ui_page_title");
-  document.documentElement.lang = LANG;
-  document.getElementById("appTitle").textContent = t("ui_title");
-  document.getElementById("newFlowBtn").textContent = t("ui_new_flow");
-  document.getElementById("deleteFlowBtn").textContent = t("ui_delete_flow");
-  closeModeSelect.title = t("ui_close_mode_title");
-  closeModeSelect.options[0].textContent = t("ui_close_always");
-  closeModeSelect.options[1].textContent = t("ui_close_on_success");
-  closeModeSelect.options[2].textContent = t("ui_close_never");
-  document.getElementById("saveBtn").textContent = t("ui_save");
-  runBtn.textContent = t("ui_run");
-  closeBrowserBtn.textContent = t("ui_close_browser_btn");
-  showTraceBtn.textContent = t("ui_show_trace");
-  document.getElementById("wikiLink").textContent = t("ui_wiki_link");
-  document.getElementById("wikiLink").title = t("ui_wiki_title");
-  document.getElementById("settingsBtn").textContent = t("ui_settings_btn");
-  document.getElementById("exportLink").textContent = t("ui_export_link");
-  document.getElementById("exportLink").title = t("ui_export_title");
-  document.getElementById("settingsTitle").textContent = t("ui_settings_title");
-  document.getElementById("settingsHint").textContent = t("ui_settings_hint");
-  document.getElementById("settingsHeadlessLabel").textContent = t("ui_settings_headless");
-  document.getElementById("settingsViewportWidthLabel").textContent = t("ui_settings_viewport_width");
-  document.getElementById("settingsViewportHeightLabel").textContent = t("ui_settings_viewport_height");
-  document.getElementById("settingsLocaleLabel").textContent = t("ui_settings_locale");
-  document.getElementById("settingsSlowMoLabel").textContent = t("ui_settings_slow_mo");
-  document.getElementById("settingsNotifyLabel").textContent = t("ui_settings_notify");
-  document.getElementById("settingsCancelBtn").textContent = t("ui_settings_cancel");
-  document.getElementById("settingsSaveBtn").textContent = t("ui_settings_save");
-  document.getElementById("stepsHint").textContent = t("ui_steps_hint");
-  document.getElementById("addRootStepBtn").textContent = t("ui_add_step");
-  document.getElementById("logTitle").textContent = t("ui_run_log");
-  document.getElementById("varsTitle").textContent = t("ui_variables");
-  document.getElementById("varsHint").textContent = t("ui_variables_hint");
-  if (lastStatus) setStatus(lastStatus);
-}
-
-langSelect.value = LANG;
-langSelect.addEventListener("change", () => {
-  LANG = langSelect.value;
-  localStorage.setItem("rpa_lang", LANG);
-  applyStaticTranslations();
-  render();
-  renderVars(lastRenderedVars);
-});
-
-let lastRenderedVars = {};
-const _renderVarsOrig = renderVars;
-renderVars = function (vars) {
-  lastRenderedVars = vars || {};
-  _renderVarsOrig(vars);
-};
-
-applyStaticTranslations();
-
-(async function init() {
-  const names = await loadFlowList();
-  if (names.length) {
-    await loadFlow(names[0]);
-  } else {
-    currentFlowName = "standard";
-    flow = { steps: [
-      newStep("goto"),
-      newStep("wait_for_login"),
-    ], settings: {} };
-    flow.steps[0].url = "https://www.1177.se/";
-    await saveFlow();
-    await loadFlowList();
-    updateExportLink();
-    render();
-  }
-})();
-</script>
-</body>
-</html>