diff --git a/admin/index.html b/admin/index.html
new file mode 100644
index 0000000..b86dfbf
--- /dev/null
+++ b/admin/index.html
@@ -0,0 +1,640 @@
+
+
+
+
+
+ Navi — Admin
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/debug/eval/index.html b/debug/eval/index.html
index 24aabb1..51e803b 100644
--- a/debug/eval/index.html
+++ b/debug/eval/index.html
@@ -248,6 +248,7 @@
diff --git a/debug/index.html b/debug/index.html
index c56543b..569d37a 100644
--- a/debug/index.html
+++ b/debug/index.html
@@ -86,6 +86,12 @@
align-items: center;
gap: 8px;
}
+ .header-right a {
+ color: var(--text2);
+ text-decoration: none;
+ font-size: 12px;
+ }
+ .header-right a:hover { color: var(--text); }
input, button, select {
font-family: inherit;
@@ -425,11 +431,15 @@
+ Admin
+ Eval
diff --git a/docs/api.md b/docs/api.md
index 0aeaa1a..69e3d72 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -685,22 +685,36 @@
#### `GET /admin/sessions`
-All sessions across all users.
+All sessions across all users. Supports pagination, search, and sorting.
+
+**Query params**
+| Param | Default | Description |
+|---|---|---|
+| `limit` | `50` | Page size |
+| `offset` | `0` | Items to skip |
+| `search` | — | Filter by session_id, name, user_id or profile_id (case-insensitive) |
+| `sort_by` | `last_active` | `last_active`, `created_at`, `name`, `profile_id`, `user_id`, `pinned` |
+| `sort_order` | `desc` | `asc` or `desc` |
**Response `200`**
```json
-[
- {
- "session_id": "...",
- "profile_id": "secretary",
- "user_id": "user-uuid",
- "name": "Research task",
- "message_count": 12,
- "pinned": false,
- "created_at": "2026-05-04T10:00:00+00:00",
- "last_active": "2026-05-04T10:30:00+00:00"
- }
-]
+{
+ "total": 128,
+ "limit": 50,
+ "offset": 0,
+ "items": [
+ {
+ "session_id": "...",
+ "profile_id": "secretary",
+ "user_id": "user-uuid",
+ "name": "Research task",
+ "message_count": 12,
+ "pinned": false,
+ "created_at": "2026-05-04T10:00:00+00:00",
+ "last_active": "2026-05-04T10:30:00+00:00"
+ }
+ ]
+}
```
---
@@ -728,11 +742,36 @@
#### `GET /admin/memory`
-All memory facts (global view). Requires `navi.memory.read_all`.
+All memory facts (global view). Requires `navi.memory.read_all`. Supports pagination, search, and sorting.
+
+**Query params**
+| Param | Default | Description |
+|---|---|---|
+| `limit` | `50` | Page size |
+| `offset` | `0` | Items to skip |
+| `search` | — | Filter by key, value or category (case-insensitive) |
+| `sort_by` | `updated_at` | `updated_at`, `category`, `key`, `confidence`, `source` |
+| `sort_order` | `desc` | `asc` or `desc` |
+| `user_id` | — | If set, return only facts for this user instead of global view |
**Response `200`**
```json
-{ "facts": [...], "count": 42 }
+{
+ "total": 128,
+ "limit": 50,
+ "offset": 0,
+ "items": [
+ {
+ "id": "...",
+ "category": "profile",
+ "key": "name",
+ "value": "Eugene",
+ "source": "conversation",
+ "confidence": 90,
+ "updated_at": "2026-05-04T10:00:00+00:00"
+ }
+ ]
+}
```
---
diff --git a/docs/memory.md b/docs/memory.md
index b16f9db..265e581 100644
--- a/docs/memory.md
+++ b/docs/memory.md
@@ -92,7 +92,7 @@
| `upsert_fact(..., user_id=None)` | Insert or update a fact scoped to user |
| `search_facts(query, limit=15, user_id=None)` | **Vector search first** (cosine distance, cutoff 0.3), then ILIKE fallback. `user_id=None` requires admin permission for global search. |
| `delete_fact(key, category=None, user_id=None)` | Delete by key, optionally filtered by category and user |
-| `get_all_facts(limit=None, user_id=None)` | All facts ordered by `(category, updated_at DESC)` |
+| `get_all_facts(limit=None, offset=0, search=None, sort_by="category", sort_order="desc", user_id=None, all_users=False)` | All facts ordered by `sort_by`. Pass `all_users=True` for admin global view. |
| `get_summary(user_id=None)` | Current narrative summary text for user |
| `set_summary(content, user_id=None)` | Replace the summary for user |
| `mark_session_extracted(session_id)` | Record extraction timestamp |
diff --git a/docs/sessions.md b/docs/sessions.md
index bfd460e..7ddfbc4 100644
--- a/docs/sessions.md
+++ b/docs/sessions.md
@@ -55,6 +55,8 @@
- `get(session_id)` → `Session | None`
- `save(session)` — serializes with `model_dump(mode='json')` (required for datetime serialization)
- `list_all(user_id=None, is_admin=False)` → if `is_admin`: all sessions; else: sessions for `user_id` or legacy (`user_id IS NULL`) sessions
+- `count_all(user_id=None, is_admin=False, search=None)` → total matching sessions
+- `search_list(limit, offset, user_id=None, is_admin=False, search=None, sort_by="last_active", sort_order="desc")` → paginated, filtered, sorted sessions
- `delete(session_id)` → `bool`
- `set_pinned(session_id, pinned)` → `bool`
- `set_name(session_id, name)` → `bool`
diff --git a/navi/api/routes/admin.py b/navi/api/routes/admin.py
index beaa760..d44aee2 100644
--- a/navi/api/routes/admin.py
+++ b/navi/api/routes/admin.py
@@ -22,22 +22,82 @@
async def admin_list_sessions(
store: Annotated[SessionStore, Depends(get_session_store)],
user: Annotated[User, Depends(require_admin)],
+ limit: int = 50,
+ offset: int = 0,
+ search: str | None = None,
+ sort_by: str = "last_active",
+ sort_order: str = "desc",
):
- """Return all sessions across all users."""
- sessions = await store.list_all(user_id=user.id, is_admin=True)
- return [
- {
- "session_id": s.id,
- "profile_id": s.profile_id,
- "user_id": s.user_id,
- "name": s.name,
- "message_count": len(s.messages),
- "pinned": s.pinned,
- "created_at": s.created_at.isoformat(),
- "last_active": s.last_active.isoformat(),
- }
- for s in sessions
- ]
+ """Return all sessions across all users with pagination, search and sorting."""
+ sessions = await store.search_list(
+ limit=limit,
+ offset=offset,
+ user_id=user.id,
+ is_admin=True,
+ search=search or None,
+ sort_by=sort_by,
+ sort_order=sort_order,
+ )
+ total = await store.count_all(
+ user_id=user.id, is_admin=True, search=search or None
+ )
+ return {
+ "total": total,
+ "limit": limit,
+ "offset": offset,
+ "items": [
+ {
+ "session_id": s.id,
+ "profile_id": s.profile_id,
+ "user_id": s.user_id,
+ "name": s.name,
+ "message_count": len(s.messages),
+ "pinned": s.pinned,
+ "created_at": s.created_at.isoformat(),
+ "last_active": s.last_active.isoformat(),
+ }
+ for s in sessions
+ ],
+ }
+
+
+@router.get("/sessions/{session_id}")
+async def admin_get_session(
+ session_id: str,
+ store: Annotated[SessionStore, Depends(get_session_store)],
+ user: Annotated[User, Depends(require_admin)],
+) -> dict:
+ """Return full session details including messages."""
+ session = await store.get(session_id)
+ if session is None:
+ raise HTTPException(status_code=404, detail="Session not found")
+ return {
+ "session_id": session.id,
+ "profile_id": session.profile_id,
+ "user_id": session.user_id,
+ "name": session.name,
+ "messages": [m.model_dump(mode="json", exclude_none=True) for m in session.messages],
+ "context_token_count": session.context_token_count,
+ "max_context_tokens": settings.ollama_num_ctx,
+ "pinned": session.pinned,
+ "created_at": session.created_at.isoformat(),
+ "last_active": session.last_active.isoformat(),
+ }
+
+
+@router.delete("/sessions/{session_id}", status_code=204)
+async def admin_delete_session(
+ session_id: str,
+ store: Annotated[SessionStore, Depends(get_session_store)],
+ user: Annotated[User, Depends(require_admin)],
+) -> None:
+ """Delete any session (bypass ownership)."""
+ from navi.session_files import delete_session_dir
+
+ deleted = await store.delete(session_id)
+ if not deleted:
+ raise HTTPException(status_code=404, detail="Session not found")
+ await delete_session_dir(session_id)
@router.get("/users")
@@ -65,17 +125,85 @@
]
+@router.get("/users/{user_id}")
+async def admin_get_user(
+ user_id: str,
+ store: Annotated[SessionStore, Depends(get_session_store)],
+ user: Annotated[User, Depends(require_admin)],
+) -> dict:
+ """Return single user details."""
+ pool = await store._get_pool()
+ async with pool.acquire() as conn:
+ row = await conn.fetchrow(
+ "SELECT id, email, display_name, role, permissions, created_at, updated_at FROM navi_users WHERE id = $1",
+ user_id,
+ )
+ if row is None:
+ raise HTTPException(status_code=404, detail="User not found")
+ return {
+ "id": row["id"],
+ "email": row["email"],
+ "display_name": row["display_name"],
+ "role": row["role"],
+ "permissions": row["permissions"],
+ "created_at": row["created_at"].isoformat(),
+ "updated_at": row["updated_at"].isoformat(),
+ }
+
+
+@router.get("/users/{user_id}/sessions")
+async def admin_get_user_sessions(
+ user_id: str,
+ store: Annotated[SessionStore, Depends(get_session_store)],
+ user: Annotated[User, Depends(require_admin)],
+):
+ """Return sessions owned by a specific user."""
+ sessions = await store.list_all(user_id=user.id, is_admin=True)
+ user_sessions = [s for s in sessions if s.user_id == user_id]
+ return [
+ {
+ "session_id": s.id,
+ "profile_id": s.profile_id,
+ "name": s.name,
+ "message_count": len(s.messages),
+ "pinned": s.pinned,
+ "created_at": s.created_at.isoformat(),
+ "last_active": s.last_active.isoformat(),
+ }
+ for s in user_sessions
+ ]
+
+
@router.get("/memory")
async def admin_list_memory(
store: Annotated[SessionStore, Depends(get_session_store)],
user: Annotated[User, Depends(require_permission("navi.memory.read_all"))],
+ limit: int = 50,
+ offset: int = 0,
+ search: str | None = None,
+ sort_by: str = "updated_at",
+ sort_order: str = "desc",
+ user_id: str | None = None,
):
- """Return all memory facts (global view)."""
+ """Return memory facts with pagination, search and sorting."""
from navi.api.deps import get_memory_store
memory = get_memory_store()
- facts = await memory.get_all_facts(limit=500)
- return {"facts": facts, "count": len(facts)}
+ facts = await memory.get_all_facts(
+ limit=limit,
+ offset=offset,
+ search=search or None,
+ sort_by=sort_by,
+ sort_order=sort_order,
+ user_id=user_id,
+ all_users=user_id is None,
+ )
+ total = await memory.fact_count(
+ user_id=user_id,
+ all_users=user_id is None,
+ search=search or None,
+ )
+ return {"total": total, "limit": limit, "offset": offset, "items": facts}
@router.patch("/users/{user_id}/role")
@@ -124,18 +252,32 @@
async def admin_update_profile_availability(
profile_id: str,
body: dict,
+ store: Annotated[SessionStore, Depends(get_session_store)],
user: Annotated[User, Depends(require_permission("navi.profiles.manage"))],
):
- """Toggle admin-only visibility for a profile."""
+ """Toggle admin-only visibility for a profile and persist to DB."""
is_admin_only = body.get("is_admin_only")
if not isinstance(is_admin_only, bool):
raise HTTPException(status_code=400, detail="is_admin_only must be a boolean")
- # Profile configuration is loaded from navi/profiles/ config files.
- # For now this is a no-op endpoint; actual persistence requires editing profile JSON.
+
+ from navi.api.deps import get_profile_registry
+ from navi.profiles._overrides import save_override
+
+ pool = await store._get_pool()
+ await save_override(pool, profile_id, is_admin_only)
+
+ # Mutate the in-memory profile so the change is effective immediately
+ # without a server restart.
+ try:
+ profile = get_profile_registry().get(profile_id)
+ profile.is_admin_only = is_admin_only
+ except Exception:
+ pass # profile may not be loaded; DB value is the source of truth anyway
+
log.info(
"admin.profile_availability",
profile_id=profile_id,
is_admin_only=is_admin_only,
admin_id=user.id,
)
- return {"ok": True, "note": "Profile availability is managed via profile config files"}
+ return {"ok": True}
diff --git a/navi/api/routes/auth.py b/navi/api/routes/auth.py
index e1b45f8..fe28a5e 100644
--- a/navi/api/routes/auth.py
+++ b/navi/api/routes/auth.py
@@ -95,19 +95,40 @@
store = get_session_store()
pool = await store._get_pool()
import json
+ profile = auth_user.profile
async with pool.acquire() as conn:
await conn.execute(
- """INSERT INTO navi_users (id, email, display_name, role, permissions, created_at, updated_at)
- VALUES ($1, $2, $3, $4, $5, $6, $6)
+ """INSERT INTO navi_users (
+ id, email, display_name, username, first_name, last_name,
+ phone, birth_date, country, city, locale,
+ role, permissions, created_at, updated_at
+ )
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $14)
ON CONFLICT (id) DO UPDATE
SET email = EXCLUDED.email,
display_name = EXCLUDED.display_name,
+ username = EXCLUDED.username,
+ first_name = EXCLUDED.first_name,
+ last_name = EXCLUDED.last_name,
+ phone = EXCLUDED.phone,
+ birth_date = EXCLUDED.birth_date,
+ country = EXCLUDED.country,
+ city = EXCLUDED.city,
+ locale = EXCLUDED.locale,
role = EXCLUDED.role,
permissions = EXCLUDED.permissions,
updated_at = EXCLUDED.updated_at""",
auth_user.user_id,
auth_user.email,
- auth_user.profile.get("display_name") or auth_user.email,
+ profile.get("display_name") or auth_user.email,
+ profile.get("username"),
+ profile.get("first_name"),
+ profile.get("last_name"),
+ profile.get("phone"),
+ profile.get("birth_date"),
+ profile.get("country"),
+ profile.get("city"),
+ profile.get("locale"),
role,
json.dumps(permissions),
datetime.now(timezone.utc),
@@ -197,6 +218,14 @@
"id": user.id,
"email": user.email,
"display_name": user.display_name,
+ "username": user.username,
+ "first_name": user.first_name,
+ "last_name": user.last_name,
+ "phone": user.phone,
+ "birth_date": user.birth_date,
+ "country": user.country,
+ "city": user.city,
+ "locale": user.locale,
"avatar_url": user.avatar_url,
"role": user.role,
"permissions": user.permissions,
diff --git a/navi/api/routes/messages.py b/navi/api/routes/messages.py
index 7933099..1d0b446 100644
--- a/navi/api/routes/messages.py
+++ b/navi/api/routes/messages.py
@@ -30,6 +30,13 @@
if session is None:
raise HTTPException(status_code=404, detail="Session not found")
check_session_access(session, user)
+
+ # Set user context for tool sandboxing
+ from navi.tools.base import current_user_id as _uid_var, current_user_role as _role_var, current_user_info as _uinfo_var
+ _uid_var.set(user.id)
+ _role_var.set(user.role)
+ _uinfo_var.set(user.model_dump(mode="json"))
+
try:
reply = await agent.run(session_id, body.content)
return {"role": "assistant", "content": reply}
diff --git a/navi/api/routes/sessions.py b/navi/api/routes/sessions.py
index 77d9dcc..d3fb32c 100644
--- a/navi/api/routes/sessions.py
+++ b/navi/api/routes/sessions.py
@@ -94,6 +94,8 @@
for session in sessions:
if not session.messages:
continue
+ if session.user_id is None:
+ continue # skip legacy sessions — no multi-user memory for unowned sessions
if session.last_active >= cutoff:
continue # still active
extracted_at = await memory.get_extracted_at(session.id)
diff --git a/navi/api/websocket.py b/navi/api/websocket.py
index ed04f25..efaa068 100644
--- a/navi/api/websocket.py
+++ b/navi/api/websocket.py
@@ -361,6 +361,17 @@
current_run = run
_runs[session_id] = run
+ # Set user context for tool sandboxing (inherited by the agent task)
+ from navi.tools.base import current_user_id as _uid_var, current_user_role as _role_var, current_user_info as _uinfo_var
+ if user is not None:
+ _uid_var.set(user.id)
+ _role_var.set(user.role)
+ _uinfo_var.set(user.model_dump(mode="json"))
+ else:
+ _uid_var.set(None)
+ _role_var.set("user")
+ _uinfo_var.set(None)
+
run.task = asyncio.create_task(
_run_agent(run, agent, session_id, user_content, raw_images, original_content)
)
diff --git a/navi/auth/__init__.py b/navi/auth/__init__.py
index 52f5209..f664181 100644
--- a/navi/auth/__init__.py
+++ b/navi/auth/__init__.py
@@ -11,6 +11,14 @@
id: str
email: str
display_name: str | None = None
+ username: str | None = None
+ first_name: str | None = None
+ last_name: str | None = None
+ phone: str | None = None
+ birth_date: str | None = None
+ country: str | None = None
+ city: str | None = None
+ locale: str | None = None
avatar_url: str | None = None
role: str = "user" # "user" | "admin"
permissions: list[str] = []
diff --git a/navi/auth/_ddl.py b/navi/auth/_ddl.py
index 8430136..8850e38 100644
--- a/navi/auth/_ddl.py
+++ b/navi/auth/_ddl.py
@@ -7,10 +7,18 @@
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
display_name TEXT,
- role TEXT NOT NULL DEFAULT 'user',
- permissions TEXT NOT NULL DEFAULT '[]',
- created_at TIMESTAMPTZ NOT NULL,
- updated_at TIMESTAMPTZ NOT NULL
+ username TEXT,
+ first_name TEXT,
+ last_name TEXT,
+ phone TEXT,
+ birth_date TEXT,
+ country TEXT,
+ city TEXT,
+ locale TEXT,
+ role TEXT NOT NULL DEFAULT 'user',
+ permissions TEXT NOT NULL DEFAULT '[]',
+ created_at TIMESTAMPTZ NOT NULL,
+ updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS user_auth_sessions (
@@ -26,13 +34,38 @@
CREATE INDEX IF NOT EXISTS idx_user_auth_sessions_user_id ON user_auth_sessions (user_id);
"""
+# Columns added after initial table creation — boot-time migration.
+_NAVI_USERS_MIGRATION_COLUMNS = [
+ ("username", "TEXT"),
+ ("first_name", "TEXT"),
+ ("last_name", "TEXT"),
+ ("phone", "TEXT"),
+ ("birth_date", "TEXT"),
+ ("country", "TEXT"),
+ ("city", "TEXT"),
+ ("locale", "TEXT"),
+]
+
async def _ensure_auth_tables() -> None:
- """Create auth tables if they don't exist."""
+ """Create auth tables if they don't exist and run boot-time migrations."""
from navi.config import settings
conn = await asyncpg.connect(settings.database_url)
try:
await conn.execute(_DDL)
+ # Migrate existing navi_users tables that lack newer columns
+ for col_name, col_type in _NAVI_USERS_MIGRATION_COLUMNS:
+ row = await conn.fetchrow(
+ """
+ SELECT 1 FROM information_schema.columns
+ WHERE table_name = 'navi_users' AND column_name = $1
+ """,
+ col_name,
+ )
+ if row is None:
+ await conn.execute(
+ f'ALTER TABLE navi_users ADD COLUMN {col_name} {col_type}'
+ )
finally:
await conn.close()
diff --git a/navi/auth/deps.py b/navi/auth/deps.py
index f485008..11c4a7c 100644
--- a/navi/auth/deps.py
+++ b/navi/auth/deps.py
@@ -109,12 +109,33 @@
break
# Upsert into navi_users
- await _upsert_navi_user(store, auth_user.user_id, auth_user.email, auth_user.profile.get("display_name"), role, permissions)
+ profile = auth_user.profile
+ await _upsert_navi_user(
+ store, auth_user.user_id, auth_user.email,
+ profile.get("display_name"), role, permissions,
+ username=profile.get("username"),
+ first_name=profile.get("first_name"),
+ last_name=profile.get("last_name"),
+ phone=profile.get("phone"),
+ birth_date=profile.get("birth_date"),
+ country=profile.get("country"),
+ city=profile.get("city"),
+ locale=profile.get("locale"),
+ )
+ profile = auth_user.profile
user = User(
id=auth_user.user_id,
email=auth_user.email,
- display_name=auth_user.profile.get("display_name") or auth_user.email,
+ display_name=profile.get("display_name") or auth_user.email,
+ username=profile.get("username"),
+ first_name=profile.get("first_name"),
+ last_name=profile.get("last_name"),
+ phone=profile.get("phone"),
+ birth_date=profile.get("birth_date"),
+ country=profile.get("country"),
+ city=profile.get("city"),
+ locale=profile.get("locale"),
avatar_url=auth_user.avatar_url,
role=role,
permissions=permissions,
@@ -193,21 +214,38 @@
async def _upsert_navi_user(
- store, user_id: str, email: str, display_name: str | None, role: str, permissions: list[str]
+ store, user_id: str, email: str, display_name: str | None, role: str, permissions: list[str],
+ username: str | None = None, first_name: str | None = None, last_name: str | None = None,
+ phone: str | None = None, birth_date: str | None = None, country: str | None = None,
+ city: str | None = None, locale: str | None = None,
) -> None:
pool = await store._get_pool()
import json
async with pool.acquire() as conn:
await conn.execute(
- """INSERT INTO navi_users (id, email, display_name, role, permissions, created_at, updated_at)
- VALUES ($1, $2, $3, $4, $5, $6, $6)
+ """INSERT INTO navi_users (
+ id, email, display_name, username, first_name, last_name,
+ phone, birth_date, country, city, locale,
+ role, permissions, created_at, updated_at
+ )
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $14)
ON CONFLICT (id) DO UPDATE
SET email = EXCLUDED.email,
display_name = EXCLUDED.display_name,
+ username = EXCLUDED.username,
+ first_name = EXCLUDED.first_name,
+ last_name = EXCLUDED.last_name,
+ phone = EXCLUDED.phone,
+ birth_date = EXCLUDED.birth_date,
+ country = EXCLUDED.country,
+ city = EXCLUDED.city,
+ locale = EXCLUDED.locale,
role = EXCLUDED.role,
permissions = EXCLUDED.permissions,
updated_at = EXCLUDED.updated_at""",
- user_id, email, display_name or email, role, json.dumps(permissions), datetime.now(timezone.utc),
+ user_id, email, display_name or email, username, first_name, last_name,
+ phone, birth_date, country, city, locale,
+ role, json.dumps(permissions), datetime.now(timezone.utc),
)
diff --git a/navi/config.py b/navi/config.py
index 9869954..3104ca2 100644
--- a/navi/config.py
+++ b/navi/config.py
@@ -38,6 +38,15 @@
# Terminal tool: "*" = allow all commands (recommended for local use)
# or comma-separated list of allowed executables, e.g. "ls,cat,git"
terminal_allowed_commands: str = "*"
+ # Terminal allowlist for non-admin users (multi-user mode).
+ # Admin bypasses this restriction.
+ terminal_user_allowed_commands: str = (
+ "ls,cat,git,grep,egrep,fgrep,head,tail,wc,find,locate,"
+ "ps,df,du,free,uptime,whoami,id,mkdir,touch,cp,mv,rm,chmod,chown,"
+ "echo,printf,sort,uniq,awk,sed,tr,cut,date,uname,hostname,which,whereis,"
+ "python,python3,node,npm,npx,pip,pip3,env,export,source,"
+ "make,cmake,gcc,g++,rustc,cargo,javac,java,go,tsc"
+ )
# SSH tool: path to JSON file with named connections
ssh_hosts_file: str = "ssh_hosts.json"
@@ -126,5 +135,9 @@
def terminal_allowed_commands_list(self) -> list[str]:
return [c.strip() for c in self.terminal_allowed_commands.split(",") if c.strip()]
+ @property
+ def terminal_user_allowed_commands_list(self) -> list[str]:
+ return [c.strip() for c in self.terminal_user_allowed_commands.split(",") if c.strip()]
+
settings = Settings()
diff --git a/navi/core/agent.py b/navi/core/agent.py
index 2ecca9e..dcae151 100644
--- a/navi/core/agent.py
+++ b/navi/core/agent.py
@@ -235,6 +235,9 @@
from navi.tools.base import current_session_id as _sid_var
_sid_var.set(session_id)
+ # current_user_id and current_user_role are set by the caller
+ # (websocket_session or messages endpoint) before run()/run_stream()
+
user_msg = Message(role="user", content=user_message, images=images or None,
created_at=datetime.now(timezone.utc))
session.messages.append(user_msg)
@@ -321,9 +324,18 @@
"""
import time as _time
import uuid as _uuid
- from navi.tools.base import current_session_id as _sid_var, current_model as _model_var
+ from navi.tools.base import (
+ current_session_id as _sid_var,
+ current_model as _model_var,
+ current_user_id as _uid_var,
+ current_user_role as _role_var,
+ current_user_info as _uinfo_var,
+ )
_prev_sid = _sid_var.get(None)
_prev_model = _model_var.get(None)
+ _prev_uid = _uid_var.get(None)
+ _prev_role = _role_var.get()
+ _prev_uinfo = _uinfo_var.get(None)
subagent_run_id = f"subagent_{_uuid.uuid4().hex[:12]}"
tool_session_id = parent_session_id or subagent_run_id
_sid_var.set(tool_session_id)
@@ -343,6 +355,14 @@
parent_session = await self._sessions.get(parent_session_id)
if parent_session:
user_id = parent_session.user_id
+ if user_id is not None:
+ _uid_var.set(user_id)
+ _role_var.set(_prev_role or "user")
+ _uinfo_var.set(_prev_uinfo)
+ else:
+ _uid_var.set(None)
+ _role_var.set("user")
+ _uinfo_var.set(None)
mem = await self._ctx_builder._memory_msg(user_id=user_id)
# Build subagent system prompt — completely separate from the parent's system prompt.
@@ -574,6 +594,9 @@
# Restore parent ContextVar values so background tasks don't inherit stale subagent IDs
_sid_var.set(_prev_sid)
_model_var.set(_prev_model)
+ _uid_var.set(_prev_uid)
+ _role_var.set(_prev_role)
+ _uinfo_var.set(_prev_uinfo)
async def run_stream(
self,
diff --git a/navi/core/context_builder.py b/navi/core/context_builder.py
index 88a9459..6a42ec9 100644
--- a/navi/core/context_builder.py
+++ b/navi/core/context_builder.py
@@ -118,6 +118,77 @@
lines.append("Before final response, update todo for every completed step, including the final one.")
return Message(role="system", content="\n".join(lines))
+ def _security_policy_msg(self) -> Message | None:
+ """Build a dynamic security policy system message based on user role."""
+ from navi.tools.base import current_user_id as _uid_var, current_user_role as _role_var
+ user_id = _uid_var.get(None)
+ role = _role_var.get()
+ if role == "admin":
+ return Message(
+ role="system",
+ content=(
+ "[Security policy]\n"
+ "Role: admin\n"
+ "Full system access. No restrictions apply.\n"
+ "You may use any tool, access any path, and execute any command."
+ ),
+ )
+ if user_id:
+ allowed = _config.settings.terminal_user_allowed_commands_list
+ return Message(
+ role="system",
+ content=(
+ "[Security policy]\n"
+ f"Role: user (user_id={user_id})\n"
+ f"Filesystem sandbox: user_data/{user_id}/\n"
+ "You MUST NOT attempt to access paths outside your sandbox.\n"
+ f"Terminal allowed commands: {', '.join(allowed)}\n"
+ "You MUST NOT use terminal for: curl, wget, ssh, scp, sudo, system-wide destructive operations, "
+ "or any command not in the allowlist.\n"
+ "If a task requires admin privileges (e.g. system-wide changes, accessing another user's files, "
+ "installing packages globally), tell the user to contact an admin.\n"
+ "Always prefer filesystem tool over terminal for file operations."
+ ),
+ )
+ # Legacy / single-user mode — no policy injected
+ return None
+
+ def _user_context_msg(self) -> Message | None:
+ """Build a [User context] system message from current_user_info ContextVar."""
+ from navi.tools.base import current_user_info as _uinfo_var, current_user_role as _role_var
+ info = _uinfo_var.get(None)
+ if not info:
+ return None
+ lines = ["[User context]"]
+ if info.get("display_name"):
+ lines.append(f"Name: {info['display_name']}")
+ if info.get("username"):
+ lines.append(f"Username: {info['username']}")
+ if info.get("first_name") or info.get("last_name"):
+ parts = []
+ if info.get("first_name"):
+ parts.append(info["first_name"])
+ if info.get("last_name"):
+ parts.append(info["last_name"])
+ lines.append(f"Full name: {' '.join(parts)}")
+ if info.get("email"):
+ lines.append(f"Email: {info['email']}")
+ if info.get("phone"):
+ lines.append(f"Phone: {info['phone']}")
+ if info.get("birth_date"):
+ lines.append(f"Birth date: {info['birth_date']}")
+ if info.get("country") or info.get("city"):
+ parts = []
+ if info.get("city"):
+ parts.append(info["city"])
+ if info.get("country"):
+ parts.append(info["country"])
+ lines.append(f"Location: {', '.join(parts)}")
+ if info.get("locale"):
+ lines.append(f"Locale: {info['locale']}")
+ lines.append(f"Role: {_role_var.get()}")
+ return Message(role="system", content="\n".join(lines))
+
def build(
self,
session_context: list[Message],
@@ -142,6 +213,17 @@
result: list[Message] = [system_msg]
if mem:
result.append(mem)
+
+ # Inject user profile context for multi-user mode
+ user_ctx = self._user_context_msg()
+ if user_ctx:
+ result.append(user_ctx)
+
+ # Inject security policy for multi-user mode
+ policy = self._security_policy_msg()
+ if policy:
+ result.append(policy)
+
if extra_system:
result.extend(extra_system)
result.extend(conv)
diff --git a/navi/core/pg_session_store.py b/navi/core/pg_session_store.py
index dcbe4a5..54e6684 100644
--- a/navi/core/pg_session_store.py
+++ b/navi/core/pg_session_store.py
@@ -173,6 +173,83 @@
result = await conn.execute("DELETE FROM sessions WHERE id = $1", session_id)
return result == "DELETE 1"
+ async def count_all(
+ self,
+ *,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ ) -> int:
+ pool = await self._get_pool()
+ async with pool.acquire() as conn:
+ conditions = []
+ params: list = []
+ param_idx = 0
+
+ def add_param(value):
+ nonlocal param_idx
+ param_idx += 1
+ params.append(value)
+ return f"${param_idx}"
+
+ if not is_admin and user_id is not None:
+ conditions.append(f"user_id = {add_param(user_id)}")
+ if search:
+ like = f"%{search}%"
+ conditions.append(
+ f"(id ILIKE {add_param(like)} OR name ILIKE {add_param(like)} OR user_id ILIKE {add_param(like)} OR profile_id ILIKE {add_param(like)})"
+ )
+
+ where = "WHERE " + " AND ".join(conditions) if conditions else ""
+ row = await conn.fetchrow(f"SELECT COUNT(*) FROM sessions {where}", *params)
+ return row["count"] if row else 0
+
+ async def search_list(
+ self,
+ *,
+ limit: int,
+ offset: int,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ sort_by: str = "last_active",
+ sort_order: str = "desc",
+ ) -> list[Session]:
+ pool = await self._get_pool()
+ async with pool.acquire() as conn:
+ conditions = []
+ params: list = []
+ param_idx = 0
+
+ def add_param(value):
+ nonlocal param_idx
+ param_idx += 1
+ params.append(value)
+ return f"${param_idx}"
+
+ if not is_admin and user_id is not None:
+ conditions.append(f"user_id = {add_param(user_id)}")
+ if search:
+ like = f"%{search}%"
+ conditions.append(
+ f"(id ILIKE {add_param(like)} OR name ILIKE {add_param(like)} OR user_id ILIKE {add_param(like)} OR profile_id ILIKE {add_param(like)})"
+ )
+
+ where = "WHERE " + " AND ".join(conditions) if conditions else ""
+
+ allowed_cols = {"created_at", "last_active", "name", "profile_id", "user_id", "pinned"}
+ col = sort_by if sort_by in allowed_cols else "last_active"
+ order = "DESC" if sort_order == "desc" else "ASC"
+ # secondary sort by pinned DESC for stability
+ order_clause = f"ORDER BY pinned DESC, {col} {order} LIMIT {add_param(limit)} OFFSET {add_param(offset)}"
+
+ rows = await conn.fetch(
+ "SELECT id, profile_id, user_id, messages, context, pinned, created_at, last_active, context_token_count, name, planning_logs "
+ f"FROM sessions {where} {order_clause}",
+ *params,
+ )
+ return [self._row_to_session(r) for r in rows]
+
def _row_to_session(self, row: asyncpg.Record) -> Session:
messages = _deserialize(row["messages"])
context_json = row["context"]
diff --git a/navi/core/session.py b/navi/core/session.py
index 686f256..969e9d2 100644
--- a/navi/core/session.py
+++ b/navi/core/session.py
@@ -48,6 +48,28 @@
) -> list[Session]: ...
@abstractmethod
+ async def count_all(
+ self,
+ *,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ ) -> int: ...
+
+ @abstractmethod
+ async def search_list(
+ self,
+ *,
+ limit: int,
+ offset: int,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ sort_by: str = "last_active",
+ sort_order: str = "desc",
+ ) -> list[Session]: ...
+
+ @abstractmethod
async def delete(self, session_id: str) -> bool: ...
@abstractmethod
@@ -97,6 +119,53 @@
sessions = [s for s in sessions if s.profile_id == profile_id]
return sessions[offset:offset + limit]
+ async def count_all(
+ self,
+ *,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ ) -> int:
+ sessions = await self.search_list(
+ limit=10000, offset=0, user_id=user_id, is_admin=is_admin, search=search
+ )
+ return len(sessions)
+
+ async def search_list(
+ self,
+ *,
+ limit: int,
+ offset: int,
+ user_id: str | None = None,
+ is_admin: bool = False,
+ search: str | None = None,
+ sort_by: str = "last_active",
+ sort_order: str = "desc",
+ ) -> list[Session]:
+ sessions = list(self._sessions.values())
+ if not is_admin and user_id is not None:
+ sessions = [s for s in sessions if s.user_id == user_id]
+ if search:
+ q = search.lower()
+ sessions = [
+ s
+ for s in sessions
+ if (s.id and q in s.id.lower())
+ or (s.name and q in s.name.lower())
+ or (s.user_id and q in s.user_id.lower())
+ or (s.profile_id and q in s.profile_id.lower())
+ ]
+ key_map = {
+ "created_at": lambda s: s.created_at,
+ "last_active": lambda s: s.last_active,
+ "name": lambda s: (s.name or "").lower(),
+ "message_count": lambda s: len(s.messages),
+ }
+ key = key_map.get(sort_by, key_map["last_active"])
+ reverse = sort_order == "desc"
+ sessions = sorted(sessions, key=key, reverse=reverse)
+ return sessions[offset:offset + limit]
+
async def delete(self, session_id: str) -> bool:
if session_id in self._sessions:
del self._sessions[session_id]
diff --git a/navi/main.py b/navi/main.py
index 1f2a53c..9e6434b 100644
--- a/navi/main.py
+++ b/navi/main.py
@@ -59,6 +59,7 @@
@app.on_event("startup")
async def _on_startup() -> None:
+ log = structlog.get_logger()
from navi.api.deps import get_registries, get_session_store
from navi.content_store import ensure_tables
from navi.session_files import cleanup_loop
@@ -71,7 +72,6 @@
await ensure_tables()
break
except Exception as e:
- log = structlog.get_logger()
if attempt < 5:
log.warning("startup.ensure_tables_retry", attempt=attempt, error=str(e))
await asyncio.sleep(2)
@@ -80,11 +80,29 @@
# Initialize registries before embed health check. The memory store gets its
# embedding backend wired during registry construction.
get_registries()
+ # Apply persisted profile overrides (e.g. is_admin_only) to in-memory profiles.
+ from navi.api.deps import get_profile_registry, get_session_store
+ from navi.profiles._overrides import ensure_table, load_overrides
+
+ try:
+ pool = await get_session_store()._get_pool()
+ await ensure_table(pool)
+ overrides = await load_overrides(pool)
+ if overrides:
+ profiles = get_profile_registry()
+ for pid, is_admin_only in overrides.items():
+ try:
+ profile = profiles.get(pid)
+ profile.is_admin_only = is_admin_only
+ except Exception:
+ pass # stale override for removed profile
+ log.info("startup.profile_overrides_applied", count=len(overrides))
+ except Exception:
+ log.warning("startup.profile_overrides_failed", exc_info=True)
# Check embedding backend health and log status
from navi.api.routes.health import _check_embed
embed_status = await _check_embed()
- log = structlog.get_logger()
if embed_status["ok"]:
log.info("startup.embed_ready", backend=embed_status["backend"])
else:
@@ -114,3 +132,8 @@
@app.get("/debug/eval", include_in_schema=False)
async def debug_eval() -> FileResponse:
return FileResponse("debug/eval/index.html", headers={"Cache-Control": "no-store"})
+
+
+@app.get("/admin", include_in_schema=False)
+async def admin_panel() -> FileResponse:
+ return FileResponse("admin/index.html", headers={"Cache-Control": "no-store"})
diff --git a/navi/memory/_facts.py b/navi/memory/_facts.py
index 2677782..2e71e6c 100644
--- a/navi/memory/_facts.py
+++ b/navi/memory/_facts.py
@@ -88,9 +88,6 @@
)
async def search_facts(self, query: str, user_id: str | None = None, limit: int = 15) -> list[dict]:
- user_clause = "AND user_id IS NULL" if user_id is None else "AND user_id = $3"
- user_param = () if user_id is None else (user_id,)
-
# 1. Try vector search if pgvector + embedding backend are available
if self._embedding_backend and await self._has_pgvector():
query_embedding = await self._generate_embedding(query)
@@ -99,17 +96,30 @@
try:
pool = await self._get_pool()
async with pool.acquire() as conn:
- rows = await conn.fetch(
- """SELECT id, category, key, value, updated_at,
- source, confidence, expires_at, source_context,
- embedding <=> $1::vector AS distance
- FROM memory_facts
- WHERE (expires_at IS NULL OR expires_at > now())
- AND (user_id IS NOT DISTINCT FROM $3)
- ORDER BY embedding <=> $1::vector
- LIMIT $2""",
- vec_str, limit, *user_param,
- )
+ if user_id is None:
+ rows = await conn.fetch(
+ """SELECT id, category, key, value, updated_at,
+ source, confidence, expires_at, source_context,
+ embedding <=> $1::vector AS distance
+ FROM memory_facts
+ WHERE (expires_at IS NULL OR expires_at > now())
+ AND user_id IS NULL
+ ORDER BY embedding <=> $1::vector
+ LIMIT $2""",
+ vec_str, limit,
+ )
+ else:
+ rows = await conn.fetch(
+ """SELECT id, category, key, value, updated_at,
+ source, confidence, expires_at, source_context,
+ embedding <=> $1::vector AS distance
+ FROM memory_facts
+ WHERE (expires_at IS NULL OR expires_at > now())
+ AND user_id = $3
+ ORDER BY embedding <=> $1::vector
+ LIMIT $2""",
+ vec_str, limit, user_id,
+ )
results = [
_row_to_dict(r)
for r in rows
@@ -200,32 +210,88 @@
)
return int(result.split()[1])
- async def get_all_facts(self, user_id: str | None = None, limit: int | None = None) -> list[dict]:
+ async def get_all_facts(
+ self,
+ user_id: str | None = None,
+ limit: int | None = None,
+ offset: int = 0,
+ search: str | None = None,
+ sort_by: str = "category",
+ sort_order: str = "desc",
+ all_users: bool = False,
+ ) -> list[dict]:
q = (
"SELECT id, category, key, value, updated_at, source, confidence, "
"expires_at, source_context FROM memory_facts "
)
params: list = []
- if user_id is None:
- q += "WHERE user_id IS NULL "
+ param_idx = 0
+
+ def add_param(value):
+ nonlocal param_idx
+ param_idx += 1
+ params.append(value)
+ return f"${param_idx}"
+
+ conditions = []
+ if not all_users:
+ if user_id is None:
+ conditions.append("user_id IS NULL")
+ else:
+ conditions.append(f"user_id = {add_param(user_id)}")
+ if search:
+ like = f"%{search}%"
+ conditions.append(
+ f"(key ILIKE {add_param(like)} OR value ILIKE {add_param(like)} OR category ILIKE {add_param(like)})"
+ )
+
+ if conditions:
+ q += "WHERE " + " AND ".join(conditions) + " "
+
+ allowed_cols = {"category", "key", "updated_at", "confidence", "source"}
+ col = sort_by if sort_by in allowed_cols else "category"
+ order = "DESC" if sort_order == "desc" else "ASC"
+ if col == "category":
+ q += f"ORDER BY {col} {order}, updated_at DESC"
else:
- q += "WHERE user_id = $1 "
- params.append(user_id)
- q += "ORDER BY category, updated_at DESC"
- if limit:
- q += f" LIMIT ${len(params) + 1}"
- params.append(limit)
+ q += f"ORDER BY {col} {order}"
+
+ if limit is not None:
+ q += f" LIMIT {add_param(limit)}"
+ if offset:
+ q += f" OFFSET {add_param(offset)}"
+
pool = await self._get_pool()
async with pool.acquire() as conn:
rows = await conn.fetch(q, *params)
return [_row_to_dict(r) for r in rows]
- async def fact_count(self, user_id: str | None = None) -> int:
+ async def fact_count(
+ self,
+ user_id: str | None = None,
+ all_users: bool = False,
+ search: str | None = None,
+ ) -> int:
+ q = "SELECT COUNT(*) FROM memory_facts"
+ params: list = []
+ conditions = []
+ if not all_users:
+ if user_id is None:
+ conditions.append("user_id IS NULL")
+ else:
+ conditions.append(f"user_id = ${len(params) + 1}")
+ params.append(user_id)
+ if search:
+ like = f"%{search}%"
+ conditions.append(
+ f"(key ILIKE ${len(params) + 1} OR value ILIKE ${len(params) + 2} OR category ILIKE ${len(params) + 3})"
+ )
+ params.extend([like, like, like])
+ if conditions:
+ q += " WHERE " + " AND ".join(conditions)
pool = await self._get_pool()
async with pool.acquire() as conn:
- if user_id is None:
- return await conn.fetchval("SELECT COUNT(*) FROM memory_facts WHERE user_id IS NULL") or 0
- return await conn.fetchval("SELECT COUNT(*) FROM memory_facts WHERE user_id = $1", user_id) or 0
+ return await conn.fetchval(q, *params) or 0
def _row_to_dict(row: asyncpg.Record) -> dict:
diff --git a/navi/memory/_summary.py b/navi/memory/_summary.py
index d6baec4..467ab0a 100644
--- a/navi/memory/_summary.py
+++ b/navi/memory/_summary.py
@@ -1,8 +1,22 @@
"""Conversation summary persistence — single-row table."""
+import zlib
from datetime import datetime, timezone
+def _summary_id(user_id: str | None) -> int:
+ """Return a deterministic primary key for the user's summary row.
+
+ Legacy single-user mode uses id=1. Multi-user mode generates a unique
+ id per user_id so the PRIMARY KEY constraint on `id` is never violated.
+ """
+ if user_id is None:
+ return 1
+ # Deterministic, non-overlapping with legacy id=1
+ # zlib.crc32 is stable across process restarts (unlike Python hash()).
+ return (abs(zlib.crc32(user_id.encode())) % 2147483646) + 2
+
+
class SummaryMixin:
"""Summary storage operations.
@@ -12,19 +26,27 @@
async def get_summary(self, user_id: str | None = None) -> str | None:
pool = await self._get_pool()
+ sid = _summary_id(user_id)
async with pool.acquire() as conn:
if user_id is None:
- return await conn.fetchval("SELECT content FROM memory_summary WHERE id=1 AND user_id IS NULL")
- return await conn.fetchval("SELECT content FROM memory_summary WHERE id=1 AND user_id = $1", user_id)
+ return await conn.fetchval(
+ "SELECT content FROM memory_summary WHERE id=$1 AND user_id IS NULL",
+ sid,
+ )
+ return await conn.fetchval(
+ "SELECT content FROM memory_summary WHERE id=$1 AND user_id = $2",
+ sid, user_id,
+ )
async def set_summary(self, content: str, user_id: str | None = None) -> None:
now = datetime.now(timezone.utc)
+ sid = _summary_id(user_id)
pool = await self._get_pool()
async with pool.acquire() as conn:
await conn.execute(
- """INSERT INTO memory_summary (id, user_id, content, generated_at) VALUES (1, $1, $2, $3)
- ON CONFLICT(id, user_id) DO UPDATE SET
+ """INSERT INTO memory_summary (id, user_id, content, generated_at) VALUES ($1, $2, $3, $4)
+ ON CONFLICT(id) DO UPDATE SET
content = EXCLUDED.content,
generated_at = EXCLUDED.generated_at""",
- user_id, content, now,
+ sid, user_id, content, now,
)
diff --git a/navi/memory/extractor.py b/navi/memory/extractor.py
index bebcb80..f4d135f 100644
--- a/navi/memory/extractor.py
+++ b/navi/memory/extractor.py
@@ -75,14 +75,19 @@
"""
Extract facts from a session and update the memory summary.
Safe to call multiple times — already-extracted sessions produce no duplicates.
+ Legacy sessions (user_id=None) are skipped — no multi-user memory for unowned sessions.
"""
+ user_id = getattr(session, "user_id", None)
+ if user_id is None:
+ return
+
facts_added = await _extract_facts(session, llm, model, memory_store)
log.info("memory.extracted", session_id=session.id, facts_added=facts_added)
await memory_store.mark_session_extracted(session.id)
if facts_added > 0:
- await _regenerate_summary(llm, model, memory_store, user_id=session.user_id)
+ await _regenerate_summary(llm, model, memory_store, user_id=user_id)
_MAX_TOOL_RESULT_LEN = 500
diff --git a/navi/profiles/_overrides.py b/navi/profiles/_overrides.py
new file mode 100644
index 0000000..c83c0de
--- /dev/null
+++ b/navi/profiles/_overrides.py
@@ -0,0 +1,50 @@
+"""Runtime profile overrides persisted in PostgreSQL.
+
+Currently stores only `is_admin_only` per profile. Base config comes from
+JSON files on disk; this layer applies runtime changes made via the admin API.
+"""
+
+from datetime import datetime, timezone
+
+import asyncpg
+import structlog
+
+log = structlog.get_logger()
+
+_DDL = """
+CREATE TABLE IF NOT EXISTS profile_overrides (
+ profile_id TEXT PRIMARY KEY,
+ is_admin_only BOOLEAN NOT NULL DEFAULT FALSE,
+ updated_at TIMESTAMPTZ NOT NULL
+)
+"""
+
+
+async def ensure_table(pool: asyncpg.Pool) -> None:
+ async with pool.acquire() as conn:
+ await conn.execute(_DDL)
+
+
+async def load_overrides(pool: asyncpg.Pool) -> dict[str, bool]:
+ """Return mapping profile_id -> is_admin_only from DB."""
+ async with pool.acquire() as conn:
+ rows = await conn.fetch("SELECT profile_id, is_admin_only FROM profile_overrides")
+ return {r["profile_id"]: r["is_admin_only"] for r in rows}
+
+
+async def save_override(
+ pool: asyncpg.Pool, profile_id: str, is_admin_only: bool
+) -> None:
+ now = datetime.now(timezone.utc)
+ async with pool.acquire() as conn:
+ await conn.execute(
+ """INSERT INTO profile_overrides (profile_id, is_admin_only, updated_at)
+ VALUES ($1, $2, $3)
+ ON CONFLICT (profile_id) DO UPDATE SET
+ is_admin_only = EXCLUDED.is_admin_only,
+ updated_at = EXCLUDED.updated_at""",
+ profile_id,
+ is_admin_only,
+ now,
+ )
+ log.info("profile.override_saved", profile_id=profile_id, is_admin_only=is_admin_only)
diff --git a/navi/tools/base.py b/navi/tools/base.py
index f86bdd8..8bc54fc 100644
--- a/navi/tools/base.py
+++ b/navi/tools/base.py
@@ -31,6 +31,17 @@
# to tools that need to make their own LLM calls (e.g. AIHelper-powered tools).
current_model: ContextVar[list[str] | str | None] = ContextVar("current_model", default=None)
+# Set by run_stream() to expose the current session's user_id to tools that need
+# per-user sandboxing (e.g. filesystem tool).
+current_user_id: ContextVar[str | None] = ContextVar("current_user_id", default=None)
+
+# Set by run_stream() alongside current_user_id. Admins bypass sandbox restrictions.
+current_user_role: ContextVar[str] = ContextVar("current_user_role", default="user")
+
+# Set by run_stream() / messages endpoint to expose the full user profile to
+# ContextBuilder so the LLM receives [User context] with name, email, locale, etc.
+current_user_info: ContextVar[dict | None] = ContextVar("current_user_info", default=None)
+
@dataclass
class ToolResult:
diff --git a/navi/tools/code_exec.py b/navi/tools/code_exec.py
index 73245c1..205b5f0 100644
--- a/navi/tools/code_exec.py
+++ b/navi/tools/code_exec.py
@@ -1,15 +1,47 @@
-"""Code execution tool — run Python code in a subprocess sandbox."""
+"""Code execution tool — run Python code in a subprocess sandbox.
+
+Multi-user safety:
+- Non-admin users run inside their sandbox directory (user_data//).
+- Temp files and working directory are restricted to the sandbox.
+- Admins bypass the sandbox and use the system temp directory.
+"""
import asyncio
import sys
import tempfile
from pathlib import Path
-from .base import Tool, ToolResult
+from .base import Tool, ToolResult, current_user_id, current_user_role
_TIMEOUT = 30
+def _resolve_working_dir(working_dir: str | None) -> Path:
+ """Resolve working directory with sandbox enforcement for non-admins."""
+ user_id = current_user_id.get(None)
+ role = current_user_role.get()
+
+ if user_id and role != "admin":
+ sandbox = Path("user_data") / user_id
+ sandbox = sandbox.expanduser().resolve()
+ sandbox.mkdir(parents=True, exist_ok=True)
+ if working_dir:
+ p = Path(working_dir).expanduser()
+ if p.is_absolute():
+ resolved = p.resolve()
+ try:
+ resolved.relative_to(sandbox)
+ return resolved
+ except ValueError:
+ return sandbox
+ return (sandbox / p).resolve()
+ return sandbox
+
+ if working_dir:
+ return Path(working_dir).expanduser().resolve()
+ return Path(tempfile.gettempdir())
+
+
class CodeExecTool(Tool):
name = "code_exec"
description = (
@@ -46,17 +78,28 @@
error="unsupported_language",
)
- with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f:
- f.write(code)
- script_path = f.name
+ role = current_user_role.get()
+ user_id = current_user_id.get(None)
+
+ cwd = _resolve_working_dir(None)
+
+ if user_id and role != "admin":
+ # Write temp file inside the sandbox so file I/O in user code
+ # is implicitly sandboxed unless they escape via absolute paths.
+ script_path = cwd / f"_navi_exec_{id(params)}.py"
+ script_path.write_text(code, encoding="utf-8")
+ else:
+ with tempfile.NamedTemporaryFile(suffix=".py", mode="w", delete=False) as f:
+ f.write(code)
+ script_path = Path(f.name)
try:
proc = await asyncio.create_subprocess_exec(
sys.executable,
- script_path,
+ str(script_path),
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
- cwd=tempfile.gettempdir(),
+ cwd=str(cwd),
)
try:
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=_TIMEOUT)
@@ -72,7 +115,7 @@
if stdout:
output_parts.append(stdout.decode(errors="replace"))
if stderr:
- output_parts.append(f"[stderr]\n{stderr.decode(errors='replace')}")
+ output_parts.append(f"[stderr]\n{stderr.decode(errors="replace")}")
success = proc.returncode == 0
return ToolResult(
@@ -84,4 +127,4 @@
except Exception as e:
return ToolResult(success=False, output=f"Execution error: {e}", error=str(e))
finally:
- Path(script_path).unlink(missing_ok=True)
+ script_path.unlink(missing_ok=True)
diff --git a/navi/tools/filesystem.py b/navi/tools/filesystem.py
index eff1a92..27d79f9 100644
--- a/navi/tools/filesystem.py
+++ b/navi/tools/filesystem.py
@@ -14,7 +14,7 @@
from navi.config import settings
-from .base import Tool, ToolResult
+from .base import Tool, ToolResult, current_user_id, current_user_role
_READ_WARN_BYTES = 100_000 # 100 KB — add size warning in output
_READ_HARD_BYTES = 1_000_000 # 1 MB — refuse full read without offset/limit
@@ -69,9 +69,41 @@
# ── Path helpers ──────────────────────────────────────────────────────────────
def _check_path(path_str: str) -> Path | None:
- """Return resolved Path if access is allowed, else None."""
+ """Return resolved Path if access is allowed, else None.
+
+ When a user_id is active (multi-user mode), all paths are resolved
+ inside user_data//. This prevents users from accessing each
+ other's files or random OS directories.
+ """
try:
- p = Path(path_str).expanduser().resolve()
+ p = Path(path_str)
+ except Exception:
+ return None
+
+ user_id = current_user_id.get(None)
+ role = current_user_role.get()
+
+ # Admins bypass sandbox and use FS_ALLOWED_PATHS directly
+ if user_id and role != "admin":
+ # Sandbox mode: resolve inside user_data//
+ sandbox = Path("user_data") / user_id
+ sandbox = sandbox.expanduser().resolve()
+ sandbox.mkdir(parents=True, exist_ok=True)
+
+ if p.is_absolute():
+ # Absolute paths must still be inside the sandbox
+ try:
+ p.resolve().relative_to(sandbox)
+ return p.resolve()
+ except ValueError:
+ return None
+ else:
+ # Relative paths are resolved against the sandbox
+ return (sandbox / p).resolve()
+
+ # Fallback to FS_ALLOWED_PATHS for single-user / legacy mode / admin
+ try:
+ p = p.expanduser().resolve()
except Exception:
return None
diff --git a/navi/tools/share_file.py b/navi/tools/share_file.py
index aefa258..356c403 100644
--- a/navi/tools/share_file.py
+++ b/navi/tools/share_file.py
@@ -8,7 +8,7 @@
from navi.config import settings
from navi.session_files import ensure_session_dir
-from .base import Tool, ToolResult, current_session_id
+from .base import Tool, ToolResult, current_session_id, current_user_role, current_user_id
def _fmt_size(n: int) -> str:
@@ -63,17 +63,37 @@
return ToolResult(success=False, output="No active session context.", error="no_session")
raw_path = Path(params["path"]).expanduser()
- if not raw_path.is_absolute():
- return ToolResult(
- success=False,
- output=(
- f"share_file requires an absolute path, got: {params['path']}\n"
- "Resolve it first with filesystem info or terminal realpath, then call share_file again."
- ),
- error="path_not_absolute",
- )
+ user_id = current_user_id.get(None)
+ role = current_user_role.get()
- src = raw_path.resolve()
+ if user_id and role != "admin":
+ sandbox = Path("user_data") / user_id
+ sandbox = sandbox.expanduser().resolve()
+ sandbox.mkdir(parents=True, exist_ok=True)
+ if raw_path.is_absolute():
+ src = raw_path.resolve()
+ try:
+ src.relative_to(sandbox)
+ except ValueError:
+ return ToolResult(
+ success=False,
+ output=f"Access denied: path is outside user sandbox.",
+ error="access_denied",
+ )
+ else:
+ src = (sandbox / raw_path).resolve()
+ else:
+ if not raw_path.is_absolute():
+ return ToolResult(
+ success=False,
+ output=(
+ f"share_file requires an absolute path, got: {params['path']}\n"
+ "Resolve it first with filesystem info or terminal realpath, then call share_file again."
+ ),
+ error="path_not_absolute",
+ )
+ src = raw_path.resolve()
+
if not src.exists():
return ToolResult(success=False, output=f"File not found: {src}", error="not_found")
if not src.is_file():
diff --git a/navi/tools/terminal.py b/navi/tools/terminal.py
index 7be3ea6..94264c2 100644
--- a/navi/tools/terminal.py
+++ b/navi/tools/terminal.py
@@ -5,18 +5,83 @@
Set TERMINAL_ALLOWED_COMMANDS to a comma-separated list to restrict to specific
executables only (e.g. "ls,cat,git").
+
+Multi-user safety:
+- Non-admin users are restricted to a sandbox directory (user_data//)
+ and a curated allowlist of safe commands.
+- Dangerous patterns (curl, wget, ssh, sudo, python -c, node -e, etc.) are
+ blocked for non-admins even if the base command is in the allowlist.
+- Admins bypass all restrictions and use TERMINAL_ALLOWED_COMMANDS directly.
"""
import asyncio
+import re
import shlex
+from pathlib import Path
from navi.config import settings
-from .base import Tool, ToolResult
+from .base import Tool, ToolResult, current_user_id, current_user_role
_TIMEOUT = 300
_MAX_OUTPUT_CHARS = 5_000
+# Substrings that make a command line dangerous for non-admin users.
+_DANGEROUS_PATTERNS = [
+ r"\bcurl\b",
+ r"\bwget\b",
+ r"\bnc\b",
+ r"\bnetcat\b",
+ r"\bssh\b",
+ r"\bscp\b",
+ r"\bsftp\b",
+ r"\bsudo\b",
+ r"\bsu\b",
+ r"\bpython\b.*-\bc\b",
+ r"\bpython3\b.*-\bc\b",
+ r"\bnode\b.*-\be\b",
+ r"\beval\b",
+ r"\bexec\b",
+ r";\s*rm\s+-rf\s+/",
+ r">\s+/dev/",
+ r"<\s*/dev/",
+]
+
+_DANGEROUS_RE = re.compile("|".join(_DANGEROUS_PATTERNS), re.IGNORECASE)
+
+
+def _check_dangerous(command: str) -> str | None:
+ """Return block reason if the command contains dangerous patterns, else None."""
+ if _DANGEROUS_RE.search(command):
+ return "Command contains disallowed patterns for non-admin users."
+ return None
+
+
+def _resolve_working_dir(working_dir: str | None) -> Path | None:
+ """Resolve working directory with sandbox enforcement for non-admins."""
+ user_id = current_user_id.get(None)
+ role = current_user_role.get()
+
+ if user_id and role != "admin":
+ sandbox = Path("user_data") / user_id
+ sandbox = sandbox.expanduser().resolve()
+ sandbox.mkdir(parents=True, exist_ok=True)
+ if working_dir:
+ p = Path(working_dir).expanduser()
+ if p.is_absolute():
+ resolved = p.resolve()
+ try:
+ resolved.relative_to(sandbox)
+ return resolved
+ except ValueError:
+ return None
+ return (sandbox / p).resolve()
+ return sandbox
+
+ if working_dir:
+ return Path(working_dir).expanduser().resolve()
+ return None
+
class TerminalTool(Tool):
name = "terminal"
@@ -53,12 +118,35 @@
if not command:
return ToolResult(success=False, output="Empty command.", error="empty_command")
- unrestricted = settings.terminal_allowed_commands.strip() == "*"
+ role = current_user_role.get()
+ user_id = current_user_id.get(None)
- if unrestricted:
- return await self._run_shell(command, working_dir, timeout)
- else:
- return await self._run_restricted(command, working_dir, timeout)
+ # Admins and single-user / legacy mode: use the existing restriction logic
+ if not user_id or role == "admin":
+ unrestricted = settings.terminal_allowed_commands.strip() == "*"
+ if unrestricted:
+ return await self._run_shell(command, working_dir, timeout)
+ else:
+ return await self._run_restricted(command, working_dir, timeout)
+
+ # Non-admin multi-user mode: sandbox + curated allowlist + dangerous-pattern block
+ cwd = _resolve_working_dir(working_dir)
+ if cwd is None and working_dir:
+ return ToolResult(
+ success=False,
+ output="Working directory is outside your sandbox.",
+ error="sandbox_violation",
+ )
+
+ danger = _check_dangerous(command)
+ if danger:
+ return ToolResult(
+ success=False,
+ output=f"Blocked: {danger}",
+ error="dangerous_command",
+ )
+
+ return await self._run_user_restricted(command, cwd, timeout)
async def _run_shell(self, command: str, cwd: str | None, timeout: int) -> ToolResult:
"""Run via shell — supports pipes, redirects, etc."""
@@ -79,7 +167,7 @@
if stdout:
output_parts.append(stdout.decode(errors="replace"))
if stderr:
- output_parts.append(f"[stderr]\n{stderr.decode(errors='replace')}")
+ output_parts.append(f"[stderr]\n{stderr.decode(errors="replace")}")
combined = "\n".join(output_parts) or "(no output)"
if len(combined) > _MAX_OUTPUT_CHARS:
@@ -131,7 +219,7 @@
if stdout:
output_parts.append(stdout.decode(errors="replace"))
if stderr:
- output_parts.append(f"[stderr]\n{stderr.decode(errors='replace')}")
+ output_parts.append(f"[stderr]\n{stderr.decode(errors="replace")}")
combined = "\n".join(output_parts) or "(no output)"
if len(combined) > _MAX_OUTPUT_CHARS:
@@ -148,3 +236,25 @@
return ToolResult(success=False, output=f"Command not found: {tokens[0]}", error="not_found")
except Exception as e:
return ToolResult(success=False, output=f"Execution error: {e}", error=str(e))
+
+ async def _run_user_restricted(self, command: str, cwd: Path | None, timeout: int) -> ToolResult:
+ """Run for non-admin users: allowlist + shell features + sandbox cwd."""
+ try:
+ tokens = shlex.split(command)
+ except ValueError as e:
+ return ToolResult(success=False, output=f"Invalid command syntax: {e}", error=str(e))
+
+ if not tokens:
+ return ToolResult(success=False, output="Empty command.", error="empty_command")
+
+ allowed = settings.terminal_user_allowed_commands_list
+ if tokens[0] not in allowed:
+ return ToolResult(
+ success=False,
+ output=f"Command '{tokens[0]}' is not in the allowed list for non-admin users. "
+ f"Allowed: {allowed}.",
+ error="not_allowed",
+ )
+
+ # Shell invocation so pipes/redirects work, but we already validated the base command
+ return await self._run_shell(command, str(cwd) if cwd else None, timeout)
diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py
index 7c53a5a..e4b0e80 100644
--- a/tests/integration/conftest.py
+++ b/tests/integration/conftest.py
@@ -53,6 +53,18 @@
backends = BackendRegistry()
backends.register("ollama", FakeLLMBackend())
+ # Wire a FakePool onto the in-memory store so admin endpoints that call
+ # store._get_pool() can work in integration tests.
+ from tests.conftest_factory import FakeConnection, FakePool
+ _fake_conn = FakeConnection()
+ _fake_pool = FakePool(_fake_conn)
+
+ async def _fake_get_pool():
+ return _fake_pool
+
+ store._get_pool = _fake_get_pool
+ store._pool = _fake_pool
+
# Patch internal singletons so original getter functions return our fakes
monkeypatch.setattr(deps, "_session_store", store)
monkeypatch.setattr(deps, "_memory_store", None)
@@ -92,6 +104,8 @@
"tools": tools,
"backends": backends,
"agent": fake_agent,
+ "fake_conn": _fake_conn,
+ "fake_pool": _fake_pool,
}
diff --git a/tests/integration/test_api_routes.py b/tests/integration/test_api_routes.py
index 66859a2..065ab3d 100644
--- a/tests/integration/test_api_routes.py
+++ b/tests/integration/test_api_routes.py
@@ -178,3 +178,131 @@
def test_send_message_not_found(self, client):
response = client.post("/sessions/nonexistent/messages", json={"content": "hi"})
assert response.status_code == 404
+
+
+class TestAdmin:
+ def test_list_users(self, client, mock_deps):
+ mock_deps["fake_conn"].enqueue([
+ {"id": "u1", "email": "a@b.com", "display_name": "A", "role": "admin",
+ "permissions": "[]", "created_at": __import__("datetime").datetime.now(__import__("datetime").timezone.utc),
+ "updated_at": __import__("datetime").datetime.now(__import__("datetime").timezone.utc)}
+ ])
+ response = client.get("/admin/users")
+ assert response.status_code == 200
+ data = response.json()
+ assert isinstance(data, list)
+ assert data[0]["id"] == "u1"
+
+ @pytest.mark.anyio
+ async def test_get_session_detail(self, client, make_session):
+ session = await make_session("secretary", [Message(role="user", content="hi")])
+ response = client.get(f"/admin/sessions/{session.id}")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["session_id"] == session.id
+ assert "messages" in data
+
+ def test_get_session_detail_not_found(self, client):
+ response = client.get("/admin/sessions/nonexistent")
+ assert response.status_code == 404
+
+ @pytest.mark.anyio
+ async def test_delete_session(self, client, make_session):
+ session = await make_session("secretary")
+ response = client.delete(f"/admin/sessions/{session.id}")
+ assert response.status_code == 204
+ assert client.get(f"/admin/sessions/{session.id}").status_code == 404
+
+ def test_delete_session_not_found(self, client):
+ response = client.delete("/admin/sessions/nonexistent")
+ assert response.status_code == 404
+
+ @pytest.mark.anyio
+ async def test_get_user_sessions(self, client, make_session):
+ session = await make_session("secretary")
+ response = client.get(f"/admin/users/{session.user_id}/sessions")
+ assert response.status_code == 200
+ data = response.json()
+ assert isinstance(data, list)
+
+ def test_get_user_detail_not_found(self, client, mock_deps):
+ mock_deps["fake_conn"].enqueue(None)
+ response = client.get("/admin/users/nonexistent")
+ assert response.status_code == 404
+
+ def test_list_profiles(self, client):
+ response = client.get("/admin/profiles")
+ assert response.status_code == 200
+ data = response.json()
+ assert isinstance(data, list)
+ assert any(p["id"] == "secretary" for p in data)
+
+ def test_update_profile_availability(self, client, mock_deps):
+ mock_deps["fake_conn"].enqueue("INSERT 0 1")
+ response = client.patch("/admin/profiles/secretary/availability", json={"is_admin_only": True})
+ assert response.status_code == 200
+ data = response.json()
+ assert data["ok"] is True
+
+ @pytest.mark.anyio
+ async def test_list_sessions_pagination(self, client, make_session):
+ sessions = [await make_session("secretary") for _ in range(5)]
+ response = client.get("/admin/sessions?limit=2&offset=0")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["total"] == 5
+ assert len(data["items"]) == 2
+ assert data["limit"] == 2
+ assert data["offset"] == 0
+
+ @pytest.mark.anyio
+ async def test_list_sessions_pagination_offset(self, client, make_session):
+ sessions = [await make_session("secretary") for _ in range(5)]
+ response = client.get("/admin/sessions?limit=2&offset=4")
+ assert response.status_code == 200
+ data = response.json()
+ assert data["total"] == 5
+ assert len(data["items"]) == 1
+
+ @pytest.mark.anyio
+ async def test_list_sessions_search(self, client, make_session, mock_deps):
+ store = mock_deps["session_store"]
+ s1 = await make_session("secretary")
+ s1.name = "alpha session"
+ await store.save(s1)
+ s2 = await make_session("developer")
+ s2.name = "beta session"
+ await store.save(s2)
+ # search by name
+ response = client.get("/admin/sessions?search=alpha")
+ assert response.status_code == 200
+ data = response.json()
+ ids = {i["session_id"] for i in data["items"]}
+ assert s1.id in ids
+ assert s2.id not in ids
+ # search by profile
+ response = client.get("/admin/sessions?search=developer")
+ assert response.status_code == 200
+ data = response.json()
+ ids = {i["session_id"] for i in data["items"]}
+ assert s2.id in ids
+
+ @pytest.mark.anyio
+ async def test_list_sessions_sort(self, client, make_session, mock_deps):
+ store = mock_deps["session_store"]
+ s1 = await make_session("secretary")
+ s1.name = "aaa"
+ await store.save(s1)
+ s2 = await make_session("secretary")
+ s2.name = "zzz"
+ await store.save(s2)
+ response = client.get("/admin/sessions?sort_by=name&sort_order=asc")
+ assert response.status_code == 200
+ data = response.json()
+ names = [i["name"] for i in data["items"]]
+ assert names.index("aaa") < names.index("zzz")
+ response = client.get("/admin/sessions?sort_by=name&sort_order=desc")
+ assert response.status_code == 200
+ data = response.json()
+ names = [i["name"] for i in data["items"]]
+ assert names.index("zzz") < names.index("aaa")
diff --git a/tests/unit/memory/test_extractor.py b/tests/unit/memory/test_extractor.py
index 78caa73..dcb0b82 100644
--- a/tests/unit/memory/test_extractor.py
+++ b/tests/unit/memory/test_extractor.py
@@ -115,7 +115,7 @@
conn = FakeConnection()
conn.enqueue("OK") # mark_session_extracted
store = make_store_with_pool(conn)
- session = FakeSession([Message(role="user", content="hi")])
+ session = FakeSession([Message(role="user", content="hi")], user_id="user-1")
await extract_and_update(session, backend, "test-model", store)
assert any("session_memory_state" in c[1] for c in conn.calls)
@@ -134,7 +134,7 @@
])
conn.enqueue("OK") # set_summary
store = make_store_with_pool(conn)
- session = FakeSession([Message(role="user", content="My name is Eugene")])
+ session = FakeSession([Message(role="user", content="My name is Eugene")], user_id="user-1")
await extract_and_update(session, backend, "test-model", store)
assert any("memory_summary" in c[1] for c in conn.calls)
@@ -143,7 +143,16 @@
conn = FakeConnection()
conn.enqueue("OK") # mark_session_extracted
store = make_store_with_pool(conn)
- session = FakeSession([Message(role="user", content="hi")])
+ session = FakeSession([Message(role="user", content="hi")], user_id="user-1")
await extract_and_update(session, backend, "test-model", store)
# Should NOT call get_all_facts or set_summary
assert not any("memory_summary" in c[1] for c in conn.calls)
+
+ async def test_skips_legacy_sessions(self):
+ backend = FakeLLMBackend(responses=["[]"])
+ conn = FakeConnection()
+ store = make_store_with_pool(conn)
+ session = FakeSession([Message(role="user", content="hi")], user_id=None)
+ await extract_and_update(session, backend, "test-model", store)
+ # No DB calls for legacy (user_id=None) sessions
+ assert len(conn.calls) == 0
diff --git a/tests/unit/memory/test_store.py b/tests/unit/memory/test_store.py
index d01c4d1..3982f1b 100644
--- a/tests/unit/memory/test_store.py
+++ b/tests/unit/memory/test_store.py
@@ -128,6 +128,17 @@
await store.get_all_facts(limit=5)
assert "LIMIT $1" in conn.calls[0][1]
+ async def test_all_users(self):
+ conn = FakeConnection()
+ conn.enqueue([FakeRecord(id="1", category="profile", key="name", value="Eugene",
+ updated_at=None, source="conversation", confidence=90,
+ expires_at=None, source_context="")])
+ store = make_store_with_pool(conn)
+ results = await store.get_all_facts(all_users=True)
+ assert len(results) == 1
+ # Should not filter by user_id at all
+ assert "user_id" not in conn.calls[0][1]
+
class TestFactCount:
async def test_returns_count(self):
@@ -136,6 +147,13 @@
store = make_store_with_pool(conn)
assert await store.fact_count() == 42
+ async def test_all_users(self):
+ conn = FakeConnection()
+ conn.enqueue(100)
+ store = make_store_with_pool(conn)
+ assert await store.fact_count(all_users=True) == 100
+ assert "WHERE" not in conn.calls[0][1]
+
class TestSummary:
async def test_get_summary(self):
diff --git a/tests/unit/profiles/conftest.py b/tests/unit/profiles/conftest.py
new file mode 100644
index 0000000..5b94757
--- /dev/null
+++ b/tests/unit/profiles/conftest.py
@@ -0,0 +1,11 @@
+"""Fixtures for profile unit tests."""
+
+import pytest
+
+from tests.conftest_factory import FakeConnection, FakePool
+
+
+@pytest.fixture
+def fake_pool():
+ conn = FakeConnection()
+ return FakePool(conn)
diff --git a/tests/unit/profiles/test_overrides.py b/tests/unit/profiles/test_overrides.py
new file mode 100644
index 0000000..3ddce71
--- /dev/null
+++ b/tests/unit/profiles/test_overrides.py
@@ -0,0 +1,26 @@
+"""Unit tests for profile_overrides persistence."""
+
+import pytest
+
+from navi.profiles._overrides import ensure_table, load_overrides, save_override
+
+
+class TestProfileOverrides:
+ async def test_ensure_table_creates_table(self, fake_pool):
+ await ensure_table(fake_pool)
+ assert any("profile_overrides" in c[1] for c in fake_pool._conn.calls)
+
+ async def test_save_and_load_override(self, fake_pool):
+ await ensure_table(fake_pool)
+ fake_pool._conn.enqueue("INSERT 0 1")
+ await save_override(fake_pool, "secretary", True)
+
+ fake_pool._conn.enqueue([{"profile_id": "secretary", "is_admin_only": True}])
+ overrides = await load_overrides(fake_pool)
+ assert overrides == {"secretary": True}
+
+ async def test_load_overrides_empty(self, fake_pool):
+ await ensure_table(fake_pool)
+ fake_pool._conn.enqueue([])
+ overrides = await load_overrides(fake_pool)
+ assert overrides == {}
diff --git a/webclient/dist/assets/index-BPJq1Rk_.css b/webclient/dist/assets/index-BPJq1Rk_.css
new file mode 100644
index 0000000..0f40100
--- /dev/null
+++ b/webclient/dist/assets/index-BPJq1Rk_.css
@@ -0,0 +1 @@
+@charset "UTF-8";.vue-recycle-scroller__resize-observer[data-v-08cc04ab]{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none;z-index:-1}.vue-recycle-scroller{position:relative}.vue-recycle-scroller.direction-vertical:not(.page-mode){overflow-y:auto}.vue-recycle-scroller.direction-horizontal:not(.page-mode){overflow-x:auto}.vue-recycle-scroller.grid-mode:not(.page-mode){overflow:auto}.vue-recycle-scroller.direction-horizontal{display:flex}.vue-recycle-scroller__slot{flex:auto 0 0}.vue-recycle-scroller__item-wrapper{flex:1;box-sizing:border-box;overflow:hidden;position:relative}.vue-recycle-scroller.ready .vue-recycle-scroller__item-view{position:absolute;top:0;left:0;will-change:transform}.vue-recycle-scroller.direction-vertical .vue-recycle-scroller__item-wrapper{width:100%}.vue-recycle-scroller.direction-horizontal .vue-recycle-scroller__item-wrapper{height:100%}.vue-recycle-scroller.ready.direction-vertical .vue-recycle-scroller__item-view{width:100%}.vue-recycle-scroller.ready.direction-horizontal .vue-recycle-scroller__item-view{height:100%}.session-list-wrap[data-v-01e3820b]{flex:1;overflow:hidden;display:flex;flex-direction:column}.session-scroller[data-v-01e3820b]{flex:1;overflow-y:auto}.empty-sessions[data-v-01e3820b]{display:flex;flex-direction:column;align-items:center;gap:8px;padding:32px 16px;color:var(--color-text-dark, #787c99);font-size:13px}.empty-sessions i[data-v-01e3820b]{font-size:32px;opacity:.4}.empty-sessions p[data-v-01e3820b]{margin:0}.sessions-loading[data-v-01e3820b]{display:flex;align-items:center;justify-content:center;gap:10px;padding:28px 16px;color:var(--color-text-dark, #787c99);font-size:12px}.sessions-loading-more[data-v-01e3820b]{padding:10px 16px 14px}.sessions-spinner[data-v-01e3820b]{width:16px;height:16px;border:2px solid var(--color-border, #3b4261);border-top-color:var(--color-accent, #7aa2f7);border-radius:50%;animation:sessions-spin-01e3820b .8s linear infinite}@keyframes sessions-spin-01e3820b{to{transform:rotate(360deg)}}.sidebar-close-btn[data-v-aed5e18a]{display:none}@media(max-width:1280px){.sidebar-close-btn[data-v-aed5e18a]{display:flex;margin-left:auto}}.sidebar-footer[data-v-aed5e18a]{padding:12px 16px;border-top:1px solid var(--color-border);display:flex;align-items:center;justify-content:space-between;gap:8px}.user-info[data-v-aed5e18a]{display:flex;align-items:center;gap:8px;text-decoration:none;color:inherit;flex:1;min-width:0;cursor:pointer}.user-info:hover .user-name[data-v-aed5e18a]{color:var(--color-text-primary)}.user-avatar[data-v-aed5e18a]{width:28px;height:28px;border-radius:50%;object-fit:cover;flex-shrink:0}.user-avatar-fallback[data-v-aed5e18a]{display:flex;align-items:center;justify-content:center;background:var(--color-primary);color:#fff;font-size:12px;font-weight:600}.user-name[data-v-aed5e18a]{font-size:13px;color:var(--color-text-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-footer-actions[data-v-aed5e18a]{display:flex;align-items:center;gap:8px}.admin-link[data-v-aed5e18a]{text-decoration:none}.confirm-panel[data-v-49a56e4f]{max-width:420px;min-height:unset}.lightbox-panel[data-v-9d2a8160]{max-width:90vw;min-height:unset;width:fit-content;cursor:default}.lightbox-body[data-v-9d2a8160]{display:flex;align-items:center;justify-content:center;padding:0;max-height:calc(90vh - 60px);overflow:hidden}.lightbox-img[data-v-9d2a8160]{display:block;max-width:100%;max-height:calc(90vh - 60px);object-fit:contain}.selection-toolbar[data-v-615fec6a]{position:fixed;transform:translate(-50%);z-index:900;pointer-events:all;background:#e0af68;color:#000}.artifacts-panel[data-v-f99bc53c]{width:var(--artifacts-width, min(600px, 42vw));min-width:var(--artifacts-width, min(600px, 42vw));max-width:var(--artifacts-width, min(600px, 42vw));display:flex;flex-direction:column;background:var(--surface, #1e1e2e);border-left:1px solid var(--border, #2a2a3e);min-height:0;margin-right:calc(-1 * var(--artifacts-width, min(600px, 42vw)));opacity:0;pointer-events:none;transform:translate(100%);position:relative;transition:margin-right .22s ease,opacity .22s ease,transform .22s ease}.artifacts-panel.is-open[data-v-f99bc53c]{margin-right:0;opacity:1;pointer-events:auto;transform:translate(0)}.artifacts-panel.is-resizing[data-v-f99bc53c]{transition:none}.artifacts-resize-handle[data-v-f99bc53c]{position:absolute;top:0;left:-4px;bottom:0;width:8px;z-index:2;cursor:col-resize;touch-action:none}.artifacts-resize-handle[data-v-f99bc53c]:after{content:"";position:absolute;top:0;left:3px;width:1px;height:100%;background:transparent;transition:background .15s ease}.artifacts-resize-handle[data-v-f99bc53c]:hover:after,.artifacts-panel.is-resizing .artifacts-resize-handle[data-v-f99bc53c]:after{background:var(--accent, #4ec9b0)}.artifacts-header[data-v-f99bc53c]{height:64px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 14px;border-bottom:1px solid var(--border, #2a2a3e)}.artifacts-title[data-v-f99bc53c]{font-size:14px;font-weight:600;color:var(--text, #cdd6f4)}.artifacts-count[data-v-f99bc53c]{margin-top:2px;font-size:12px;color:var(--text-muted, #6c7086)}.artifacts-empty[data-v-f99bc53c]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-muted, #6c7086);font-size:13px}.artifacts-empty i[data-v-f99bc53c]{font-size:28px}.artifacts-list[data-v-f99bc53c]{max-height:232px;overflow-y:auto;border-bottom:1px solid var(--border, #2a2a3e)}.artifact-item[data-v-f99bc53c]{width:100%;min-height:58px;display:flex;align-items:center;gap:10px;padding:10px 14px;border:0;border-left:3px solid transparent;background:transparent;color:var(--text, #cdd6f4);text-align:left;cursor:pointer}.artifact-item[data-v-f99bc53c]:hover{background:#ffffff08}.artifact-item.is-active[data-v-f99bc53c]{border-left-color:var(--accent, #4ec9b0);background:#4ec9b014}.artifact-icon[data-v-f99bc53c]{width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;color:var(--accent, #4ec9b0);background:var(--accent-muted, #313244)}.artifact-info[data-v-f99bc53c]{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px}.artifact-title[data-v-f99bc53c],.artifact-detail-title[data-v-f99bc53c],.artifact-detail-filename[data-v-f99bc53c]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.artifact-title[data-v-f99bc53c]{font-size:13px;font-weight:500}.artifact-meta[data-v-f99bc53c]{font-size:11px;color:var(--text-muted, #6c7086)}.artifact-detail[data-v-f99bc53c]{flex:1;min-height:0;display:flex;flex-direction:column}.artifact-detail-header[data-v-f99bc53c]{padding:12px 14px 8px}.artifact-detail-title[data-v-f99bc53c]{font-size:14px;font-weight:600;color:var(--text, #cdd6f4)}.artifact-detail-filename[data-v-f99bc53c]{margin-top:3px;font-size:12px;color:var(--text-muted, #6c7086)}.artifact-actions[data-v-f99bc53c]{display:grid;grid-template-columns:repeat(auto-fit,minmax(42px,1fr));gap:6px;padding:0 14px 12px}.artifact-action[data-v-f99bc53c]{height:32px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--border, #2a2a3e);background:var(--surface-elevated, #242438);color:var(--text, #cdd6f4);text-decoration:none;cursor:pointer}.artifact-action[data-v-f99bc53c]:hover{border-color:var(--accent, #4ec9b0);color:var(--accent, #4ec9b0)}.artifact-preview[data-v-f99bc53c]{flex:1;min-height:0;border-top:1px solid var(--border, #2a2a3e);background:#11111b}.artifact-frame[data-v-f99bc53c],.artifact-image[data-v-f99bc53c],.artifact-video[data-v-f99bc53c]{width:100%;height:100%;border:0;display:block}.artifact-image[data-v-f99bc53c],.artifact-video[data-v-f99bc53c]{object-fit:contain}.artifact-source[data-v-f99bc53c]{height:100%;overflow:auto;background:#11111b}.artifact-source-code[data-v-f99bc53c],.artifact-source-error[data-v-f99bc53c]{margin:0;padding:14px;white-space:pre;font-family:IBM Plex Mono,monospace;font-size:12px;line-height:1.6;color:var(--text, #cdd6f4)}.artifact-source-error[data-v-f99bc53c]{color:var(--error, #f7768e)}.artifact-source-state[data-v-f99bc53c]{display:flex;align-items:center;gap:8px;padding:14px;color:var(--text-muted, #6c7086);font-size:12px}.artifact-no-preview[data-v-f99bc53c]{height:100%;min-height:220px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--text-muted, #6c7086);font-size:13px}.artifact-no-preview i[data-v-f99bc53c]{font-size:28px}@media(max-width:980px){.artifacts-panel[data-v-f99bc53c]{position:fixed;top:0;right:0;bottom:0;z-index:120;margin-right:0;transform:translate(100%);width:100vw;min-width:100vw;max-width:100vw}.artifacts-resize-handle[data-v-f99bc53c]{display:none}.artifacts-panel.is-open[data-v-f99bc53c]{transform:translate(0)}}@media(min-width:981px){.artifacts-close[data-v-f99bc53c]{display:none}}@media(prefers-reduced-motion:reduce){.artifacts-panel[data-v-f99bc53c]{transition:none}}.login-overlay[data-v-ff97de1a]{position:fixed;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#111;z-index:9999}.login-card[data-v-ff97de1a]{display:flex;flex-direction:column;align-items:center;gap:16px;padding:48px 40px;border:1px solid var(--color-border);border-radius:16px;max-width:360px;width:90%}.login-logo[data-v-ff97de1a]{width:64px;height:64px}.login-title[data-v-ff97de1a]{font-size:24px;font-weight:700;color:var(--color-text-primary);margin:0}.login-subtitle[data-v-ff97de1a]{font-size:14px;color:var(--color-text-secondary);margin:0}.login-btn[data-v-ff97de1a]{margin-top:8px;width:100%;justify-content:center}.app-shell[data-v-c95f3398]{display:flex;flex:1 1 0;min-height:0;min-width:0;overflow:hidden}.sidebar-backdrop[data-v-c95f3398]{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background:#0009;z-index:99}@media(max-width:1280px){.sidebar-backdrop[data-v-c95f3398]{display:block}}.artifacts-backdrop[data-v-c95f3398]{display:none;position:fixed;top:0;right:0;bottom:0;left:0;background:#00000073;z-index:119}@media(max-width:980px){.artifacts-backdrop[data-v-c95f3398]{display:block}}.backdrop-fade-enter-active[data-v-c95f3398],.backdrop-fade-leave-active[data-v-c95f3398]{transition:opacity .18s ease}.backdrop-fade-enter-from[data-v-c95f3398],.backdrop-fade-leave-to[data-v-c95f3398]{opacity:0}.welcome-sidebar-btn[data-v-fe297861]{display:none;position:absolute;top:12px;left:12px}@media(max-width:1280px){.welcome-sidebar-btn[data-v-fe297861]{display:flex}}.context-bar-pct[data-v-fe8d0787]{font-size:11px;font-weight:600;font-variant-numeric:tabular-nums;color:var(--color-success);white-space:nowrap}.context-bar-pct.is-warn[data-v-fe8d0787]{color:var(--color-warning)}.context-bar-pct.is-crit[data-v-fe8d0787]{color:var(--color-error)}.btn-artifacts-toggle[data-v-ed66e8ab]{position:relative}.artifact-count[data-v-ed66e8ab]{position:absolute;top:-3px;right:-3px;min-width:16px;height:16px;display:inline-flex;align-items:center;justify-content:center;padding:0 4px;font-size:10px;line-height:1;background:var(--accent, #4ec9b0);color:#11111b}.content-card[data-v-929d4ed2]{margin:8px 0;background:var(--surface, #1e1e2e);border:1px solid var(--border, #2a2a3e);overflow:hidden}.content-card summary[data-v-929d4ed2]{display:flex;align-items:center;gap:8px;padding:10px 14px;cursor:pointer;-webkit-user-select:none;user-select:none;list-style:none}.content-card summary[data-v-929d4ed2]::-webkit-details-marker{display:none}.content-icon[data-v-929d4ed2]{font-size:1.2em;line-height:1}.content-title[data-v-929d4ed2]{flex:1;font-size:.9em;font-weight:500;color:var(--text, #cdd6f4);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.content-badge[data-v-929d4ed2]{font-size:.7em;text-transform:uppercase;padding:2px 8px;background:var(--accent-muted, #313244);color:var(--accent, #4ec9b0)}.content-actions[data-v-929d4ed2]{display:inline-flex;align-items:center;gap:4px}.content-actions a[data-v-929d4ed2],.content-actions button[data-v-929d4ed2]{width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;border:1px solid var(--border, #2a2a3e);background:var(--surface-elevated, #242438);color:var(--text, #cdd6f4);text-decoration:none;cursor:pointer}.content-actions a[data-v-929d4ed2]:hover,.content-actions button[data-v-929d4ed2]:hover{color:var(--accent, #4ec9b0);border-color:var(--accent, #4ec9b0)}.content-chevron[data-v-929d4ed2]{transition:transform .2s;color:var(--text-muted, #6c7086)}.content-card[open] .content-chevron[data-v-929d4ed2]{transform:rotate(180deg)}.content-body[data-v-929d4ed2]{border-top:1px solid var(--border, #2a2a3e);overflow:auto}.content-iframe[data-v-929d4ed2]{width:100%;height:300px;border:none;display:block}@media(min-width:768px){.content-iframe[data-v-929d4ed2]{height:500px}}.content-image[data-v-929d4ed2]{width:100%;max-height:60vh;object-fit:contain;cursor:zoom-in;display:block;background:var(--surface, #1e1e2e)}.content-video[data-v-929d4ed2]{width:100%;max-height:60vh;display:block;background:var(--surface, #1e1e2e)}.content-source[data-v-929d4ed2]{max-height:500px;overflow:auto;background:#11111b}.content-source-code[data-v-929d4ed2],.content-source-error[data-v-929d4ed2]{margin:0;padding:14px;white-space:pre;font-family:IBM Plex Mono,monospace;font-size:12px;line-height:1.6;color:var(--text, #cdd6f4)}.content-source-error[data-v-929d4ed2]{color:var(--error, #f7768e)}.content-source-state[data-v-929d4ed2]{display:flex;align-items:center;gap:8px;padding:14px;color:var(--text-muted, #6c7086);font-size:12px}.content-unknown[data-v-929d4ed2]{padding:24px;text-align:center;color:var(--text-muted, #6c7086);display:flex;flex-direction:column;align-items:center;gap:10px}.unknown-icon[data-v-929d4ed2]{font-size:2em;line-height:1}.unknown-name[data-v-929d4ed2]{font-size:.95em;font-weight:500;color:var(--text, #cdd6f4);word-break:break-word;max-width:100%}.unknown-open[data-v-929d4ed2]{display:inline-block;margin-top:4px;padding:6px 14px;border-radius:0;background:var(--accent-muted, #313244);color:var(--accent, #4ec9b0);text-decoration:none;font-size:.85em;font-weight:500;transition:background .15s}.unknown-open[data-v-929d4ed2]:hover{background:var(--border, #2a2a3e)}.content-card.is-stl .content-badge[data-v-929d4ed2]{color:#f5c2e7}.content-card.is-html .content-badge[data-v-929d4ed2]{color:#89b4fa}.content-card.is-svg .content-badge[data-v-929d4ed2]{color:#a6e3a1}.content-card.is-pdf .content-badge[data-v-929d4ed2]{color:#fab387}.content-card.is-image .content-badge[data-v-929d4ed2]{color:#89dceb}.content-card.is-video .content-badge[data-v-929d4ed2]{color:#f38ba8}.stream-cursor[data-v-1fd4166a]{display:inline-block;width:2px;height:1em;background:currentColor;margin-left:2px;vertical-align:text-bottom;animation:blink-1fd4166a 1s step-end infinite}@keyframes blink-1fd4166a{0%,to{opacity:1}50%{opacity:0}}.msg-rate-btn[data-v-1fd4166a]{opacity:.55;transition:opacity .15s,color .15s}.msg-rate-btn[data-v-1fd4166a]:hover{opacity:1}.msg-rate-btn.active[data-v-1fd4166a]{opacity:1;color:var(--accent, #4ec9b0)}.message-list-wrap[data-v-6967a257]{flex:1;min-height:0;position:relative;display:flex;flex-direction:column}.messages-group[data-v-6967a257]{display:flex;flex-direction:column;gap:32px}.scroll-to-bottom-btn[data-v-6967a257]{position:absolute;bottom:16px;right:16px;width:40px;height:40px;border-radius:50%;background:var(--color-secondary, #7aa2f7);color:var(--color-black, #1a1b2e);border:none;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;box-shadow:0 2px 12px #0006;opacity:.9;transition:opacity .15s ease,transform .15s ease;z-index:10}.scroll-to-bottom-btn[data-v-6967a257]:hover{opacity:1;transform:translateY(-2px)}.scroll-btn-enter-active[data-v-6967a257],.scroll-btn-leave-active[data-v-6967a257]{transition:opacity .2s ease,transform .2s ease}.scroll-btn-enter-from[data-v-6967a257],.scroll-btn-leave-to[data-v-6967a257]{opacity:0;transform:translateY(8px)}.file-preview-image[data-v-0239fe0c]{position:relative}.file-preview-image .file-preview-remove[data-v-0239fe0c]{position:absolute;top:-4px;right:-4px;background:var(--surface-panel, #1f2335);border:1px solid var(--border-color-muted);width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:10px;cursor:pointer;color:var(--color-text-dark)}.file-preview-image .file-preview-remove[data-v-0239fe0c]:hover{color:var(--color-error)}.reconnect-notice[data-v-8e422257]{display:flex;align-items:center;gap:6px;padding:4px 8px;font-size:12px;color:var(--color-warning, #e0af68);margin-bottom:4px}.reconnect-banner[data-v-9312ef2d]{display:flex;align-items:center;gap:8px;padding:6px 16px;background:#e0af681a;border-bottom:1px solid rgba(224,175,104,.3);font-size:13px;color:var(--color-warning, #e0af68)}@font-face{font-family:IBM Plex Mono;src:url(/assets/fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf) format("truetype");font-weight:400;font-style:normal;font-display:swap}@font-face{font-family:IBM Plex Mono;src:url(/assets/fonts/IBM_Plex_Mono/IBMPlexMono-Medium.ttf) format("truetype");font-weight:500;font-style:normal;font-display:swap}@font-face{font-family:IBM Plex Mono;src:url(/assets/fonts/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf) format("truetype");font-weight:600;font-style:normal;font-display:swap}@font-face{font-family:IBM Plex Mono;src:url(/assets/fonts/IBM_Plex_Mono/IBMPlexMono-Bold.ttf) format("truetype");font-weight:700;font-style:normal;font-display:swap}@font-face{font-family:IBM Plex Mono;src:url(/assets/fonts/IBM_Plex_Mono/IBMPlexMono-Italic.ttf) format("truetype");font-weight:400;font-style:italic;font-display:swap}.container{padding:18px}.section{margin-bottom:48px}.section-title,.block{margin-bottom:34px}.block-title{margin-bottom:22px}p,.text{margin-bottom:15px}.hint{margin-top:8px}.list{padding-left:22px;margin-bottom:15px}.list-item{margin-bottom:8px}.list-nested{margin-top:8px}.table{margin-bottom:22px}.table-caption{margin-bottom:8px}.form-group{margin-bottom:15px}.label{margin-bottom:5px;display:block}.input,.textarea,.select{margin-top:5px}.toast{padding:15px}.toast-stack{gap:8px}@keyframes terminal_scan_x{0%{transform:translate(-120%)}to{transform:translate(220%)}}@keyframes terminal_scan_y{0%{transform:translateY(-120%)}to{transform:translateY(220%)}}@keyframes terminal_pulse{0%,to{box-shadow:0 0 #c0caf500}50%{box-shadow:0 0 0 4px #c0caf52e}}@keyframes panel_boot{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes overlay_reveal{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes tooltip_reveal{0%{opacity:0;transform:translate(-50%) translateY(5px)}to{opacity:1;transform:translate(-50%) translateY(0)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}html{font-size:100%}body{font-family:IBM Plex Mono,monospace;font-size:15px;font-weight:400;line-height:1.6;letter-spacing:0;color:#c0caf5}h1,h2,h3,h4,h5,h6{font-family:IBM Plex Mono,monospace;font-weight:600;line-height:1.25;margin:0}h1.contrast,h2.contrast,h3.contrast,h4.contrast,h5.contrast,h6.contrast{background:#c0caf5;color:#16161e;display:inline;padding:0 8px}h1{font-size:34px;letter-spacing:0}h2{font-size:26px}h3{font-size:22px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px;font-weight:500}.text,p{font-size:15px;line-height:1.6}.text-sm{font-size:13px;line-height:1.4}.text-lg{font-size:16px;line-height:1.6}.text-lead{max-width:760px;color:#c0caf5;font-size:16px;font-weight:500;line-height:1.6}.text-muted{font-size:13px;color:#787c99}.text-strong,strong{font-weight:600}.text-bold{font-weight:700}.text-italic,em{font-style:italic}.text-success{color:#9ece6a}.text-warning{color:#e0af68}.text-danger,.text-error{color:#f7768e}.text-info{color:#bb9af7}.eyebrow{display:inline-flex;width:max-content;max-width:100%;padding:5px 8px;color:#16161e;background:#7aa2f7;font-size:12px;font-weight:700;line-height:1;text-transform:uppercase}.caption{color:#787c99;font-size:12px;line-height:1.4}code,pre,.code{font-family:IBM Plex Mono,monospace;font-size:15px;line-height:1.4;background-color:#1f2335}.text-primary{color:#c0caf5}.text-secondary{color:#a9b1d6}pre{font-size:15px;line-height:1.6;white-space:pre-wrap}pre code,.code{tab-size:2;-moz-tab-size:2}.code{display:inline-flex;padding:0 5px;color:#7aa2f7;border:2px solid rgba(122,162,247,.24)}.kbd{display:inline-flex;align-items:center;min-height:24px;padding:0 8px;border:2px solid rgba(192,202,245,.24);border-bottom-color:#7aa2f7;color:#c0caf5;background:#c0caf50b;font-family:IBM Plex Mono,monospace;font-size:12px;font-weight:700;line-height:1;text-transform:uppercase}.quote{max-width:760px;margin:0;padding:15px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;color:#a9b1d6;background:#c0caf50b;font-size:15px;line-height:1.6}.quote cite{display:block;margin-top:12px;color:#7aa2f7;font-size:13px;font-style:normal;text-transform:uppercase}a{font-weight:500;text-decoration:none;color:#7aa2f7}@media(hover:hover)and (pointer:fine){a:hover{color:#e0af68}}@media(hover:none)and (pointer:coarse){a:active{color:#e0af68}}.link{font-size:inherit;font-weight:500}.label{font-size:13px;font-weight:500;line-height:1.4}.hint,.meta{font-size:12px;line-height:1.4}.table{font-size:13px;line-height:1.4}.table th{font-weight:600}.table td{font-weight:400}.list{font-size:15px;line-height:1.6}.list-item{font-size:inherit}.modal-title{font-size:20px;font-weight:600}.modal-body{font-size:15px}.toast-title{font-size:14px;font-weight:600}.toast-text{font-size:13px;line-height:1.4}.palette{display:flex;flex-direction:row;gap:8px}.palette .color .color-box{width:92px;height:68px}body .bg-primary{background:#c0caf5}body .bg-secondary{background:#7aa2f7}body .bg-success{background:#9ece6a}body .bg-accent{background:#ff9e64}body .bg-info{background:#bb9af7}body .bg-warning{background:#e0af68}body .bg-error{background:#f7768e}body .text-color-primary{color:#c0caf5}body .text-color-secondary{color:#7aa2f7}body .text-color-success{color:#9ece6a}body .text-color-accent{color:#ff9e64}body .text-color-info{color:#bb9af7}body .text-color-warning{color:#e0af68}body .text-color-error{color:#f7768e}.loader{width:32px;aspect-ratio:1;--c:no-repeat linear-gradient(#FF3C00 0 0);background:var(--c) 0 0,var(--c) 0 100%,var(--c) 50% 0,var(--c) 50% 100%,var(--c) 100% 0,var(--c) 100% 100%;animation:l12 1s infinite}@keyframes l12{0%,to{background-size:20% 50%}16.67%{background-size:20% 30%,20% 30%,20% 50%,20% 50%,20% 50%,20% 50%}33.33%{background-size:20% 30%,20% 30%,20% 30%,20% 30%,20% 50%,20% 50%}50%{background-size:20% 30%,20% 30%,20% 30%,20% 30%,20% 30%,20% 30%}66.67%{background-size:20% 50%,20% 50%,20% 30%,20% 30%,20% 30%,20% 30%}83.33%{background-size:20% 50%,20% 50%,20% 50%,20% 50%,20% 30%,20% 30%}}.circle-loader{display:flex;flex-direction:row;align-items:center;gap:8px}.circle-loader .ph,.circle-loader .ph-bold{font-size:26px;transform-origin:50% 50%;animation:icon_spin 1.2s linear infinite}.progress{display:flex;flex-direction:column;gap:8px;width:100%;max-width:640px}.progress .progress-header{display:flex;align-items:center;justify-content:space-between;gap:12px;color:#a9b1d6;font-size:13px;font-weight:600;text-transform:uppercase}.progress .progress-value{color:#c0caf5;font-family:IBM Plex Mono,monospace}.progress .progress-track{position:relative;width:100%;height:18px;overflow:hidden;border:2px solid rgba(192,202,245,.24);background:#c0caf50b}.progress .progress-bar{display:block;position:relative;overflow:hidden;width:var(--progress-value, 0%);height:100%;background:#7aa2f7;transition:width .28s ease}.progress.progress-success .progress-bar{background:#9ece6a}.progress.progress-warning .progress-bar{background:#e0af68}.progress.progress-danger .progress-bar,.progress.progress-error .progress-bar{background:#f7768e}.progress.progress-striped .progress-bar{background-image:repeating-linear-gradient(90deg,transparent 0,transparent 14px,rgba(22,22,30,.2) 14px,rgba(22,22,30,.2) 16px)}.progress.progress-animated .progress-bar:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;width:48%;background:linear-gradient(90deg,transparent,rgba(192,202,245,.28),transparent);transform:translate(-120%);animation:progress_scan 1.4s ease infinite}.usage-meter{display:grid;gap:12px;width:100%;max-width:420px;padding:15px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.usage-meter .usage-meter-title{display:flex;align-items:center;justify-content:space-between;gap:12px;margin:0;font-size:16px;font-weight:700;line-height:1;text-transform:uppercase}.usage-meter .usage-meter-value{color:#7aa2f7;font-family:IBM Plex Mono,monospace;font-size:13px}.usage-meter .usage-meter-meta{margin:0;color:#a9b1d6;font-size:13px;line-height:1.4}.progress-stages{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;width:100%;max-width:720px}.progress-stages .progress-stage{min-height:42px;padding:8px 12px;border:2px solid rgba(192,202,245,.24);color:#787c99;background:#c0caf50b;font-size:13px;font-weight:600;line-height:1.4;text-transform:uppercase}.progress-stages .progress-stage-complete{color:#16161e;background:#9ece6a;border-color:#9ece6a}.progress-stages .progress-stage-current{color:#16161e;background:#e0af68;border-color:#e0af68}@media(max-width:767px){.progress-stages{grid-template-columns:1fr 1fr}}@media(max-width:479px){.progress-stages{grid-template-columns:1fr}}@keyframes progress_scan{0%{transform:translate(-120%)}to{transform:translate(220%)}}@keyframes icon_spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.btn{display:inline-flex;align-items:center;justify-content:center;min-height:46px;font-family:IBM Plex Mono,monospace;font-size:15px;font-weight:600;line-height:1;letter-spacing:.04em;padding:12px 22px;border-radius:0;border-width:2px;border-left-width:6px;border-style:solid;border-color:#c0caf5;text-transform:uppercase;background-color:transparent;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:background-color,border-color,color,opacity}.btn:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}.btn.with-icon{border-left-width:46px;position:relative}.btn.with-icon .ph,.btn.with-icon .ph-bold{position:absolute;color:#16161e;left:-46px;top:1px;font-size:26px;height:100%;display:inline-flex;align-items:center;width:46px;justify-content:center;transition-duration:.2s;transition-property:color,left}@media(hover:hover)and (pointer:fine){.btn:hover.with-icon:not(.loading-state):not(.btn-small) .ph,.btn:hover.with-icon:not(.loading-state):not(.btn-small) .ph-bold{left:-28px}}@media(hover:none)and (pointer:coarse){.btn:active.with-icon:not(.loading-state):not(.btn-small) .ph,.btn:active.with-icon:not(.loading-state):not(.btn-small) .ph-bold{left:-28px}}.btn.btn-primary{color:#c0caf5;border-color:#c0caf5}@media(hover:hover)and (pointer:fine){.btn.btn-primary:hover{background-color:#c0caf5;color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-primary:active{background-color:#c0caf5;color:#16161e}}.btn.btn-secondary{color:#7aa2f7;border-color:#7aa2f7}@media(hover:hover)and (pointer:fine){.btn.btn-secondary:hover{background-color:#7aa2f7;color:#16161e}.btn.btn-secondary:hover.with-icon .ph,.btn.btn-secondary:hover.with-icon .ph-bold{color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-secondary:active{background-color:#7aa2f7;color:#16161e}.btn.btn-secondary:active.with-icon .ph,.btn.btn-secondary:active.with-icon .ph-bold{color:#16161e}}.btn.btn-accent{color:#ff9e64;border-color:#ff9e64}@media(hover:hover)and (pointer:fine){.btn.btn-accent:hover{background-color:#ff9e64;color:#16161e}.btn.btn-accent:hover.with-icon .ph,.btn.btn-accent:hover.with-icon .ph-bold{color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-accent:active{background-color:#ff9e64;color:#16161e}.btn.btn-accent:active.with-icon .ph,.btn.btn-accent:active.with-icon .ph-bold{color:#16161e}}.btn.btn-danger{color:#f7768e;border-color:#f7768e}@media(hover:hover)and (pointer:fine){.btn.btn-danger:hover{background-color:#f7768e;color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-danger:active{background-color:#f7768e;color:#16161e}}.btn.btn-warning{color:#e0af68;border-color:#e0af68}@media(hover:hover)and (pointer:fine){.btn.btn-warning:hover{background-color:#e0af68;color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-warning:active{background-color:#e0af68;color:#16161e}}.btn.btn-success{color:#9ece6a;border-color:#9ece6a}@media(hover:hover)and (pointer:fine){.btn.btn-success:hover{background-color:#9ece6a;color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-success:active{background-color:#9ece6a;color:#16161e}}.btn.btn-info{color:#bb9af7;border-color:#bb9af7}@media(hover:hover)and (pointer:fine){.btn.btn-info:hover{background-color:#bb9af7;color:#16161e}.btn.btn-info:hover.with-icon .ph,.btn.btn-info:hover.with-icon .ph-bold{color:#16161e}}@media(hover:none)and (pointer:coarse){.btn.btn-info:active{background-color:#bb9af7;color:#16161e}.btn.btn-info:active.with-icon .ph,.btn.btn-info:active.with-icon .ph-bold{color:#16161e}}.btn[disabled]:not(.loading-state){color:#787c99;border-color:#c0caf53d;background-color:#1f2335;cursor:not-allowed;opacity:.72}.btn[disabled]:not(.loading-state).with-icon .ph,.btn[disabled]:not(.loading-state).with-icon .ph-bold{color:#787c99}@media(hover:hover)and (pointer:fine){.btn[disabled]:not(.loading-state):hover{background-color:#1f2335;color:#787c99}.btn[disabled]:not(.loading-state):hover.with-icon .ph,.btn[disabled]:not(.loading-state):hover.with-icon .ph-bold{color:#787c99}}@media(hover:none)and (pointer:coarse){.btn[disabled]:not(.loading-state):active{background-color:#1f2335;color:#787c99}.btn[disabled]:not(.loading-state):active.with-icon .ph,.btn[disabled]:not(.loading-state):active.with-icon .ph-bold{color:#787c99}}.btn[disabled]:not(.loading-state).with-icon:not(.btn-small) .ph,.btn[disabled]:not(.loading-state).with-icon:not(.btn-small) .ph-bold{left:-28px}.btn.btn-small{font-size:13px;font-weight:500;min-height:38px;padding:8px}.btn.btn-small.with-icon{border-left-width:32px}.btn.btn-small.with-icon .ph,.btn.btn-small.with-icon .ph-bold{top:0;left:-40px;font-size:22px}.btn.btn-small.with-icon.loading-state .ph,.btn.btn-small.with-icon.loading-state .ph-bold{font-size:26px}.btn.btn-large{font-size:16px;font-weight:700;min-height:54px;padding:15px 48px}.btn.loading-state{color:#16161e!important;border-color:#c0caf5!important;background-color:#c0caf5!important}.btn.loading-state .ph,.btn.loading-state .ph-bold{font-size:26px;transform-origin:50% 50%;animation:icon_spin 1.2s linear infinite}.btn-icon{display:flex;justify-content:center;align-items:center;width:38px;height:38px;background:transparent;color:#c0caf5;font-size:22px;border:2px solid transparent;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background,border-color}.btn-icon:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}.btn-icon .ph{transform:rotate(0);transition-duration:.2s;transition-property:transform}@media(hover:hover)and (pointer:fine){.btn-icon:not(.without-hover):hover{color:#e0af68;border-color:#e0af68}.btn-icon:not(.without-hover):hover .ph{transform:rotate(90deg)}}@media(hover:none)and (pointer:coarse){.btn-icon:not(.without-hover):active{color:#e0af68;border-color:#e0af68}.btn-icon:not(.without-hover):active .ph{transform:rotate(90deg)}}.form-group{width:100%;max-width:600px}.form-group .label{display:flex;flex-direction:column;font-size:15px;width:100%;position:relative}.form-group .label>.ph{position:absolute;color:#c0caf5;left:0;bottom:1px;font-size:26px;height:54px;display:inline-flex;align-items:center;width:46px;justify-content:center;transition-duration:.2s;transition-property:color,left}.form-group .label .input{min-height:54px;font-family:IBM Plex Mono,monospace;font-size:15px;font-weight:500;line-height:1;letter-spacing:.04em;padding:12px 22px;margin-top:8px;border-radius:0;border-width:2px;border-bottom-width:6px;border-style:solid;border-color:#c0caf5;color:#c0caf5;background-color:#c0caf50b;transition-duration:.2s;transition-timing-function:ease;transition-property:background-color,border-color,color}@media(hover:hover)and (pointer:fine){.form-group .label .input:hover{border-bottom-color:#787c99}}@media(hover:none)and (pointer:coarse){.form-group .label .input:active{border-bottom-color:#787c99}}.form-group .label .input:focus{outline:2px solid #E0AF68;outline-offset:3px;border-color:#7aa2f7;background-color:transparent}.form-group .label .input:disabled{color:#787c99;border-color:#c0caf53d;background:#1f2335;cursor:not-allowed;opacity:.72}.form-group .label .input[readonly]{color:#a9b1d6;border-color:#c0caf53d;background:#c0caf508}.form-group .label .input::placeholder{color:#787c99}.form-group .label .input::-webkit-search-cancel-button,.form-group .label .input::-webkit-search-decoration,.form-group .label .input::-webkit-search-results-button,.form-group .label .input::-webkit-search-results-decoration{display:none;-webkit-appearance:none}.form-group .label textarea.input{height:108px;line-height:1.25;resize:none}.form-group .label .ph+.input,.form-group .label .ph+.select-wrap .select{padding-left:46px}.form-group .label .select-wrap{margin-top:8px}.form-group .label .select{width:100%;height:54px;margin-top:0;appearance:none;-webkit-appearance:none;-moz-appearance:none}.form-group .label .select:focus{outline:none}.form-group .label .select option{color:#c0caf5;background:#1f2335}.form-group .label .select-wrap:after{content:"";position:absolute;right:22px;bottom:18px;transform:translateY(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-top:10px solid #C0CAF5;pointer-events:none}.form-group .label.error .input:not(:focus){border-color:#f7768e}.form-group .label.error+.input-info{color:#e0af68}.form-group .label.success .input:not(:focus){border-color:#9ece6a}.form-group .label.success+.input-info{color:#9ece6a}.form-group .label.warning .input:not(:focus){border-color:#e0af68}.form-group .label.warning+.input-info{color:#e0af68}.form-group .input-info{font-size:14px;margin-top:8px}.form-group .input-info .ph{position:relative;top:1px}.form-group .input-info.error{color:#e0af68}.form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:15px;width:100%;max-width:760px}.fieldset{width:100%;max-width:760px;margin:0;padding:18px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.legend{padding:5px 8px;color:#16161e;background:#c0caf5;font-size:13px;font-weight:700;line-height:1;text-transform:uppercase}.file-upload{display:inline-flex;align-items:center;gap:8px;min-height:46px;padding:8px 12px;border:2px solid #7AA2F7;border-left-width:6px;color:#7aa2f7;background:#c0caf50b;font-size:13px;font-weight:700;text-transform:uppercase;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background,border-color}.file-upload input[type=file]{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@media(hover:hover)and (pointer:fine){.file-upload:hover{color:#16161e;background:#7aa2f7}}@media(hover:none)and (pointer:coarse){.file-upload:active{color:#16161e;background:#7aa2f7}}.file-upload:focus-within{outline:2px solid #E0AF68;outline-offset:3px}.range{width:100%;max-width:600px;accent-color:#7AA2F7}.range input[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:46px;margin:0;background:transparent;cursor:pointer}.range input[type=range]::-webkit-slider-runnable-track{height:6px;background:#c0caf516;border:2px solid rgba(192,202,245,.24)}.range input[type=range]::-webkit-slider-thumb{width:18px;height:38px;margin-top:-19px;border:2px solid #7AA2F7;background:#7aa2f7;-webkit-appearance:none}.range input[type=range]::-moz-range-track{height:6px;background:#c0caf516;border:2px solid rgba(192,202,245,.24)}.range input[type=range]::-moz-range-thumb{width:18px;height:38px;border:2px solid #7AA2F7;border-radius:0;background:#7aa2f7}@media(max-width:767px){.form-grid{grid-template-columns:1fr}}.radio{display:inline-flex;flex-direction:row;gap:8px;align-items:center}.radio input[type=radio]{display:none}.radio .radio-control{display:inline-block;border-radius:100%;border:2px solid #C0CAF5;width:18px;height:18px;background:transparent;transition-duration:.2s;transition-property:background,border-color}@media(hover:hover)and (pointer:fine){.radio:hover .radio-control{background:#414868}}@media(hover:none)and (pointer:coarse){.radio:active .radio-control{background:#414868}}.radio input[type=radio]:checked+.radio-control{background:#c0caf5}.radio input[type=radio]:disabled+.radio-control{background:#414868;border-color:#414868}.radio input[type=radio]:focus-visible+.radio-control{outline:2px solid #E0AF68;outline-offset:3px}.radio .radio-label{font-size:15px}.checkbox{display:inline-flex;flex-direction:row;gap:8px;align-items:center}.checkbox input[type=checkbox]{display:none}.checkbox .checkbox-control{height:16px;width:32px;border:2px solid #C0CAF5;position:relative;background:transparent;transition-duration:.2s;transition-property:border-color,background;display:block}.checkbox .checkbox-control:before{content:"";display:block;height:20px;width:20px;background:#c0caf5;position:absolute;left:-5px;top:-5px;transition-duration:.2s;transition-property:left,background}@media(hover:hover)and (pointer:fine){.checkbox:hover .checkbox-control{background:#414868}}@media(hover:none)and (pointer:coarse){.checkbox:active .checkbox-control{background:#414868}}.checkbox input[type=checkbox]:checked:not(:disabled)+.checkbox-control{background:#7aa2f7;border-color:#7aa2f7}.checkbox input[type=checkbox]:checked+.checkbox-control:before{left:17px}.checkbox input[type=checkbox]:disabled+.checkbox-control{border-color:#414868}.checkbox input[type=checkbox]:focus-visible+.checkbox-control{outline:2px solid #E0AF68;outline-offset:3px}.checkbox input[type=checkbox]:disabled+.checkbox-control:before{background:#414868}.input-group{display:flex;align-items:stretch;width:100%;max-width:600px;min-height:54px;border:2px solid #C0CAF5;border-bottom-width:6px;background:#c0caf50b;transition-duration:.2s;transition-timing-function:ease;transition-property:border-color,background}.input-group:focus-within{outline:2px solid #E0AF68;outline-offset:3px;border-color:#7aa2f7;background:transparent}.input-group .input-group-addon,.input-group .input-group-action{display:inline-flex;align-items:center;justify-content:center;min-width:54px;padding:0 12px;color:#a9b1d6;background:#c0caf50b;border:0;font-family:IBM Plex Mono,monospace;font-size:13px;font-weight:600;text-transform:uppercase}.input-group .input-group-action{color:#c0caf5;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background}@media(hover:hover)and (pointer:fine){.input-group .input-group-action:hover{color:#16161e;background:#e0af68}}@media(hover:none)and (pointer:coarse){.input-group .input-group-action:active{color:#16161e;background:#e0af68}}.input-group .input-group-input{flex:1 1 auto;min-width:0;border:0;padding:12px 15px;color:#c0caf5;background:transparent;font-family:IBM Plex Mono,monospace;font-size:15px;font-weight:500;letter-spacing:.04em}.input-group .input-group-input:focus{outline:none}.input-group .input-group-input::placeholder{color:#787c99}.input-group .input-group-input::-webkit-search-cancel-button,.input-group .input-group-input::-webkit-search-decoration,.input-group .input-group-input::-webkit-search-results-button,.input-group .input-group-input::-webkit-search-results-decoration{display:none;-webkit-appearance:none}.input-group .ph,.input-group .ph-bold{font-size:22px}.input-group.input-group-compact{min-height:46px}.input-group.input-group-compact .input-group-addon,.input-group.input-group-compact .input-group-action{min-width:46px}.input-group.input-group-compact .input-group-input{padding:8px 12px;font-size:13px}.search-field{max-width:420px}.list{display:flex;flex-direction:column;gap:5px;list-style-type:none;padding-left:0}.list .list-item{display:flex;flex-direction:row;align-items:center;gap:8px;margin-left:0}.list.list-ordered{list-style-type:decimal;display:list-item;margin-left:30px}.list.list-ordered .list-item{display:list-item}.list.list-definition{width:100%;max-width:620px;gap:0;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.list.list-definition .list-row{display:grid;grid-template-columns:minmax(120px,.32fr) minmax(0,1fr);gap:15px;align-items:start;padding:12px 15px;border-bottom:2px solid rgba(192,202,245,.08);transition-duration:.2s;transition-timing-function:ease;transition-property:background,border-color}.list.list-definition .list-row .list-term{display:inline-flex;width:max-content;max-width:100%;margin:0;padding:5px 8px;color:#16161e;background:#c0caf5;font-size:13px;font-weight:700;line-height:1;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:background,transform}.list.list-definition .list-row .list-desc{margin:0;color:#a9b1d6;font-size:13px;line-height:1.6;transition-duration:.2s;transition-timing-function:ease;transition-property:color,transform}.list.list-definition .list-row:last-child{border-bottom:0}@media(hover:hover)and (pointer:fine){.list.list-definition .list-row:hover{background:#c0caf516}.list.list-definition .list-row:hover .list-term{background:#7aa2f7;transform:translate(5px)}.list.list-definition .list-row:hover .list-desc{color:#c0caf5;transform:translate(5px)}}@media(hover:none)and (pointer:coarse){.list.list-definition .list-row:active{background:#c0caf516}.list.list-definition .list-row:active .list-term{background:#7aa2f7;transform:translate(5px)}.list.list-definition .list-row:active .list-desc{color:#c0caf5;transform:translate(5px)}}.list.list-nav{max-width:420px;width:100%;gap:0}.list.list-nav .list-item{display:flex;flex-direction:column;align-items:flex-start;height:50px;margin:0}.list.list-nav .list-item .list-action{display:flex;justify-content:space-between;align-items:center;width:100%;height:100%;padding:8px 12px;border:2px solid transparent;font-size:15px;background:#1f2335;color:inherit;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:background,border-color,color}.list.list-nav .list-item .list-action:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){.list.list-nav .list-item .list-action:hover{background:#7aa2f7;color:#16161e}}@media(hover:none)and (pointer:coarse){.list.list-nav .list-item .list-action:active{background:#7aa2f7;color:#16161e}}.list.list-nav .list-item .list-action .list-label{display:flex;flex-direction:row;gap:8px;align-items:center;letter-spacing:0;font-weight:400}.list.list-nav .list-item .list-action .list-meta{padding:8px;background:#9ece6a;color:#16161e;display:flex}.list.list-nav .list-item.list-item-active .list-action{background:#7aa2f7;color:#16161e;border-color:#7aa2f7}.list.list-actions{width:100%;max-width:420px;gap:22px}.list.list-actions .list-item{justify-content:space-between;align-items:flex-start;padding:12px 0;border-bottom:2px solid rgba(192,202,245,.08)}.list.list-actions .list-item .list-content{display:flex;flex-direction:column;gap:8px}.list.list-actions .list-item .list-content .list-title{font-size:16px;line-height:1}.list.list-actions .list-item .list-content .list-subtitle{color:#787c99}@media(hover:hover)and (pointer:fine){.list.list-actions .list-item:hover .list-title{color:#7aa2f7}}@media(hover:none)and (pointer:coarse){.list.list-actions .list-item:active .list-title{color:#7aa2f7}}@media(max-width:479px){.list.list-definition .list-row{grid-template-columns:1fr;gap:8px}}.badge{position:relative;overflow:hidden;background:#c0caf5;color:#16161e;padding:5px 8px;font-size:13px;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;display:inline-flex;align-items:center;min-height:24px;transition-duration:.2s;transition-timing-function:ease;transition-property:filter,transform,border-color,color,background}.badge:after{content:"";position:absolute;inset:0 auto 0 0;width:40%;background:linear-gradient(90deg,transparent,rgba(22,22,30,.16),transparent);opacity:0;pointer-events:none;transform:translate(-120%)}@media(hover:hover)and (pointer:fine){.badge:hover{filter:saturate(1.12);transform:translateY(-1px)}.badge:hover:after{opacity:1;animation:terminal_scan_x .7s ease}}@media(hover:none)and (pointer:coarse){.badge:active{filter:saturate(1.12);transform:translateY(-1px)}.badge:active:after{opacity:1;animation:terminal_scan_x .7s ease}}.badge.badge-success{background:#9ece6a}.badge.badge-warning{background:#e0af68}.badge.badge-error,.badge.badge-danger{background:#f7768e}.badge.badge-info{background:#bb9af7;color:#16161e}.badge.badge-secondary{background:#7aa2f7;color:#16161e}.badge.badge-primary-outline{color:#c0caf5;border:2px solid #C0CAF5;background:transparent;padding:3px 8px}.chip-group{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.chip{display:inline-flex;align-items:center;gap:8px;min-height:30px;padding:5px 12px;border:2px solid rgba(192,202,245,.24);color:#a9b1d6;background:#c0caf50b;font-family:IBM Plex Mono,monospace;font-size:12px;font-weight:600;line-height:1;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background,border-color,opacity}.chip .ph,.chip .ph-bold{font-size:18px}.chip:before{content:"";display:inline-block;width:7px;height:7px;flex:0 0 auto;background:#787c99;transition-duration:.2s;transition-timing-function:ease;transition-property:background,box-shadow,transform}.chip:has(.ph):before,.chip:has(.ph-bold):before{display:none}.chip .chip-remove{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-right:-5px;border:0;color:inherit;background:transparent;font:inherit;cursor:pointer}.chip .chip-remove:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}.chip.chip-primary{color:#c0caf5;background:#c0caf514;border-color:#c0caf5}.chip.chip-primary:before{background:#c0caf5}.chip.chip-secondary{color:#7aa2f7;background:#7aa2f714;border-color:#7aa2f7}.chip.chip-secondary:before{background:#7aa2f7}.chip.chip-success{color:#9ece6a;background:#9ece6a14;border-color:#9ece6a}.chip.chip-success:before{background:#9ece6a}.chip.chip-warning{color:#e0af68;background:#e0af6814;border-color:#e0af68}.chip.chip-warning:before{background:#e0af68}.chip.chip-danger,.chip.chip-error{color:#f7768e;background:#f7768e14;border-color:#f7768e}.chip.chip-danger:before,.chip.chip-error:before{background:#f7768e}.chip.chip-selected,.chip[aria-pressed=true],.chip[aria-selected=true]{color:#16161e;background:#c0caf5;border-color:#c0caf5}.chip.chip-selected:before,.chip[aria-pressed=true]:before,.chip[aria-selected=true]:before{background:#16161e}.chip.chip-selected.chip-secondary,.chip.chip-secondary[aria-pressed=true],.chip.chip-secondary[aria-selected=true]{background:#7aa2f7;border-color:#7aa2f7}.chip.chip-disabled,.chip:disabled{color:#787c99;background:#1f2335;border-color:#c0caf53d;cursor:not-allowed;opacity:.7}.chip.chip-disabled:before,.chip:disabled:before{background:#414868}button.chip,a.chip{cursor:pointer}button.chip:focus-visible,a.chip:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){button.chip:hover,a.chip:hover{color:#c0caf5;background:#c0caf516;border-color:#7aa2f7}button.chip:hover:before,a.chip:hover:before{background:#7aa2f7;animation:terminal_pulse .7s ease;transform:scale(1.12)}}@media(hover:none)and (pointer:coarse){button.chip:active,a.chip:active{color:#c0caf5;background:#c0caf516;border-color:#7aa2f7}button.chip:active:before,a.chip:active:before{background:#7aa2f7;animation:terminal_pulse .7s ease;transform:scale(1.12)}}.avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;flex:0 0 auto;overflow:hidden;border:2px solid rgba(192,202,245,.24);color:#16161e;background:#c0caf5;font-family:IBM Plex Mono,monospace;font-size:13px;font-weight:700;line-height:1;text-transform:uppercase}.avatar img{width:100%;height:100%;object-fit:cover}.avatar .ph,.avatar .ph-bold{font-size:22px}.avatar .avatar-status{position:absolute;right:-2px;bottom:-2px;width:13px;height:13px;border:2px solid #16161E;background:#787c99;transition-duration:.2s;transition-timing-function:ease;transition-property:background,box-shadow}.avatar.avatar-sm{width:38px;height:38px;font-size:12px}.avatar.avatar-sm .ph,.avatar.avatar-sm .ph-bold{font-size:18px}.avatar.avatar-lg{width:54px;height:54px;font-size:14px}.avatar.avatar-lg .ph,.avatar.avatar-lg .ph-bold{font-size:26px}.avatar.avatar-secondary{background:#7aa2f7}.avatar.avatar-success{background:#9ece6a}.avatar.avatar-warning{background:#e0af68}.avatar.avatar-danger,.avatar.avatar-error{background:#f7768e}.avatar.avatar-outline{color:#c0caf5;background:#c0caf50b;border-color:#c0caf5}.avatar.is-online .avatar-status{background:#9ece6a;animation:terminal_pulse 1.8s ease infinite}.avatar.is-busy .avatar-status{background:#e0af68}.avatar.is-offline .avatar-status{background:#787c99}.identity{display:inline-flex;align-items:center;gap:12px;min-width:0}.identity-content{display:flex;flex-direction:column;gap:5px;min-width:0}.identity-title{color:#c0caf5;font-size:15px;font-weight:600;line-height:1}.identity-meta{color:#787c99;font-size:13px;line-height:1.4}.avatar-stack{display:inline-flex;align-items:center}.avatar-stack .avatar{margin-right:-8px;border-color:#16161e}.avatar-stack .avatar-stack-count{display:inline-flex;align-items:center;justify-content:center;min-width:46px;height:46px;padding:0 8px;border:2px solid #16161E;color:#16161e;background:#e0af68;font-size:13px;font-weight:700}.table{width:100%;text-align:left;border:2px solid rgba(192,202,245,.24);border-collapse:collapse;background:#c0caf50b}.table .table-caption{text-align:left;font-size:16px;background:#c0caf5;width:max-content;color:#16161e;padding:5px 12px;margin-bottom:0;font-weight:700;text-transform:uppercase}.table.table-empty{width:100%}.table.table-empty .is-empty{width:100%;padding:15px;font-size:13px;color:#787c99;text-align:left}.table .table-row th,.table .table-row td{padding:12px 18px;font-size:13px;vertical-align:middle;border-bottom:2px solid rgba(192,202,245,.08)}.table .table-row th{color:#c0caf5;background:#c0caf50a;text-transform:uppercase;letter-spacing:.04em}.table .table-head{border-bottom:2px solid #C0CAF5}.table .table-body .table-row{transition-duration:.2s;transition-timing-function:ease;transition-property:background,color}.table .table-body .table-row td{transition-duration:.2s;transition-timing-function:ease;transition-property:color,background}@media(hover:hover)and (pointer:fine){.table .table-body .table-row:hover{background:#7aa2f714}.table .table-body .table-row:hover td:first-child{color:#7aa2f7}}@media(hover:none)and (pointer:coarse){.table .table-body .table-row:active{background:#7aa2f714}.table .table-body .table-row:active td:first-child{color:#7aa2f7}}.table .table-foot th,.table .table-foot td{padding-top:15px}.table.table-compact .table-caption{font-size:14px}.table.table-compact .table-row th,.table.table-compact .table-row td{padding:8px 12px;font-size:12px}.table.table-compact .table-cell-mono{color:#a9b1d6;font-family:IBM Plex Mono,monospace;letter-spacing:0}.table.table-compact .table-cell-actions{width:1%;white-space:nowrap}.table-wrapper{width:100%;overflow-x:auto}.toolbar{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;width:100%;padding:12px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.toolbar .toolbar-group{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:0}.toolbar .toolbar-title{margin:0;font-size:16px;font-weight:700;line-height:1;text-transform:uppercase}.toolbar .toolbar-meta{color:#787c99;font-size:13px}.pagination{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.pagination .pagination-item{display:inline-flex;align-items:center;justify-content:center;min-width:38px;height:38px;padding:0 12px;border:2px solid rgba(192,202,245,.24);color:#a9b1d6;background:#c0caf50b;font-family:IBM Plex Mono,monospace;font-size:13px;font-weight:600;line-height:1;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background,border-color,opacity}.pagination .pagination-item:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){.pagination .pagination-item:hover{color:#16161e;background:#7aa2f7;border-color:#7aa2f7}}@media(hover:none)and (pointer:coarse){.pagination .pagination-item:active{color:#16161e;background:#7aa2f7;border-color:#7aa2f7}}.pagination .pagination-item.pagination-item-active,.pagination .pagination-item[aria-current=page]{color:#16161e;background:#c0caf5;border-color:#c0caf5}.pagination .pagination-item:disabled,.pagination .pagination-item.pagination-item-disabled{color:#787c99;background:#1f2335;border-color:#c0caf53d;cursor:not-allowed;opacity:.72}.pagination .pagination-ellipsis{color:#787c99;padding:0 5px}.empty-state{max-width:560px;padding:22px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.empty-state .empty-state-icon{display:inline-flex;align-items:center;justify-content:center;width:54px;height:54px;margin-bottom:15px;color:#16161e;background:#c0caf5;font-size:26px}.empty-state .empty-state-title{margin:0 0 8px;font-size:20px;font-weight:700;text-transform:uppercase}.empty-state .empty-state-text{max-width:440px;margin:0 0 18px;color:#a9b1d6;line-height:1.6}.empty-state .empty-state-actions{display:flex;flex-wrap:wrap;gap:8px}.empty-state.empty-state-error{border-color:#f7768e}.empty-state.empty-state-error .empty-state-icon{background:#f7768e}.skeleton{display:block;position:relative;overflow:hidden;background:#c0caf516}.skeleton:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;transform:translate(-100%);background:linear-gradient(90deg,transparent,rgba(192,202,245,.12),transparent);animation:skeleton_shimmer 1.6s infinite}.skeleton.skeleton-line{width:100%;height:14px}.skeleton.skeleton-title{width:60%;height:22px}.skeleton.skeleton-block{width:100%;height:120px}.skeleton.skeleton-square{width:54px;height:54px}.skeleton-stack{display:flex;flex-direction:column;gap:12px;max-width:520px;padding:15px;border:2px solid rgba(192,202,245,.24);background:#c0caf50b}@keyframes skeleton_shimmer{to{transform:translate(100%)}}.page-header{position:relative;display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:18px;width:100%;padding:18px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b;overflow:hidden;animation:panel_boot .28s ease both}.page-header:after{content:"";position:absolute;top:0;left:0;width:34%;height:2px;background:linear-gradient(90deg,transparent,#7AA2F7,transparent);opacity:.72;pointer-events:none;transform:translate(-120%)}@media(hover:hover)and (pointer:fine){.page-header:hover:after{animation:terminal_scan_x .9s ease}}@media(hover:none)and (pointer:coarse){.page-header:active:after{animation:terminal_scan_x .9s ease}}.page-header .page-header-content{display:flex;flex-direction:column;gap:8px;min-width:min(100%,320px)}.page-header .page-header-kicker{color:#7aa2f7;font-size:13px;font-weight:700;line-height:1;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:color}.page-header .page-header-title{margin:0;color:#c0caf5;font-size:26px;font-weight:700;line-height:1.15}.page-header .page-header-subtitle{max-width:720px;margin:0;color:#a9b1d6;font-size:15px;line-height:1.6}.page-header .page-header-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;color:#787c99;font-size:13px}.page-header .page-header-actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px}.page-header.page-header-compact{align-items:center;padding:15px}.page-header.page-header-compact .page-header-title{font-size:20px}.page-header.page-header-accent{border-color:#7aa2f7;background:#7aa2f70e}.description-list{display:grid;width:100%;max-width:760px;margin:0;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.description-list .description-list-row{display:grid;grid-template-columns:minmax(140px,.36fr) minmax(0,1fr);gap:15px;padding:12px 15px;border-bottom:2px solid rgba(192,202,245,.08);transition-duration:.2s;transition-timing-function:ease;transition-property:background}.description-list .description-list-row:last-child{border-bottom:0}@media(hover:hover)and (pointer:fine){.description-list .description-list-row:hover{background:#c0caf516}.description-list .description-list-row:hover .description-list-term{color:#7aa2f7}.description-list .description-list-row:hover .description-list-value{transform:translate(5px)}}@media(hover:none)and (pointer:coarse){.description-list .description-list-row:active{background:#c0caf516}.description-list .description-list-row:active .description-list-term{color:#7aa2f7}.description-list .description-list-row:active .description-list-value{transform:translate(5px)}}.description-list .description-list-term{margin:0;color:#787c99;font-size:13px;font-weight:600;line-height:1.4;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:color}.description-list .description-list-value{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;margin:0;color:#c0caf5;font-size:15px;line-height:1.4;transition-duration:.2s;transition-timing-function:ease;transition-property:transform}.description-list .description-list-value-muted{color:#a9b1d6}.description-list.description-list-compact{max-width:520px}.description-list.description-list-compact .description-list-row{grid-template-columns:minmax(112px,.42fr) minmax(0,1fr);gap:12px;padding:8px 12px}.description-list.description-list-compact .description-list-term,.description-list.description-list-compact .description-list-value{font-size:13px}@media(max-width:479px){.description-list .description-list-row{grid-template-columns:1fr;gap:5px}}.steps{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;width:100%;max-width:900px;margin:0;padding:0;list-style:none}.steps .step{position:relative;display:flex;flex-direction:column;gap:8px;min-height:120px;padding:15px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.steps .step-marker{display:inline-flex;align-items:center;justify-content:center;width:38px;height:38px;color:#c0caf5;border:2px solid rgba(192,202,245,.24);font-size:13px;font-weight:700;line-height:1}.steps .step-title{margin:0;font-size:14px;font-weight:700;line-height:1.25;text-transform:uppercase}.steps .step-text{margin:0;color:#a9b1d6;font-size:13px;line-height:1.4}.steps .step-complete{border-color:#9ece6a}.steps .step-complete .step-marker{color:#16161e;background:#9ece6a;border-color:#9ece6a}.steps .step-current{border-color:#7aa2f7}.steps .step-current .step-marker{color:#16161e;background:#7aa2f7;border-color:#7aa2f7}.steps .step-disabled{opacity:.62}.steps.steps-vertical{grid-template-columns:1fr;max-width:520px;gap:0}.steps.steps-vertical .step{min-height:auto;border-bottom-width:0}.steps.steps-vertical .step:last-child{border-bottom-width:2px}@media(max-width:1023px){.steps{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(max-width:479px){.steps{grid-template-columns:1fr}}.timeline{display:grid;gap:0;width:100%;max-width:760px;margin:0;padding:0;list-style:none}.timeline .timeline-item{position:relative;display:grid;grid-template-columns:46px minmax(0,1fr);gap:12px;min-height:88px}.timeline .timeline-item:before{content:"";position:absolute;top:46px;bottom:0;left:22px;width:2px;background:#c0caf53d}.timeline .timeline-item:last-child:before{display:none}.timeline .timeline-marker{position:relative;z-index:1;display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;border:2px solid rgba(192,202,245,.24);color:#a9b1d6;background:#16161e;font-size:18px;transition-duration:.2s;transition-timing-function:ease;transition-property:border-color,background,color,box-shadow,transform}.timeline .timeline-content{min-width:0;padding:0 0 18px}.timeline .timeline-card{padding:15px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b;transition-duration:.2s;transition-timing-function:ease;transition-property:border-color,background,transform}.timeline .timeline-header{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.timeline .timeline-title{margin:0;font-size:14px;font-weight:700;line-height:1.25;text-transform:uppercase}.timeline .timeline-time{color:#787c99;font-size:12px;font-family:IBM Plex Mono,monospace;line-height:1.4}.timeline .timeline-text{margin:0;color:#a9b1d6;font-size:13px;line-height:1.4}.timeline .timeline-meta{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.timeline .timeline-item-success .timeline-marker,.timeline .timeline-item-success .timeline-card{border-color:#9ece6a}.timeline .timeline-item-success .timeline-marker{color:#16161e;background:#9ece6a}.timeline .timeline-item-warning .timeline-marker,.timeline .timeline-item-warning .timeline-card{border-color:#e0af68}.timeline .timeline-item-warning .timeline-marker{color:#16161e;background:#e0af68}.timeline .timeline-item-danger .timeline-marker,.timeline .timeline-item-danger .timeline-card,.timeline .timeline-item-error .timeline-marker,.timeline .timeline-item-error .timeline-card{border-color:#f7768e}.timeline .timeline-item-danger .timeline-marker,.timeline .timeline-item-error .timeline-marker{color:#16161e;background:#f7768e}@media(hover:hover)and (pointer:fine){.timeline .timeline-item:hover .timeline-marker{box-shadow:0 0 0 4px #7aa2f724;transform:scale(1.04)}.timeline .timeline-item:hover .timeline-card{background:#c0caf516;transform:translate(5px)}}@media(hover:none)and (pointer:coarse){.timeline .timeline-item:active .timeline-marker{box-shadow:0 0 0 4px #7aa2f724;transform:scale(1.04)}.timeline .timeline-item:active .timeline-card{background:#c0caf516;transform:translate(5px)}}.activity-log{display:grid;width:100%;max-width:720px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.activity-log .activity-log-row{display:grid;grid-template-columns:minmax(120px,.24fr) minmax(0,1fr) auto;gap:12px;align-items:center;padding:12px 15px;border-bottom:2px solid rgba(192,202,245,.08);transition-duration:.2s;transition-timing-function:ease;transition-property:background}.activity-log .activity-log-row:last-child{border-bottom:0}@media(hover:hover)and (pointer:fine){.activity-log .activity-log-row:hover{background:#c0caf516}}@media(hover:none)and (pointer:coarse){.activity-log .activity-log-row:active{background:#c0caf516}}.activity-log .activity-log-time{color:#787c99;font-family:IBM Plex Mono,monospace;font-size:12px}.activity-log .activity-log-title{color:#c0caf5;font-size:13px;font-weight:600;line-height:1.4}@media(max-width:479px){.activity-log .activity-log-row{grid-template-columns:1fr;gap:8px}}.accordion{display:grid;width:100%;max-width:760px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.accordion-item{border-bottom:2px solid rgba(192,202,245,.08);overflow:hidden}.accordion-item:last-child{border-bottom:0}.accordion-item[open] .accordion-summary{color:#16161e;background:#c0caf5}.accordion-item[open] .accordion-icon{transform:rotate(180deg)}.accordion-summary{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:46px;padding:12px 15px;color:#c0caf5;cursor:pointer;font-size:13px;font-weight:700;line-height:1;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background}.accordion-summary::-webkit-details-marker{display:none}.accordion-summary::marker{content:""}.accordion-summary:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){.accordion-summary:hover{color:#16161e;background:#7aa2f7}}@media(hover:none)and (pointer:coarse){.accordion-summary:active{color:#16161e;background:#7aa2f7}}.accordion-summary-content{display:flex;align-items:center;gap:8px;min-width:0}.accordion-icon{flex:0 0 auto;font-size:18px;transition-duration:.2s;transition-property:transform}.accordion-panel{overflow:hidden;padding:15px;color:#a9b1d6;font-size:13px;line-height:1.6;transition-duration:.28s;transition-timing-function:ease;transition-property:height,opacity,transform}.accordion-panel p{margin-top:0}.accordion-panel p:last-child{margin-bottom:0}.disclosure{max-width:520px;border:2px solid rgba(192,202,245,.24);background:#c0caf50b}.disclosure .accordion-summary{min-height:38px;padding:8px 12px}.disclosure .accordion-panel{padding:12px}.drawer{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;display:flex;justify-content:flex-end;pointer-events:none}.drawer .drawer-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1010;background:#16161e;opacity:0;transition-duration:.28s;transition-timing-function:ease;transition-property:opacity;pointer-events:auto}.drawer .drawer-panel{position:relative;z-index:1020;width:min(460px,100vw - 18px);min-height:100vh;display:flex;flex-direction:column;gap:15px;background:#16161e;border-left:2px solid #C0CAF5;box-shadow:-18px 0 42px #16161e61;opacity:0;transform:translate(100%);transition-duration:.28s;transition-timing-function:ease;transition-property:opacity,transform;pointer-events:auto}.drawer .drawer-header{display:flex;align-items:center;justify-content:space-between;padding-right:15px;border-bottom:2px solid rgba(192,202,245,.24)}.drawer .drawer-title{margin:0;padding:12px 15px;background:#c0caf5;color:#16161e;text-transform:uppercase;letter-spacing:.04em}.drawer .drawer-body{flex:1;overflow-y:auto;padding:18px}.drawer .drawer-footer{padding:18px;border-top:2px solid rgba(192,202,245,.24)}.drawer .drawer-footer .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:12px;width:100%}.drawer.drawer-left{justify-content:flex-start}.drawer.drawer-left .drawer-panel{border-left:0;border-right:2px solid #C0CAF5;box-shadow:18px 0 42px #16161e61;transform:translate(-100%)}.drawer.a-show .drawer-backdrop{opacity:.82}.drawer.a-show .drawer-panel{opacity:1;transform:translate(0)}.drawer.a-hide .drawer-backdrop{opacity:0}.drawer.a-hide .drawer-panel{opacity:0;transform:translate(100%)}.drawer.a-hide.drawer-left .drawer-panel{transform:translate(-100%)}.drawer-preview{display:grid;grid-template-columns:minmax(0,1fr) minmax(180px,280px);gap:18px;align-items:stretch;padding:18px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;background:#c0caf50b}.drawer-preview .drawer-preview-content{display:flex;flex-direction:column;gap:12px}.drawer-preview .drawer-preview-panel{display:flex;flex-direction:column;gap:12px;padding:15px;border:2px solid #7AA2F7;background:#1f2335}.drawer-preview .drawer-preview-title{margin:0;color:#7aa2f7;font-size:14px;text-transform:uppercase}.drawer-preview .drawer-preview-text{margin:0;color:#a9b1d6;font-size:13px;line-height:1.6}@media(max-width:720px){.drawer-preview{grid-template-columns:1fr}}.toast{position:fixed;z-index:1100;bottom:-100px;right:15px;max-width:420px;background:#1f2335;border:2px solid #C0CAF5;border-left-width:6px;padding:0;opacity:0;transition-duration:.28s;transition-timing-function:ease;transition-property:opacity,top,bottom}.toast.a-show{bottom:15px;opacity:1}.toast.a-hide{bottom:115px;opacity:0}.toast .toast-content{display:flex;flex-direction:column;gap:0}.toast .toast-content .toast-title{min-height:38px;font-size:16px;display:flex;flex-direction:row;gap:8px;align-items:center;color:#16161e;background:#c0caf5;padding:8px 42px 8px 12px;text-transform:uppercase}.toast .toast-content .toast-text{font-size:13px;padding:22px 15px;margin:0}.toast .toast-close{position:absolute;top:0;right:0;color:#16161e;height:38px;width:38px;border-color:transparent}@media(hover:hover)and (pointer:fine){.toast .toast-close:hover{color:#16161e;background:#16161e1f;border-color:#16161e6b}}@media(hover:none)and (pointer:coarse){.toast .toast-close:active{color:#16161e;background:#16161e1f;border-color:#16161e6b}}.toast.toast-info{border-color:#bb9af7}.toast.toast-info .toast-title{background:#bb9af7}.toast.toast-success{border-color:#9ece6a}.toast.toast-success .toast-title{background:#9ece6a}.toast.toast-warning{border-color:#e0af68}.toast.toast-warning .toast-title{background:#e0af68}.toast.toast-danger{border-color:#f7768e}.toast.toast-danger .toast-title{background:#f7768e}.card{position:relative;max-width:340px;width:100%;overflow:hidden;background:#c0caf50b;border:2px solid #C0CAF5;transition-duration:.2s;transition-timing-function:ease;transition-property:border-color,background,box-shadow,transform}.card:after{content:"";position:absolute;inset:0 auto 0 0;width:34%;background:linear-gradient(90deg,transparent,rgba(192,202,245,.08),transparent);opacity:0;pointer-events:none;transform:translate(-120%)}.card .card-title{color:#16161e;background:#c0caf5;padding:8px 12px;font-weight:700;text-transform:uppercase}.card .card-content{padding:15px;height:100%}.card .card-content .card-thumb{display:block;width:min(68%,190px);margin:18px auto 22px;transition-duration:.2s;transition-timing-function:ease;transition-property:transform,filter}.card .card-content p{margin-top:8px;margin-bottom:0}.card .card-footer{padding:8px 15px 15px}@media(hover:hover)and (pointer:fine){.card:hover{border-color:#7aa2f7;background:#c0caf516;box-shadow:0 14px 32px #16161e57;transform:translateY(-2px)}.card:hover:after{opacity:1;animation:terminal_scan_x .85s ease}.card:hover .card-thumb{filter:saturate(1.12);transform:translateY(-2px)}}@media(hover:none)and (pointer:coarse){.card:active{border-color:#7aa2f7;background:#c0caf516;box-shadow:0 14px 32px #16161e57;transform:translateY(-2px)}.card:active:after{opacity:1;animation:terminal_scan_x .85s ease}.card:active .card-thumb{filter:saturate(1.12);transform:translateY(-2px)}}.card.status-card{max-width:220px;overflow:hidden;transition-duration:.2s;transition-property:border-color}.card.status-card .status-icon-container{position:relative}.card.status-card .status-icon-container .status-indicator{position:absolute;top:-15px;left:-5px;font-size:22px;color:#f7768e}.card.status-card .status-icon-container .status-indicator.status-online{color:#9ece6a}.card.status-card .status-icon-container .status-icon{display:flex;flex-direction:row;align-items:center;justify-content:center;font-size:56px;height:108px;width:100%;transition-duration:.2s;transition-property:color,transform}.card.status-card .card-title{display:flex;width:100%;font-size:14px;font-weight:700;align-items:center;flex-direction:row;flex-wrap:nowrap;justify-content:space-between;transition-duration:.2s;transition-property:background-color,color}.card.status-card .status-name{font-size:13px;line-height:1.4}@media(hover:hover)and (pointer:fine){.card.status-card:hover .status-icon{transform:translateY(-2px) scale(1.03)}}@media(hover:none)and (pointer:coarse){.card.status-card:active .status-icon{transform:translateY(-2px) scale(1.03)}}.card.status-card.card-success{border-color:#9ece6a}.card.status-card.card-success .card-title,.card.status-card.card-success .toast-title,.card.status-card.card-success .modal-title{color:#16161e;background:#9ece6a}.card.status-card.card-success .status-icon{color:#9ece6a}.card.status-card.card-warning{border-color:#e0af68}.card.status-card.card-warning .card-title,.card.status-card.card-warning .toast-title,.card.status-card.card-warning .modal-title{color:#16161e;background:#e0af68}.card.status-card.card-warning .status-icon{color:#e0af68}.card.status-card.card-info{border-color:#bb9af7}.card.status-card.card-info .card-title,.card.status-card.card-info .toast-title,.card.status-card.card-info .modal-title{color:#16161e;background:#bb9af7}.card.status-card.card-info .status-icon{color:#bb9af7}.card.status-card.card-secondary{border-color:#7aa2f7}.card.status-card.card-secondary .card-title,.card.status-card.card-secondary .toast-title,.card.status-card.card-secondary .modal-title{color:#16161e;background:#7aa2f7}.card.status-card.card-secondary .status-icon{color:#7aa2f7}.card.status-card.card-danger,.card.status-card.card-error{border-color:#f7768e}.card.status-card.card-danger .card-title,.card.status-card.card-danger .toast-title,.card.status-card.card-danger .modal-title,.card.status-card.card-error .card-title,.card.status-card.card-error .toast-title,.card.status-card.card-error .modal-title{color:#16161e;background:#f7768e}.card.status-card.card-danger .status-icon,.card.status-card.card-error .status-icon{color:#f7768e}.card.metric-card{max-width:320px;border-color:#c0caf53d}.card.metric-card .card-content{display:flex;flex-direction:column;gap:15px}.card.metric-card .metric-card-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.card.metric-card .metric-card-label{margin:0;color:#a9b1d6;font-size:13px;font-weight:600;text-transform:uppercase}.card.metric-card .metric-card-icon{display:inline-flex;align-items:center;justify-content:center;width:46px;height:46px;color:#16161e;background:#7aa2f7;font-size:22px;transition-duration:.2s;transition-timing-function:ease;transition-property:background,transform}.card.metric-card .metric-card-value{margin:0;color:#c0caf5;font-size:34px;font-weight:700;line-height:1.15}.card.metric-card .metric-card-meta{display:flex;flex-wrap:wrap;align-items:center;gap:8px;color:#787c99;font-size:13px}.card.metric-card .metric-card-delta{color:#9ece6a;font-weight:700}.card.metric-card .metric-card-delta.metric-card-delta-negative{color:#f7768e}@media(hover:hover)and (pointer:fine){.card.metric-card:hover .metric-card-icon{background:#c0caf5;transform:translateY(-2px)}}@media(hover:none)and (pointer:coarse){.card.metric-card:active .metric-card-icon{background:#c0caf5;transform:translateY(-2px)}}.card.action-card{max-width:360px;border-color:#7aa2f7}.card.action-card .card-content{display:flex;flex-direction:column;gap:15px}.card.action-card .action-card-kicker{display:inline-flex;width:max-content;padding:5px 8px;color:#16161e;background:#7aa2f7;font-size:12px;font-weight:700;line-height:1;text-transform:uppercase;transition-duration:.2s;transition-timing-function:ease;transition-property:background,transform}.card.action-card .action-card-title{margin:0;font-size:20px;font-weight:700;line-height:1.25;text-transform:uppercase}.card.action-card .action-card-text{margin:0;color:#a9b1d6;font-size:13px;line-height:1.6}.card.action-card .action-card-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:8px}@media(hover:hover)and (pointer:fine){.card.action-card:hover .action-card-kicker{background:#c0caf5;transform:translate(5px)}}@media(hover:none)and (pointer:coarse){.card.action-card:active .action-card-kicker{background:#c0caf5;transform:translate(5px)}}.modal{position:fixed;top:0;bottom:0;left:0;right:0;z-index:1000;display:flex;flex-direction:column;align-items:center;justify-content:center}.modal .modal-backdrop{position:fixed;z-index:1010;top:0;bottom:0;left:0;right:0;background:#16161e;opacity:0;transition-duration:.25s;transition-property:opacity}.modal .modal-panel{position:relative;z-index:1020;width:100%;max-width:960px;margin:200px 18px 18px;height:auto;min-height:200px;max-height:calc(100vh - 48px);padding:0;display:flex;flex-direction:column;gap:15px;background:#16161e;border:2px solid #C0CAF5;border-left-width:6px;opacity:0;transition-duration:.28s;transition-timing-function:ease;transition-property:opacity,margin-top}.modal .modal-panel .modal-header{display:flex;flex-direction:row;justify-content:space-between;align-items:center;padding-right:15px}.modal .modal-panel .modal-header .modal-title{padding:12px 15px;background:#c0caf5;color:#16161e;text-transform:uppercase;letter-spacing:.04em}.modal .modal-panel .modal-body{max-height:700px;overflow-y:auto;padding:18px}.modal .modal-panel .modal-footer{padding:18px}.modal .modal-panel .modal-footer .actions{display:flex;flex-direction:row;justify-content:flex-end;gap:15px;width:100%}.modal.a-show .modal-backdrop{opacity:1}.modal.a-show .modal-panel{opacity:1;margin-top:0}.modal.a-hide .modal-backdrop{opacity:0}.modal.a-hide .modal-panel{opacity:0;margin-top:-200px}.alert{position:relative;overflow:hidden;margin-bottom:12px;padding:12px 15px;border:2px solid transparent;border-left-style:solid;border-left-width:6px;background:#c0caf50b;color:#c0caf5;font-weight:500;line-height:1.4;transition-duration:.2s;transition-timing-function:ease;transition-property:background,color,border-color}.alert:after{content:"";position:absolute;inset:0 auto 0 0;width:36%;background:linear-gradient(90deg,transparent,rgba(192,202,245,.12),transparent);opacity:0;pointer-events:none;transform:translate(-120%)}@media(hover:hover)and (pointer:fine){.alert:hover:after{opacity:1;animation:terminal_scan_x .8s ease}}@media(hover:none)and (pointer:coarse){.alert:active:after{opacity:1;animation:terminal_scan_x .8s ease}}.alert.alert-primary{border-color:#c0caf5;background:#c0caf51a;color:#c0caf5}.alert.alert-success{border-color:#9ece6a;background:#9ece6a1a;color:#9ece6a}.alert.alert-secondary{border-color:#7aa2f7;background:#7aa2f71a;color:#7aa2f7}.alert.alert-info{border-color:#bb9af7;background:#bb9af71a;color:#c0caf5}.alert.alert-warning{border-color:#e0af68;background:#e0af681a;color:#e0af68}.alert.alert-error,.alert.alert-danger{border-color:#f7768e;background:#f7768e1a;color:#f7768e}.advanced-select-container{position:relative;height:0}.advanced-select{position:absolute;z-index:100;top:6px;width:100%;height:auto;max-height:200px;overflow-y:auto;background:#16161e;border:2px solid #C0CAF5;border-left-width:6px;margin-top:20px;opacity:0;visibility:hidden;transition-property:opacity,margin-top,visibility;transition-duration:.2s;transition-timing-function:ease}.advanced-select.a-show{opacity:1;margin-top:0;visibility:visible}.advanced-select .popup-options-container .not-found{width:100%;padding:15px;text-align:center;display:none}.advanced-select .popup-options-container .not-found.show{display:block}.advanced-select .popup-options-container .options{width:100%;display:none}.advanced-select .popup-options-container .options.show{display:block}.advanced-select .popup-options-container .options .option{padding:8px 15px;transition-property:color,background;transition-duration:.15s}.advanced-select .popup-options-container .options .option.hide{display:none}.advanced-select .popup-options-container .options .option:hover,.advanced-select .popup-options-container .options .option.focus{color:#16161e;background:#e0af68}.component.editable-string-component .editable-string-content{display:flex;flex-direction:row;align-items:center;gap:8px;font-size:inherit}.component.editable-string-component .editable-string-content .editable-string{font-size:inherit;border-bottom:2px solid rgba(192,202,245,.24)}@media(hover:hover)and (pointer:fine){.component.editable-string-component .edit-text-btn:hover,.component.editable-string-component .apply-changes-btn:hover,.component.editable-string-component .cancel-changes-btn:hover{color:#16161e;background:#e0af68}}@media(hover:none)and (pointer:coarse){.component.editable-string-component .edit-text-btn:active,.component.editable-string-component .apply-changes-btn:active,.component.editable-string-component .cancel-changes-btn:active{color:#16161e;background:#e0af68}}.component.editable-string-component .apply-changes-btn{color:#e0af68}.component.editable-string-component .editable-string-form{display:flex;flex-direction:row;align-items:center;gap:8px}.component.editable-string-component .editable-string-form .form-group{max-width:260px;margin:0}.component.editable-string-component .editable-string-form .form-group .input{padding:8px 15px}.tabs{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:15px}.tabs .tab{display:inline-flex;align-items:center;min-height:38px;padding:8px 12px;border:2px solid rgba(192,202,245,.24);border-left-width:6px;color:#a9b1d6;background:#c0caf50b;font-family:IBM Plex Mono,monospace;font-size:13px;font-weight:600;line-height:1;text-transform:uppercase;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background,border-color}.tabs .tab:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){.tabs .tab:hover{color:#16161e;background:#7aa2f7;border-color:#7aa2f7}}@media(hover:none)and (pointer:coarse){.tabs .tab:active{color:#16161e;background:#7aa2f7;border-color:#7aa2f7}}.tabs .tab.tab-active,.tabs .tab[aria-selected=true]{color:#16161e;background:#c0caf5;border-color:#c0caf5}.dropdown,.popover{position:relative;display:inline-flex}.dropdown-menu,.popover-panel,.tooltip-panel{z-index:40;background:#1f2335;border:2px solid rgba(192,202,245,.24);border-left-width:6px;box-shadow:0 14px 36px #16161e5c}.dropdown-menu,.popover-panel{position:absolute;top:calc(100% + 8px);left:0;min-width:220px;display:none;transform-origin:top left}.dropdown.is-open .dropdown-menu,.popover.is-open .popover-panel{display:block;animation:overlay_reveal .2s ease both}.dropdown-menu{padding:5px}.dropdown-menu .dropdown-item{display:flex;align-items:center;gap:8px;width:100%;min-height:38px;padding:8px 12px;border:0;color:#c0caf5;background:transparent;font-family:IBM Plex Mono,monospace;font-size:13px;text-align:left;cursor:pointer;transition-duration:.2s;transition-timing-function:ease;transition-property:color,background}.dropdown-menu .dropdown-item .ph,.dropdown-menu .dropdown-item .ph-bold{font-size:18px}.dropdown-menu .dropdown-item:focus-visible{outline:2px solid #E0AF68;outline-offset:3px}@media(hover:hover)and (pointer:fine){.dropdown-menu .dropdown-item:hover{color:#16161e;background:#7aa2f7}}@media(hover:none)and (pointer:coarse){.dropdown-menu .dropdown-item:active{color:#16161e;background:#7aa2f7}}.dropdown-menu .dropdown-item.dropdown-item-danger{color:#f7768e}@media(hover:hover)and (pointer:fine){.dropdown-menu .dropdown-item.dropdown-item-danger:hover{color:#16161e;background:#f7768e}}@media(hover:none)and (pointer:coarse){.dropdown-menu .dropdown-item.dropdown-item-danger:active{color:#16161e;background:#f7768e}}.popover-panel{width:min(320px,100vw - 22px);padding:15px}.popover-panel .popover-title{margin:0 0 8px;font-size:14px;font-weight:700;text-transform:uppercase}.popover-panel .popover-text{margin:0;color:#a9b1d6;font-size:13px;line-height:1.6}.tooltip{position:relative;display:inline-flex}.tooltip-panel{position:absolute;left:50%;bottom:calc(100% + 8px);width:max-content;max-width:260px;padding:8px 12px;color:#c0caf5;font-size:12px;line-height:1.4;transform:translate(-50%);opacity:0;visibility:hidden;pointer-events:none;transition-duration:.15s;transition-timing-function:ease;transition-property:opacity,visibility}.tooltip:hover .tooltip-panel,.tooltip:focus-within .tooltip-panel,.tooltip.is-open .tooltip-panel{opacity:1;visibility:visible;animation:tooltip_reveal .15s ease both}.m-0{margin:0!important}.mt-0{margin-top:0!important}.mr-0{margin-right:0!important}.mb-0{margin-bottom:0!important}.ml-0{margin-left:0!important}.mx-0{margin-left:0!important;margin-right:0!important}.my-0{margin-top:0!important;margin-bottom:0!important}.p-0{padding:0!important}.pt-0{padding-top:0!important}.pr-0{padding-right:0!important}.pb-0{padding-bottom:0!important}.pl-0{padding-left:0!important}.px-0{padding-left:0!important;padding-right:0!important}.py-0{padding-top:0!important;padding-bottom:0!important}.g-0{gap:0!important}.gx-0{column-gap:0!important}.gy-0{row-gap:0!important}.m-1{margin:5px!important}.mt-1{margin-top:5px!important}.mr-1{margin-right:5px!important}.mb-1{margin-bottom:5px!important}.ml-1{margin-left:5px!important}.mx-1{margin-left:5px!important;margin-right:5px!important}.my-1{margin-top:5px!important;margin-bottom:5px!important}.p-1{padding:5px!important}.pt-1{padding-top:5px!important}.pr-1{padding-right:5px!important}.pb-1{padding-bottom:5px!important}.pl-1{padding-left:5px!important}.px-1{padding-left:5px!important;padding-right:5px!important}.py-1{padding-top:5px!important;padding-bottom:5px!important}.g-1{gap:5px!important}.gx-1{column-gap:5px!important}.gy-1{row-gap:5px!important}.m-2{margin:8px!important}.mt-2{margin-top:8px!important}.mr-2{margin-right:8px!important}.mb-2{margin-bottom:8px!important}.ml-2{margin-left:8px!important}.mx-2{margin-left:8px!important;margin-right:8px!important}.my-2{margin-top:8px!important;margin-bottom:8px!important}.p-2{padding:8px!important}.pt-2{padding-top:8px!important}.pr-2{padding-right:8px!important}.pb-2{padding-bottom:8px!important}.pl-2{padding-left:8px!important}.px-2{padding-left:8px!important;padding-right:8px!important}.py-2{padding-top:8px!important;padding-bottom:8px!important}.g-2{gap:8px!important}.gx-2{column-gap:8px!important}.gy-2{row-gap:8px!important}.m-3{margin:12px!important}.mt-3{margin-top:12px!important}.mr-3{margin-right:12px!important}.mb-3{margin-bottom:12px!important}.ml-3{margin-left:12px!important}.mx-3{margin-left:12px!important;margin-right:12px!important}.my-3{margin-top:12px!important;margin-bottom:12px!important}.p-3{padding:12px!important}.pt-3{padding-top:12px!important}.pr-3{padding-right:12px!important}.pb-3{padding-bottom:12px!important}.pl-3{padding-left:12px!important}.px-3{padding-left:12px!important;padding-right:12px!important}.py-3{padding-top:12px!important;padding-bottom:12px!important}.g-3{gap:12px!important}.gx-3{column-gap:12px!important}.gy-3{row-gap:12px!important}.m-4{margin:15px!important}.mt-4{margin-top:15px!important}.mr-4{margin-right:15px!important}.mb-4{margin-bottom:15px!important}.ml-4{margin-left:15px!important}.mx-4{margin-left:15px!important;margin-right:15px!important}.my-4{margin-top:15px!important;margin-bottom:15px!important}.p-4{padding:15px!important}.pt-4{padding-top:15px!important}.pr-4{padding-right:15px!important}.pb-4{padding-bottom:15px!important}.pl-4{padding-left:15px!important}.px-4{padding-left:15px!important;padding-right:15px!important}.py-4{padding-top:15px!important;padding-bottom:15px!important}.g-4{gap:15px!important}.gx-4{column-gap:15px!important}.gy-4{row-gap:15px!important}.m-5{margin:18px!important}.mt-5{margin-top:18px!important}.mr-5{margin-right:18px!important}.mb-5{margin-bottom:18px!important}.ml-5{margin-left:18px!important}.mx-5{margin-left:18px!important;margin-right:18px!important}.my-5{margin-top:18px!important;margin-bottom:18px!important}.p-5{padding:18px!important}.pt-5{padding-top:18px!important}.pr-5{padding-right:18px!important}.pb-5{padding-bottom:18px!important}.pl-5{padding-left:18px!important}.px-5{padding-left:18px!important;padding-right:18px!important}.py-5{padding-top:18px!important;padding-bottom:18px!important}.g-5{gap:18px!important}.gx-5{column-gap:18px!important}.gy-5{row-gap:18px!important}.m-6{margin:22px!important}.mt-6{margin-top:22px!important}.mr-6{margin-right:22px!important}.mb-6{margin-bottom:22px!important}.ml-6{margin-left:22px!important}.mx-6{margin-left:22px!important;margin-right:22px!important}.my-6{margin-top:22px!important;margin-bottom:22px!important}.p-6{padding:22px!important}.pt-6{padding-top:22px!important}.pr-6{padding-right:22px!important}.pb-6{padding-bottom:22px!important}.pl-6{padding-left:22px!important}.px-6{padding-left:22px!important;padding-right:22px!important}.py-6{padding-top:22px!important;padding-bottom:22px!important}.g-6{gap:22px!important}.gx-6{column-gap:22px!important}.gy-6{row-gap:22px!important}.m-7{margin:26px!important}.mt-7{margin-top:26px!important}.mr-7{margin-right:26px!important}.mb-7{margin-bottom:26px!important}.ml-7{margin-left:26px!important}.mx-7{margin-left:26px!important;margin-right:26px!important}.my-7{margin-top:26px!important;margin-bottom:26px!important}.p-7{padding:26px!important}.pt-7{padding-top:26px!important}.pr-7{padding-right:26px!important}.pb-7{padding-bottom:26px!important}.pl-7{padding-left:26px!important}.px-7{padding-left:26px!important;padding-right:26px!important}.py-7{padding-top:26px!important;padding-bottom:26px!important}.g-7{gap:26px!important}.gx-7{column-gap:26px!important}.gy-7{row-gap:26px!important}.m-8{margin:34px!important}.mt-8{margin-top:34px!important}.mr-8{margin-right:34px!important}.mb-8{margin-bottom:34px!important}.ml-8{margin-left:34px!important}.mx-8{margin-left:34px!important;margin-right:34px!important}.my-8{margin-top:34px!important;margin-bottom:34px!important}.p-8{padding:34px!important}.pt-8{padding-top:34px!important}.pr-8{padding-right:34px!important}.pb-8{padding-bottom:34px!important}.pl-8{padding-left:34px!important}.px-8{padding-left:34px!important;padding-right:34px!important}.py-8{padding-top:34px!important;padding-bottom:34px!important}.g-8{gap:34px!important}.gx-8{column-gap:34px!important}.gy-8{row-gap:34px!important}.m-9{margin:42px!important}.mt-9{margin-top:42px!important}.mr-9{margin-right:42px!important}.mb-9{margin-bottom:42px!important}.ml-9{margin-left:42px!important}.mx-9{margin-left:42px!important;margin-right:42px!important}.my-9{margin-top:42px!important;margin-bottom:42px!important}.p-9{padding:42px!important}.pt-9{padding-top:42px!important}.pr-9{padding-right:42px!important}.pb-9{padding-bottom:42px!important}.pl-9{padding-left:42px!important}.px-9{padding-left:42px!important;padding-right:42px!important}.py-9{padding-top:42px!important;padding-bottom:42px!important}.g-9{gap:42px!important}.gx-9{column-gap:42px!important}.gy-9{row-gap:42px!important}.m-10{margin:48px!important}.mt-10{margin-top:48px!important}.mr-10{margin-right:48px!important}.mb-10{margin-bottom:48px!important}.ml-10{margin-left:48px!important}.mx-10{margin-left:48px!important;margin-right:48px!important}.my-10{margin-top:48px!important;margin-bottom:48px!important}.p-10{padding:48px!important}.pt-10{padding-top:48px!important}.pr-10{padding-right:48px!important}.pb-10{padding-bottom:48px!important}.pl-10{padding-left:48px!important}.px-10{padding-left:48px!important;padding-right:48px!important}.py-10{padding-top:48px!important;padding-bottom:48px!important}.g-10{gap:48px!important}.gx-10{column-gap:48px!important}.gy-10{row-gap:48px!important}.m-11{margin:64px!important}.mt-11{margin-top:64px!important}.mr-11{margin-right:64px!important}.mb-11{margin-bottom:64px!important}.ml-11{margin-left:64px!important}.mx-11{margin-left:64px!important;margin-right:64px!important}.my-11{margin-top:64px!important;margin-bottom:64px!important}.p-11{padding:64px!important}.pt-11{padding-top:64px!important}.pr-11{padding-right:64px!important}.pb-11{padding-bottom:64px!important}.pl-11{padding-left:64px!important}.px-11{padding-left:64px!important;padding-right:64px!important}.py-11{padding-top:64px!important;padding-bottom:64px!important}.g-11{gap:64px!important}.gx-11{column-gap:64px!important}.gy-11{row-gap:64px!important}.m-12{margin:80px!important}.mt-12{margin-top:80px!important}.mr-12{margin-right:80px!important}.mb-12{margin-bottom:80px!important}.ml-12{margin-left:80px!important}.mx-12{margin-left:80px!important;margin-right:80px!important}.my-12{margin-top:80px!important;margin-bottom:80px!important}.p-12{padding:80px!important}.pt-12{padding-top:80px!important}.pr-12{padding-right:80px!important}.pb-12{padding-bottom:80px!important}.pl-12{padding-left:80px!important}.px-12{padding-left:80px!important;padding-right:80px!important}.py-12{padding-top:80px!important;padding-bottom:80px!important}.g-12{gap:80px!important}.gx-12{column-gap:80px!important}.gy-12{row-gap:80px!important}.row{display:flex;flex-direction:row}@media(max-width:1279px){.row.adaptive{flex-direction:column}}.column{display:flex;flex-direction:column}.f-grid{display:flex;flex-direction:row;flex-wrap:wrap}.grid{display:grid}.grid-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}.items-start{align-items:flex-start!important}.items-center{align-items:center!important}.items-end{align-items:flex-end!important}.justify-start{justify-content:flex-start!important}.justify-center{justify-content:center!important}.justify-between{justify-content:space-between!important}.justify-end{justify-content:flex-end!important}.w-100{width:100%}.w-auto{width:auto!important}.w-fit{width:fit-content!important}.w-200{width:200%}.h-100{height:100%}.min-w-0{min-width:0!important}.overflow-hidden{overflow:hidden!important}.overflow-auto{overflow:auto!important}.fs-xs{font-size:12px}.fs-sm{font-size:13px}.fs-md{font-size:14px}.fs-base{font-size:15px}.fs-lg{font-size:16px}.fs-xl{font-size:20px}.text-left{text-align:left!important}.text-center{text-align:center!important}.text-right{text-align:right!important}.text-uppercase{text-transform:uppercase!important}.text-nowrap{white-space:nowrap!important}.d-none{display:none!important}.d-block{display:block!important}.d-inline-flex{display:inline-flex!important}.d-flex{display:flex!important}.d-grid{display:grid!important}@media(max-width:767px){.grid-2,.grid-3{grid-template-columns:1fr}}*{box-sizing:border-box}html,body{padding:0;margin:0}body{background-color:#16161e;color:#c0caf5}*::-webkit-scrollbar-corner{background:transparent;height:1px}.ph.normalize{position:relative;top:.15em}html,body{height:100%}*::-webkit-scrollbar{width:10px}*::-webkit-scrollbar-track{width:10px;background:#16161e;cursor:pointer}*::-webkit-scrollbar-thumb{width:10px;background:#414868;cursor:default}*::-webkit-scrollbar-corner{background:transparent}*::-webkit-scrollbar-button{display:none}#app{display:flex;height:100vh;height:100dvh;overflow:hidden}.app-main{flex:1 1 0;min-width:0;min-height:0;display:flex;flex-direction:column;overflow:hidden;background:#16161e}.app-sidebar{width:380px;min-width:380px;max-width:380px;display:flex;flex-direction:column;background:#1f2335;border-right:2px solid rgba(192,202,245,.24);overflow:hidden;flex-shrink:0;transition:width .22s ease,min-width .22s ease,max-width .22s ease,border-color .22s ease,transform .28s ease}.app-sidebar.is-hidden-by-artifacts{width:0;min-width:0;max-width:0;border-right-color:transparent}@media(max-width:1280px){.app-sidebar{position:fixed;top:0;right:0;bottom:0;left:0;z-index:100;width:min(380px,92vw)!important;min-width:min(380px,92vw)!important;transform:translate(-100%);transition:transform .28s ease}.app-sidebar.is-mobile-open{transform:translate(0)}.app-sidebar.is-hidden-by-artifacts{width:min(380px,92vw)!important;min-width:min(380px,92vw)!important;max-width:min(380px,92vw)!important;border-right-color:#c0caf53d}}@media(max-width:980px){.app-sidebar,.app-sidebar.is-hidden-by-artifacts{width:100vw!important;min-width:100vw!important;max-width:100vw!important}}.sidebar-header{padding:15px;border-bottom:2px solid rgba(192,202,245,.24);display:flex;flex-direction:column;gap:8px}.sidebar-controls-row{display:flex;align-items:center;gap:8px}.sidebar-controls-row .sidebar-profile-select{flex:1;min-width:0}.sidebar-logo{display:flex;align-items:center;gap:8px;text-decoration:none}.sidebar-logo img{width:28px;height:28px}.sidebar-logo span{font-size:18px;font-weight:700;color:#c0caf5;letter-spacing:-.3px}.sidebar-sessions{flex:1;overflow:hidden;display:flex;flex-direction:column}.sessions-label{padding:8px 15px;font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:#787c99}.sidebar-profile-select{position:relative}.sidebar-profile-select select{width:100%;-moz-appearance:none;appearance:none;-webkit-appearance:none;min-height:46px;font-family:inherit;font-size:13px;font-weight:500;padding:5px 34px 5px 15px;border:2px solid rgba(192,202,245,.24);border-bottom-width:6px;border-bottom-color:#c0caf5;color:#c0caf5;background-color:#c0caf50b;cursor:pointer;transition:border-color .2s ease,background-color .2s ease;outline:none}.sidebar-profile-select select:hover{border-bottom-color:#787c99}.sidebar-profile-select select:focus{outline:2px solid #E0AF68;outline-offset:3px;border-color:#7aa2f7;background-color:transparent}.sidebar-profile-select select option{background:#1f2335;color:#c0caf5}.sidebar-profile-select:after{content:"";position:absolute;right:15px;top:50%;transform:translateY(-50%);width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid #A9B1D6;pointer-events:none}.session-item{display:flex;align-items:flex-start;gap:8px;padding:8px 15px;cursor:pointer;border-radius:0;transition:background .15s ease;position:relative}.session-item:hover{background:#c0caf50b}.session-item:hover .session-actions{opacity:1}@media(hover:none){.session-item .session-actions{opacity:1}}.session-item.is-active{background:#c0caf516;border-left:6px solid #7AA2F7;padding-left:9px}.session-item.is-pinned{background:#e0af6812}.session-item.is-pinned .session-pin-icon{opacity:1;color:#e0af68}.session-item.is-pinned .session-name{color:#e0af68}.session-pin-icon{color:#787c99;font-size:14px;opacity:0;transition:opacity .15s ease;flex-shrink:0;margin-top:2px}.session-info{flex:1;overflow:hidden}.session-name{font-size:13px;color:#a9b1d6;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.session-time{font-size:11px;color:#787c99;line-height:1.4;margin-top:2px}.session-preview{font-size:12px;color:#c0caf5;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;line-height:1.4;margin-top:3px}.session-actions{display:flex;gap:2px;opacity:0;transition:opacity .15s ease;flex-shrink:0;align-self:flex-start;margin-top:1px}.session-actions .btn-icon{width:26px;height:26px;font-size:14px}.chat-area{flex:1 1 0;min-height:0;min-width:0;display:flex;flex-direction:column;overflow:hidden;background:#16161e}.chat-header{display:flex;align-items:center;gap:8px;padding:8px 15px;border-bottom:2px solid rgba(192,202,245,.24);background:#1f2335;flex-shrink:0;min-height:54px}.chat-header-info{flex:1;display:flex;align-items:center;gap:8px;overflow:hidden;min-width:0}@media(max-width:768px){.chat-header-info{flex-direction:column;align-items:flex-start;gap:2px}}.chat-header-title{font-size:14px;font-weight:600;color:#c0caf5;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex-shrink:1;min-width:0}.btn-sidebar-toggle{display:none}@media(max-width:1280px){.btn-sidebar-toggle{display:flex}}.context-bar{display:flex;align-items:center;flex-shrink:0;cursor:default}.profile-badge{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;background:#c0caf50b;border:2px solid rgba(192,202,245,.24);font-size:12px;color:#a9b1d6;white-space:nowrap}.message-list{flex:1;overflow-y:auto;overflow-x:hidden;padding:22px 15px;display:flex;flex-direction:column;gap:15px;min-height:0}@media(max-width:768px){.message-list{padding-left:10px;padding-right:10px}}.message-list .vue-recycle-scroller{flex:1}.message-list-inner{display:flex;flex-direction:column;gap:34px;width:100%;max-width:920px;margin:0 auto;padding:34px 22px;transition:opacity .2s ease}@media(max-width:768px){.message-list-inner{padding-left:0;padding-right:0}}.message-list-inner.is-hidden{opacity:0;pointer-events:none}.msg-user{display:flex;justify-content:flex-end;gap:8px}.msg-user-bubble{background:#7aa2f7;color:#16161e;padding:8px 15px;font-size:14px;line-height:1.6;word-wrap:break-word;width:100%}.msg-user-bubble .msg-user-quote{margin:0 0 5px;padding:2px 0 2px 8px;border-left:3px solid rgba(22,22,30,.35);opacity:.7;font-size:13px}.msg-user-attachments{display:flex;flex-wrap:wrap;gap:5px;margin-top:5px}.msg-user-image{width:80px;height:80px;object-fit:cover;cursor:pointer;transition:opacity .15s}.msg-user-image:hover{opacity:.75}.msg-user-file-pill{display:inline-flex;align-items:center;gap:5px;padding:2px 8px;background:#16161e4d;font-size:12px}.msg-time{font-size:11px;color:#787c99;line-height:1;margin-top:4px}.msg-meta-row{display:flex;align-items:center;gap:8px;margin-top:2px;min-width:0}.msg-meta-item{display:inline-flex;align-items:center;gap:4px;font-size:11px;color:#787c99;white-space:nowrap}.msg-meta-item i{font-size:12px;opacity:.7}.msg-user-wrap{display:flex;flex-direction:column;align-items:flex-end;max-width:70%}@media(max-width:768px){.msg-user-wrap{max-width:90%}}.msg-user-footer{display:flex;align-items:center;gap:5px;align-self:flex-end}.msg-copy-btn{width:22px;height:22px;min-width:22px;padding:0;font-size:12px;opacity:.5;transition:opacity .15s}.msg-copy-btn:hover{opacity:1}.msg-meta-row>.msg-copy-btn{margin-left:auto}.msg-meta-row .msg-rate-btn{width:22px;height:22px;min-width:22px;padding:0;font-size:13px}.msg-assistant{display:flex;flex-direction:column;gap:8px;max-width:100%}.planning-indicator{display:flex;align-items:center;gap:8px;font-size:13px;font-style:italic;color:#787c99;padding:5px 0}.planning-indicator.is-executing{color:#a9b1d6;font-style:normal}.planning-label{opacity:.75;transition:opacity .2s}.msg-assistant-avatar{width:28px;height:28px;border-radius:50%;flex-shrink:0}.msg-assistant-content{font-size:14px;line-height:1.7;color:#c0caf5}.msg-assistant-content p{margin:0 0 15px}.msg-assistant-content p:last-child{margin-bottom:0}.msg-assistant-content h1,.msg-assistant-content h2,.msg-assistant-content h3,.msg-assistant-content h4{margin:22px 0 8px;color:#c0caf5}.msg-assistant-content ul,.msg-assistant-content ol{padding-left:22px;margin-bottom:15px}.msg-assistant-content li{margin-bottom:8px}.msg-assistant-content blockquote{margin:8px 0;padding-left:15px;border-left:6px solid #7AA2F7;color:#a9b1d6}.msg-assistant-content a{color:#7aa2f7}.msg-assistant-content strong{color:#c0caf5}.msg-assistant-content hr{border:none;border-top:1px solid rgba(192,202,245,.24);margin:15px 0}.msg-assistant-content .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:15px 0}.msg-assistant-content .table{margin:15px 0}.msg-assistant-content .table thead{border-bottom:2px solid #C0CAF5}.msg-assistant-content .table tbody tr{transition:background .2s ease}.msg-assistant-content .table tbody tr:hover{background:#7aa2f714}.msg-assistant-content .table tbody tr:hover td:first-child{color:#7aa2f7}.msg-assistant-content .table tr th,.msg-assistant-content .table tr td{padding:8px 15px;font-size:13px;vertical-align:middle;border-bottom:2px solid rgba(192,202,245,.08)}.msg-assistant-content .table tr th{color:#c0caf5;background:#c0caf50a;text-transform:uppercase;letter-spacing:.06em}.msg-assistant-content :not(pre)>code{font-family:IBM Plex Mono,monospace;font-size:13px;background:#c0caf516;padding:1px 5px;color:#7dcfff}.msg-assistant-content .code-block{margin:15px 0;background:#1f2335;border:2px solid rgba(192,202,245,.24);overflow:hidden}.msg-assistant-content .code-block .code-header{display:flex;align-items:center;justify-content:space-between;padding:5px 15px;background:#c0caf516;border-bottom:2px solid rgba(192,202,245,.24);font-size:12px;color:#787c99;font-family:IBM Plex Mono,monospace}.msg-assistant-content .code-block pre{margin:0;background:#1f2335!important}.msg-assistant-content .code-block pre code{font-family:IBM Plex Mono,monospace;font-size:13px;display:block;padding:15px;overflow-x:auto}.thinking-card{margin-bottom:8px}.thinking-card summary{list-style:none;display:flex;align-items:center;gap:8px;padding:8px 15px;cursor:pointer;color:#787c99;font-size:13px;font-style:italic;background:#c0caf50b;border:2px solid rgba(192,202,245,.24);transition:background .15s ease;-webkit-user-select:none;user-select:none}.thinking-card summary::-webkit-details-marker{display:none}.thinking-card summary:hover{background:#c0caf516}.thinking-card[open] summary{border-bottom-color:transparent}.thinking-card .thinking-chevron{margin-left:auto;font-size:18px;color:#787c99;flex-shrink:0;transition:transform .15s ease}.thinking-card[open] .thinking-chevron{transform:rotate(180deg)}.thinking-card .thinking-body{padding:15px;font-size:13px;font-style:italic;color:#a9b1d6;line-height:1.6;background:#c0caf50b;border:2px solid rgba(192,202,245,.24);border-top:none;white-space:pre-wrap}.plan-card{border:2px solid #BB9AF7;border-left-width:6px;border-left-color:#bb9af7;overflow:hidden;font-size:13px;margin-bottom:8px}.plan-card summary{list-style:none;display:flex;align-items:center;gap:8px;padding:8px 15px;cursor:pointer;background:#bb9af714;color:#bb9af7;font-size:13px;-webkit-user-select:none;user-select:none}.plan-card summary::-webkit-details-marker{display:none}.plan-card summary:hover{background:#bb9af724}.plan-card summary .plan-chevron{margin-left:auto;font-size:18px;color:#bb9af799;transition:transform .15s ease;flex-shrink:0}.plan-card[open] .plan-chevron{transform:rotate(180deg)}.plan-card .plan-body{padding:15px;font-size:13px;color:#a9b1d6;line-height:1.6;background:#bb9af70a;border-top:2px solid rgba(187,154,247,.3)}.plan-card .plan-body p{margin:0 0 8px}.plan-card .plan-body p:last-child{margin-bottom:0}.plan-card .plan-body ul,.plan-card .plan-body ol{padding-left:22px;margin-bottom:8px}.plan-card .plan-body li{margin-bottom:5px}.plan-card .plan-body strong{color:#c0caf5}.tool-card{border:2px solid rgba(192,202,245,.24);border-left-width:6px;margin-bottom:8px;border-left-color:#7aa2f7;overflow:hidden;font-size:13px}.tool-card.is-success{border-left-color:#9ece6a}.tool-card.is-error{border-left-color:#f7768e}.tool-card summary{list-style:none;display:flex;align-items:center;gap:8px;padding:8px 15px;cursor:pointer;background:#c0caf50b;color:#a9b1d6;-webkit-user-select:none;user-select:none}.tool-card summary::-webkit-details-marker{display:none}.tool-card summary:hover{background:#c0caf516}.tool-card .tool-name{flex:1;font-family:IBM Plex Mono,monospace;font-size:12px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tool-card .tool-running-time{font-family:IBM Plex Mono,monospace;font-size:11px;color:#787c99;white-space:nowrap}.tool-card .tool-status-icon{display:flex;align-items:center;justify-content:center;width:18px;height:18px;font-size:18px;flex-shrink:0}.tool-card .tool-emoji{font-size:18px;line-height:1;flex-shrink:0}.tool-card .tool-chevron{font-size:18px;color:#787c99;flex-shrink:0;transition:transform .15s ease}.tool-card[open] .tool-chevron{transform:rotate(180deg)}.tool-card-body{background:#c0caf50b;border-top:2px solid rgba(192,202,245,.24)}.tool-running-banner{display:flex;align-items:center;gap:8px;padding:8px 15px;color:#a9b1d6;background:#7aa2f714;border-bottom:1px solid rgba(192,202,245,.24);font-size:12px}.tool-section{padding:8px 15px}.tool-section+.tool-section{border-top:1px solid rgba(192,202,245,.24)}.tool-section-label{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:#787c99;margin-bottom:5px}.tool-code{font-family:IBM Plex Mono,monospace;font-size:12px;color:#a9b1d6;white-space:pre-wrap;word-break:break-all;max-height:200px;overflow-y:auto}.subagent-steps{display:flex;flex-direction:column;gap:5px;padding:5px 15px 15px;background:#c0caf50b}.subagent-planning-indicator{display:flex;align-items:center;gap:8px;padding:5px 15px;font-size:12px;font-style:italic;color:#787c99;background:#c0caf50b}.summary-card{border:2px solid #BB9AF7;overflow:hidden;font-size:13px}.summary-card summary{list-style:none;display:flex;align-items:center;gap:8px;padding:8px 15px;cursor:pointer;background:#bb9af714;color:#bb9af7;-webkit-user-select:none;user-select:none}.summary-card summary::-webkit-details-marker{display:none}.compression-notice{font-size:12px}.compression-notice summary{list-style:none;text-align:center;padding:8px;color:#787c99;display:flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;-webkit-user-select:none;user-select:none}.compression-notice summary::-webkit-details-marker{display:none}.compression-notice summary:before,.compression-notice summary:after{content:"";flex:1;height:1px;background:#c0caf53d}.compression-notice .compression-counts{opacity:.7}.compression-notice .compression-chevron{font-size:18px;transition:transform .15s ease}.compression-notice[open] .compression-chevron{transform:rotate(180deg)}.compression-notice .compression-body{padding:15px;font-size:13px;color:#a9b1d6;line-height:1.6;background:#c0caf50b;border:2px solid rgba(192,202,245,.24)}.input-bar{flex-shrink:0;padding:8px 15px 15px;background:#16161e;border-top:2px solid rgba(192,202,245,.24)}.input-dropzone{position:relative}.input-dropzone.is-dragging:after{content:"Drop files here";position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;background:#7aa2f71f;border:2px dashed #7AA2F7;font-size:14px;color:#7aa2f7;pointer-events:none;z-index:10}.file-preview-strip{display:flex;flex-wrap:wrap;gap:5px;margin-bottom:5px}.file-preview-image{position:relative;width:60px;height:60px}.file-preview-image img{width:100%;height:100%;object-fit:cover;border:2px solid rgba(192,202,245,.24)}.file-preview-pill{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;background:#c0caf50b;border:2px solid rgba(192,202,245,.24);font-size:12px;color:#a9b1d6;max-width:180px}.file-preview-pill span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.file-preview-remove{background:none;border:none;cursor:pointer;color:#787c99;padding:0;display:flex;align-items:center;font-size:14px;transition:color .15s ease;flex-shrink:0}.file-preview-remove:hover{color:#f7768e}.upload-progress{height:2px;background:#c0caf516;margin-bottom:5px;overflow:hidden}.upload-progress-fill{height:100%;background:#7aa2f7;transition:width .2s ease}.input-row{display:flex;align-items:center;gap:8px;background:#1f2335;border:2px solid rgba(192,202,245,.24);padding:8px;transition:border-color .15s ease;max-width:920px;margin:0 auto}.input-row:focus-within{border-color:#7aa2f7}.input-textarea{flex:1;background:none;border:none;outline:none;resize:none;color:#c0caf5;font-size:14px;line-height:1.5;font-family:inherit;min-height:24px;max-height:144px;overflow-y:auto;padding:2px 0}.input-textarea::placeholder{color:#787c99}.welcome-screen{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:34px;gap:34px;text-align:center}@media(max-width:768px){.welcome-screen{padding:52px 15px 15px;gap:15px;justify-content:flex-start}}.welcome-logo{display:flex;flex-direction:column;align-items:center;gap:8px}.welcome-logo img{width:64px;height:64px}.welcome-logo h1{font-size:32px;font-weight:700;letter-spacing:-.5px;color:#c0caf5;margin:0}.welcome-logo p{font-size:15px;color:#a9b1d6;margin:0}@media(max-width:768px){.welcome-logo{flex-direction:row;flex-wrap:wrap;justify-content:center;gap:8px}.welcome-logo img{width:36px;height:36px}.welcome-logo h1{font-size:22px}.welcome-logo p{font-size:13px;flex-basis:100%}}.welcome-profiles{display:flex;gap:15px;flex-wrap:wrap;justify-content:center;max-width:600px}@media(max-width:768px){.welcome-profiles{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;width:100%;max-width:100%}}.welcome-profile-card{display:flex;flex-direction:column;align-items:center;gap:8px;padding:15px;background:#c0caf50b;border:2px solid rgba(192,202,245,.24);cursor:pointer;transition:border-color .15s ease,background .15s ease;width:160px;text-align:center}.welcome-profile-card:hover,.welcome-profile-card.is-selected{border-color:#7aa2f7;background:#c0caf516}.welcome-profile-card.is-selected{border-color:#7aa2f7}.welcome-profile-card .profile-icon{font-size:32px;color:#7aa2f7}.welcome-profile-card .profile-name{font-size:14px;font-weight:600;color:#c0caf5}.welcome-profile-card .profile-desc{font-size:12px;color:#787c99;line-height:1.4}@media(max-width:768px){.welcome-profile-card{width:auto;padding:10px;gap:6px}.welcome-profile-card .profile-icon{font-size:22px}.welcome-profile-card .profile-name{font-size:13px}.welcome-profile-card .profile-desc{font-size:11px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}}.empty-chat{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:15px;color:#787c99;padding:34px}.empty-chat .empty-icon{font-size:56px;opacity:.4}.empty-chat p{font-size:15px;margin:0}.spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(192,202,245,.24);border-top-color:#7aa2f7;border-radius:50%;animation:spin .6s linear infinite;flex-shrink:0}@keyframes spin{to{transform:rotate(360deg)}}.fade-enter-active,.fade-leave-active{transition:opacity .15s ease}.fade-enter-from,.fade-leave-to{opacity:0}.msg-enter{animation:msgEnter .2s ease both}@keyframes msgEnter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.slide-left-enter-active,.slide-left-leave-active{transition:transform .28s ease,opacity .28s ease}.slide-left-enter-from,.slide-left-leave-to{transform:translate(-100%);opacity:0}.modal-enter-active,.modal-leave-active{transition:opacity .28s ease}.modal-enter-active .modal-panel,.modal-leave-active .modal-panel{transition:opacity .28s ease,margin-top .28s ease}.modal-enter-from,.modal-leave-to{opacity:0}.modal-enter-from .modal-panel,.modal-leave-to .modal-panel{opacity:0;margin-top:200px}.btn-icon:not(.with-rotate) .ph{transform:none!important;transition:none!important}.tool-card[open] .tool-chevron,.thinking-card[open] .tool-chevron{transform:rotate(180deg)!important}
diff --git a/webclient/dist/assets/index-BSatN9tx.js b/webclient/dist/assets/index-BSatN9tx.js
deleted file mode 100644
index dbca455..0000000
--- a/webclient/dist/assets/index-BSatN9tx.js
+++ /dev/null
@@ -1,93 +0,0 @@
-var eS=Object.defineProperty;var tS=(t,e,n)=>e in t?eS(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var dt=(t,e,n)=>tS(t,typeof e!="symbol"?e+"":e,n);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const s of a.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&r(s)}).observe(document,{childList:!0,subtree:!0});function n(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(i){if(i.ep)return;i.ep=!0;const a=n(i);fetch(i.href,a)}})();/**
-* @vue/shared v3.5.32
-* (c) 2018-present Yuxi (Evan) You and Vue contributors
-* @license MIT
-**/function yc(t){const e=Object.create(null);for(const n of t.split(","))e[n]=1;return n=>n in e}const ut={},Sr=[],En=()=>{},ng=()=>!1,Ui=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&(t.charCodeAt(2)>122||t.charCodeAt(2)<97),Fi=t=>t.startsWith("onUpdate:"),yt=Object.assign,Ic=(t,e)=>{const n=t.indexOf(e);n>-1&&t.splice(n,1)},nS=Object.prototype.hasOwnProperty,it=(t,e)=>nS.call(t,e),Ae=Array.isArray,fr=t=>jr(t)==="[object Map]",rg=t=>jr(t)==="[object Set]",E_=t=>jr(t)==="[object Date]",Ue=t=>typeof t=="function",Et=t=>typeof t=="string",Vt=t=>typeof t=="symbol",at=t=>t!==null&&typeof t=="object",ig=t=>(at(t)||Ue(t))&&Ue(t.then)&&Ue(t.catch),ag=Object.prototype.toString,jr=t=>ag.call(t),rS=t=>jr(t).slice(8,-1),og=t=>jr(t)==="[object Object]",Bi=t=>Et(t)&&t!=="NaN"&&t[0]!=="-"&&""+parseInt(t,10)===t,kr=yc(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Gi=t=>{const e=Object.create(null);return(n=>e[n]||(e[n]=t(n)))},iS=/-\w/g,Gt=Gi(t=>t.replace(iS,e=>e.slice(1).toUpperCase())),aS=/\B([A-Z])/g,Gn=Gi(t=>t.replace(aS,"-$1").toLowerCase()),Yi=Gi(t=>t.charAt(0).toUpperCase()+t.slice(1)),Ei=Gi(t=>t?`on${Yi(t)}`:""),mn=(t,e)=>!Object.is(t,e),Si=(t,...e)=>{for(let n=0;n{Object.defineProperty(t,e,{configurable:!0,enumerable:!1,writable:r,value:n})},Ac=t=>{const e=parseFloat(t);return isNaN(e)?t:e},oS=t=>{const e=Et(t)?Number(t):NaN;return isNaN(e)?t:e};let S_;const qi=()=>S_||(S_=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function ar(t){if(Ae(t)){const e={};for(let n=0;n{if(n){const r=n.split(lS);r.length>1&&(e[r[0].trim()]=r[1].trim())}}),e}function Ze(t){let e="";if(Et(t))e=t;else if(Ae(t))for(let n=0;n!!(t&&t.__v_isRef===!0),xe=t=>Et(t)?t:t==null?"":Ae(t)||at(t)&&(t.toString===ag||!Ue(t.toString))?cg(t)?xe(t.value):JSON.stringify(t,_g,2):String(t),_g=(t,e)=>cg(e)?_g(t,e.value):fr(e)?{[`Map(${e.size})`]:[...e.entries()].reduce((n,[r,i],a)=>(n[la(r,a)+" =>"]=i,n),{})}:rg(e)?{[`Set(${e.size})`]:[...e.values()].map(n=>la(n))}:Vt(e)?la(e):at(e)&&!Ae(e)&&!og(e)?String(e):e,la=(t,e="")=>{var n;return Vt(t)?`Symbol(${(n=t.description)!=null?n:e})`:t};/**
-* @vue/reactivity v3.5.32
-* (c) 2018-present Yuxi (Evan) You and Vue contributors
-* @license MIT
-**/let Mt;class dg{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.__v_skip=!0,this.parent=Mt,!e&&Mt&&(this.index=(Mt.scopes||(Mt.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let e,n;if(this.scopes)for(e=0,n=this.scopes.length;e0&&--this._on===0&&(Mt=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){this._active=!1;let n,r;for(n=0,r=this.effects.length;n0)return;if(Fr){let e=Fr;for(Fr=void 0;e;){const n=e.next;e.next=void 0,e.flags&=-9,e=n}}let t;for(;Ur;){let e=Ur;for(Ur=void 0;e;){const n=e.next;if(e.next=void 0,e.flags&=-9,e.flags&1)try{e.trigger()}catch(r){t||(t=r)}e=n}}if(t)throw t}function Eg(t){for(let e=t.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function Sg(t){let e,n=t.depsTail,r=n;for(;r;){const i=r.prevDep;r.version===-1?(r===n&&(n=i),wc(r),ES(r)):e=r,r.dep.activeLink=r.prevActiveLink,r.prevActiveLink=void 0,r=i}t.deps=e,t.depsTail=n}function ic(t){for(let e=t.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(fg(e.dep.computed)||e.dep.version!==e.version))return!0;return!!t._dirty}function fg(t){if(t.flags&4&&!(t.flags&16)||(t.flags&=-17,t.globalVersion===$r)||(t.globalVersion=$r,!t.isSSR&&t.flags&128&&(!t.deps&&!t._dirty||!ic(t))))return;t.flags|=2;const e=t.dep,n=pt,r=tn;pt=t,tn=!0;try{Eg(t);const i=t.fn(t._value);(e.version===0||mn(i,t._value))&&(t.flags|=128,t._value=i,e.version++)}catch(i){throw e.version++,i}finally{pt=n,tn=r,Sg(t),t.flags&=-3}}function wc(t,e=!1){const{dep:n,prevSub:r,nextSub:i}=t;if(r&&(r.nextSub=i,t.prevSub=void 0),i&&(i.prevSub=r,t.nextSub=void 0),n.subs===t&&(n.subs=r,!r&&n.computed)){n.computed.flags&=-5;for(let a=n.computed.deps;a;a=a.nextDep)wc(a,!0)}!e&&!--n.sc&&n.map&&n.map.delete(n.key)}function ES(t){const{prevDep:e,nextDep:n}=t;e&&(e.nextDep=n,t.prevDep=void 0),n&&(n.prevDep=e,t.nextDep=void 0)}let tn=!0;const Tg=[];function Dn(){Tg.push(tn),tn=!1}function Mn(){const t=Tg.pop();tn=t===void 0?!0:t}function f_(t){const{cleanup:e}=t;if(t.cleanup=void 0,e){const n=pt;pt=void 0;try{e()}finally{pt=n}}}let $r=0;class SS{constructor(e,n){this.sub=e,this.dep=n,this.version=n.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class Pc{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!pt||!tn||pt===this.computed)return;let n=this.activeLink;if(n===void 0||n.sub!==pt)n=this.activeLink=new SS(pt,this),pt.deps?(n.prevDep=pt.depsTail,pt.depsTail.nextDep=n,pt.depsTail=n):pt.deps=pt.depsTail=n,bg(n);else if(n.version===-1&&(n.version=this.version,n.nextDep)){const r=n.nextDep;r.prevDep=n.prevDep,n.prevDep&&(n.prevDep.nextDep=r),n.prevDep=pt.depsTail,n.nextDep=void 0,pt.depsTail.nextDep=n,pt.depsTail=n,pt.deps===n&&(pt.deps=r)}return n}trigger(e){this.version++,$r++,this.notify(e)}notify(e){xc();try{for(let n=this.subs;n;n=n.prevSub)n.sub.notify()&&n.sub.dep.notify()}finally{Lc()}}}function bg(t){if(t.dep.sc++,t.sub.flags&4){const e=t.dep.computed;if(e&&!t.dep.subs){e.flags|=20;for(let r=e.deps;r;r=r.nextDep)bg(r)}const n=t.dep.subs;n!==t&&(t.prevSub=n,n&&(n.nextSub=t)),t.dep.subs=t}}const hi=new WeakMap,jn=Symbol(""),ac=Symbol(""),Vr=Symbol("");function xt(t,e,n){if(tn&&pt){let r=hi.get(t);r||hi.set(t,r=new Map);let i=r.get(n);i||(r.set(n,i=new Pc),i.map=r,i.key=n),i.track()}}function Nn(t,e,n,r,i,a){const s=hi.get(t);if(!s){$r++;return}const o=c=>{c&&c.trigger()};if(xc(),e==="clear")s.forEach(o);else{const c=Ae(t),_=c&&Bi(n);if(c&&n==="length"){const l=Number(r);s.forEach((d,u)=>{(u==="length"||u===Vr||!Vt(u)&&u>=l)&&o(d)})}else switch((n!==void 0||s.has(void 0))&&o(s.get(n)),_&&o(s.get(Vr)),e){case"add":c?_&&o(s.get("length")):(o(s.get(jn)),fr(t)&&o(s.get(ac)));break;case"delete":c||(o(s.get(jn)),fr(t)&&o(s.get(ac)));break;case"set":fr(t)&&o(s.get(jn));break}}Lc()}function fS(t,e){const n=hi.get(t);return n&&n.get(e)}function _r(t){const e=Xe(t);return e===t?e:(xt(e,"iterate",Vr),$t(t)?e:e.map(nn))}function Hi(t){return xt(t=Xe(t),"iterate",Vr),t}function un(t,e){return xn(t)?Rr(An(t)?nn(e):e):nn(e)}const TS={__proto__:null,[Symbol.iterator](){return _a(this,Symbol.iterator,t=>un(this,t))},concat(...t){return _r(this).concat(...t.map(e=>Ae(e)?_r(e):e))},entries(){return _a(this,"entries",t=>(t[1]=un(this,t[1]),t))},every(t,e){return Rn(this,"every",t,e,void 0,arguments)},filter(t,e){return Rn(this,"filter",t,e,n=>n.map(r=>un(this,r)),arguments)},find(t,e){return Rn(this,"find",t,e,n=>un(this,n),arguments)},findIndex(t,e){return Rn(this,"findIndex",t,e,void 0,arguments)},findLast(t,e){return Rn(this,"findLast",t,e,n=>un(this,n),arguments)},findLastIndex(t,e){return Rn(this,"findLastIndex",t,e,void 0,arguments)},forEach(t,e){return Rn(this,"forEach",t,e,void 0,arguments)},includes(...t){return da(this,"includes",t)},indexOf(...t){return da(this,"indexOf",t)},join(t){return _r(this).join(t)},lastIndexOf(...t){return da(this,"lastIndexOf",t)},map(t,e){return Rn(this,"map",t,e,void 0,arguments)},pop(){return Ar(this,"pop")},push(...t){return Ar(this,"push",t)},reduce(t,...e){return T_(this,"reduce",t,e)},reduceRight(t,...e){return T_(this,"reduceRight",t,e)},shift(){return Ar(this,"shift")},some(t,e){return Rn(this,"some",t,e,void 0,arguments)},splice(...t){return Ar(this,"splice",t)},toReversed(){return _r(this).toReversed()},toSorted(t){return _r(this).toSorted(t)},toSpliced(...t){return _r(this).toSpliced(...t)},unshift(...t){return Ar(this,"unshift",t)},values(){return _a(this,"values",t=>un(this,t))}};function _a(t,e,n){const r=Hi(t),i=r[e]();return r!==t&&!$t(t)&&(i._next=i.next,i.next=()=>{const a=i._next();return a.done||(a.value=n(a.value)),a}),i}const bS=Array.prototype;function Rn(t,e,n,r,i,a){const s=Hi(t),o=s!==t&&!$t(t),c=s[e];if(c!==bS[e]){const d=c.apply(t,a);return o?nn(d):d}let _=n;s!==t&&(o?_=function(d,u){return n.call(this,un(t,d),u,t)}:n.length>2&&(_=function(d,u){return n.call(this,d,u,t)}));const l=c.call(s,_,r);return o&&i?i(l):l}function T_(t,e,n,r){const i=Hi(t),a=i!==t&&!$t(t);let s=n,o=!1;i!==t&&(a?(o=r.length===0,s=function(_,l,d){return o&&(o=!1,_=un(t,_)),n.call(this,_,un(t,l),d,t)}):n.length>3&&(s=function(_,l,d){return n.call(this,_,l,d,t)}));const c=i[e](s,...r);return o?un(t,c):c}function da(t,e,n){const r=Xe(t);xt(r,"iterate",Vr);const i=r[e](...n);return(i===-1||i===!1)&&$i(n[0])?(n[0]=Xe(n[0]),r[e](...n)):i}function Ar(t,e,n=[]){Dn(),xc();const r=Xe(t)[e].apply(t,n);return Lc(),Mn(),r}const RS=yc("__proto__,__v_isRef,__isVue"),Rg=new Set(Object.getOwnPropertyNames(Symbol).filter(t=>t!=="arguments"&&t!=="caller").map(t=>Symbol[t]).filter(Vt));function hS(t){Vt(t)||(t=String(t));const e=Xe(this);return xt(e,"has",t),e.hasOwnProperty(t)}class hg{constructor(e=!1,n=!1){this._isReadonly=e,this._isShallow=n}get(e,n,r){if(n==="__v_skip")return e.__v_skip;const i=this._isReadonly,a=this._isShallow;if(n==="__v_isReactive")return!i;if(n==="__v_isReadonly")return i;if(n==="__v_isShallow")return a;if(n==="__v_raw")return r===(i?a?xS:Og:a?Ng:vg).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(r)?e:void 0;const s=Ae(e);if(!i){let c;if(s&&(c=TS[n]))return c;if(n==="hasOwnProperty")return hS}const o=Reflect.get(e,n,bt(e)?e:r);if((Vt(n)?Rg.has(n):RS(n))||(i||xt(e,"get",n),a))return o;if(bt(o)){const c=s&&Bi(n)?o:o.value;return i&&at(c)?sc(c):c}return at(o)?i?sc(o):ei(o):o}}class Cg extends hg{constructor(e=!1){super(!1,e)}set(e,n,r,i){let a=e[n];const s=Ae(e)&&Bi(n);if(!this._isShallow){const _=xn(a);if(!$t(r)&&!xn(r)&&(a=Xe(a),r=Xe(r)),!s&&bt(a)&&!bt(r))return _||(a.value=r),!0}const o=s?Number(n)t,ci=t=>Reflect.getPrototypeOf(t);function yS(t,e,n){return function(...r){const i=this.__v_raw,a=Xe(i),s=fr(a),o=t==="entries"||t===Symbol.iterator&&s,c=t==="keys"&&s,_=i[t](...r),l=n?oc:e?Rr:nn;return!e&&xt(a,"iterate",c?ac:jn),yt(Object.create(_),{next(){const{value:d,done:u}=_.next();return u?{value:d,done:u}:{value:o?[l(d[0]),l(d[1])]:l(d),done:u}}})}}function _i(t){return function(...e){return t==="delete"?!1:t==="clear"?void 0:this}}function IS(t,e){const n={get(i){const a=this.__v_raw,s=Xe(a),o=Xe(i);t||(mn(i,o)&&xt(s,"get",i),xt(s,"get",o));const{has:c}=ci(s),_=e?oc:t?Rr:nn;if(c.call(s,i))return _(a.get(i));if(c.call(s,o))return _(a.get(o));a!==s&&a.get(i)},get size(){const i=this.__v_raw;return!t&&xt(Xe(i),"iterate",jn),i.size},has(i){const a=this.__v_raw,s=Xe(a),o=Xe(i);return t||(mn(i,o)&&xt(s,"has",i),xt(s,"has",o)),i===o?a.has(i):a.has(i)||a.has(o)},forEach(i,a){const s=this,o=s.__v_raw,c=Xe(o),_=e?oc:t?Rr:nn;return!t&&xt(c,"iterate",jn),o.forEach((l,d)=>i.call(a,_(l),_(d),s))}};return yt(n,t?{add:_i("add"),set:_i("set"),delete:_i("delete"),clear:_i("clear")}:{add(i){const a=Xe(this),s=ci(a),o=Xe(i),c=!e&&!$t(i)&&!xn(i)?o:i;return s.has.call(a,c)||mn(i,c)&&s.has.call(a,i)||mn(o,c)&&s.has.call(a,o)||(a.add(c),Nn(a,"add",c,c)),this},set(i,a){!e&&!$t(a)&&!xn(a)&&(a=Xe(a));const s=Xe(this),{has:o,get:c}=ci(s);let _=o.call(s,i);_||(i=Xe(i),_=o.call(s,i));const l=c.call(s,i);return s.set(i,a),_?mn(a,l)&&Nn(s,"set",i,a):Nn(s,"add",i,a),this},delete(i){const a=Xe(this),{has:s,get:o}=ci(a);let c=s.call(a,i);c||(i=Xe(i),c=s.call(a,i)),o&&o.call(a,i);const _=a.delete(i);return c&&Nn(a,"delete",i,void 0),_},clear(){const i=Xe(this),a=i.size!==0,s=i.clear();return a&&Nn(i,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(i=>{n[i]=yS(i,t,e)}),n}function kc(t,e){const n=IS(t,e);return(r,i,a)=>i==="__v_isReactive"?!t:i==="__v_isReadonly"?t:i==="__v_raw"?r:Reflect.get(it(n,i)&&i in r?n:r,i,a)}const AS={get:kc(!1,!1)},DS={get:kc(!1,!0)},MS={get:kc(!0,!1)};const vg=new WeakMap,Ng=new WeakMap,Og=new WeakMap,xS=new WeakMap;function LS(t){switch(t){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function wS(t){return t.__v_skip||!Object.isExtensible(t)?0:LS(rS(t))}function ei(t){return xn(t)?t:Uc(t,!1,vS,AS,vg)}function yg(t){return Uc(t,!1,OS,DS,Ng)}function sc(t){return Uc(t,!0,NS,MS,Og)}function Uc(t,e,n,r,i){if(!at(t)||t.__v_raw&&!(e&&t.__v_isReactive))return t;const a=wS(t);if(a===0)return t;const s=i.get(t);if(s)return s;const o=new Proxy(t,a===2?r:n);return i.set(t,o),o}function An(t){return xn(t)?An(t.__v_raw):!!(t&&t.__v_isReactive)}function xn(t){return!!(t&&t.__v_isReadonly)}function $t(t){return!!(t&&t.__v_isShallow)}function $i(t){return t?!!t.__v_raw:!1}function Xe(t){const e=t&&t.__v_raw;return e?Xe(e):t}function Vi(t){return!it(t,"__v_skip")&&Object.isExtensible(t)&&sg(t,"__v_skip",!0),t}const nn=t=>at(t)?ei(t):t,Rr=t=>at(t)?sc(t):t;function bt(t){return t?t.__v_isRef===!0:!1}function le(t){return Ig(t,!1)}function Er(t){return Ig(t,!0)}function Ig(t,e){return bt(t)?t:new PS(t,e)}class PS{constructor(e,n){this.dep=new Pc,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=n?e:Xe(e),this._value=n?e:nn(e),this.__v_isShallow=n}get value(){return this.dep.track(),this._value}set value(e){const n=this._rawValue,r=this.__v_isShallow||$t(e)||xn(e);e=r?e:Xe(e),mn(e,n)&&(this._rawValue=e,this._value=r?e:nn(e),this.dep.trigger())}}function X(t){return bt(t)?t.value:t}function ue(t){return Ue(t)?t():X(t)}const kS={get:(t,e,n)=>e==="__v_raw"?t:X(Reflect.get(t,e,n)),set:(t,e,n,r)=>{const i=t[e];return bt(i)&&!bt(n)?(i.value=n,!0):Reflect.set(t,e,n,r)}};function Ag(t){return An(t)?t:new Proxy(t,kS)}function US(t){const e=Ae(t)?new Array(t.length):{};for(const n in t)e[n]=BS(t,n);return e}class FS{constructor(e,n,r){this._object=e,this._defaultValue=r,this.__v_isRef=!0,this._value=void 0,this._key=Vt(n)?n:String(n),this._raw=Xe(e);let i=!0,a=e;if(!Ae(e)||Vt(this._key)||!Bi(this._key))do i=!$i(a)||$t(a);while(i&&(a=a.__v_raw));this._shallow=i}get value(){let e=this._object[this._key];return this._shallow&&(e=X(e)),this._value=e===void 0?this._defaultValue:e}set value(e){if(this._shallow&&bt(this._raw[this._key])){const n=this._object[this._key];if(bt(n)){n.value=e;return}}this._object[this._key]=e}get dep(){return fS(this._raw,this._key)}}function BS(t,e,n){return new FS(t,e,n)}class GS{constructor(e,n,r){this.fn=e,this.setter=n,this._value=void 0,this.dep=new Pc(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=$r-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!n,this.isSSR=r}notify(){if(this.flags|=16,!(this.flags&8)&&pt!==this)return gg(this,!0),!0}get value(){const e=this.dep.track();return fg(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}}function YS(t,e,n=!1){let r,i;return Ue(t)?r=t:(r=t.get,i=t.set),new GS(r,i,n)}const di={},Ci=new WeakMap;let Qn;function qS(t,e=!1,n=Qn){if(n){let r=Ci.get(n);r||Ci.set(n,r=[]),r.push(t)}}function HS(t,e,n=ut){const{immediate:r,deep:i,once:a,scheduler:s,augmentJob:o,call:c}=n,_=C=>i?C:$t(C)||i===!1||i===0?On(C,1):On(C);let l,d,u,m,p=!1,E=!1;if(bt(t)?(d=()=>t.value,p=$t(t)):An(t)?(d=()=>_(t),p=!0):Ae(t)?(E=!0,p=t.some(C=>An(C)||$t(C)),d=()=>t.map(C=>{if(bt(C))return C.value;if(An(C))return _(C);if(Ue(C))return c?c(C,2):C()})):Ue(t)?e?d=c?()=>c(t,2):t:d=()=>{if(u){Dn();try{u()}finally{Mn()}}const C=Qn;Qn=l;try{return c?c(t,3,[m]):t(m)}finally{Qn=C}}:d=En,e&&i){const C=d,A=i===!0?1/0:i;d=()=>On(C(),A)}const f=ug(),T=()=>{l.stop(),f&&f.active&&Ic(f.effects,l)};if(a&&e){const C=e;e=(...A)=>{C(...A),T()}}let b=E?new Array(t.length).fill(di):di;const v=C=>{if(!(!(l.flags&1)||!l.dirty&&!C))if(e){const A=l.run();if(i||p||(E?A.some((N,x)=>mn(N,b[x])):mn(A,b))){u&&u();const N=Qn;Qn=l;try{const x=[A,b===di?void 0:E&&b[0]===di?[]:b,m];b=A,c?c(e,3,x):e(...x)}finally{Qn=N}}}else l.run()};return o&&o(v),l=new pg(d),l.scheduler=s?()=>s(v,!1):v,m=C=>qS(C,!1,l),u=l.onStop=()=>{const C=Ci.get(l);if(C){if(c)c(C,4);else for(const A of C)A();Ci.delete(l)}},e?r?v(!0):b=l.run():s?s(v.bind(null,!0),!0):l.run(),T.pause=l.pause.bind(l),T.resume=l.resume.bind(l),T.stop=T,T}function On(t,e=1/0,n){if(e<=0||!at(t)||t.__v_skip||(n=n||new Map,(n.get(t)||0)>=e))return t;if(n.set(t,e),e--,bt(t))On(t.value,e,n);else if(Ae(t))for(let r=0;r{On(r,e,n)});else if(og(t)){for(const r in t)On(t[r],e,n);for(const r of Object.getOwnPropertySymbols(t))Object.prototype.propertyIsEnumerable.call(t,r)&&On(t[r],e,n)}return t}/**
-* @vue/runtime-core v3.5.32
-* (c) 2018-present Yuxi (Evan) You and Vue contributors
-* @license MIT
-**/function ti(t,e,n,r){try{return r?t(...r):t()}catch(i){zi(i,e,n)}}function rn(t,e,n,r){if(Ue(t)){const i=ti(t,e,n,r);return i&&ig(i)&&i.catch(a=>{zi(a,e,n)}),i}if(Ae(t)){const i=[];for(let a=0;a>>1,i=Ut[r],a=zr(i);a=zr(n)?Ut.push(t):Ut.splice(VS(e),0,t),t.flags|=1,Mg()}}function Mg(){vi||(vi=Dg.then(Lg))}function zS(t){Ae(t)?Tr.push(...t):Fn&&t.id===-1?Fn.splice(ur+1,0,t):t.flags&1||(Tr.push(t),t.flags|=1),Mg()}function b_(t,e,n=_n+1){for(;nzr(n)-zr(r));if(Tr.length=0,Fn){Fn.push(...e);return}for(Fn=e,ur=0;urt.id==null?t.flags&2?-1:1/0:t.id;function Lg(t){try{for(_n=0;_n{r._d&&Ii(-1);const a=Ni(e);let s;try{s=t(...i)}finally{Ni(a),r._d&&Ii(1)}return s};return r._n=!0,r._c=!0,r._d=!0,r}function Pg(t,e){if(It===null)return t;const n=Ji(It),r=t.dirs||(t.dirs=[]);for(let i=0;i1)return n&&Ue(e)?e.call(r&&r.proxy):e}}function WS(){return!!(Vc()||tr)}const KS=Symbol.for("v-scx"),QS=()=>er(KS);function Ye(t,e,n){return kg(t,e,n)}function kg(t,e,n=ut){const{immediate:r,deep:i,flush:a,once:s}=n,o=yt({},n),c=e&&r||!e&&a!=="post";let _;if(Xr){if(a==="sync"){const m=QS();_=m.__watcherHandles||(m.__watcherHandles=[])}else if(!c){const m=()=>{};return m.stop=En,m.resume=En,m.pause=En,m}}const l=wt;o.call=(m,p,E)=>rn(m,l,p,E);let d=!1;a==="post"?o.scheduler=m=>{kt(m,l&&l.suspense)}:a!=="sync"&&(d=!0,o.scheduler=(m,p)=>{p?m():Fc(m)}),o.augmentJob=m=>{e&&(m.flags|=4),d&&(m.flags|=2,l&&(m.id=l.uid,m.i=l))};const u=HS(t,e,o);return Xr&&(_?_.push(u):c&&u()),u}function XS(t,e,n){const r=this.proxy,i=Et(t)?t.includes(".")?Ug(r,t):()=>r[t]:t.bind(r,r);let a;Ue(e)?a=e:(a=e.handler,n=e);const s=ri(this),o=kg(i,a.bind(r),n);return s(),o}function Ug(t,e){const n=e.split(".");return()=>{let r=t;for(let i=0;it.__isTeleport,Xn=t=>t&&(t.disabled||t.disabled===""),ZS=t=>t&&(t.defer||t.defer===""),R_=t=>typeof SVGElement<"u"&&t instanceof SVGElement,h_=t=>typeof MathMLElement=="function"&&t instanceof MathMLElement,lc=(t,e)=>{const n=t&&t.to;return Et(n)?e?e(n):null:n},JS={name:"Teleport",__isTeleport:!0,process(t,e,n,r,i,a,s,o,c,_){const{mc:l,pc:d,pbc:u,o:{insert:m,querySelector:p,createText:E,createComment:f}}=_,T=Xn(e.props);let{dynamicChildren:b}=e;const v=(N,x,O)=>{N.shapeFlag&16&&l(N.children,x,O,i,a,s,o,c)},C=(N=e)=>{const x=Xn(N.props),O=N.target=lc(N.props,p),M=cc(O,N,E,m);O&&(s!=="svg"&&R_(O)?s="svg":s!=="mathml"&&h_(O)&&(s="mathml"),i&&i.isCE&&(i.ce._teleportTargets||(i.ce._teleportTargets=new Set)).add(O),x||(v(N,O,M),wr(N,!1)))},A=N=>{const x=()=>{Vn.get(N)===x&&(Vn.delete(N),Xn(N.props)&&(v(N,n,N.anchor),wr(N,!0)),C(N))};Vn.set(N,x),kt(x,a)};if(t==null){const N=e.el=E(""),x=e.anchor=E("");if(m(N,n,r),m(x,n,r),ZS(e.props)||a&&a.pendingBranch){A(e);return}T&&(v(e,n,x),wr(e,!0)),C()}else{e.el=t.el;const N=e.anchor=t.anchor,x=Vn.get(t);if(x){x.flags|=8,Vn.delete(t),A(e);return}e.targetStart=t.targetStart;const O=e.target=t.target,M=e.targetAnchor=t.targetAnchor,q=Xn(t.props),V=q?n:O,y=q?N:M;if(s==="svg"||R_(O)?s="svg":(s==="mathml"||h_(O))&&(s="mathml"),b?(u(t.dynamicChildren,b,V,i,a,s,o),Hc(t,e,!0)):c||d(t,e,V,y,i,a,s,o,!1),T)q?e.props&&t.props&&e.props.to!==t.props.to&&(e.props.to=t.props.to):ui(e,n,N,_,1);else if((e.props&&e.props.to)!==(t.props&&t.props.to)){const Z=e.target=lc(e.props,p);Z&&ui(e,Z,null,_,0)}else q&&ui(e,O,M,_,1);wr(e,T)}},remove(t,e,n,{um:r,o:{remove:i}},a){const{shapeFlag:s,children:o,anchor:c,targetStart:_,targetAnchor:l,target:d,props:u}=t;let m=a||!Xn(u);const p=Vn.get(t);if(p&&(p.flags|=8,Vn.delete(t),m=!1),d&&(i(_),i(l)),a&&i(c),s&16)for(let E=0;E{t.isMounted=!0}),or(()=>{t.isUnmounting=!0}),t}const Jt=[Function,Array],Gg={mode:String,appear:Boolean,persisted:Boolean,onBeforeEnter:Jt,onEnter:Jt,onAfterEnter:Jt,onEnterCancelled:Jt,onBeforeLeave:Jt,onLeave:Jt,onAfterLeave:Jt,onLeaveCancelled:Jt,onBeforeAppear:Jt,onAppear:Jt,onAfterAppear:Jt,onAppearCancelled:Jt},Yg=t=>{const e=t.subTree;return e.component?Yg(e.component):e},tf={name:"BaseTransition",props:Gg,setup(t,{slots:e}){const n=Vc(),r=ef();return()=>{const i=e.default&&$g(e.default(),!0);if(!i||!i.length)return;const a=qg(i),s=Xe(t),{mode:o}=s;if(r.isLeaving)return ua(a);const c=C_(a);if(!c)return ua(a);let _=_c(c,s,r,n,d=>_=d);c.type!==Lt&&Wr(c,_);let l=n.subTree&&C_(n.subTree);if(l&&l.type!==Lt&&!Zn(l,c)&&Yg(n).type!==Lt){let d=_c(l,s,r,n);if(Wr(l,d),o==="out-in"&&c.type!==Lt)return r.isLeaving=!0,d.afterLeave=()=>{r.isLeaving=!1,n.job.flags&8||n.update(),delete d.afterLeave,l=void 0},ua(a);o==="in-out"&&c.type!==Lt?d.delayLeave=(u,m,p)=>{const E=Hg(r,l);E[String(l.key)]=l,u[dn]=()=>{m(),u[dn]=void 0,delete _.delayedLeave,l=void 0},_.delayedLeave=()=>{p(),delete _.delayedLeave,l=void 0}}:l=void 0}else l&&(l=void 0);return a}}};function qg(t){let e=t[0];if(t.length>1){for(const n of t)if(n.type!==Lt){e=n;break}}return e}const nf=tf;function Hg(t,e){const{leavingVNodes:n}=t;let r=n.get(e.type);return r||(r=Object.create(null),n.set(e.type,r)),r}function _c(t,e,n,r,i){const{appear:a,mode:s,persisted:o=!1,onBeforeEnter:c,onEnter:_,onAfterEnter:l,onEnterCancelled:d,onBeforeLeave:u,onLeave:m,onAfterLeave:p,onLeaveCancelled:E,onBeforeAppear:f,onAppear:T,onAfterAppear:b,onAppearCancelled:v}=e,C=String(t.key),A=Hg(n,t),N=(M,q)=>{M&&rn(M,r,9,q)},x=(M,q)=>{const V=q[1];N(M,q),Ae(M)?M.every(y=>y.length<=1)&&V():M.length<=1&&V()},O={mode:s,persisted:o,beforeEnter(M){let q=c;if(!n.isMounted)if(a)q=f||c;else return;M[dn]&&M[dn](!0);const V=A[C];V&&Zn(t,V)&&V.el[dn]&&V.el[dn](),N(q,[M])},enter(M){if(A[C]===t)return;let q=_,V=l,y=d;if(!n.isMounted)if(a)q=T||_,V=b||l,y=v||d;else return;let Z=!1;M[Dr]=Se=>{Z||(Z=!0,Se?N(y,[M]):N(V,[M]),O.delayedLeave&&O.delayedLeave(),M[Dr]=void 0)};const ne=M[Dr].bind(null,!1);q?x(q,[M,ne]):ne()},leave(M,q){const V=String(t.key);if(M[Dr]&&M[Dr](!0),n.isUnmounting)return q();N(u,[M]);let y=!1;M[dn]=ne=>{y||(y=!0,q(),ne?N(E,[M]):N(p,[M]),M[dn]=void 0,A[V]===t&&delete A[V])};const Z=M[dn].bind(null,!1);A[V]=t,m?x(m,[M,Z]):Z()},clone(M){const q=_c(M,e,n,r,i);return i&&i(q),q}};return O}function ua(t){if(Wi(t))return t=Bn(t),t.children=null,t}function C_(t){if(!Wi(t))return Bg(t.type)&&t.children?qg(t.children):t;if(t.component)return t.component.subTree;const{shapeFlag:e,children:n}=t;if(n){if(e&16)return n[0];if(e&32&&Ue(n.default))return n.default()}}function Wr(t,e){t.shapeFlag&6&&t.component?(t.transition=e,Wr(t.component.subTree,e)):t.shapeFlag&128?(t.ssContent.transition=e.clone(t.ssContent),t.ssFallback.transition=e.clone(t.ssFallback)):t.transition=e}function $g(t,e=!1,n){let r=[],i=0;for(let a=0;a1)for(let a=0;aBr(E,e&&(Ae(e)?e[f]:e),n,r,i));return}if(br(r)&&!i){r.shapeFlag&512&&r.type.__asyncResolved&&r.component.subTree.component&&Br(t,e,n,r.component.subTree);return}const a=r.shapeFlag&4?Ji(r.component):r.el,s=i?null:a,{i:o,r:c}=t,_=e&&e.r,l=o.refs===ut?o.refs={}:o.refs,d=o.setupState,u=Xe(d),m=d===ut?ng:E=>v_(l,E)?!1:it(u,E),p=(E,f)=>!(f&&v_(l,f));if(_!=null&&_!==c){if(N_(e),Et(_))l[_]=null,m(_)&&(d[_]=null);else if(bt(_)){const E=e;p(_,E.k)&&(_.value=null),E.k&&(l[E.k]=null)}}if(Ue(c))ti(c,o,12,[s,l]);else{const E=Et(c),f=bt(c);if(E||f){const T=()=>{if(t.f){const b=E?m(c)?d[c]:l[c]:p()||!t.k?c.value:l[t.k];if(i)Ae(b)&&Ic(b,a);else if(Ae(b))b.includes(a)||b.push(a);else if(E)l[c]=[a],m(c)&&(d[c]=l[c]);else{const v=[a];p(c,t.k)&&(c.value=v),t.k&&(l[t.k]=v)}}else E?(l[c]=s,m(c)&&(d[c]=s)):f&&(p(c,t.k)&&(c.value=s),t.k&&(l[t.k]=s))};if(s){const b=()=>{T(),Oi.delete(t)};b.id=-1,Oi.set(t,b),kt(b,n)}else N_(t),T()}}}function N_(t){const e=Oi.get(t);e&&(e.flags|=8,Oi.delete(t))}qi().requestIdleCallback;qi().cancelIdleCallback;const br=t=>!!t.type.__asyncLoader,Wi=t=>t.type.__isKeepAlive;function Gc(t,e){Wg(t,"a",e)}function zg(t,e){Wg(t,"da",e)}function Wg(t,e,n=wt){const r=t.__wdc||(t.__wdc=()=>{let i=n;for(;i;){if(i.isDeactivated)return;i=i.parent}return t()});if(Ki(e,r,n),n){let i=n.parent;for(;i&&i.parent;)Wi(i.parent.vnode)&&rf(r,e,n,i),i=i.parent}}function rf(t,e,n,r){const i=Ki(e,t,r,!0);Yn(()=>{Ic(r[e],i)},n)}function Ki(t,e,n=wt,r=!1){if(n){const i=n[t]||(n[t]=[]),a=e.__weh||(e.__weh=(...s)=>{Dn();const o=ri(n),c=rn(e,n,t,s);return o(),Mn(),c});return r?i.unshift(a):i.push(a),a}}const Ln=t=>(e,n=wt)=>{(!Xr||t==="sp")&&Ki(t,(...r)=>e(...r),n)},af=Ln("bm"),Sn=Ln("m"),of=Ln("bu"),sf=Ln("u"),or=Ln("bum"),Yn=Ln("um"),lf=Ln("sp"),cf=Ln("rtg"),_f=Ln("rtc");function df(t,e=wt){Ki("ec",t,e)}const uf="components",Kg=Symbol.for("v-ndc");function Qi(t){return Et(t)?pf(uf,t,!1)||t:t||Kg}function pf(t,e,n=!0,r=!1){const i=It||wt;if(i){const a=i.type;{const o=Jf(a,!1);if(o&&(o===e||o===Gt(e)||o===Yi(Gt(e))))return a}const s=O_(i[t]||a[t],e)||O_(i.appContext[t],e);return!s&&r?a:s}}function O_(t,e){return t&&(t[e]||t[Gt(e)]||t[Yi(Gt(e))])}function an(t,e,n,r){let i;const a=n,s=Ae(t);if(s||Et(t)){const o=s&&An(t);let c=!1,_=!1;o&&(c=!$t(t),_=xn(t),t=Hi(t)),i=new Array(t.length);for(let l=0,d=t.length;le(o,c,void 0,a));else{const o=Object.keys(t);i=new Array(o.length);for(let c=0,_=o.length;c<_;c++){const l=o[c];i[c]=e(t[l],l,c,a)}}else i=[];return i}function mf(t,e){for(let n=0;n{const a=r.fn(...i);return a&&(a.key=r.key),a}:r.fn)}return t}function gn(t,e,n={},r,i){if(It.ce||It.parent&&br(It.parent)&&It.parent.ce){const _=Object.keys(n).length>0;return e!=="default"&&(n.name=e),w(),gt(ct,null,[Qe("slot",n,r)],_?-2:64)}let a=t[e];a&&a._c&&(a._d=!1),w();const s=a&&Qg(a(n)),o=n.key||s&&s.key,c=gt(ct,{key:(o&&!Vt(o)?o:`_${e}`)+(!s&&r?"_fb":"")},s||[],s&&t._===1?64:-2);return c.scopeId&&(c.slotScopeIds=[c.scopeId+"-s"]),a&&a._c&&(a._d=!0),c}function Qg(t){return t.some(e=>Qr(e)?!(e.type===Lt||e.type===ct&&!Qg(e.children)):!0)?t:null}function gf(t,e){const n={};for(const r in t)n[Ei(r)]=t[r];return n}const dc=t=>t?gE(t)?Ji(t):dc(t.parent):null,Gr=yt(Object.create(null),{$:t=>t,$el:t=>t.vnode.el,$data:t=>t.data,$props:t=>t.props,$attrs:t=>t.attrs,$slots:t=>t.slots,$refs:t=>t.refs,$parent:t=>dc(t.parent),$root:t=>dc(t.root),$host:t=>t.ce,$emit:t=>t.emit,$options:t=>Zg(t),$forceUpdate:t=>t.f||(t.f=()=>{Fc(t.update)}),$nextTick:t=>t.n||(t.n=Ft.bind(t.proxy)),$watch:t=>XS.bind(t)}),pa=(t,e)=>t!==ut&&!t.__isScriptSetup&&it(t,e),Ef={get({_:t},e){if(e==="__v_skip")return!0;const{ctx:n,setupState:r,data:i,props:a,accessCache:s,type:o,appContext:c}=t;if(e[0]!=="$"){const u=s[e];if(u!==void 0)switch(u){case 1:return r[e];case 2:return i[e];case 4:return n[e];case 3:return a[e]}else{if(pa(r,e))return s[e]=1,r[e];if(i!==ut&&it(i,e))return s[e]=2,i[e];if(it(a,e))return s[e]=3,a[e];if(n!==ut&&it(n,e))return s[e]=4,n[e];uc&&(s[e]=0)}}const _=Gr[e];let l,d;if(_)return e==="$attrs"&&xt(t.attrs,"get",""),_(t);if((l=o.__cssModules)&&(l=l[e]))return l;if(n!==ut&&it(n,e))return s[e]=4,n[e];if(d=c.config.globalProperties,it(d,e))return d[e]},set({_:t},e,n){const{data:r,setupState:i,ctx:a}=t;return pa(i,e)?(i[e]=n,!0):r!==ut&&it(r,e)?(r[e]=n,!0):it(t.props,e)||e[0]==="$"&&e.slice(1)in t?!1:(a[e]=n,!0)},has({_:{data:t,setupState:e,accessCache:n,ctx:r,appContext:i,props:a,type:s}},o){let c;return!!(n[o]||t!==ut&&o[0]!=="$"&&it(t,o)||pa(e,o)||it(a,o)||it(r,o)||it(Gr,o)||it(i.config.globalProperties,o)||(c=s.__cssModules)&&c[o])},defineProperty(t,e,n){return n.get!=null?t._.accessCache[e]=0:it(n,"value")&&this.set(t,e,n.value,null),Reflect.defineProperty(t,e,n)}};function y_(t){return Ae(t)?t.reduce((e,n)=>(e[n]=null,e),{}):t}let uc=!0;function Sf(t){const e=Zg(t),n=t.proxy,r=t.ctx;uc=!1,e.beforeCreate&&I_(e.beforeCreate,t,"bc");const{data:i,computed:a,methods:s,watch:o,provide:c,inject:_,created:l,beforeMount:d,mounted:u,beforeUpdate:m,updated:p,activated:E,deactivated:f,beforeDestroy:T,beforeUnmount:b,destroyed:v,unmounted:C,render:A,renderTracked:N,renderTriggered:x,errorCaptured:O,serverPrefetch:M,expose:q,inheritAttrs:V,components:y,directives:Z,filters:ne}=e;if(_&&ff(_,r,null),s)for(const se in s){const be=s[se];Ue(be)&&(r[se]=be.bind(n))}if(i){const se=i.call(n,n);at(se)&&(t.data=ei(se))}if(uc=!0,a)for(const se in a){const be=a[se],me=Ue(be)?be.bind(n,n):Ue(be.get)?be.get.bind(n,n):En,Le=!Ue(be)&&Ue(be.set)?be.set.bind(n):En,Be=fe({get:me,set:Le});Object.defineProperty(r,se,{enumerable:!0,configurable:!0,get:()=>Be.value,set:He=>Be.value=He})}if(o)for(const se in o)Xg(o[se],r,n,se);if(c){const se=Ue(c)?c.call(n):c;Reflect.ownKeys(se).forEach(be=>{pr(be,se[be])})}l&&I_(l,t,"c");function oe(se,be){Ae(be)?be.forEach(me=>se(me.bind(n))):be&&se(be.bind(n))}if(oe(af,d),oe(Sn,u),oe(of,m),oe(sf,p),oe(Gc,E),oe(zg,f),oe(df,O),oe(_f,N),oe(cf,x),oe(or,b),oe(Yn,C),oe(lf,M),Ae(q))if(q.length){const se=t.exposed||(t.exposed={});q.forEach(be=>{Object.defineProperty(se,be,{get:()=>n[be],set:me=>n[be]=me,enumerable:!0})})}else t.exposed||(t.exposed={});A&&t.render===En&&(t.render=A),V!=null&&(t.inheritAttrs=V),y&&(t.components=y),Z&&(t.directives=Z),M&&Vg(t)}function ff(t,e,n=En){Ae(t)&&(t=pc(t));for(const r in t){const i=t[r];let a;at(i)?"default"in i?a=er(i.from||r,i.default,!0):a=er(i.from||r):a=er(i),bt(a)?Object.defineProperty(e,r,{enumerable:!0,configurable:!0,get:()=>a.value,set:s=>a.value=s}):e[r]=a}}function I_(t,e,n){rn(Ae(t)?t.map(r=>r.bind(e.proxy)):t.bind(e.proxy),e,n)}function Xg(t,e,n,r){let i=r.includes(".")?Ug(n,r):()=>n[r];if(Et(t)){const a=e[t];Ue(a)&&Ye(i,a)}else if(Ue(t))Ye(i,t.bind(n));else if(at(t))if(Ae(t))t.forEach(a=>Xg(a,e,n,r));else{const a=Ue(t.handler)?t.handler.bind(n):e[t.handler];Ue(a)&&Ye(i,a,t)}}function Zg(t){const e=t.type,{mixins:n,extends:r}=e,{mixins:i,optionsCache:a,config:{optionMergeStrategies:s}}=t.appContext,o=a.get(e);let c;return o?c=o:!i.length&&!n&&!r?c=e:(c={},i.length&&i.forEach(_=>yi(c,_,s,!0)),yi(c,e,s)),at(e)&&a.set(e,c),c}function yi(t,e,n,r=!1){const{mixins:i,extends:a}=e;a&&yi(t,a,n,!0),i&&i.forEach(s=>yi(t,s,n,!0));for(const s in e)if(!(r&&s==="expose")){const o=Tf[s]||n&&n[s];t[s]=o?o(t[s],e[s]):e[s]}return t}const Tf={data:A_,props:D_,emits:D_,methods:Pr,computed:Pr,beforeCreate:Pt,created:Pt,beforeMount:Pt,mounted:Pt,beforeUpdate:Pt,updated:Pt,beforeDestroy:Pt,beforeUnmount:Pt,destroyed:Pt,unmounted:Pt,activated:Pt,deactivated:Pt,errorCaptured:Pt,serverPrefetch:Pt,components:Pr,directives:Pr,watch:Rf,provide:A_,inject:bf};function A_(t,e){return e?t?function(){return yt(Ue(t)?t.call(this,this):t,Ue(e)?e.call(this,this):e)}:e:t}function bf(t,e){return Pr(pc(t),pc(e))}function pc(t){if(Ae(t)){const e={};for(let n=0;ne==="modelValue"||e==="model-value"?t.modelModifiers:t[`${e}Modifiers`]||t[`${Gt(e)}Modifiers`]||t[`${Gn(e)}Modifiers`];function Nf(t,e,...n){if(t.isUnmounted)return;const r=t.vnode.props||ut;let i=n;const a=e.startsWith("update:"),s=a&&vf(r,e.slice(7));s&&(s.trim&&(i=n.map(l=>Et(l)?l.trim():l)),s.number&&(i=n.map(Ac)));let o,c=r[o=Ei(e)]||r[o=Ei(Gt(e))];!c&&a&&(c=r[o=Ei(Gn(e))]),c&&rn(c,t,6,i);const _=r[o+"Once"];if(_){if(!t.emitted)t.emitted={};else if(t.emitted[o])return;t.emitted[o]=!0,rn(_,t,6,i)}}const Of=new WeakMap;function jg(t,e,n=!1){const r=n?Of:e.emitsCache,i=r.get(t);if(i!==void 0)return i;const a=t.emits;let s={},o=!1;if(!Ue(t)){const c=_=>{const l=jg(_,e,!0);l&&(o=!0,yt(s,l))};!n&&e.mixins.length&&e.mixins.forEach(c),t.extends&&c(t.extends),t.mixins&&t.mixins.forEach(c)}return!a&&!o?(at(t)&&r.set(t,null),null):(Ae(a)?a.forEach(c=>s[c]=null):yt(s,a),at(t)&&r.set(t,s),s)}function Xi(t,e){return!t||!Ui(e)?!1:(e=e.slice(2).replace(/Once$/,""),it(t,e[0].toLowerCase()+e.slice(1))||it(t,Gn(e))||it(t,e))}function M_(t){const{type:e,vnode:n,proxy:r,withProxy:i,propsOptions:[a],slots:s,attrs:o,emit:c,render:_,renderCache:l,props:d,data:u,setupState:m,ctx:p,inheritAttrs:E}=t,f=Ni(t);let T,b;try{if(n.shapeFlag&4){const C=i||r,A=C;T=pn(_.call(A,C,l,d,m,u,p)),b=o}else{const C=e;T=pn(C.length>1?C(d,{attrs:o,slots:s,emit:c}):C(d,null)),b=e.props?o:yf(o)}}catch(C){Yr.length=0,zi(C,t,1),T=Qe(Lt)}let v=T;if(b&&E!==!1){const C=Object.keys(b),{shapeFlag:A}=v;C.length&&A&7&&(a&&C.some(Fi)&&(b=If(b,a)),v=Bn(v,b,!1,!0))}return n.dirs&&(v=Bn(v,null,!1,!0),v.dirs=v.dirs?v.dirs.concat(n.dirs):n.dirs),n.transition&&Wr(v,n.transition),T=v,Ni(f),T}const yf=t=>{let e;for(const n in t)(n==="class"||n==="style"||Ui(n))&&((e||(e={}))[n]=t[n]);return e},If=(t,e)=>{const n={};for(const r in t)(!Fi(r)||!(r.slice(9)in e))&&(n[r]=t[r]);return n};function Af(t,e,n){const{props:r,children:i,component:a}=t,{props:s,children:o,patchFlag:c}=e,_=a.emitsOptions;if(e.dirs||e.transition)return!0;if(n&&c>=0){if(c&1024)return!0;if(c&16)return r?x_(r,s,_):!!s;if(c&8){const l=e.dynamicProps;for(let d=0;dObject.create(tE),rE=t=>Object.getPrototypeOf(t)===tE;function Mf(t,e,n,r=!1){const i={},a=nE();t.propsDefaults=Object.create(null),iE(t,e,i,a);for(const s in t.propsOptions[0])s in i||(i[s]=void 0);n?t.props=r?i:yg(i):t.type.props?t.props=i:t.props=a,t.attrs=a}function xf(t,e,n,r){const{props:i,attrs:a,vnode:{patchFlag:s}}=t,o=Xe(i),[c]=t.propsOptions;let _=!1;if((r||s>0)&&!(s&16)){if(s&8){const l=t.vnode.dynamicProps;for(let d=0;d{c=!0;const[u,m]=aE(d,e,!0);yt(s,u),m&&o.push(...m)};!n&&e.mixins.length&&e.mixins.forEach(l),t.extends&&l(t.extends),t.mixins&&t.mixins.forEach(l)}if(!a&&!c)return at(t)&&r.set(t,Sr),Sr;if(Ae(a))for(let l=0;lt==="_"||t==="_ctx"||t==="$stable",qc=t=>Ae(t)?t.map(pn):[pn(t)],wf=(t,e,n)=>{if(e._n)return e;const r=At((...i)=>qc(e(...i)),n);return r._c=!1,r},oE=(t,e,n)=>{const r=t._ctx;for(const i in t){if(Yc(i))continue;const a=t[i];if(Ue(a))e[i]=wf(i,a,r);else if(a!=null){const s=qc(a);e[i]=()=>s}}},sE=(t,e)=>{const n=qc(e);t.slots.default=()=>n},lE=(t,e,n)=>{for(const r in e)(n||!Yc(r))&&(t[r]=e[r])},Pf=(t,e,n)=>{const r=t.slots=nE();if(t.vnode.shapeFlag&32){const i=e._;i?(lE(r,e,n),n&&sg(r,"_",i,!0)):oE(e,r)}else e&&sE(t,e)},kf=(t,e,n)=>{const{vnode:r,slots:i}=t;let a=!0,s=ut;if(r.shapeFlag&32){const o=e._;o?n&&o===1?a=!1:lE(i,e,n):(a=!e.$stable,oE(e,i)),s=e}else e&&(sE(t,e),s={default:1});if(a)for(const o in i)!Yc(o)&&s[o]==null&&delete i[o]},kt=Yf;function Uf(t){return Ff(t)}function Ff(t,e){const n=qi();n.__VUE__=!0;const{insert:r,remove:i,patchProp:a,createElement:s,createText:o,createComment:c,setText:_,setElementText:l,parentNode:d,nextSibling:u,setScopeId:m=En,insertStaticContent:p}=t,E=(g,S,I,F=null,B=null,G=null,Q=void 0,j=null,J=!!S.dynamicChildren)=>{if(g===S)return;g&&!Zn(g,S)&&(F=D(g),He(g,B,G,!0),g=null),S.patchFlag===-2&&(J=!1,S.dynamicChildren=null);const{type:W,ref:Ce,shapeFlag:_e}=S;switch(W){case Zi:f(g,S,I,F);break;case Lt:T(g,S,I,F);break;case fi:g==null&&b(S,I,F,Q);break;case ct:y(g,S,I,F,B,G,Q,j,J);break;default:_e&1?A(g,S,I,F,B,G,Q,j,J):_e&6?Z(g,S,I,F,B,G,Q,j,J):(_e&64||_e&128)&&W.process(g,S,I,F,B,G,Q,j,J,ee)}Ce!=null&&B?Br(Ce,g&&g.ref,G,S||g,!S):Ce==null&&g&&g.ref!=null&&Br(g.ref,null,G,g,!0)},f=(g,S,I,F)=>{if(g==null)r(S.el=o(S.children),I,F);else{const B=S.el=g.el;S.children!==g.children&&_(B,S.children)}},T=(g,S,I,F)=>{g==null?r(S.el=c(S.children||""),I,F):S.el=g.el},b=(g,S,I,F)=>{[g.el,g.anchor]=p(g.children,S,I,F,g.el,g.anchor)},v=({el:g,anchor:S},I,F)=>{let B;for(;g&&g!==S;)B=u(g),r(g,I,F),g=B;r(S,I,F)},C=({el:g,anchor:S})=>{let I;for(;g&&g!==S;)I=u(g),i(g),g=I;i(S)},A=(g,S,I,F,B,G,Q,j,J)=>{if(S.type==="svg"?Q="svg":S.type==="math"&&(Q="mathml"),g==null)N(S,I,F,B,G,Q,j,J);else{const W=g.el&&g.el._isVueCE?g.el:null;try{W&&W._beginPatch(),M(g,S,B,G,Q,j,J)}finally{W&&W._endPatch()}}},N=(g,S,I,F,B,G,Q,j)=>{let J,W;const{props:Ce,shapeFlag:_e,transition:ce,dirs:ve}=g;if(J=g.el=s(g.type,G,Ce&&Ce.is,Ce),_e&8?l(J,g.children):_e&16&&O(g.children,J,null,F,B,ma(g,G),Q,j),ve&&$n(g,null,F,"created"),x(J,g,g.scopeId,Q,F),Ce){for(const qe in Ce)qe!=="value"&&!kr(qe)&&a(J,qe,null,Ce[qe],G,F);"value"in Ce&&a(J,"value",null,Ce.value,G),(W=Ce.onVnodeBeforeMount)&&ln(W,F,g)}ve&&$n(g,null,F,"beforeMount");const Me=Bf(B,ce);Me&&ce.beforeEnter(J),r(J,S,I),((W=Ce&&Ce.onVnodeMounted)||Me||ve)&&kt(()=>{try{W&&ln(W,F,g),Me&&ce.enter(J),ve&&$n(g,null,F,"mounted")}finally{}},B)},x=(g,S,I,F,B)=>{if(I&&m(g,I),F)for(let G=0;G{for(let W=J;W{const j=S.el=g.el;let{patchFlag:J,dynamicChildren:W,dirs:Ce}=S;J|=g.patchFlag&16;const _e=g.props||ut,ce=S.props||ut;let ve;if(I&&zn(I,!1),(ve=ce.onVnodeBeforeUpdate)&&ln(ve,I,S,g),Ce&&$n(S,g,I,"beforeUpdate"),I&&zn(I,!0),(_e.innerHTML&&ce.innerHTML==null||_e.textContent&&ce.textContent==null)&&l(j,""),W?q(g.dynamicChildren,W,j,I,F,ma(S,B),G):Q||be(g,S,j,null,I,F,ma(S,B),G,!1),J>0){if(J&16)V(j,_e,ce,I,B);else if(J&2&&_e.class!==ce.class&&a(j,"class",null,ce.class,B),J&4&&a(j,"style",_e.style,ce.style,B),J&8){const Me=S.dynamicProps;for(let qe=0;qe{ve&&ln(ve,I,S,g),Ce&&$n(S,g,I,"updated")},F)},q=(g,S,I,F,B,G,Q)=>{for(let j=0;j{if(S!==I){if(S!==ut)for(const G in S)!kr(G)&&!(G in I)&&a(g,G,S[G],null,B,F);for(const G in I){if(kr(G))continue;const Q=I[G],j=S[G];Q!==j&&G!=="value"&&a(g,G,j,Q,B,F)}"value"in I&&a(g,"value",S.value,I.value,B)}},y=(g,S,I,F,B,G,Q,j,J)=>{const W=S.el=g?g.el:o(""),Ce=S.anchor=g?g.anchor:o("");let{patchFlag:_e,dynamicChildren:ce,slotScopeIds:ve}=S;ve&&(j=j?j.concat(ve):ve),g==null?(r(W,I,F),r(Ce,I,F),O(S.children||[],I,Ce,B,G,Q,j,J)):_e>0&&_e&64&&ce&&g.dynamicChildren&&g.dynamicChildren.length===ce.length?(q(g.dynamicChildren,ce,I,B,G,Q,j),(S.key!=null||B&&S===B.subTree)&&Hc(g,S,!0)):be(g,S,I,Ce,B,G,Q,j,J)},Z=(g,S,I,F,B,G,Q,j,J)=>{S.slotScopeIds=j,g==null?S.shapeFlag&512?B.ctx.activate(S,I,F,Q,J):ne(S,I,F,B,G,Q,J):Se(g,S,J)},ne=(g,S,I,F,B,G,Q)=>{const j=g.component=Wf(g,F,B);if(Wi(g)&&(j.ctx.renderer=ee),Kf(j,!1,Q),j.asyncDep){if(B&&B.registerDep(j,oe,Q),!g.el){const J=j.subTree=Qe(Lt);T(null,J,S,I),g.placeholder=J.el}}else oe(j,g,S,I,B,G,Q)},Se=(g,S,I)=>{const F=S.component=g.component;if(Af(g,S,I))if(F.asyncDep&&!F.asyncResolved){se(F,S,I);return}else F.next=S,F.update();else S.el=g.el,F.vnode=S},oe=(g,S,I,F,B,G,Q)=>{const j=()=>{if(g.isMounted){let{next:_e,bu:ce,u:ve,parent:Me,vnode:qe}=g;{const H=cE(g);if(H){_e&&(_e.el=qe.el,se(g,_e,Q)),H.asyncDep.then(()=>{kt(()=>{g.isUnmounted||W()},B)});return}}let We=_e,nt;zn(g,!1),_e?(_e.el=qe.el,se(g,_e,Q)):_e=qe,ce&&Si(ce),(nt=_e.props&&_e.props.onVnodeBeforeUpdate)&&ln(nt,Me,_e,qe),zn(g,!0);const Ke=M_(g),k=g.subTree;g.subTree=Ke,E(k,Ke,d(k.el),D(k),g,B,G),_e.el=Ke.el,We===null&&Df(g,Ke.el),ve&&kt(ve,B),(nt=_e.props&&_e.props.onVnodeUpdated)&&kt(()=>ln(nt,Me,_e,qe),B)}else{let _e;const{el:ce,props:ve}=S,{bm:Me,m:qe,parent:We,root:nt,type:Ke}=g,k=br(S);zn(g,!1),Me&&Si(Me),!k&&(_e=ve&&ve.onVnodeBeforeMount)&&ln(_e,We,S),zn(g,!0);{nt.ce&&nt.ce._hasShadowRoot()&&nt.ce._injectChildStyle(Ke,g.parent?g.parent.type:void 0);const H=g.subTree=M_(g);E(null,H,I,F,g,B,G),S.el=H.el}if(qe&&kt(qe,B),!k&&(_e=ve&&ve.onVnodeMounted)){const H=S;kt(()=>ln(_e,We,H),B)}(S.shapeFlag&256||We&&br(We.vnode)&&We.vnode.shapeFlag&256)&&g.a&&kt(g.a,B),g.isMounted=!0,S=I=F=null}};g.scope.on();const J=g.effect=new pg(j);g.scope.off();const W=g.update=J.run.bind(J),Ce=g.job=J.runIfDirty.bind(J);Ce.i=g,Ce.id=g.uid,J.scheduler=()=>Fc(Ce),zn(g,!0),W()},se=(g,S,I)=>{S.component=g;const F=g.vnode.props;g.vnode=S,g.next=null,xf(g,S.props,F,I),kf(g,S.children,I),Dn(),b_(g),Mn()},be=(g,S,I,F,B,G,Q,j,J=!1)=>{const W=g&&g.children,Ce=g?g.shapeFlag:0,_e=S.children,{patchFlag:ce,shapeFlag:ve}=S;if(ce>0){if(ce&128){Le(W,_e,I,F,B,G,Q,j,J);return}else if(ce&256){me(W,_e,I,F,B,G,Q,j,J);return}}ve&8?(Ce&16&&K(W,B,G),_e!==W&&l(I,_e)):Ce&16?ve&16?Le(W,_e,I,F,B,G,Q,j,J):K(W,B,G,!0):(Ce&8&&l(I,""),ve&16&&O(_e,I,F,B,G,Q,j,J))},me=(g,S,I,F,B,G,Q,j,J)=>{g=g||Sr,S=S||Sr;const W=g.length,Ce=S.length,_e=Math.min(W,Ce);let ce;for(ce=0;ce<_e;ce++){const ve=S[ce]=J?vn(S[ce]):pn(S[ce]);E(g[ce],ve,I,null,B,G,Q,j,J)}W>Ce?K(g,B,G,!0,!1,_e):O(S,I,F,B,G,Q,j,J,_e)},Le=(g,S,I,F,B,G,Q,j,J)=>{let W=0;const Ce=S.length;let _e=g.length-1,ce=Ce-1;for(;W<=_e&&W<=ce;){const ve=g[W],Me=S[W]=J?vn(S[W]):pn(S[W]);if(Zn(ve,Me))E(ve,Me,I,null,B,G,Q,j,J);else break;W++}for(;W<=_e&&W<=ce;){const ve=g[_e],Me=S[ce]=J?vn(S[ce]):pn(S[ce]);if(Zn(ve,Me))E(ve,Me,I,null,B,G,Q,j,J);else break;_e--,ce--}if(W>_e){if(W<=ce){const ve=ce+1,Me=vece)for(;W<=_e;)He(g[W],B,G,!0),W++;else{const ve=W,Me=W,qe=new Map;for(W=Me;W<=ce;W++){const Ee=S[W]=J?vn(S[W]):pn(S[W]);Ee.key!=null&&qe.set(Ee.key,W)}let We,nt=0;const Ke=ce-Me+1;let k=!1,H=0;const ae=new Array(Ke);for(W=0;W=Ke){He(Ee,B,G,!0);continue}let he;if(Ee.key!=null)he=qe.get(Ee.key);else for(We=Me;We<=ce;We++)if(ae[We-Me]===0&&Zn(Ee,S[We])){he=We;break}he===void 0?He(Ee,B,G,!0):(ae[he-Me]=W+1,he>=H?H=he:k=!0,E(Ee,S[he],I,null,B,G,Q,j,J),nt++)}const ye=k?Gf(ae):Sr;for(We=ye.length-1,W=Ke-1;W>=0;W--){const Ee=Me+W,he=S[Ee],we=S[Ee+1],ze=Ee+1{const{el:G,type:Q,transition:j,children:J,shapeFlag:W}=g;if(W&6){Be(g.component.subTree,S,I,F);return}if(W&128){g.suspense.move(S,I,F);return}if(W&64){Q.move(g,S,I,ee);return}if(Q===ct){r(G,S,I);for(let _e=0;_ej.enter(G),B);else{const{leave:_e,delayLeave:ce,afterLeave:ve}=j,Me=()=>{g.ctx.isUnmounted?i(G):r(G,S,I)},qe=()=>{G._isLeaving&&G[dn](!0),_e(G,()=>{Me(),ve&&ve()})};ce?ce(G,Me,qe):qe()}else r(G,S,I)},He=(g,S,I,F=!1,B=!1)=>{const{type:G,props:Q,ref:j,children:J,dynamicChildren:W,shapeFlag:Ce,patchFlag:_e,dirs:ce,cacheIndex:ve,memo:Me}=g;if(_e===-2&&(B=!1),j!=null&&(Dn(),Br(j,null,I,g,!0),Mn()),ve!=null&&(S.renderCache[ve]=void 0),Ce&256){S.ctx.deactivate(g);return}const qe=Ce&1&&ce,We=!br(g);let nt;if(We&&(nt=Q&&Q.onVnodeBeforeUnmount)&&ln(nt,S,g),Ce&6)$(g.component,I,F);else{if(Ce&128){g.suspense.unmount(I,F);return}qe&&$n(g,null,S,"beforeUnmount"),Ce&64?g.type.remove(g,S,I,ee,F):W&&!W.hasOnce&&(G!==ct||_e>0&&_e&64)?K(W,S,I,!1,!0):(G===ct&&_e&384||!B&&Ce&16)&&K(J,S,I),F&&st(g)}const Ke=Me!=null&&ve==null;(We&&(nt=Q&&Q.onVnodeUnmounted)||qe||Ke)&&kt(()=>{nt&&ln(nt,S,g),qe&&$n(g,null,S,"unmounted"),Ke&&(g.el=null)},I)},st=g=>{const{type:S,el:I,anchor:F,transition:B}=g;if(S===ct){L(I,F);return}if(S===fi){C(g);return}const G=()=>{i(I),B&&!B.persisted&&B.afterLeave&&B.afterLeave()};if(g.shapeFlag&1&&B&&!B.persisted){const{leave:Q,delayLeave:j}=B,J=()=>Q(I,G);j?j(g.el,G,J):J()}else G()},L=(g,S)=>{let I;for(;g!==S;)I=u(g),i(g),g=I;i(S)},$=(g,S,I)=>{const{bum:F,scope:B,job:G,subTree:Q,um:j,m:J,a:W}=g;w_(J),w_(W),F&&Si(F),B.stop(),G&&(G.flags|=8,He(Q,g,S,I)),j&&kt(j,S),kt(()=>{g.isUnmounted=!0},S)},K=(g,S,I,F=!1,B=!1,G=0)=>{for(let Q=G;Q{if(g.shapeFlag&6)return D(g.component.subTree);if(g.shapeFlag&128)return g.suspense.next();const S=u(g.anchor||g.el),I=S&&S[Fg];return I?u(I):S};let P=!1;const z=(g,S,I)=>{let F;g==null?S._vnode&&(He(S._vnode,null,null,!0),F=S._vnode.component):E(S._vnode||null,g,S,null,null,null,I),S._vnode=g,P||(P=!0,b_(F),xg(),P=!1)},ee={p:E,um:He,m:Be,r:st,mt:ne,mc:O,pc:be,pbc:q,n:D,o:t};return{render:z,hydrate:void 0,createApp:Cf(z)}}function ma({type:t,props:e},n){return n==="svg"&&t==="foreignObject"||n==="mathml"&&t==="annotation-xml"&&e&&e.encoding&&e.encoding.includes("html")?void 0:n}function zn({effect:t,job:e},n){n?(t.flags|=32,e.flags|=4):(t.flags&=-33,e.flags&=-5)}function Bf(t,e){return(!t||t&&!t.pendingBranch)&&e&&!e.persisted}function Hc(t,e,n=!1){const r=t.children,i=e.children;if(Ae(r)&&Ae(i))for(let a=0;a>1,t[n[o]]<_?a=o+1:s=o;_0&&(e[r]=n[a-1]),n[a]=r)}}for(a=n.length,s=n[a-1];a-- >0;)n[a]=s,s=e[s];return n}function cE(t){const e=t.subTree.component;if(e)return e.asyncDep&&!e.asyncResolved?e:cE(e)}function w_(t){if(t)for(let e=0;et.__isSuspense;function Yf(t,e){e&&e.pendingBranch?Ae(t)?e.effects.push(...t):e.effects.push(t):zS(t)}const ct=Symbol.for("v-fgt"),Zi=Symbol.for("v-txt"),Lt=Symbol.for("v-cmt"),fi=Symbol.for("v-stc"),Yr=[];let Ht=null;function w(t=!1){Yr.push(Ht=t?null:[])}function qf(){Yr.pop(),Ht=Yr[Yr.length-1]||null}let Kr=1;function Ii(t,e=!1){Kr+=t,t<0&&Ht&&e&&(Ht.hasOnce=!0)}function uE(t){return t.dynamicChildren=Kr>0?Ht||Sr:null,qf(),Kr>0&&Ht&&Ht.push(t),t}function U(t,e,n,r,i,a){return uE(h(t,e,n,r,i,a,!0))}function gt(t,e,n,r,i){return uE(Qe(t,e,n,r,i,!0))}function Qr(t){return t?t.__v_isVNode===!0:!1}function Zn(t,e){return t.type===e.type&&t.key===e.key}const pE=({key:t})=>t??null,Ti=({ref:t,ref_key:e,ref_for:n})=>(typeof t=="number"&&(t=""+t),t!=null?Et(t)||bt(t)||Ue(t)?{i:It,r:t,k:e,f:!!n}:t:null);function h(t,e=null,n=null,r=0,i=null,a=t===ct?0:1,s=!1,o=!1){const c={__v_isVNode:!0,__v_skip:!0,type:t,props:e,key:e&&pE(e),ref:e&&Ti(e),scopeId:wg,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:a,patchFlag:r,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:It};return o?($c(c,n),a&128&&t.normalize(c)):n&&(c.shapeFlag|=Et(n)?8:16),Kr>0&&!s&&Ht&&(c.patchFlag>0||a&6)&&c.patchFlag!==32&&Ht.push(c),c}const Qe=Hf;function Hf(t,e=null,n=null,r=0,i=null,a=!1){if((!t||t===Kg)&&(t=Lt),Qr(t)){const o=Bn(t,e,!0);return n&&$c(o,n),Kr>0&&!a&&Ht&&(o.shapeFlag&6?Ht[Ht.indexOf(t)]=o:Ht.push(o)),o.patchFlag=-2,o}if(jf(t)&&(t=t.__vccOpts),e){e=mE(e);let{class:o,style:c}=e;o&&!Et(o)&&(e.class=Ze(o)),at(c)&&($i(c)&&!Ae(c)&&(c=yt({},c)),e.style=ar(c))}const s=Et(t)?1:dE(t)?128:Bg(t)?64:at(t)?4:Ue(t)?2:0;return h(t,e,n,r,i,s,a,!0)}function mE(t){return t?$i(t)||rE(t)?yt({},t):t:null}function Bn(t,e,n=!1,r=!1){const{props:i,ref:a,patchFlag:s,children:o,transition:c}=t,_=e?Ai(i||{},e):i,l={__v_isVNode:!0,__v_skip:!0,type:t.type,props:_,key:_&&pE(_),ref:e&&e.ref?n&&a?Ae(a)?a.concat(Ti(e)):[a,Ti(e)]:Ti(e):a,scopeId:t.scopeId,slotScopeIds:t.slotScopeIds,children:o,target:t.target,targetStart:t.targetStart,targetAnchor:t.targetAnchor,staticCount:t.staticCount,shapeFlag:t.shapeFlag,patchFlag:e&&t.type!==ct?s===-1?16:s|16:s,dynamicProps:t.dynamicProps,dynamicChildren:t.dynamicChildren,appContext:t.appContext,dirs:t.dirs,transition:c,component:t.component,suspense:t.suspense,ssContent:t.ssContent&&Bn(t.ssContent),ssFallback:t.ssFallback&&Bn(t.ssFallback),placeholder:t.placeholder,el:t.el,anchor:t.anchor,ctx:t.ctx,ce:t.ce};return c&&r&&Wr(l,c.clone(l)),l}function Ct(t=" ",e=0){return Qe(Zi,null,t,e)}function $f(t,e){const n=Qe(fi,null,t);return n.staticCount=e,n}function Ie(t="",e=!1){return e?(w(),gt(Lt,null,t)):Qe(Lt,null,t)}function pn(t){return t==null||typeof t=="boolean"?Qe(Lt):Ae(t)?Qe(ct,null,t.slice()):Qr(t)?vn(t):Qe(Zi,null,String(t))}function vn(t){return t.el===null&&t.patchFlag!==-1||t.memo?t:Bn(t)}function $c(t,e){let n=0;const{shapeFlag:r}=t;if(e==null)e=null;else if(Ae(e))n=16;else if(typeof e=="object")if(r&65){const i=e.default;i&&(i._c&&(i._d=!1),$c(t,i()),i._c&&(i._d=!0));return}else{n=32;const i=e._;!i&&!rE(e)?e._ctx=It:i===3&&It&&(It.slots._===1?e._=1:(e._=2,t.patchFlag|=1024))}else Ue(e)?(e={default:e,_ctx:It},n=32):(e=String(e),r&64?(n=16,e=[Ct(e)]):n=8);t.children=e,t.shapeFlag|=n}function Ai(...t){const e={};for(let n=0;nwt||It;let Di,gc;{const t=qi(),e=(n,r)=>{let i;return(i=t[n])||(i=t[n]=[]),i.push(r),a=>{i.length>1?i.forEach(s=>s(a)):i[0](a)}};Di=e("__VUE_INSTANCE_SETTERS__",n=>wt=n),gc=e("__VUE_SSR_SETTERS__",n=>Xr=n)}const ri=t=>{const e=wt;return Di(t),t.scope.on(),()=>{t.scope.off(),Di(e)}},P_=()=>{wt&&wt.scope.off(),Di(null)};function gE(t){return t.vnode.shapeFlag&4}let Xr=!1;function Kf(t,e=!1,n=!1){e&&gc(e);const{props:r,children:i}=t.vnode,a=gE(t);Mf(t,r,a,e),Pf(t,i,n||e);const s=a?Qf(t,e):void 0;return e&&gc(!1),s}function Qf(t,e){const n=t.type;t.accessCache=Object.create(null),t.proxy=new Proxy(t.ctx,Ef);const{setup:r}=n;if(r){Dn();const i=t.setupContext=r.length>1?Zf(t):null,a=ri(t),s=ti(r,t,0,[t.props,i]),o=ig(s);if(Mn(),a(),(o||t.sp)&&!br(t)&&Vg(t),o){if(s.then(P_,P_),e)return s.then(c=>{k_(t,c)}).catch(c=>{zi(c,t,0)});t.asyncDep=s}else k_(t,s)}else EE(t)}function k_(t,e,n){Ue(e)?t.type.__ssrInlineRender?t.ssrRender=e:t.render=e:at(e)&&(t.setupState=Ag(e)),EE(t)}function EE(t,e,n){const r=t.type;t.render||(t.render=r.render||En);{const i=ri(t);Dn();try{Sf(t)}finally{Mn(),i()}}}const Xf={get(t,e){return xt(t,"get",""),t[e]}};function Zf(t){const e=n=>{t.exposed=n||{}};return{attrs:new Proxy(t.attrs,Xf),slots:t.slots,emit:t.emit,expose:e}}function Ji(t){return t.exposed?t.exposeProxy||(t.exposeProxy=new Proxy(Ag(Vi(t.exposed)),{get(e,n){if(n in e)return e[n];if(n in Gr)return Gr[n](t)},has(e,n){return n in e||n in Gr}})):t.proxy}function Jf(t,e=!0){return Ue(t)?t.displayName||t.name:t.name||e&&t.__name}function jf(t){return Ue(t)&&"__vccOpts"in t}const fe=(t,e)=>YS(t,e,Xr);function eT(t,e,n){try{Ii(-1);const r=arguments.length;return r===2?at(e)&&!Ae(e)?Qr(e)?Qe(t,null,[e]):Qe(t,e):Qe(t,null,e):(r>3?n=Array.prototype.slice.call(arguments,2):r===3&&Qr(n)&&(n=[n]),Qe(t,e,n))}finally{Ii(1)}}const tT="3.5.32";/**
-* @vue/runtime-dom v3.5.32
-* (c) 2018-present Yuxi (Evan) You and Vue contributors
-* @license MIT
-**/let Ec;const U_=typeof window<"u"&&window.trustedTypes;if(U_)try{Ec=U_.createPolicy("vue",{createHTML:t=>t})}catch{}const SE=Ec?t=>Ec.createHTML(t):t=>t,nT="http://www.w3.org/2000/svg",rT="http://www.w3.org/1998/Math/MathML",Cn=typeof document<"u"?document:null,F_=Cn&&Cn.createElement("template"),iT={insert:(t,e,n)=>{e.insertBefore(t,n||null)},remove:t=>{const e=t.parentNode;e&&e.removeChild(t)},createElement:(t,e,n,r)=>{const i=e==="svg"?Cn.createElementNS(nT,t):e==="mathml"?Cn.createElementNS(rT,t):n?Cn.createElement(t,{is:n}):Cn.createElement(t);return t==="select"&&r&&r.multiple!=null&&i.setAttribute("multiple",r.multiple),i},createText:t=>Cn.createTextNode(t),createComment:t=>Cn.createComment(t),setText:(t,e)=>{t.nodeValue=e},setElementText:(t,e)=>{t.textContent=e},parentNode:t=>t.parentNode,nextSibling:t=>t.nextSibling,querySelector:t=>Cn.querySelector(t),setScopeId(t,e){t.setAttribute(e,"")},insertStaticContent(t,e,n,r,i,a){const s=n?n.previousSibling:e.lastChild;if(i&&(i===a||i.nextSibling))for(;e.insertBefore(i.cloneNode(!0),n),!(i===a||!(i=i.nextSibling)););else{F_.innerHTML=SE(r==="svg"?``:r==="mathml"?``:t);const o=F_.content;if(r==="svg"||r==="mathml"){const c=o.firstChild;for(;c.firstChild;)o.appendChild(c.firstChild);o.removeChild(c)}e.insertBefore(o,n)}return[s?s.nextSibling:e.firstChild,n?n.previousSibling:e.lastChild]}},kn="transition",Mr="animation",Zr=Symbol("_vtc"),fE={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},aT=yt({},Gg,fE),oT=t=>(t.displayName="Transition",t.props=aT,t),hr=oT((t,{slots:e})=>eT(nf,sT(t),e)),Wn=(t,e=[])=>{Ae(t)?t.forEach(n=>n(...e)):t&&t(...e)},B_=t=>t?Ae(t)?t.some(e=>e.length>1):t.length>1:!1;function sT(t){const e={};for(const y in t)y in fE||(e[y]=t[y]);if(t.css===!1)return e;const{name:n="v",type:r,duration:i,enterFromClass:a=`${n}-enter-from`,enterActiveClass:s=`${n}-enter-active`,enterToClass:o=`${n}-enter-to`,appearFromClass:c=a,appearActiveClass:_=s,appearToClass:l=o,leaveFromClass:d=`${n}-leave-from`,leaveActiveClass:u=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=t,p=lT(i),E=p&&p[0],f=p&&p[1],{onBeforeEnter:T,onEnter:b,onEnterCancelled:v,onLeave:C,onLeaveCancelled:A,onBeforeAppear:N=T,onAppear:x=b,onAppearCancelled:O=v}=e,M=(y,Z,ne,Se)=>{y._enterCancelled=Se,Kn(y,Z?l:o),Kn(y,Z?_:s),ne&&ne()},q=(y,Z)=>{y._isLeaving=!1,Kn(y,d),Kn(y,m),Kn(y,u),Z&&Z()},V=y=>(Z,ne)=>{const Se=y?x:b,oe=()=>M(Z,y,ne);Wn(Se,[Z,oe]),G_(()=>{Kn(Z,y?c:a),hn(Z,y?l:o),B_(Se)||Y_(Z,r,E,oe)})};return yt(e,{onBeforeEnter(y){Wn(T,[y]),hn(y,a),hn(y,s)},onBeforeAppear(y){Wn(N,[y]),hn(y,c),hn(y,_)},onEnter:V(!1),onAppear:V(!0),onLeave(y,Z){y._isLeaving=!0;const ne=()=>q(y,Z);hn(y,d),y._enterCancelled?(hn(y,u),$_(y)):($_(y),hn(y,u)),G_(()=>{y._isLeaving&&(Kn(y,d),hn(y,m),B_(C)||Y_(y,r,f,ne))}),Wn(C,[y,ne])},onEnterCancelled(y){M(y,!1,void 0,!0),Wn(v,[y])},onAppearCancelled(y){M(y,!0,void 0,!0),Wn(O,[y])},onLeaveCancelled(y){q(y),Wn(A,[y])}})}function lT(t){if(t==null)return null;if(at(t))return[ga(t.enter),ga(t.leave)];{const e=ga(t);return[e,e]}}function ga(t){return oS(t)}function hn(t,e){e.split(/\s+/).forEach(n=>n&&t.classList.add(n)),(t[Zr]||(t[Zr]=new Set)).add(e)}function Kn(t,e){e.split(/\s+/).forEach(r=>r&&t.classList.remove(r));const n=t[Zr];n&&(n.delete(e),n.size||(t[Zr]=void 0))}function G_(t){requestAnimationFrame(()=>{requestAnimationFrame(t)})}let cT=0;function Y_(t,e,n,r){const i=t._endId=++cT,a=()=>{i===t._endId&&r()};if(n!=null)return setTimeout(a,n);const{type:s,timeout:o,propCount:c}=_T(t,e);if(!s)return r();const _=s+"end";let l=0;const d=()=>{t.removeEventListener(_,u),a()},u=m=>{m.target===t&&++l>=c&&d()};setTimeout(()=>{l(n[p]||"").split(", "),i=r(`${kn}Delay`),a=r(`${kn}Duration`),s=q_(i,a),o=r(`${Mr}Delay`),c=r(`${Mr}Duration`),_=q_(o,c);let l=null,d=0,u=0;e===kn?s>0&&(l=kn,d=s,u=a.length):e===Mr?_>0&&(l=Mr,d=_,u=c.length):(d=Math.max(s,_),l=d>0?s>_?kn:Mr:null,u=l?l===kn?a.length:c.length:0);const m=l===kn&&/\b(?:transform|all)(?:,|$)/.test(r(`${kn}Property`).toString());return{type:l,timeout:d,propCount:u,hasTransform:m}}function q_(t,e){for(;t.lengthH_(n)+H_(t[r])))}function H_(t){return t==="auto"?0:Number(t.slice(0,-1).replace(",","."))*1e3}function $_(t){return(t?t.ownerDocument:document).body.offsetHeight}function dT(t,e,n){const r=t[Zr];r&&(e=(e?[e,...r]:[...r]).join(" ")),e==null?t.removeAttribute("class"):n?t.setAttribute("class",e):t.className=e}const V_=Symbol("_vod"),uT=Symbol("_vsh"),pT=Symbol(""),mT=/(?:^|;)\s*display\s*:/;function gT(t,e,n){const r=t.style,i=Et(n);let a=!1;if(n&&!i){if(e)if(Et(e))for(const s of e.split(";")){const o=s.slice(0,s.indexOf(":")).trim();n[o]==null&&bi(r,o,"")}else for(const s in e)n[s]==null&&bi(r,s,"");for(const s in n)s==="display"&&(a=!0),bi(r,s,n[s])}else if(i){if(e!==n){const s=r[pT];s&&(n+=";"+s),r.cssText=n,a=mT.test(n)}}else e&&t.removeAttribute("style");V_ in t&&(t[V_]=a?r.display:"",t[uT]&&(r.display="none"))}const z_=/\s*!important$/;function bi(t,e,n){if(Ae(n))n.forEach(r=>bi(t,e,r));else if(n==null&&(n=""),e.startsWith("--"))t.setProperty(e,n);else{const r=ET(t,e);z_.test(n)?t.setProperty(Gn(r),n.replace(z_,""),"important"):t[r]=n}}const W_=["Webkit","Moz","ms"],Ea={};function ET(t,e){const n=Ea[e];if(n)return n;let r=Gt(e);if(r!=="filter"&&r in t)return Ea[e]=r;r=Yi(r);for(let i=0;iSa||(bT.then(()=>Sa=0),Sa=Date.now());function hT(t,e){const n=r=>{if(!r._vts)r._vts=Date.now();else if(r._vts<=n.attached)return;rn(CT(r,n.value),e,5,[r])};return n.value=t,n.attached=RT(),n}function CT(t,e){if(Ae(e)){const n=t.stopImmediatePropagation;return t.stopImmediatePropagation=()=>{n.call(t),t._stopped=!0},e.map(r=>i=>!i._stopped&&r&&r(i))}else return e}const j_=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&t.charCodeAt(2)>96&&t.charCodeAt(2)<123,vT=(t,e,n,r,i,a)=>{const s=i==="svg";e==="class"?dT(t,r,s):e==="style"?gT(t,n,r):Ui(e)?Fi(e)||fT(t,e,n,r,a):(e[0]==="."?(e=e.slice(1),!0):e[0]==="^"?(e=e.slice(1),!1):NT(t,e,r,s))?(X_(t,e,r),!t.tagName.includes("-")&&(e==="value"||e==="checked"||e==="selected")&&Q_(t,e,r,s,a,e!=="value")):t._isVueCE&&(OT(t,e)||t._def.__asyncLoader&&(/[A-Z]/.test(e)||!Et(r)))?X_(t,Gt(e),r,a,e):(e==="true-value"?t._trueValue=r:e==="false-value"&&(t._falseValue=r),Q_(t,e,r,s))};function NT(t,e,n,r){if(r)return!!(e==="innerHTML"||e==="textContent"||e in t&&j_(e)&&Ue(n));if(e==="spellcheck"||e==="draggable"||e==="translate"||e==="autocorrect"||e==="sandbox"&&t.tagName==="IFRAME"||e==="form"||e==="list"&&t.tagName==="INPUT"||e==="type"&&t.tagName==="TEXTAREA")return!1;if(e==="width"||e==="height"){const i=t.tagName;if(i==="IMG"||i==="VIDEO"||i==="CANVAS"||i==="SOURCE")return!1}return j_(e)&&Et(n)?!1:e in t}function OT(t,e){const n=t._def.props;if(!n)return!1;const r=Gt(e);return Array.isArray(n)?n.some(i=>Gt(i)===r):Object.keys(n).some(i=>Gt(i)===r)}const ed=t=>{const e=t.props["onUpdate:modelValue"]||!1;return Ae(e)?n=>Si(e,n):e};function yT(t){t.target.composing=!0}function td(t){const e=t.target;e.composing&&(e.composing=!1,e.dispatchEvent(new Event("input")))}const fa=Symbol("_assign");function nd(t,e,n){return e&&(t=t.trim()),n&&(t=Ac(t)),t}const IT={created(t,{modifiers:{lazy:e,trim:n,number:r}},i){t[fa]=ed(i);const a=r||i.props&&i.props.type==="number";mr(t,e?"change":"input",s=>{s.target.composing||t[fa](nd(t.value,n,a))}),(n||a)&&mr(t,"change",()=>{t.value=nd(t.value,n,a)}),e||(mr(t,"compositionstart",yT),mr(t,"compositionend",td),mr(t,"change",td))},mounted(t,{value:e}){t.value=e??""},beforeUpdate(t,{value:e,oldValue:n,modifiers:{lazy:r,trim:i,number:a}},s){if(t[fa]=ed(s),t.composing)return;const o=(a||t.type==="number")&&!/^0\d/.test(t.value)?Ac(t.value):t.value,c=e??"";if(o===c)return;const _=t.getRootNode();(_ instanceof Document||_ instanceof ShadowRoot)&&_.activeElement===t&&t.type!=="range"&&(r&&e===n||i&&t.value.trim()===c)||(t.value=c)}},AT=["ctrl","shift","alt","meta"],DT={stop:t=>t.stopPropagation(),prevent:t=>t.preventDefault(),self:t=>t.target!==t.currentTarget,ctrl:t=>!t.ctrlKey,shift:t=>!t.shiftKey,alt:t=>!t.altKey,meta:t=>!t.metaKey,left:t=>"button"in t&&t.button!==0,middle:t=>"button"in t&&t.button!==1,right:t=>"button"in t&&t.button!==2,exact:(t,e)=>AT.some(n=>t[`${n}Key`]&&!e.includes(n))},rr=(t,e)=>{if(!t)return t;const n=t._withMods||(t._withMods={}),r=e.join(".");return n[r]||(n[r]=((i,...a)=>{for(let s=0;s{const n=t._withKeys||(t._withKeys={}),r=e.join(".");return n[r]||(n[r]=(i=>{if(!("key"in i))return;const a=Gn(i.key);if(e.some(s=>s===a||MT[s]===a))return t(i)}))},xT=yt({patchProp:vT},iT);let id;function LT(){return id||(id=Uf(xT))}const wT=((...t)=>{const e=LT().createApp(...t),{mount:n}=e;return e.mount=r=>{const i=kT(r);if(!i)return;const a=e._component;!Ue(a)&&!a.render&&!a.template&&(a.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const s=n(i,!1,PT(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),s},e});function PT(t){if(t instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&t instanceof MathMLElement)return"mathml"}function kT(t){return Et(t)?document.querySelector(t):t}/*!
- * pinia v3.0.4
- * (c) 2025 Eduardo San Martin Morote
- * @license MIT
- */let TE;const ji=t=>TE=t,bE=Symbol();function Sc(t){return t&&typeof t=="object"&&Object.prototype.toString.call(t)==="[object Object]"&&typeof t.toJSON!="function"}var qr;(function(t){t.direct="direct",t.patchObject="patch object",t.patchFunction="patch function"})(qr||(qr={}));function UT(){const t=Mc(!0),e=t.run(()=>le({}));let n=[],r=[];const i=Vi({install(a){ji(i),i._a=a,a.provide(bE,i),a.config.globalProperties.$pinia=i,r.forEach(s=>n.push(s)),r=[]},use(a){return this._a?n.push(a):r.push(a),this},_p:n,_a:null,_e:t,_s:new Map,state:e});return i}const RE=()=>{};function ad(t,e,n,r=RE){t.add(e);const i=()=>{t.delete(e)&&r()};return!n&&ug()&&gS(i),i}function dr(t,...e){t.forEach(n=>{n(...e)})}const FT=t=>t(),od=Symbol(),Ta=Symbol();function fc(t,e){t instanceof Map&&e instanceof Map?e.forEach((n,r)=>t.set(r,n)):t instanceof Set&&e instanceof Set&&e.forEach(t.add,t);for(const n in e){if(!e.hasOwnProperty(n))continue;const r=e[n],i=t[n];Sc(i)&&Sc(r)&&t.hasOwnProperty(n)&&!bt(r)&&!An(r)?t[n]=fc(i,r):t[n]=r}return t}const BT=Symbol();function GT(t){return!Sc(t)||!Object.prototype.hasOwnProperty.call(t,BT)}const{assign:Un}=Object;function YT(t){return!!(bt(t)&&t.effect)}function qT(t,e,n,r){const{state:i,actions:a,getters:s}=e,o=n.state.value[t];let c;function _(){o||(n.state.value[t]=i?i():{});const l=US(n.state.value[t]);return Un(l,a,Object.keys(s||{}).reduce((d,u)=>(d[u]=Vi(fe(()=>{ji(n);const m=n._s.get(t);return s[u].call(m,m)})),d),{}))}return c=hE(t,_,e,n,r,!0),c}function hE(t,e,n={},r,i,a){let s;const o=Un({actions:{}},n),c={deep:!0};let _,l,d=new Set,u=new Set,m;const p=r.state.value[t];!a&&!p&&(r.state.value[t]={});let E;function f(O){let M;_=l=!1,typeof O=="function"?(O(r.state.value[t]),M={type:qr.patchFunction,storeId:t,events:m}):(fc(r.state.value[t],O),M={type:qr.patchObject,payload:O,storeId:t,events:m});const q=E=Symbol();Ft().then(()=>{E===q&&(_=!0)}),l=!0,dr(d,M,r.state.value[t])}const T=a?function(){const{state:M}=n,q=M?M():{};this.$patch(V=>{Un(V,q)})}:RE;function b(){s.stop(),d.clear(),u.clear(),r._s.delete(t)}const v=(O,M="")=>{if(od in O)return O[Ta]=M,O;const q=function(){ji(r);const V=Array.from(arguments),y=new Set,Z=new Set;function ne(se){y.add(se)}function Se(se){Z.add(se)}dr(u,{args:V,name:q[Ta],store:A,after:ne,onError:Se});let oe;try{oe=O.apply(this&&this.$id===t?this:A,V)}catch(se){throw dr(Z,se),se}return oe instanceof Promise?oe.then(se=>(dr(y,se),se)).catch(se=>(dr(Z,se),Promise.reject(se))):(dr(y,oe),oe)};return q[od]=!0,q[Ta]=M,q},C={_p:r,$id:t,$onAction:ad.bind(null,u),$patch:f,$reset:T,$subscribe(O,M={}){const q=ad(d,O,M.detached,()=>V()),V=s.run(()=>Ye(()=>r.state.value[t],y=>{(M.flush==="sync"?l:_)&&O({storeId:t,type:qr.direct,events:m},y)},Un({},c,M)));return q},$dispose:b},A=ei(C);r._s.set(t,A);const x=(r._a&&r._a.runWithContext||FT)(()=>r._e.run(()=>(s=Mc()).run(()=>e({action:v}))));for(const O in x){const M=x[O];if(bt(M)&&!YT(M)||An(M))a||(p&>(M)&&(bt(M)?M.value=p[O]:fc(M,p[O])),r.state.value[t][O]=M);else if(typeof M=="function"){const q=v(M,O);x[O]=q,o.actions[O]=M}}return Un(A,x),Un(Xe(A),x),Object.defineProperty(A,"$state",{get:()=>r.state.value[t],set:O=>{f(M=>{Un(M,O)})}}),r._p.forEach(O=>{Un(A,s.run(()=>O({store:A,app:r._a,pinia:r,options:o})))}),p&&a&&n.hydrate&&n.hydrate(A.$state,p),_=!0,l=!0,A}/*! #__NO_SIDE_EFFECTS__ */function ea(t,e,n){let r;const i=typeof e=="function";r=i?n:e;function a(s,o){const c=WS();return s=s||(c?er(bE,null):null),s&&ji(s),s=TE,s._s.has(t)||(i?hE(t,e,r,s):qT(t,r,s)),s._s.get(t)}return a.$id=t,a}const CE="";window.addEventListener("unhandledrejection",t=>{console.error("[API] Unhandled rejection:",t.reason)});async function zt(t,e,n){const r={method:t,headers:{},credentials:"include"};n!==void 0&&(r.headers["Content-Type"]="application/json",r.body=JSON.stringify(n));const i=await fetch(`${CE}${e}`,r);if(!i.ok){const a=await i.text().catch(()=>"");throw new Error(`${t} ${e} → ${i.status}: ${a}`)}return i.status===204?null:i.json()}function HT(){return zt("GET","/auth/me")}function $T(){return zt("POST","/auth/logout")}function VT(){return zt("GET","/agents/profiles")}function sd({limit:t=30,offset:e=0,profileId:n=null}={}){const r=new URLSearchParams({limit:String(t),offset:String(e)});return n&&r.set("profile_id",n),zt("GET",`/sessions?${r.toString()}`)}function ld(t){return zt("GET",`/sessions/${t}`)}function zT(t){return zt("POST","/sessions",{profile_id:t})}function WT(t){return zt("DELETE",`/sessions/${t}`)}function KT(t,e){return zt("PATCH",`/sessions/${t}/pin`,{pinned:e})}function QT(t){return zt("POST",`/sessions/${t}/stop`)}function XT(t){return zt("POST",`/sessions/${t}/generate-name`)}function ZT(t){return zt("GET",`/sessions/${t}/content`)}function JT(t){return zt("GET",`/eval/feedback/${t}`)}function jT(t,e,n){return zt("POST","/eval/feedback",{session_id:t,message_index:e,rating:n})}async function eb(t,e){const n=new FormData;n.append("file",e);const r=await fetch(`${CE}/sessions/${t}/files`,{method:"POST",credentials:"include",body:n});if(!r.ok){const i=await r.text().catch(()=>"");throw new Error(`Upload failed: ${r.status}: ${i}`)}return r.json()}const sr=ea("sessions",()=>{const e=le([]),n=le(!1),r=le(!1),i=le(!0),a=le(0),s=le(null);async function o(p=null){s.value=p,n.value=!0;try{const E=await sd({limit:30,offset:0,profileId:p}),f=Array.isArray(E)?E:E.items;e.value=f,i.value=Array.isArray(E)?!1:E.has_more,a.value=Array.isArray(E)?f.length:E.next_offset}finally{n.value=!1}}async function c(){if(!(n.value||r.value||!i.value)){r.value=!0;try{const p=await sd({limit:30,offset:a.value,profileId:s.value}),E=Array.isArray(p)?p:p.items,f=new Set(e.value.map(T=>T.session_id));e.value=[...e.value,...E.filter(T=>!f.has(T.session_id))],i.value=Array.isArray(p)?!1:p.has_more,a.value=Array.isArray(p)?e.value.length:p.next_offset}finally{r.value=!1}}}async function _(p){const E=await zT(p);return e.value.unshift({session_id:E.session_id,profile_id:E.profile_id,created_at:E.created_at,last_active:E.created_at,message_count:0,preview:"",pinned:!1}),a.value+=1,E}async function l(p){await WT(p),e.value=e.value.filter(E=>E.session_id!==p),a.value=Math.max(0,a.value-1)}async function d(p,E){await KT(p,E);const f=e.value.map(T=>T.session_id===p?{...T,pinned:E}:T);f.sort((T,b)=>(b.pinned?1:0)-(T.pinned?1:0)),e.value=f}function u(p,E){const f=e.value.find(T=>T.session_id===p);f&&(f.preview=E)}function m(p,E){const f=e.value.find(T=>T.session_id===p);f&&(f.name=E)}return{sessions:e,loading:n,loadingMore:r,hasMore:i,currentProfileId:s,fetchSessions:o,fetchMoreSessions:c,createSession:_,deleteSession:l,pinSession:d,updatePreview:u,updateName:m}}),ii=ea("profiles",()=>{const t=le([]),e=le(null),n=le(!1);async function r(){n.value=!0;try{t.value=await VT(),t.value.length&&!e.value&&(e.value=t.value[0].id)}finally{n.value=!1}}function i(a){return t.value.find(s=>s.id===a)??null}return{profiles:t,selectedProfileId:e,loading:n,fetchProfiles:r,getProfile:i}});let cd=null;async function tb(t){try{const e=sr(),n=e.sessions.find(i=>i.session_id===t);if(n!=null&&n.name)return;const{name:r}=await XT(t);r&&e.updateName(t,r)}catch{}}const qt=ea("chat",()=>{const t=le(null),e=le(null),n=le([]),r=le(!1),i=le([]),a=le([]),s=le([]),o=le(0),c=le(0),_=le(!1),l=Er(null),d=le(!1);async function u(L){if(t.value!==L){cd=L,_.value=!0,n.value=[],s.value=[],r.value=!1,l.value=null,o.value=0,c.value=0;try{const $=await ld(L);if(cd!==L)return;e.value=$.profile_id??null,n.value=st($.messages??[]),await E(L),await f(L),$.context_token_count&&(o.value=$.context_token_count),$.max_context_tokens&&(c.value=$.max_context_tokens),t.value=L,location.hash=L}finally{_.value=!1}}}function m(){t.value=null,e.value=null,n.value=[],s.value=[],r.value=!1,l.value=null,o.value=0,c.value=0,location.hash=""}async function p(L){if(L)try{const $=await ld(L);e.value=$.profile_id??null,n.value=st($.messages??[]),await E(L),await f(L),$.context_token_count&&(o.value=$.context_token_count),$.max_context_tokens&&(c.value=$.max_context_tokens),l.value=null,r.value=!1}catch{}}async function E(L){var $,K;try{const{feedback:D=[]}=await JT(L);if(!D.length)return;const P=new Map(D.map(z=>[z.message_index,z.rating]));for(const z of n.value){if(z.role!=="assistant")continue;const ee=(K=($=z.id)==null?void 0:$.startsWith)!=null&&K.call($,"h_")?Number(z.id.slice(2)):NaN;Number.isInteger(ee)&&P.has(ee)&&(z.rating=P.get(ee))}}catch{}}async function f(L=t.value){if(!L){s.value=[];return}try{const{content:$=[]}=await ZT(L);s.value=$}catch{s.value=[]}}function T(L){if(!(L!=null&&L.filename))return;const $=L.id||L.filename,K=s.value.findIndex(P=>(P.id||P.filename)===$),D={...L};K===-1?s.value.unshift(D):s.value.splice(K,1,{...s.value[K],...D})}async function b(L,$){var z,ee;if(!t.value||!L)return;const K=(ee=(z=L.id)==null?void 0:z.startsWith)!=null&&ee.call(z,"h_")?Number(L.id.slice(2)):NaN;if(!Number.isInteger(K))return;const D=L.rating===$?0:$,P=L.rating??0;L.rating=D;try{await jT(t.value,K,D)}catch(ge){throw L.rating=P,ge}}function v(L){t.value&&(L?localStorage.setItem(`draft:${t.value}`,L):localStorage.removeItem(`draft:${t.value}`))}function C(L){return localStorage.getItem(`draft:${L}`)??""}function A(){if(l.value){const $=n.value.indexOf(l.value);$!==-1&&n.value.splice($,1),l.value=null}r.value=!0;const L={id:`stream_${Date.now()}`,role:"assistant",type:"stream",thinking:null,tools:[],text:"",done:!1,time:new Date().toISOString(),animate:!d.value,statusLabel:null};n.value.push(L),l.value=n.value[n.value.length-1]}function N(){d.value=!0}function x(){d.value=!1,l.value&&(l.value.animate=!0)}function O(L){const $=l.value;$&&($.thinking||($.thinking={text:"",done:!1}),$.thinking.text+=L)}function M(){const L=l.value;L!=null&&L.thinking&&(L.thinking.done=!0)}function q(L){for(let $=L.tools.length-1;$>=0;$--){const K=L.tools[$];if(K.kind==="tool"&&K.name==="spawn_agent")return K}}function V(L){const $=l.value;if(!$)return;const K={kind:"turn_thinking",isSubagent:L.is_subagent??!1,thinking:{text:L.thinking??"",done:!0}};if(L.is_subagent){const D=q($);if(D){D.steps.push(K);return}}$.tools.push(K)}function y(L){const $=l.value;if($){if(L.is_subagent){const K=q($);K&&(K.planningLabel=L.label??"");return}$.statusLabel=L.label??""}}function Z(L){const $=l.value;if($){if(L.is_subagent){const K=q($);K&&(K.planningLabel=null,K.steps.push({kind:"plan",text:L.plan??""}));return}$.statusLabel=null,$.tools.push({kind:"plan",text:L.plan??""})}}function ne(L){const $=l.value;if(!$)return;const K={kind:"tool",id:`tool_${Date.now()}`,name:L.tool,args:L.args,result:null,success:null,pending:!0,startedAt:Date.now(),isSubagent:L.is_subagent??!1,steps:[]};if(L.is_subagent){const D=q($);if(D){D.steps.push(K);return}}$.tools.push(K)}function Se(L){var D;const $=l.value;if(!$)return;if(L.is_subagent){const P=q($);if(P){let z=null;for(let ee=P.steps.length-1;ee>=0;ee--){const ge=P.steps[ee];if(ge.kind==="tool"&&ge.name===L.tool&&ge.pending){z=ge;break}}if(z){z.result=L.result,z.success=L.success!==!1,z.pending=!1;return}}}let K=null;for(let P=$.tools.length-1;P>=0;P--){const z=$.tools[P];if(z.kind==="tool"&&z.name===L.tool&&z.pending){K=z;break}}if(K&&(K.result=L.result,K.success=L.success!==!1,K.pending=!1,L.metadata&&(K.metadata=L.metadata),L.tool==="content_publish"&&K.success&&L.metadata&&T(L.metadata),L.tool==="filesystem"&&K.success&&t.value)){const P=typeof K.args=="object"?(D=K.args)==null?void 0:D.action:null;["write","edit"].includes(P)&&f(t.value)}}function oe(L){const $=l.value;$&&($.statusLabel&&($.statusLabel=null),$.text+=L)}function se(L){const $=l.value;$&&($.done=!0,$.elapsed_seconds=(L==null?void 0:L.elapsed_seconds)??null,$.tool_call_count=(L==null?void 0:L.tool_call_count)??null,$.token_count=(L==null?void 0:L.token_count)??null,l.value=null,!$.thinking&&!$.tools.length&&!$.text&&(n.value=n.value.filter(K=>K!==$))),r.value=!1,(L==null?void 0:L.context_tokens)!=null&&(o.value=L.context_tokens),(L==null?void 0:L.max_context_tokens)!=null&&(c.value=L.max_context_tokens),t.value&&($!=null&&$.text)&&sr().updatePreview(t.value,$.text.slice(0,80)),t.value&&tb(t.value)}function be(){const L=l.value;L&&(L.done=!0,l.value=null,!L.thinking&&!L.tools.length&&!L.text&&(n.value=n.value.filter($=>$!==L))),r.value=!1}function me(L){e.value=L.profile_id}function Le(L){(L==null?void 0:L.context_tokens)!=null&&(o.value=L.context_tokens),(L==null?void 0:L.max_context_tokens)!=null&&(c.value=L.max_context_tokens),n.value.push({id:`compress_${Date.now()}`,role:"system",type:"compression_notice",before:L.messages_before,after:L.messages_after,summary:L.summary??""})}function Be(L){r.value=!1,l.value=null,n.value.push({id:`err_${Date.now()}`,role:"system",type:"error",text:L.message??"An error occurred"})}function He(L,$,K){n.value.push({id:`user_${Date.now()}`,role:"user",text:L,images:[...$],files:[...K],time:new Date().toISOString(),animate:!0})}function st(L){var D,P;const $=[];let K=0;for(;Ktypeof ge=="string"&&ge.startsWith("data:")?ge:`data:image/jpeg;base64,${String(ge??"")}`);$.push({id:`h_${K}`,role:"user",text:z.content??"",images:ee,files:z.files??[],time:z.created_at??null}),K++;continue}if(z.role==="assistant"){const ee=`h_${K}`,ge=[];let g=null,S="",I=null;for(;K=L.length||L[K].role!=="assistant")break}}if(g||ge.length||S){let F=null,B=null,G=null;const Q=Number(ee.slice(2));for(let j=Q;j{const t=le(null),e=le(!1),n=le(!1),r=fe(()=>t.value!==null),i=fe(()=>{var l;return((l=t.value)==null?void 0:l.role)==="admin"});function a(l){return t.value?t.value.role==="admin"?!0:(t.value.permissions||[]).includes(l):!1}async function s(){var l;console.log("[auth] fetchMe start"),e.value=!0;try{t.value=await HT(),console.log("[auth] fetchMe success",t.value)}catch(d){throw console.log("[auth] fetchMe error",d.message),(l=d.message)!=null&&l.includes("401")&&(t.value=null),d}finally{e.value=!1,console.log("[auth] fetchMe loading=false")}}async function o(){console.log("[auth] fetchStatus start");try{const l=await fetch("/auth/status");if(console.log("[auth] fetchStatus response",l.status,l.ok),l.ok){const d=await l.json();console.log("[auth] fetchStatus data",d),n.value=!!d.configured,console.log("[auth] fetchStatus authConfigured set to",n.value)}else console.log("[auth] fetchStatus not ok"),n.value=!1}catch(l){console.log("[auth] fetchStatus error",l),n.value=!1}}function c(){window.location.href="/auth/login"}async function _(){try{await $T()}catch{}t.value=null,window.location.reload()}return{user:t,loading:e,authConfigured:n,isAuthenticated:r,isAdmin:i,hasPermission:a,fetchMe:s,fetchStatus:o,login:c,logout:_}}),zc="/images/logo.svg";function nb(t){return{all:t=t||new Map,on:function(e,n){var r=t.get(e);r&&r.push(n)||t.set(e,[n])},off:function(e,n){var r=t.get(e);r&&r.splice(r.indexOf(n)>>>0,1)},emit:function(e,n){(t.get(e)||[]).slice().map(function(r){r(n)}),(t.get("*")||[]).slice().map(function(r){r(e,n)})}}}function Wc(t,e,n){if(!n)return e;const r=t==null?void 0:t[n];if(r==null)throw new Error(`Key is ${r} on item (keyField is '${n}')`);return r}function Jn(t,e){return t.map((n,r)=>Wc(n,r,e))}function rb(t,e,n){const r=[],i=[];for(let a=0;a0?c:null)}return{keys:r,sizes:i}}function ib(t,e,n){if(!t||t.keys.length!==e.length||t.sizes.length!==e.length)return!1;for(let r=0;r0&&(r[t.keys[i]]=a)}return r}function NE(t,e){if(!t.length||e.length<=t.length)return 0;const n=t[0],r=e.indexOf(n);if(r<=0||r+t.lengthe.length-r)return 0;for(let i=0;i=n&&c<=o?null:t{}:n.onVscrollUpdate(f),d=fe(()=>{const y=ue(t);if(n.vscrollData.simpleArray){if(y.index==null)throw new Error("index is required when using simple-array mode with dynamic item measurement");return y.index}if(n.vscrollData.keyField in y.item)return y.item[n.vscrollData.keyField];throw new Error(`keyField '${n.vscrollData.keyField}' not found in your item. You should set a valid keyField prop on your Scroller`)}),u=fe(()=>n.vscrollData.sizes[d.value]||0),m=fe(()=>ue(t).active&&n.vscrollData.active);function p(){m.value?a!==d.value&&(a=d.value,i=null,s=null,C(d.value)):i=d.value}function E(){ue(t).watchData&&!n.resizeObserver?c=Ye(()=>ue(t).item,()=>{T()},{deep:!0}):c&&(c(),c=null)}function f({force:y}){!m.value&&y&&(s=d.value),(i===d.value||y||!u.value)&&p()}function T(){p()}function b(y){n.undefinedMap[y]&&n.undefinedSizeCount.value--,n.undefinedMap[y]=void 0}function v(y,Z){if(n.vscrollData.sizes[y]){b(y);return}if(Z){n.undefinedMap[y]||n.undefinedSizeCount.value++,n.undefinedMap[y]=!0;return}n.undefinedMap[y]&&(n.undefinedSizeCount.value--,n.undefinedMap[y]=!1)}function C(y){Ft(()=>{if(d.value===y){const Z=ue(e);if(!Z)return;const ne=Z.offsetWidth,Se=Z.offsetHeight;A(ne,Se)}a=null})}function A(y,Z){const ne=~~(n.direction.value==="vertical"?Z:y);ne&&u.value!==ne&&N(ne)}function N(y){var Z,ne;b(d.value),n.vscrollData.sizes[d.value]=y,ue(t).emitResize&&((ne=(Z=ue(r))==null?void 0:Z.onResize)==null||ne.call(Z,d.value))}function x(){if(!n.resizeObserver||o)return;const y=ue(e);y&&(n.resizeObserver.observe(y),y.$_vs_id=d.value,y.$_vs_onResize=M,o=!0)}function O(){if(!n.resizeObserver||!o)return;const y=ue(e);y&&(n.resizeObserver.unobserve(y),y.$_vs_onResize=void 0,o=!1)}function M(y,Z,ne){d.value===y&&A(Z,ne)}_.push(Ye(()=>ue(t).watchData,()=>{E()})),n.resizeObserver||_.push(Ye(()=>ue(t).sizeDependencies,()=>{T()},{deep:!0})),_.push(Ye(d,(y,Z)=>{const ne=ue(e);ne&&(ne.$_vs_id=y),b(Z),v(y,m.value);const Se=n.vscrollData.sizes[y];if(!Se){i=y,T();return}b(y),o&&(n.vscrollData.sizes[y]=Se)})),_.push(Ye(m,y=>{v(d.value,y),n.resizeObserver?y?x():O():y&&s===d.value&&p()})),E();function q(){m.value&&(p(),x())}function V(){l(),O(),b(d.value);const y=ue(e);y&&(y.$_vs_id=void 0,y.$_vs_onResize=void 0),c&&(c(),c=null);for(const Z of _)Z();_.length=0}return{id:d,size:u,finalActive:m,updateSize:p,mount:q,unmount:V}}const ab={itemsLimit:1e3};function yE(t){return typeof window<"u"&&t===window}const ob=(()=>{if(typeof document>"u")return"negative";const t=document.createElement("div"),e=document.createElement("div");t.style.width="4px",t.style.height="1px",t.style.overflow="auto",t.style.direction="rtl",e.style.width="8px",e.style.height="1px",t.appendChild(e),document.body.appendChild(t),t.scrollLeft=-1;const n=t.scrollLeft<0;return document.body.removeChild(t),n?"negative":"default"})();function gr(t,e,n){return e!=="horizontal"||!n||yE(n)||getComputedStyle(n).direction!=="rtl"?t:ob==="negative"?-t:t}function sb(t,e,n){return gr(t,e,n)}function ba(t,e,n,r){const i=sb(n,e,t),a=!!(r!=null&&r.smooth);if(yE(t)){e==="vertical"?t.scrollTo({top:i,behavior:a?"smooth":"auto"}):t.scrollTo({left:i,behavior:a?"smooth":"auto"});return}if(typeof t.scrollTo=="function"){t.scrollTo(e==="vertical"?{top:i,behavior:a?"smooth":"auto"}:{left:i,behavior:a?"smooth":"auto"});return}e==="vertical"?t.scrollTop=i:t.scrollLeft=i}function lb(t,e,n){return n?e==="vertical"?window.innerHeight:window.innerWidth:e==="vertical"?t.clientHeight:t.clientWidth}const cb=/auto|scroll/;function IE(t,e){return t.parentNode===null?e:IE(t.parentNode,[...e,t])}function Ra(t,e){return getComputedStyle(t,null).getPropertyValue(e)}function _b(t){return Ra(t,"overflow")+Ra(t,"overflow-y")+Ra(t,"overflow-x")}function db(t){return cb.test(_b(t))}function pi(t){if(!(t instanceof HTMLElement||t instanceof SVGElement))return;const e=IE(t.parentNode,[]);for(let n=0;n{const k=ue(t);return k.items.length>0&&typeof k.items[0]!="object"}),Z=fe(()=>{const k=ue(t);if(k.itemSize===null){const H={[-1]:{accumulator:0}},ae=k.items,ye=k.sizeField??"size",Ee=k.minItemSize,he=V.value;let we=1e4,ze=0,vt;for(let Pe=0,St=ae.length;Pea.value.filter(k=>k.nr.used).sort((k,H)=>k.nr.index-H.nr.index)),Se=fe(()=>{const k=ue(t),H=y.value?null:k.keyField;return rb(k.items,H,(ae,ye,Ee)=>k.itemSize!=null?k.itemSize:V.value[Ee]||(ae==null?void 0:ae[k.sizeField??"size"])||void 0)});function oe(k){const H=ue(t);return V.value=Tc(k,H.items,y.value?null:H.keyField),Object.keys(V.value).length>0}function se(k){let H=d.get(k);return H||(H=[],d.set(k,H)),H}function be(k,H,ae,ye,Ee){const he=Vi({id:pb++,index:H,used:!0,key:ye,type:Ee}),we=yg({item:ae,position:0,offset:0,nr:he,_vs_styleStamp:0});return k.push(we),we}function me(k){const H=se(k);if(H&&H.length){const ae=H.pop();return ae.nr.used=!0,ha(ae),ae}}function Le(k){const H=k.nr.type;se(H).push(k),k.nr.used=!1,k.position=-9999,ha(k),l.delete(k.nr.key)}function Be(){l.clear(),d.clear();for(let k=0,H=a.value.length;k{q.delete(H),k()}),q.add(H),H}function st(){for(const k of q)cancelAnimationFrame(k);q.clear()}function L(){f&&(clearTimeout(f),f=null),T&&(clearTimeout(T),T=null),b&&(clearTimeout(b),b=null),x&&(clearTimeout(x),x=null),O&&(clearTimeout(O),O=null)}function $(){var k;(k=i==null?void 0:i.onResize)==null||k.call(i),o.value&&ce(!1)}function K(){N&&!M&&F();const k=ue(t);if(!u){if(u=!0,f)return;const H=()=>He(()=>{u=!1;const{continuous:ae}=ce(!1,!0);ae||(T&&clearTimeout(T),T=setTimeout(K,k.updateInterval+100))});H(),k.updateInterval&&(f=setTimeout(()=>{f=null,u&&H()},k.updateInterval))}}function D(k,H){var ae,ye;o.value&&(k||H.boundingClientRect.width!==0||H.boundingClientRect.height!==0?((ae=i==null?void 0:i.onVisible)==null||ae.call(i),He(()=>{ce(!1)})):(ye=i==null?void 0:i.onHidden)==null||ye.call(i))}function P(){const k=ue(e),H=k?pi(k):void 0;return window.document&&(H===window.document.documentElement||H===window.document.body)?window:H||window}function z(){const k=ue(n);return k?ue(t).direction==="vertical"?k.scrollHeight:k.scrollWidth:0}function ee(){const k=ue(e);if(!k)return{start:0,end:0};const H=ue(t),ae=H.direction==="vertical";let ye;if(H.pageMode){const Ee=k.getBoundingClientRect(),he=ae?Ee.height:Ee.width;let we=-(ae?Ee.top:Ee.left),ze=ae?window.innerHeight:window.innerWidth;we<0&&(ze+=we,we=0),we+ze>he&&(ze=he-we),ye={start:we,end:we+ze}}else ae?ye={start:k.scrollTop,end:k.scrollTop+k.clientHeight}:ye={start:gr(k.scrollLeft,H.direction,k),end:gr(k.scrollLeft,H.direction,k)+k.clientWidth};return ye}function ge(){const k=ue(e);if(!k)return{start:0,end:0};if(ue(t).direction==="vertical"){const H=gr(k.scrollLeft,"horizontal",k);return{start:H,end:H+k.clientWidth}}return{start:k.scrollTop,end:k.scrollTop+k.clientHeight}}function g(k){const H=ue(t);if(H.itemSize!=null)return H.itemSize;const ae=Z.value[k];return(ae==null?void 0:ae.size)||Number(H.minItemSize)||0}function S(k){var H;const ae=ue(t),ye=ae.gridItems||1;return k<=0?0:ae.itemSize!=null?Math.floor(k/ye)*ae.itemSize:((H=Z.value[k-1])==null?void 0:H.accumulator)||0}function I(k){const H=ue(t),ae=H.items.length,ye=H.gridItems||1;if(!ae)return 0;if(H.itemSize!=null){const ze=Math.floor(k/H.itemSize)*ye;return Math.min(Math.max(ze,0),ae-1)}let Ee=0,he=ae-1,we=0;for(;Ee<=he;){const ze=Math.floor((Ee+he)/2);S(ze)<=k?(we=ze,Ee=ze+1):he=ze-1}return we}function F(){x&&(clearTimeout(x),x=null),N=null}function B(){x&&clearTimeout(x),x=setTimeout(()=>{N=null,x=null},150)}function G(k,H){if(!k.length){F();return}const ae=Math.max(ee().start-z(),0),ye=Math.min(I(ae),k.length-1),Ee=k[ye],he=H?Ee==null?void 0:Ee[H]:ye;if(he==null){F();return}const we=z()+S(ye);N={key:he,offset:ee().start-we}}function Q(k){if(!N)return!1;const H=ue(t),ae=k??H.items,ye=y.value?null:H.keyField,Ee=Jn(ae,ye).indexOf(N.key);if(Ee===-1)return F(),!1;const he=z()+S(Ee)+N.offset,we=ee().start;return Math.abs(he-we)<.5?!1:(M=!0,nt(he),He(()=>{M=!1}),!0)}function j(){ue(t).pageMode?J():W()}function J(){C=P(),C.addEventListener("scroll",K,ub()?{passive:!0}:!1),C.addEventListener("resize",$)}function W(){C&&(C.removeEventListener("scroll",K),C.removeEventListener("resize",$),C=null)}function Ce(k,H,ae,ye,Ee,he){const we=Math.ceil(k/H)*ae,ze=Math.max(0,Math.floor(Ee.start/ae)),vt=Math.min(Math.ceil(Ee.end/ae),Math.ceil(k/H)),Pe=Math.max(0,Math.floor(he.start/ye)),St=Math.min(Math.ceil(he.end/ye),H),ft=[];for(let Y=ze;Y=k)break;ft.push(je)}}const Je=ft[0]??0,R=ft.at(-1)??-1;return{renderedIndices:ft,startIndex:Je,endIndex:R+1,visibleStartIndex:Je,visibleEndIndex:R,totalSize:we}}function _e(){const k=ue(t);if(!k.gridItems||k.itemSize==null)return!1;const H=ue(e);if(!H)return!1;const ae=k.itemSecondarySize||k.itemSize,ye=k.direction==="vertical"?H.clientWidth:H.clientHeight;return ae*k.gridItems>ye}function ce(k,H=!1){var ae,ye;const Ee=ue(t),he=Ee.itemSize,we=Ee.gridItems||1,ze=Ee.itemSecondarySize||he,vt=v,Pe=Ee.typeField,St=y.value?null:Ee.keyField,ft=Ee.items,Je=ft.length,R=Z.value,Y=l,re=a.value;let Ne=null,je=null,Ge,ie,pe,Re,rt;if(!Je)Ge=ie=Re=rt=pe=0;else if(E)Ge=Re=0,ie=rt=Math.min(Ee.prerender,ft.length),pe=0;else{const $e=ee(),Rt=ge();if(H){let Tt=$e.start-m;Tt<0&&(Tt=-Tt);let on=Rt.start-p;on<0&&(on=-on);const qn=he===null&&Tt>=vt||he!==null&&Tt>=he,mt=we>1&&he!=null&&on>=ze;if(!qn&&!mt)return{continuous:!0}}m=$e.start,p=Rt.start;const Kt=Ee.buffer;$e.start-=Kt,$e.end+=Kt,Rt.start-=Kt,Rt.end+=Kt;let jt=0;const wn=ue(n);wn&&(jt=wn.scrollHeight,$e.start-=jt);const Pn=ue(r);if(Pn){const Tt=Pn.scrollHeight;$e.end+=Tt}if(he===null){let Tt,on=0,qn=Je-1,mt=~~(Je/2),cr;do cr=mt,Tt=R[mt].accumulator,Tt<$e.start?on=mt:mt$e.start&&(qn=mt),mt=~~((on+qn)/2);while(mt!==cr);for(mt<0&&(mt=0),Ge=mt,pe=R[Je-1].accumulator,ie=mt;ieJe&&(ie=Je)),Re=Ge;Re1){const Tt=Ce(Je,we,he,ze,$e,Rt);Ne=Tt.renderedIndices,je=new Set(Ne),Ge=Tt.startIndex,ie=Tt.endIndex,Re=Tt.visibleStartIndex,rt=Tt.visibleEndIndex,pe=Tt.totalSize}else{Ge=~~($e.start/he*we);const Tt=Ge%we;Ge-=Tt,ie=Math.ceil($e.end/he*we),Re=Math.max(0,Math.floor(($e.start-jt)/he*we)),rt=Math.floor(($e.end-jt)/he*we),Ge<0&&(Ge=0),ie>Je&&(ie=Je),Re<0&&(Re=0),rt>Je&&(rt=Je),pe=Math.ceil(Je/we)*he}}ie-Ge>ab.itemsLimit&&ve(),s.value=pe;let De;const fn=Ge<=_&&ie>=c;if(!fn||k)Be();else for(let $e=0,Rt=re.length;$e