From b02e9da943f5728d7ac3f18b3c45931140c03d7f Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 18 Feb 2026 06:37:27 +0100 Subject: [PATCH] WIP: multi virtual printer support --- backend/app/api/routes/settings.py | 6 +- backend/app/api/routes/virtual_printers.py | 380 ++++++ backend/app/core/database.py | 65 + backend/app/main.py | 59 +- backend/app/models/virtual_printer.py | 28 + backend/app/services/network_utils.py | 99 +- .../services/virtual_printer/bind_server.py | 4 +- .../services/virtual_printer/certificate.py | 11 +- .../services/virtual_printer/ftp_server.py | 10 +- .../app/services/virtual_printer/manager.py | 1041 ++++++++--------- .../services/virtual_printer/mqtt_server.py | 54 +- .../services/virtual_printer/ssdp_server.py | 46 +- .../app/services/virtual_printer/tcp_proxy.py | 20 +- frontend/src/api/client.ts | 65 + .../components/VirtualPrinterAddDialog.tsx | 156 +++ .../src/components/VirtualPrinterCard.tsx | 459 ++++++++ .../src/components/VirtualPrinterList.tsx | 128 ++ frontend/src/i18n/locales/de.ts | 47 +- frontend/src/i18n/locales/en.ts | 47 +- frontend/src/i18n/locales/fr.ts | 8 + frontend/src/i18n/locales/it.ts | 8 + frontend/src/i18n/locales/ja.ts | 47 +- frontend/src/pages/SettingsPage.tsx | 4 +- static/assets/index-BGInJFa3.css | 1 + static/assets/index-DF7TfzH1.css | 1 - .../{index-D8lA4NE5.js => index-_Fo-S_XR.js} | 834 ++++++------- static/index.html | 4 +- 27 files changed, 2535 insertions(+), 1097 deletions(-) create mode 100644 backend/app/api/routes/virtual_printers.py create mode 100644 backend/app/models/virtual_printer.py create mode 100644 frontend/src/components/VirtualPrinterAddDialog.tsx create mode 100644 frontend/src/components/VirtualPrinterCard.tsx create mode 100644 frontend/src/components/VirtualPrinterList.tsx create mode 100644 static/assets/index-BGInJFa3.css delete mode 100644 static/assets/index-DF7TfzH1.css rename static/assets/{index-D8lA4NE5.js => index-_Fo-S_XR.js} (66%) diff --git a/backend/app/api/routes/settings.py b/backend/app/api/routes/settings.py index a3846eb06..8e8a094be 100644 --- a/backend/app/api/routes/settings.py +++ b/backend/app/api/routes/settings.py @@ -520,10 +520,10 @@ async def restore_backup( async def get_network_interfaces( _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_READ), ): - """Get available network interfaces for SSDP proxy configuration.""" - from backend.app.services.network_utils import get_network_interfaces + """Get available network interfaces with all IPs (primary + aliases).""" + from backend.app.services.network_utils import get_all_interface_ips - interfaces = get_network_interfaces() + interfaces = get_all_interface_ips() return {"interfaces": interfaces} diff --git a/backend/app/api/routes/virtual_printers.py b/backend/app/api/routes/virtual_printers.py new file mode 100644 index 000000000..1964c9f5b --- /dev/null +++ b/backend/app/api/routes/virtual_printers.py @@ -0,0 +1,380 @@ +import logging + +from fastapi import APIRouter, Depends +from fastapi.responses import JSONResponse +from pydantic import BaseModel +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from backend.app.core.auth import RequirePermissionIfAuthEnabled +from backend.app.core.database import get_db +from backend.app.core.permissions import Permission +from backend.app.models.user import User + +logger = logging.getLogger(__name__) + +router = APIRouter(prefix="/virtual-printers", tags=["virtual-printers"]) + + +class VirtualPrinterCreate(BaseModel): + name: str = "Bambuddy" + enabled: bool = False + mode: str = "immediate" + model: str | None = None + access_code: str | None = None + target_printer_id: int | None = None + bind_ip: str | None = None + remote_interface_ip: str | None = None + + +class VirtualPrinterUpdate(BaseModel): + name: str | None = None + enabled: bool | None = None + mode: str | None = None + model: str | None = None + access_code: str | None = None + target_printer_id: int | None = None + bind_ip: str | None = None + remote_interface_ip: str | None = None + + +def _vp_to_dict(vp, status: dict | None = None) -> dict: + """Convert VirtualPrinter model to response dict.""" + from backend.app.services.virtual_printer import VIRTUAL_PRINTER_MODELS + from backend.app.services.virtual_printer.manager import DEFAULT_VIRTUAL_PRINTER_MODEL, _get_serial_for_model + + model_code = vp.model or DEFAULT_VIRTUAL_PRINTER_MODEL + serial = _get_serial_for_model(model_code, vp.serial_suffix) + + return { + "id": vp.id, + "name": vp.name, + "enabled": vp.enabled, + "mode": vp.mode, + "model": model_code, + "model_name": VIRTUAL_PRINTER_MODELS.get(model_code, model_code), + "access_code_set": bool(vp.access_code), + "serial": serial, + "target_printer_id": vp.target_printer_id, + "bind_ip": vp.bind_ip, + "remote_interface_ip": vp.remote_interface_ip, + "position": vp.position, + "status": status or {"running": False, "pending_files": 0}, + } + + +@router.get("") +async def list_virtual_printers( + db: AsyncSession = Depends(get_db), + _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_READ), +): + """List all virtual printers with status.""" + from backend.app.models.virtual_printer import VirtualPrinter + from backend.app.services.virtual_printer import VIRTUAL_PRINTER_MODELS, virtual_printer_manager + + result = await db.execute(select(VirtualPrinter).order_by(VirtualPrinter.position, VirtualPrinter.id)) + vps = result.scalars().all() + + printers = [] + for vp in vps: + instance = virtual_printer_manager.get_instance(vp.id) + status = instance.get_status() if instance else {"running": False, "pending_files": 0} + printers.append(_vp_to_dict(vp, status)) + + return { + "printers": printers, + "models": VIRTUAL_PRINTER_MODELS, + } + + +@router.post("") +async def create_virtual_printer( + body: VirtualPrinterCreate, + db: AsyncSession = Depends(get_db), + _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_UPDATE), +): + """Create a new virtual printer.""" + from backend.app.models.virtual_printer import VirtualPrinter + from backend.app.services.virtual_printer import VIRTUAL_PRINTER_MODELS, virtual_printer_manager + from backend.app.services.virtual_printer.manager import DEFAULT_VIRTUAL_PRINTER_MODEL + + # Validate mode + if body.mode not in ("immediate", "review", "print_queue", "proxy"): + return JSONResponse(status_code=400, content={"detail": "Invalid mode"}) + + # Validate model + if body.model and body.model not in VIRTUAL_PRINTER_MODELS: + return JSONResponse( + status_code=400, + content={"detail": f"Invalid model. Must be one of: {', '.join(VIRTUAL_PRINTER_MODELS.keys())}"}, + ) + + # Validate access code length + if body.access_code and len(body.access_code) != 8: + return JSONResponse(status_code=400, content={"detail": "Access code must be exactly 8 characters"}) + + # Validation when enabling + if body.enabled: + if not body.bind_ip: + return JSONResponse(status_code=400, content={"detail": "Bind IP is required when enabling"}) + if body.mode == "proxy": + if not body.target_printer_id: + return JSONResponse(status_code=400, content={"detail": "Target printer is required for proxy mode"}) + else: + if not body.access_code: + return JSONResponse(status_code=400, content={"detail": "Access code is required when enabling"}) + + # Validate proxy target printer exists + if body.target_printer_id: + from backend.app.models.printer import Printer + + result = await db.execute(select(Printer).where(Printer.id == body.target_printer_id)) + if not result.scalar_one_or_none(): + return JSONResponse( + status_code=400, content={"detail": f"Printer with ID {body.target_printer_id} not found"} + ) + + # Validate bind_ip uniqueness (against all enabled VPs) + if body.bind_ip: + result = await db.execute( + select(VirtualPrinter).where( + VirtualPrinter.bind_ip == body.bind_ip, + VirtualPrinter.enabled == True, # noqa: E712 + ) + ) + if result.scalar_one_or_none(): + return JSONResponse(status_code=400, content={"detail": f"Bind IP {body.bind_ip} is already in use"}) + + # Generate next serial suffix + result = await db.execute(select(VirtualPrinter.serial_suffix).order_by(VirtualPrinter.id.desc())) + last_suffix = result.scalar() + if last_suffix: + try: + next_num = int(last_suffix) + 1 + new_suffix = str(next_num).zfill(9) + except ValueError: + new_suffix = "391800002" + else: + new_suffix = "391800001" + + # Get next position + result = await db.execute(select(VirtualPrinter.position).order_by(VirtualPrinter.position.desc())) + last_pos = result.scalar() + next_pos = (last_pos or 0) + 1 + + vp = VirtualPrinter( + name=body.name, + enabled=body.enabled, + mode=body.mode, + model=body.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + access_code=body.access_code, + target_printer_id=body.target_printer_id, + bind_ip=body.bind_ip, + remote_interface_ip=body.remote_interface_ip, + serial_suffix=new_suffix, + position=next_pos, + ) + db.add(vp) + await db.commit() + await db.refresh(vp) + + logger.info("Created virtual printer: %s (id=%d)", vp.name, vp.id) + + # Sync services if enabled + if body.enabled: + try: + await virtual_printer_manager.sync_from_db() + except Exception as e: + logger.error("Failed to start virtual printer after create: %s", e) + + return _vp_to_dict(vp) + + +@router.get("/{vp_id}") +async def get_virtual_printer( + vp_id: int, + db: AsyncSession = Depends(get_db), + _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_READ), +): + """Get a single virtual printer with status.""" + from backend.app.models.virtual_printer import VirtualPrinter + from backend.app.services.virtual_printer import virtual_printer_manager + + result = await db.execute(select(VirtualPrinter).where(VirtualPrinter.id == vp_id)) + vp = result.scalar_one_or_none() + if not vp: + return JSONResponse(status_code=404, content={"detail": "Virtual printer not found"}) + + instance = virtual_printer_manager.get_instance(vp.id) + status = instance.get_status() if instance else {"running": False, "pending_files": 0} + + return _vp_to_dict(vp, status) + + +@router.put("/{vp_id}") +async def update_virtual_printer( + vp_id: int, + body: VirtualPrinterUpdate, + db: AsyncSession = Depends(get_db), + _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_UPDATE), +): + """Update a virtual printer.""" + from backend.app.models.virtual_printer import VirtualPrinter + from backend.app.services.virtual_printer import VIRTUAL_PRINTER_MODELS, virtual_printer_manager + + result = await db.execute(select(VirtualPrinter).where(VirtualPrinter.id == vp_id)) + vp = result.scalar_one_or_none() + if not vp: + return JSONResponse(status_code=404, content={"detail": "Virtual printer not found"}) + + logger.debug( + "Update VP %d: body=%s, current state: mode=%s, enabled=%s, access_code_set=%s, bind_ip=%s, target=%s", + vp_id, + body.model_dump(exclude_unset=True), + vp.mode, + vp.enabled, + bool(vp.access_code), + vp.bind_ip, + vp.target_printer_id, + ) + + # Apply updates + if body.name is not None: + vp.name = body.name + if body.mode is not None: + if body.mode not in ("immediate", "review", "print_queue", "proxy"): + return JSONResponse(status_code=400, content={"detail": "Invalid mode"}) + vp.mode = body.mode + if body.model is not None: + if body.model not in VIRTUAL_PRINTER_MODELS: + return JSONResponse( + status_code=400, + content={"detail": f"Invalid model. Must be one of: {', '.join(VIRTUAL_PRINTER_MODELS.keys())}"}, + ) + vp.model = body.model + if body.access_code is not None: + if body.access_code and len(body.access_code) != 8: + return JSONResponse(status_code=400, content={"detail": "Access code must be exactly 8 characters"}) + vp.access_code = body.access_code + if body.target_printer_id is not None: + from backend.app.models.printer import Printer + + result = await db.execute(select(Printer).where(Printer.id == body.target_printer_id)) + if not result.scalar_one_or_none(): + return JSONResponse( + status_code=400, content={"detail": f"Printer with ID {body.target_printer_id} not found"} + ) + vp.target_printer_id = body.target_printer_id + if body.bind_ip is not None: + vp.bind_ip = body.bind_ip + if body.remote_interface_ip is not None: + vp.remote_interface_ip = body.remote_interface_ip + + # Determine final enabled state + explicitly_enabling = body.enabled is True + new_enabled = body.enabled if body.enabled is not None else vp.enabled + effective_mode = vp.mode + + if explicitly_enabling: + # User is explicitly toggling on — enforce all requirements + if not vp.bind_ip: + logger.warning("Update VP %d rejected: no bind_ip", vp_id) + return JSONResponse(status_code=400, content={"detail": "Bind IP is required when enabling"}) + # Validate bind_ip uniqueness (against all enabled VPs) + existing = await db.execute( + select(VirtualPrinter).where( + VirtualPrinter.bind_ip == vp.bind_ip, + VirtualPrinter.id != vp_id, + VirtualPrinter.enabled == True, # noqa: E712 + ) + ) + conflict = existing.scalar_one_or_none() + if conflict: + logger.warning( + "Update VP %d rejected: bind_ip %s already in use by VP %d (enabled=%s, mode=%s)", + vp_id, + vp.bind_ip, + conflict.id, + conflict.enabled, + conflict.mode, + ) + return JSONResponse( + status_code=400, + content={"detail": f"Bind IP {vp.bind_ip} is already in use by '{conflict.name}'"}, + ) + if effective_mode == "proxy": + if not vp.target_printer_id: + logger.warning("Update VP %d rejected: no target_printer_id for proxy mode", vp_id) + return JSONResponse(status_code=400, content={"detail": "Target printer is required for proxy mode"}) + else: + if not vp.access_code: + logger.warning( + "Update VP %d rejected: no access_code for non-proxy enable (mode=%s)", vp_id, effective_mode + ) + return JSONResponse(status_code=400, content={"detail": "Access code is required when enabling"}) + elif new_enabled and body.enabled is None: + # VP is already enabled and user is changing other fields — + # auto-disable if new state doesn't meet requirements + if not vp.bind_ip: + new_enabled = False + elif effective_mode == "proxy": + if not vp.target_printer_id: + new_enabled = False + else: + if not vp.access_code: + new_enabled = False + + vp.enabled = new_enabled + + await db.commit() + await db.refresh(vp) + + logger.info("Updated virtual printer: %s (id=%d)", vp.name, vp.id) + + # Sync services + try: + await virtual_printer_manager.sync_from_db() + except Exception as e: + logger.error("Failed to sync virtual printers after update: %s", e) + + instance = virtual_printer_manager.get_instance(vp.id) + status = instance.get_status() if instance else {"running": False, "pending_files": 0} + + return _vp_to_dict(vp, status) + + +@router.delete("/{vp_id}") +async def delete_virtual_printer( + vp_id: int, + db: AsyncSession = Depends(get_db), + _: User | None = RequirePermissionIfAuthEnabled(Permission.SETTINGS_UPDATE), +): + """Delete a virtual printer.""" + from sqlalchemy import delete as sql_delete + + from backend.app.models.virtual_printer import VirtualPrinter + from backend.app.services.virtual_printer import virtual_printer_manager + + result = await db.execute(select(VirtualPrinter).where(VirtualPrinter.id == vp_id)) + vp = result.scalar_one_or_none() + if not vp: + return JSONResponse(status_code=404, content={"detail": "Virtual printer not found"}) + + vp_name = vp.name + + # Stop instance if running + await virtual_printer_manager.remove_instance(vp_id) + + # Delete from DB + await db.execute(sql_delete(VirtualPrinter).where(VirtualPrinter.id == vp_id)) + await db.commit() + + logger.info("Deleted virtual printer: %s (id=%d)", vp_name, vp_id) + + # Resync remaining services + try: + await virtual_printer_manager.sync_from_db() + except Exception as e: + logger.error("Failed to sync virtual printers after delete: %s", e) + + return {"detail": "Deleted", "id": vp_id} diff --git a/backend/app/core/database.py b/backend/app/core/database.py index 33e2e37a6..5016795c5 100644 --- a/backend/app/core/database.py +++ b/backend/app/core/database.py @@ -103,6 +103,7 @@ async def init_db(): spool_k_profile, spool_usage_history, user, + virtual_printer, ) async with engine.begin() as conn: @@ -1216,6 +1217,70 @@ async def run_migrations(conn): except OperationalError: pass # Already applied + # Migration: Migrate single virtual printer key-value settings to virtual_printers table + try: + # Check if virtual_printers table has any rows + result = await conn.execute(text("SELECT COUNT(*) FROM virtual_printers")) + count = result.scalar() or 0 + + if count == 0: + # Check if old key-value settings exist + result = await conn.execute(text("SELECT value FROM settings WHERE key = 'virtual_printer_enabled'")) + row = result.fetchone() + if row: + # Old settings exist — migrate to first virtual printer row + old_enabled = row[0] == "true" if row[0] else False + + result = await conn.execute( + text("SELECT value FROM settings WHERE key = 'virtual_printer_access_code'") + ) + row = result.fetchone() + old_access_code = row[0] if row else None + + result = await conn.execute(text("SELECT value FROM settings WHERE key = 'virtual_printer_mode'")) + row = result.fetchone() + old_mode = row[0] if row else "immediate" + if old_mode == "queue": + old_mode = "review" + + result = await conn.execute(text("SELECT value FROM settings WHERE key = 'virtual_printer_model'")) + row = result.fetchone() + old_model = row[0] if row else "3DPrinter-X1-Carbon" + + result = await conn.execute( + text("SELECT value FROM settings WHERE key = 'virtual_printer_target_printer_id'") + ) + row = result.fetchone() + old_target_id = int(row[0]) if row and row[0] else None + + result = await conn.execute( + text("SELECT value FROM settings WHERE key = 'virtual_printer_remote_interface_ip'") + ) + row = result.fetchone() + old_remote_iface = row[0] if row else None + + await conn.execute( + text(""" + INSERT INTO virtual_printers + (name, enabled, mode, model, access_code, target_printer_id, + bind_ip, remote_interface_ip, serial_suffix, position) + VALUES + (:name, :enabled, :mode, :model, :access_code, :target_id, + NULL, :remote_iface, '391800001', 0) + """), + { + "name": "Bambuddy", + "enabled": old_enabled, + "mode": old_mode or "immediate", + "model": old_model, + "access_code": old_access_code, + "target_id": old_target_id, + "remote_iface": old_remote_iface, + }, + ) + except OperationalError: + pass # Table may not exist yet on first run + async def seed_notification_templates(): """Seed default notification templates if they don't exist.""" diff --git a/backend/app/main.py b/backend/app/main.py index 9e395ec87..abd234831 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -205,6 +205,7 @@ from backend.app.api.routes import ( system, updates, users, + virtual_printers, webhook, websocket, ) @@ -3237,55 +3238,15 @@ async def lifespan(app: FastAPI): # Start printer runtime tracking start_runtime_tracking() - # Initialize virtual printer manager + # Initialize virtual printer manager and sync from DB from backend.app.services.virtual_printer import virtual_printer_manager virtual_printer_manager.set_session_factory(async_session) - - # Auto-start virtual printer if enabled - async with async_session() as db: - from backend.app.api.routes.settings import get_setting - - vp_enabled = await get_setting(db, "virtual_printer_enabled") - if vp_enabled and vp_enabled.lower() == "true": - vp_access_code = await get_setting(db, "virtual_printer_access_code") or "" - vp_mode = await get_setting(db, "virtual_printer_mode") or "immediate" - vp_model = await get_setting(db, "virtual_printer_model") or "" - vp_target_printer_id = await get_setting(db, "virtual_printer_target_printer_id") - vp_remote_iface = await get_setting(db, "virtual_printer_remote_interface_ip") or "" - - # Look up printer IP and serial if in proxy mode - vp_target_ip = "" - vp_target_serial = "" - if vp_mode == "proxy" and vp_target_printer_id: - from backend.app.models.printer import Printer - - result = await db.execute(select(Printer).where(Printer.id == int(vp_target_printer_id))) - printer = result.scalar_one_or_none() - if printer: - vp_target_ip = printer.ip_address - vp_target_serial = printer.serial_number - - # Proxy mode requires target IP, other modes require access code - can_start = (vp_mode == "proxy" and vp_target_ip) or (vp_mode != "proxy" and vp_access_code) - - if can_start: - try: - await virtual_printer_manager.configure( - enabled=True, - access_code=vp_access_code, - mode=vp_mode, - model=vp_model, - target_printer_ip=vp_target_ip, - target_printer_serial=vp_target_serial, - remote_interface_ip=vp_remote_iface, - ) - if vp_mode == "proxy": - logging.info("Virtual printer proxy started (target=%s)", vp_target_ip) - else: - logging.info("Virtual printer started (model=%s)", vp_model or "default") - except Exception as e: - logging.warning("Failed to start virtual printer: %s", e) + try: + await virtual_printer_manager.sync_from_db() + logging.info("Virtual printer manager synced from database") + except Exception as e: + logging.warning("Failed to sync virtual printers: %s", e) yield @@ -3299,9 +3260,8 @@ async def lifespan(app: FastAPI): printer_manager.disconnect_all() await close_spoolman_client() - # Stop virtual printer if running - if virtual_printer_manager.is_enabled: - await virtual_printer_manager.configure(enabled=False) + # Stop all virtual printer services + await virtual_printer_manager.stop_all() await mqtt_smart_plug_service.disconnect(timeout=2) @@ -3494,6 +3454,7 @@ app.include_router(pending_uploads.router, prefix=app_settings.api_prefix) app.include_router(firmware.router, prefix=app_settings.api_prefix) app.include_router(github_backup.router, prefix=app_settings.api_prefix) app.include_router(metrics.router, prefix=app_settings.api_prefix) +app.include_router(virtual_printers.router, prefix=app_settings.api_prefix) # Serve static files (React build) diff --git a/backend/app/models/virtual_printer.py b/backend/app/models/virtual_printer.py new file mode 100644 index 000000000..8abd95117 --- /dev/null +++ b/backend/app/models/virtual_printer.py @@ -0,0 +1,28 @@ +from datetime import datetime + +from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, func +from sqlalchemy.orm import Mapped, mapped_column + +from backend.app.core.database import Base + + +class VirtualPrinter(Base): + """Virtual printer configuration for multi-instance support.""" + + __tablename__ = "virtual_printers" + + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] = mapped_column(String(100), default="Bambuddy") + enabled: Mapped[bool] = mapped_column(Boolean, default=False) + mode: Mapped[str] = mapped_column(String(20), default="immediate") # immediate|review|print_queue|proxy + model: Mapped[str | None] = mapped_column(String(50), nullable=True) # SSDP model code (server mode) + access_code: Mapped[str | None] = mapped_column(String(8), nullable=True) # 8 chars (server mode) + target_printer_id: Mapped[int | None] = mapped_column( + Integer, ForeignKey("printers.id", ondelete="SET NULL"), nullable=True + ) # proxy mode + bind_ip: Mapped[str | None] = mapped_column(String(45), nullable=True) # dedicated IP (proxy mode) + remote_interface_ip: Mapped[str | None] = mapped_column(String(45), nullable=True) # SSDP advertise IP + serial_suffix: Mapped[str] = mapped_column(String(9), default="391800001") # unique per printer + position: Mapped[int] = mapped_column(Integer, default=0) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now()) + updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now()) diff --git a/backend/app/services/network_utils.py b/backend/app/services/network_utils.py index 3fd937022..5afb84148 100644 --- a/backend/app/services/network_utils.py +++ b/backend/app/services/network_utils.py @@ -1,15 +1,26 @@ """Network utility functions for interface detection.""" import ipaddress +import json import logging +import shutil import socket import struct +import subprocess logger = logging.getLogger(__name__) # Interfaces to exclude from selection EXCLUDED_INTERFACE_PREFIXES = ("lo", "docker", "br-", "veth", "virbr") +# Resolve full path to `ip` command (may not be in PATH for service users) +_IP_CMD: str | None = shutil.which("ip") or shutil.which("ip", path="/usr/sbin:/sbin:/usr/bin:/bin") + + +def _is_excluded(name: str) -> bool: + """Check if an interface name should be excluded.""" + return any(name.startswith(prefix) for prefix in EXCLUDED_INTERFACE_PREFIXES) + def get_network_interfaces() -> list[dict]: """Get all network interfaces with their IPs and subnets. @@ -26,7 +37,7 @@ def get_network_interfaces() -> list[dict]: name = iface[1] # Skip excluded interfaces - if any(name.startswith(prefix) for prefix in EXCLUDED_INTERFACE_PREFIXES): + if _is_excluded(name): continue try: @@ -76,6 +87,88 @@ def get_network_interfaces() -> list[dict]: return interfaces +def get_all_interface_ips() -> list[dict]: + """Get all IPs (primary + aliases) for all non-excluded interfaces. + + Uses `ip -j addr show` to see secondary/alias IPs that ioctl misses. + Falls back to ioctl-based get_network_interfaces() if `ip` is unavailable. + + Returns: + List of dicts with name, ip, netmask, subnet, is_alias, label + """ + if not _IP_CMD: + logger.debug("ip command not found, using ioctl fallback") + return _fallback_get_all_ips() + + try: + result = subprocess.run( + [_IP_CMD, "-j", "addr", "show"], + capture_output=True, + text=True, + timeout=5, + ) + if result.returncode != 0: + logger.warning("ip addr show failed: %s", result.stderr) + return _fallback_get_all_ips() + + interfaces_data = json.loads(result.stdout) + except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError) as e: + logger.warning("Failed to run ip -j addr show: %s", e) + return _fallback_get_all_ips() + + entries = [] + for iface in interfaces_data: + ifname = iface.get("ifname", "") + if _is_excluded(ifname): + continue + + ipv4_count = 0 + for addr_info in iface.get("addr_info", []): + if addr_info.get("family") != "inet": + continue + + ip = addr_info.get("local", "") + prefix = addr_info.get("prefixlen", 24) + label = addr_info.get("label", ifname) + + try: + network = ipaddress.IPv4Network(f"{ip}/{prefix}", strict=False) + netmask = str(network.netmask) + except ValueError: + continue + + # An alias has ":" in label (e.g. eth0:vp1) or is not the first IPv4 + is_alias = ":" in label or ipv4_count > 0 + + entries.append( + { + "name": ifname, + "ip": ip, + "netmask": netmask, + "subnet": str(network), + "is_alias": is_alias, + "label": label, + } + ) + ipv4_count += 1 + + # Sort: primary IPs first per interface, then by interface name + entries.sort(key=lambda e: (e["name"], e["is_alias"], e["ip"])) + return entries + + +def _fallback_get_all_ips() -> list[dict]: + """Fallback: wrap get_network_interfaces() result with alias fields.""" + return [ + { + **iface, + "is_alias": False, + "label": iface["name"], + } + for iface in get_network_interfaces() + ] + + def find_interface_for_ip(target_ip: str) -> dict | None: """Find which interface is on the same subnet as the target IP. @@ -91,9 +184,11 @@ def find_interface_for_ip(target_ip: str) -> dict | None: logger.error("Invalid target IP: %s", target_ip) return None - interfaces = get_network_interfaces() + interfaces = get_all_interface_ips() for iface in interfaces: + if iface.get("is_alias"): + continue try: network = ipaddress.IPv4Network(iface["subnet"], strict=False) if target in network: diff --git a/backend/app/services/virtual_printer/bind_server.py b/backend/app/services/virtual_printer/bind_server.py index 2ff08e3cf..fcea2fc45 100644 --- a/backend/app/services/virtual_printer/bind_server.py +++ b/backend/app/services/virtual_printer/bind_server.py @@ -39,11 +39,13 @@ class BindServer: model: str, name: str, version: str = "01.00.00.00", + bind_address: str = "0.0.0.0", # nosec B104 ): self.serial = serial self.model = model self.name = name self.version = version + self.bind_address = bind_address self._server: asyncio.Server | None = None self._running = False @@ -59,7 +61,7 @@ class BindServer: self._running = True self._server = await asyncio.start_server( self._handle_client, - "0.0.0.0", # nosec B104 + self.bind_address, BIND_PORT, ) diff --git a/backend/app/services/virtual_printer/certificate.py b/backend/app/services/virtual_printer/certificate.py index 98a0f83c5..7f30a9944 100644 --- a/backend/app/services/virtual_printer/certificate.py +++ b/backend/app/services/virtual_printer/certificate.py @@ -48,17 +48,20 @@ class CertificateService: - Printer cert with CN=serial_number, signed by the CA """ - def __init__(self, cert_dir: Path, serial: str = DEFAULT_SERIAL): + def __init__(self, cert_dir: Path, serial: str = DEFAULT_SERIAL, shared_ca_dir: Path | None = None): """Initialize the certificate service. Args: - cert_dir: Directory to store certificates + cert_dir: Directory to store per-instance certificates serial: Serial number to use as CN in printer certificate + shared_ca_dir: If set, CA cert/key are read from this directory + instead of cert_dir (for multi-instance shared CA) """ self.cert_dir = cert_dir self.serial = serial - self.ca_cert_path = cert_dir / "bbl_ca.crt" - self.ca_key_path = cert_dir / "bbl_ca.key" + ca_dir = shared_ca_dir or cert_dir + self.ca_cert_path = ca_dir / "bbl_ca.crt" + self.ca_key_path = ca_dir / "bbl_ca.key" self.cert_path = cert_dir / "virtual_printer.crt" self.key_path = cert_dir / "virtual_printer.key" diff --git a/backend/app/services/virtual_printer/ftp_server.py b/backend/app/services/virtual_printer/ftp_server.py index 635bf32d5..0d2aea62d 100644 --- a/backend/app/services/virtual_printer/ftp_server.py +++ b/backend/app/services/virtual_printer/ftp_server.py @@ -34,6 +34,7 @@ class FTPSession: on_file_received: Callable[[Path, str], None] | None, passive_port_range: tuple[int, int] = (50000, 50100), pasv_address: str = "", + bind_address: str = "0.0.0.0", # nosec B104 ): self.reader = reader self.writer = writer @@ -43,6 +44,7 @@ class FTPSession: self.on_file_received = on_file_received self.passive_port_range = passive_port_range self.pasv_address = pasv_address + self.bind_address = bind_address self.authenticated = False self.username: str | None = None @@ -218,7 +220,7 @@ class FTPSession: try: self.data_server = await asyncio.start_server( self._handle_data_connection, - "0.0.0.0", # nosec B104 + self.bind_address, port, ssl=self.ssl_context, ) @@ -524,6 +526,7 @@ class VirtualPrinterFTPServer: key_path: Path, port: int = FTP_PORT, on_file_received: Callable[[Path, str], None] | None = None, + bind_address: str = "0.0.0.0", # nosec B104 ): """Initialize the FTPS server. @@ -534,6 +537,7 @@ class VirtualPrinterFTPServer: key_path: Path to TLS private key file port: Port to listen on (default 990) on_file_received: Callback when file upload completes (path, source_ip) + bind_address: IP address to bind to (default 0.0.0.0) """ self.upload_dir = upload_dir self.access_code = access_code @@ -541,6 +545,7 @@ class VirtualPrinterFTPServer: self.key_path = key_path self.port = port self.on_file_received = on_file_received + self.bind_address = bind_address self._server: asyncio.Server | None = None self._running = False self._ssl_context: ssl.SSLContext | None = None @@ -575,7 +580,7 @@ class VirtualPrinterFTPServer: # Create server with SSL - TLS handshake happens before any FTP data self._server = await asyncio.start_server( self._handle_client, - "0.0.0.0", # nosec B104 + self.bind_address, self.port, ssl=self._ssl_context, # This makes it implicit FTPS! ) @@ -619,6 +624,7 @@ class VirtualPrinterFTPServer: on_file_received=self.on_file_received, passive_port_range=(self.PASSIVE_PORT_MIN, self.PASSIVE_PORT_MAX), pasv_address=self._pasv_address, + bind_address=self.bind_address, ) # Track the session task so we can cancel it on stop diff --git a/backend/app/services/virtual_printer/manager.py b/backend/app/services/virtual_printer/manager.py index 095eda0b0..9452b1628 100644 --- a/backend/app/services/virtual_printer/manager.py +++ b/backend/app/services/virtual_printer/manager.py @@ -1,10 +1,7 @@ """Virtual Printer Manager - coordinates SSDP, MQTT, and FTP services. -Supports multiple modes: -- immediate: Archive uploads immediately -- review: Queue uploads for user review before archiving -- print_queue: Archive and add to print queue (unassigned) -- proxy: Transparent TCP proxy to a real printer (for remote slicer access) +Each virtual printer runs its own independent services (FTP, MQTT, SSDP, Bind) +bound to its dedicated IP address, regardless of mode. """ import asyncio @@ -78,497 +75,143 @@ MODEL_SERIAL_PREFIXES = { DEFAULT_VIRTUAL_PRINTER_MODEL = "3DPrinter-X1-Carbon" # X1C -class VirtualPrinterManager: - """Manages the virtual printer lifecycle and coordinates all services.""" +def _get_serial_for_model(model: str, serial_suffix: str) -> str: + """Get serial number for the given model and suffix.""" + prefix = MODEL_SERIAL_PREFIXES.get(model, "00M09A") + return f"{prefix}{serial_suffix}" - # Fixed configuration - PRINTER_NAME = "Bambuddy" - SERIAL_SUFFIX = "391800001" # Fixed suffix for virtual printer - def __init__(self): - """Initialize the virtual printer manager.""" - self._session_factory: Callable | None = None - self._enabled = False - self._access_code = "" - self._mode = "immediate" - self._model = DEFAULT_VIRTUAL_PRINTER_MODEL - self._target_printer_ip = "" # For proxy mode - self._target_printer_serial = "" # For proxy mode (real printer's serial) - self._remote_interface_ip = "" # For proxy mode SSDP (LAN B - slicer network) +class VirtualPrinterInstance: + """Per-printer state and file handling logic. - # Service instances - self._ssdp: VirtualPrinterSSDPServer | None = None - self._ssdp_proxy: SSDPProxy | None = None - self._ftp: VirtualPrinterFTPServer | None = None - self._mqtt: SimpleMQTTServer | None = None - self._bind: BindServer | None = None # For server mode (bind/detect on port 3000) - self._proxy: SlicerProxyManager | None = None # For proxy mode + Each instance represents one virtual printer with its own config, + upload directory, certificates, and file handling mode. + """ - # Background tasks - self._tasks: list[asyncio.Task] = [] - - # Directories - self._base_dir = app_settings.base_dir / "virtual_printer" - self._upload_dir = self._base_dir / "uploads" - self._cert_dir = self._base_dir / "certs" - - # Create directories early to avoid permission issues later - # If running in Docker, these need to be on a writable volume - self._ensure_directories() - - # Certificate service - self._cert_service = CertificateService(self._cert_dir) - - # Track pending uploads for MQTT correlation - self._pending_files: dict[str, Path] = {} - - def _ensure_directories(self) -> None: - """Create and verify virtual printer directories are writable. - - Creates all required directories at startup to catch permission - issues early rather than when the user tries to enable features. - """ - dirs_to_create = [ - self._base_dir, - self._upload_dir, - self._upload_dir / "cache", - self._cert_dir, - ] - - logger.info("Checking virtual printer directories in %s", self._base_dir) - - for dir_path in dirs_to_create: - try: - dir_path.mkdir(parents=True, exist_ok=True) - except PermissionError: - logger.error( - f"Cannot create directory {dir_path}: Permission denied. " - f"For Docker: ensure the data volume is writable by the container user. " - f"For bare metal: run 'sudo chown -R $(whoami) {self._base_dir}'" - ) - continue - - # Verify directory is writable by attempting to create a test file - test_file = dir_path / ".write_test" - try: - test_file.touch() - test_file.unlink(missing_ok=True) - except PermissionError: - logger.error( - f"Directory {dir_path} exists but is not writable. " - f"For Docker: ensure the data volume is writable by the container user (uid/gid). " - f"For bare metal: run 'sudo chown -R $(whoami) {self._base_dir}'" - ) - - def _get_serial_for_model(self, model: str) -> str: - """Get appropriate serial number for the given model. - - Args: - model: SSDP model code (e.g., 'BL-P001', 'C11') - - Returns: - Serial number with correct prefix for the model - """ - prefix = MODEL_SERIAL_PREFIXES.get(model, "00M09A") - return f"{prefix}{self.SERIAL_SUFFIX}" - - @property - def printer_serial(self) -> str: - """Get the current printer serial number based on model.""" - return self._get_serial_for_model(self._model) - - def set_session_factory(self, session_factory: Callable) -> None: - """Set the database session factory. - - Args: - session_factory: Async context manager for database sessions - """ + def __init__( + self, + *, + vp_id: int, + name: str, + mode: str, + model: str, + access_code: str, + serial_suffix: str, + target_printer_ip: str = "", + target_printer_serial: str = "", + bind_ip: str = "", + remote_interface_ip: str = "", + base_dir: Path, + session_factory: Callable | None = None, + ): + self.id = vp_id + self.name = name + self.mode = mode + self.model = model + self.access_code = access_code + self.serial_suffix = serial_suffix + self.target_printer_ip = target_printer_ip + self.target_printer_serial = target_printer_serial + self.bind_ip = bind_ip + self.remote_interface_ip = remote_interface_ip self._session_factory = session_factory + # Directories + self.upload_dir = base_dir / "uploads" / str(vp_id) + self.cert_dir = base_dir / "certs" / str(vp_id) + shared_ca_dir = base_dir / "certs" + + # Ensure directories exist + self.upload_dir.mkdir(parents=True, exist_ok=True) + (self.upload_dir / "cache").mkdir(exist_ok=True) + self.cert_dir.mkdir(parents=True, exist_ok=True) + + # Certificate service (shared CA, per-instance printer cert) + self._cert_service = CertificateService( + cert_dir=self.cert_dir, + serial=self.serial, + shared_ca_dir=shared_ca_dir, + ) + + # Pending files for MQTT correlation + self._pending_files: dict[str, Path] = {} + + # Per-instance services + self._proxy: SlicerProxyManager | None = None + self._ftp: VirtualPrinterFTPServer | None = None + self._mqtt: SimpleMQTTServer | None = None + self._bind: BindServer | None = None + self._ssdp: VirtualPrinterSSDPServer | None = None + self._ssdp_proxy: SSDPProxy | None = None + self._tasks: list[asyncio.Task] = [] + @property - def is_enabled(self) -> bool: - """Check if virtual printer is enabled.""" - return self._enabled + def serial(self) -> str: + """Full serial number for this virtual printer.""" + return _get_serial_for_model(self.model or DEFAULT_VIRTUAL_PRINTER_MODEL, self.serial_suffix) + + @property + def cert_path(self) -> Path: + return self._cert_service.cert_path + + @property + def key_path(self) -> Path: + return self._cert_service.key_path + + @property + def is_proxy(self) -> bool: + return self.mode == "proxy" @property def is_running(self) -> bool: - """Check if virtual printer services are running.""" return len(self._tasks) > 0 and all(not t.done() for t in self._tasks) - async def configure( - self, - enabled: bool, - access_code: str = "", - mode: str = "immediate", - model: str = "", - target_printer_ip: str = "", - target_printer_serial: str = "", - remote_interface_ip: str = "", - ) -> None: - """Configure and start/stop virtual printer. - - Args: - enabled: Whether to enable the virtual printer - access_code: Authentication password for slicer connections - mode: Archive mode - 'immediate', 'review', 'print_queue', or 'proxy' - model: SSDP model code (e.g., 'BL-P001' for X1C) - target_printer_ip: Target printer IP for proxy mode - target_printer_serial: Target printer serial for proxy mode - remote_interface_ip: IP of interface on slicer network (LAN B) for SSDP proxy - """ - # Proxy mode has different requirements - if mode == "proxy": - if enabled and not target_printer_ip: - raise ValueError("Target printer IP is required for proxy mode") - # Access code not required for proxy mode (uses printer's credentials) - else: - if enabled and not access_code: - raise ValueError("Access code is required when enabling virtual printer") - - # Validate model if provided - new_model = model if model and model in VIRTUAL_PRINTER_MODELS else self._model - model_changed = new_model != self._model - mode_changed = mode != self._mode - target_changed = target_printer_ip != self._target_printer_ip - serial_changed = target_printer_serial != self._target_printer_serial - remote_iface_changed = remote_interface_ip != self._remote_interface_ip - old_mode = self._mode - - logger.debug( - f"configure() called: enabled={enabled}, self._enabled={self._enabled}, " - f"mode={mode}, old_mode={old_mode}, model={model}, new_model={new_model}, " - f"target_printer_ip={target_printer_ip}, target_printer_serial={target_printer_serial}, " - f"remote_interface_ip={remote_interface_ip}" - ) - - self._access_code = access_code - self._mode = mode - self._model = new_model - self._target_printer_ip = target_printer_ip - self._target_printer_serial = target_printer_serial - self._remote_interface_ip = remote_interface_ip - - needs_restart = ( - model_changed - or mode_changed - or remote_iface_changed - or (mode == "proxy" and (target_changed or serial_changed)) - ) - - if enabled and not self._enabled: - logger.info("Starting virtual printer (was disabled)") - await self._start() - elif not enabled and self._enabled: - logger.info("Stopping virtual printer (was enabled)") - await self._stop() - elif enabled and self._enabled and needs_restart: - # Configuration changed while running - restart services - logger.info("Configuration changed (mode=%s→%s), restarting...", old_mode, mode) - await self._stop() - # Give time for ports to be released - await asyncio.sleep(0.5) - await self._start() - logger.info("Virtual printer restarted with new configuration") - else: - logger.debug("No state change needed (enabled=%s, self._enabled=%s)", enabled, self._enabled) - - self._enabled = enabled - - async def _start(self) -> None: - """Start all virtual printer services.""" - logger.info("Starting virtual printer services (mode=%s)...", self._mode) - - # Proxy mode uses different services - if self._mode == "proxy": - await self._start_proxy_mode() - return - - # Standard modes (immediate, review, print_queue) use FTP/MQTT servers - await self._start_server_mode() - - async def _start_proxy_mode(self) -> None: - """Start virtual printer in proxy mode (TLS terminating relay).""" - logger.info("Starting proxy mode to %s", self._target_printer_ip) - - # In proxy mode, use the REAL printer's serial number - # This ensures MQTT topic subscriptions match the real printer's topics - proxy_serial = self._target_printer_serial or self.printer_serial - logger.info("Proxy mode using serial: %s", proxy_serial) - - # Update certificate service with the real printer's serial - self._cert_service.serial = proxy_serial - - # Regenerate printer cert if needed (CA is preserved) - # Include remote interface IP in SAN so slicer TLS succeeds - additional_ips = [] - if self._remote_interface_ip: - additional_ips.append(self._remote_interface_ip) + def generate_certificates(self) -> tuple[Path, Path]: + """Generate certificates for this instance.""" + self._cert_service.serial = self.serial if not self.is_proxy else (self.target_printer_serial or self.serial) + additional_ips = [self.remote_interface_ip] if self.remote_interface_ip else None + if self.bind_ip: + additional_ips = additional_ips or [] + additional_ips.append(self.bind_ip) self._cert_service.delete_printer_certificate() - cert_path, key_path = self._cert_service.generate_certificates(additional_ips=additional_ips or None) - logger.info("Generated certificate for proxy serial: %s", proxy_serial) + return self._cert_service.generate_certificates(additional_ips=additional_ips) - # Initialize TLS proxy with our certificates - self._proxy = SlicerProxyManager( - target_host=self._target_printer_ip, - cert_path=cert_path, - key_path=key_path, - on_activity=self._on_proxy_activity, - ) + # -- File handling callbacks -- - # Start services as background tasks - async def run_with_logging(coro, name): - try: - await coro - except Exception as e: - logger.error("Virtual printer %s failed: %s", name, e) + async def on_file_received(self, file_path: Path, source_ip: str) -> None: + """Handle file upload completion from FTP.""" + logger.info("[VP %s] Received file: %s from %s", self.name, file_path.name, source_ip) - self._tasks = [] - - # SSDP setup: use SSDPProxy if remote interface is configured - # Local interface is auto-detected from target printer IP - if self._remote_interface_ip: - # Auto-detect local interface based on target printer IP - from backend.app.services.network_utils import find_interface_for_ip - - local_iface = find_interface_for_ip(self._target_printer_ip) - if local_iface: - local_interface_ip = local_iface["ip"] - logger.info( - f"SSDP proxy mode: LAN A ({local_interface_ip}, auto-detected) -> LAN B ({self._remote_interface_ip})" - ) - self._ssdp_proxy = SSDPProxy( - local_interface_ip=local_interface_ip, - remote_interface_ip=self._remote_interface_ip, - target_printer_ip=self._target_printer_ip, - ) - self._tasks.append( - asyncio.create_task( - run_with_logging(self._ssdp_proxy.start(), "SSDP Proxy"), - name="virtual_printer_ssdp_proxy", - ) - ) - else: - logger.warning( - f"Could not auto-detect local interface for printer {self._target_printer_ip}, " - "falling back to single-interface SSDP" - ) - self._start_fallback_ssdp(proxy_serial, run_with_logging) - else: - # Single interface: broadcast SSDP on same network (fallback) - self._start_fallback_ssdp(proxy_serial, run_with_logging) - - # Add TLS proxy task - self._tasks.append( - asyncio.create_task( - run_with_logging(self._proxy.start(), "Proxy"), - name="virtual_printer_proxy", - ) - ) - - logger.info( - "Virtual printer proxy target: FTP %s:%d, MQTT %s:%d, Bind %s:%d", - self._target_printer_ip, - SlicerProxyManager.PRINTER_FTP_PORT, - self._target_printer_ip, - SlicerProxyManager.PRINTER_MQTT_PORT, - self._target_printer_ip, - SlicerProxyManager.PRINTER_BIND_PORT, - ) - - def _start_fallback_ssdp(self, proxy_serial: str, run_with_logging) -> None: - """Start single-interface SSDP server as fallback.""" - logger.info("SSDP broadcast mode (single interface)") - self._ssdp = VirtualPrinterSSDPServer( - name=f"{self.PRINTER_NAME} (Proxy)", - serial=proxy_serial, - model=self._model, - ) - self._tasks.append( - asyncio.create_task( - run_with_logging(self._ssdp.start(), "SSDP"), - name="virtual_printer_ssdp", - ) - ) - - async def _start_server_mode(self) -> None: - """Start virtual printer in server mode (FTP/MQTT servers).""" - # Update certificate service with current serial (based on model) - current_serial = self.printer_serial - self._cert_service.serial = current_serial - - # Regenerate printer cert if serial changed (CA is preserved) - # Include remote interface IP in SAN so slicer TLS succeeds on that interface - additional_ips = [] - if self._remote_interface_ip: - additional_ips.append(self._remote_interface_ip) - self._cert_service.delete_printer_certificate() - cert_path, key_path = self._cert_service.generate_certificates(additional_ips=additional_ips or None) - logger.info("Generated certificate for serial: %s", current_serial) - - # Create directories - self._upload_dir.mkdir(parents=True, exist_ok=True) - (self._upload_dir / "cache").mkdir(exist_ok=True) - - # Initialize services - self._ssdp = VirtualPrinterSSDPServer( - name=self.PRINTER_NAME, - serial=self.printer_serial, - model=self._model, - advertise_ip=self._remote_interface_ip, - ) - - self._ftp = VirtualPrinterFTPServer( - upload_dir=self._upload_dir, - access_code=self._access_code, - cert_path=cert_path, - key_path=key_path, - on_file_received=self._on_file_received, - ) - - self._mqtt = SimpleMQTTServer( - serial=self.printer_serial, - access_code=self._access_code, - cert_path=cert_path, - key_path=key_path, - on_print_command=self._on_print_command, - ) - - # Bind server responds to slicer detect/bind requests on port 3000 - self._bind = BindServer( - serial=self.printer_serial, - model=self._model, - name=self.PRINTER_NAME, - ) - - # Start services as background tasks - # Wrap each in error handler so one failure doesn't stop others - async def run_with_logging(coro, name): - try: - await coro - except Exception as e: - logger.error("Virtual printer %s failed: %s", name, e) - - self._tasks = [ - asyncio.create_task(run_with_logging(self._ssdp.start(), "SSDP"), name="virtual_printer_ssdp"), - asyncio.create_task(run_with_logging(self._ftp.start(), "FTP"), name="virtual_printer_ftp"), - asyncio.create_task(run_with_logging(self._mqtt.start(), "MQTT"), name="virtual_printer_mqtt"), - asyncio.create_task(run_with_logging(self._bind.start(), "Bind"), name="virtual_printer_bind"), - ] - - logger.info("Virtual printer '%s' started (serial: %s)", self.PRINTER_NAME, self.printer_serial) - - def _on_proxy_activity(self, name: str, message: str) -> None: - """Handle proxy activity for logging.""" - logger.info("Proxy %s: %s", name, message) - - async def _stop(self) -> None: - """Stop all virtual printer services.""" - logger.info("Stopping virtual printer services...") - - # Stop services first - this closes servers and cancels active sessions - if self._ftp: - await self._ftp.stop() - self._ftp = None - - if self._mqtt: - await self._mqtt.stop() - self._mqtt = None - - if self._ssdp: - await self._ssdp.stop() - self._ssdp = None - - if self._bind: - await self._bind.stop() - self._bind = None - - if self._ssdp_proxy: - await self._ssdp_proxy.stop() - self._ssdp_proxy = None - - if self._proxy: - await self._proxy.stop() - self._proxy = None - - # Cancel remaining tasks with short timeout - for task in self._tasks: - task.cancel() - - if self._tasks: - try: - await asyncio.wait_for(asyncio.gather(*self._tasks, return_exceptions=True), timeout=1.0) - except TimeoutError: - logger.debug("Some tasks didn't stop in time") - - self._tasks = [] - - logger.info("Virtual printer stopped") - - async def _on_file_received(self, file_path: Path, source_ip: str) -> None: - """Handle file upload completion from FTP. - - Args: - file_path: Path to uploaded file - source_ip: IP address of the uploading slicer - """ - logger.info("Virtual printer received file: %s from %s", file_path.name, source_ip) - - # Store file reference for MQTT correlation self._pending_files[file_path.name] = file_path - # Handle based on mode: - # - immediate: archive right away - # - review: create pending upload record for user review before archiving - # - print_queue: archive and add to print queue (unassigned) - if self._mode == "immediate": + if self.mode == "immediate": await self._archive_file(file_path, source_ip) - elif self._mode == "print_queue": + elif self.mode == "print_queue": await self._add_to_print_queue(file_path, source_ip) else: - # "review" mode (or legacy "queue" mode) await self._queue_file(file_path, source_ip) - # Reset MQTT status back to IDLE after file processing - # This tells the slicer the printer is done with the file + # Reset MQTT status back to IDLE if self._mqtt and file_path.suffix.lower() == ".3mf": self._mqtt.set_gcode_state("IDLE") - async def _on_print_command(self, filename: str, data: dict) -> None: - """Handle print command from MQTT. - - In a real printer, this would start the print. For virtual printer, - we just log it since archiving is handled by file upload. - - Args: - filename: Name of the file to print - data: Print command data (contains settings like timelapse, bed_leveling, etc.) - """ - logger.info("Virtual printer received print command for: %s", filename) - logger.debug("Print command data: %s", data) - - # The file should already be archived from FTP upload - # This command just confirms the slicer's intent to "print" + async def on_print_command(self, filename: str, data: dict) -> None: + """Handle print command from MQTT.""" + logger.info("[VP %s] Print command for: %s", self.name, filename) async def _archive_file(self, file_path: Path, source_ip: str) -> None: - """Archive file immediately. - - Args: - file_path: Path to the 3MF file - source_ip: IP address of uploader - """ + """Archive file immediately.""" if not self._session_factory: logger.error("Cannot archive: no database session factory configured") return - # Only archive 3MF files if file_path.suffix.lower() != ".3mf": logger.debug("Skipping non-3MF file: %s", file_path.name) - # Remove from pending and clean up self._pending_files.pop(file_path.name, None) try: file_path.unlink() except OSError: - pass # Best-effort removal of non-3MF file; may already be gone + pass return try: @@ -576,10 +219,8 @@ class VirtualPrinterManager: async with self._session_factory() as db: service = ArchiveService(db) - - # Archive the print archive = await service.archive_print( - printer_id=None, # No physical printer + printer_id=None, source_file=file_path, print_data={ "status": "archived", @@ -587,42 +228,30 @@ class VirtualPrinterManager: "source_ip": source_ip, }, ) - if archive: - logger.info("Archived virtual printer upload: %s - %s", archive.id, archive.print_name) - - # Clean up uploaded file (it's now copied to archive) + logger.info("[VP %s] Archived: %s - %s", self.name, archive.id, archive.print_name) try: file_path.unlink() except OSError: - pass # Best-effort cleanup of uploaded file after archiving - # Remove from pending + pass self._pending_files.pop(file_path.name, None) else: logger.error("Failed to archive file: %s", file_path.name) - - except Exception as e: # Mixed async DB + archive operations + except Exception as e: logger.error("Error archiving file: %s", e) async def _queue_file(self, file_path: Path, source_ip: str) -> None: - """Queue file for user review. - - Args: - file_path: Path to the 3MF file - source_ip: IP address of uploader - """ + """Queue file for user review.""" if not self._session_factory: logger.error("Cannot queue: no database session factory configured") return - # Only queue 3MF files if file_path.suffix.lower() != ".3mf": - logger.debug("Skipping non-3MF file: %s", file_path.name) self._pending_files.pop(file_path.name, None) try: file_path.unlink() except OSError: - pass # Best-effort removal of non-3MF file; may already be gone + pass return try: @@ -639,34 +268,23 @@ class VirtualPrinterManager: ) db.add(pending) await db.commit() - - logger.info("Queued virtual printer upload: %s - %s", pending.id, file_path.name) - - # Remove from pending files dict + logger.info("[VP %s] Queued: %s - %s", self.name, pending.id, file_path.name) self._pending_files.pop(file_path.name, None) - except Exception as e: logger.error("Error queueing file: %s", e) async def _add_to_print_queue(self, file_path: Path, source_ip: str) -> None: - """Archive file and add to print queue (unassigned). - - Args: - file_path: Path to the 3MF file - source_ip: IP address of uploader - """ + """Archive file and add to print queue (unassigned).""" if not self._session_factory: logger.error("Cannot add to print queue: no database session factory configured") return - # Only process 3MF files if file_path.suffix.lower() != ".3mf": - logger.debug("Skipping non-3MF file: %s", file_path.name) self._pending_files.pop(file_path.name, None) try: file_path.unlink() except OSError: - pass # Best-effort removal of non-3MF file; may already be gone + pass return try: @@ -675,10 +293,8 @@ class VirtualPrinterManager: async with self._session_factory() as db: service = ArchiveService(db) - - # First, archive the print archive = await service.archive_print( - printer_id=None, # No physical printer + printer_id=None, source_file=file_path, print_data={ "status": "archived", @@ -686,63 +302,426 @@ class VirtualPrinterManager: "source_ip": source_ip, }, ) - if archive: - logger.info("Archived virtual printer upload: %s - %s", archive.id, archive.print_name) - - # Now add to print queue (unassigned) + logger.info("[VP %s] Archived: %s - %s", self.name, archive.id, archive.print_name) queue_item = PrintQueueItem( - printer_id=None, # Unassigned - user will assign later + printer_id=None, archive_id=archive.id, - position=1, # Will be adjusted when assigned to a printer + position=1, status="pending", ) db.add(queue_item) await db.commit() - - logger.info( - "Added to print queue (unassigned): queue_id=%s, archive_id=%s", queue_item.id, archive.id - ) - - # Clean up uploaded file (it's now copied to archive) + logger.info("[VP %s] Added to queue: %s", self.name, queue_item.id) try: file_path.unlink() except OSError: - pass # Best-effort cleanup of uploaded file after archiving and queuing - # Remove from pending + pass self._pending_files.pop(file_path.name, None) else: logger.error("Failed to archive file: %s", file_path.name) - - except Exception as e: # Mixed async DB + archive + queue operations + except Exception as e: logger.error("Error adding to print queue: %s", e) - def get_status(self) -> dict: - """Get virtual printer status. + # -- Service lifecycle -- - Returns: - Status dictionary with enabled, running, mode, etc. - """ - status = { - "enabled": self._enabled, + async def start_server(self) -> None: + """Start server-mode services (FTP, MQTT, SSDP, Bind) on this VP's bind_ip.""" + logger.info("[VP %s] Starting server-mode services on %s", self.name, self.bind_ip) + + cert_path, key_path = self.generate_certificates() + bind_addr = self.bind_ip or "0.0.0.0" # nosec B104 + + async def run_with_logging(coro, svc_name): + try: + await coro + except Exception as e: + logger.error("[VP %s] %s failed: %s", self.name, svc_name, e) + + self._tasks = [] + + # FTP server + self._ftp = VirtualPrinterFTPServer( + upload_dir=self.upload_dir, + access_code=self.access_code, + cert_path=cert_path, + key_path=key_path, + on_file_received=self.on_file_received, + bind_address=bind_addr, + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._ftp.start(), "FTP"), + name=f"vp_{self.id}_ftp", + ) + ) + + # MQTT server + self._mqtt = SimpleMQTTServer( + serial=self.serial, + access_code=self.access_code, + cert_path=cert_path, + key_path=key_path, + on_print_command=self.on_print_command, + model=self.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + bind_address=bind_addr, + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._mqtt.start(), "MQTT"), + name=f"vp_{self.id}_mqtt", + ) + ) + + # Bind server + self._bind = BindServer( + serial=self.serial, + model=self.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + name=self.name, + bind_address=bind_addr, + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._bind.start(), "Bind"), + name=f"vp_{self.id}_bind", + ) + ) + + # SSDP server + self._ssdp = VirtualPrinterSSDPServer( + name=self.name, + serial=self.serial, + model=self.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + advertise_ip=self.remote_interface_ip or self.bind_ip or "", + bind_ip=bind_addr, + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._ssdp.start(), "SSDP"), + name=f"vp_{self.id}_ssdp", + ) + ) + + logger.info("[VP %s] Server-mode services started on %s", self.name, bind_addr) + + async def stop_server(self) -> None: + """Stop server-mode services.""" + if self._ftp: + await self._ftp.stop() + self._ftp = None + if self._mqtt: + await self._mqtt.stop() + self._mqtt = None + if self._bind: + await self._bind.stop() + self._bind = None + if self._ssdp: + await self._ssdp.stop() + self._ssdp = None + await self._cancel_tasks() + + async def start_proxy(self) -> None: + """Start proxy mode services for this instance.""" + logger.info("[VP %s] Starting proxy mode to %s", self.name, self.target_printer_ip) + + cert_path, key_path = self.generate_certificates() + + self._proxy = SlicerProxyManager( + target_host=self.target_printer_ip, + cert_path=cert_path, + key_path=key_path, + on_activity=lambda n, m: logger.info("[VP %s] Proxy %s: %s", self.name, n, m), + bind_address=self.bind_ip or "0.0.0.0", # nosec B104 + ) + + async def run_with_logging(coro, svc_name): + try: + await coro + except Exception as e: + logger.error("[VP %s] %s failed: %s", self.name, svc_name, e) + + self._tasks = [] + + # SSDP for proxy + proxy_serial = self.target_printer_serial or self.serial + if self.remote_interface_ip: + from backend.app.services.network_utils import find_interface_for_ip + + local_iface = find_interface_for_ip(self.target_printer_ip) + if local_iface: + self._ssdp_proxy = SSDPProxy( + local_interface_ip=local_iface["ip"], + remote_interface_ip=self.remote_interface_ip, + target_printer_ip=self.target_printer_ip, + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._ssdp_proxy.start(), "SSDP Proxy"), + name=f"vp_{self.id}_ssdp_proxy", + ) + ) + else: + self._start_fallback_ssdp(proxy_serial, run_with_logging) + else: + self._start_fallback_ssdp(proxy_serial, run_with_logging) + + self._tasks.append( + asyncio.create_task( + run_with_logging(self._proxy.start(), "Proxy"), + name=f"vp_{self.id}_proxy", + ) + ) + + def _start_fallback_ssdp(self, proxy_serial: str, run_with_logging) -> None: + """Start single-interface SSDP server as fallback for proxy mode.""" + self._ssdp = VirtualPrinterSSDPServer( + name=f"{self.name} (Proxy)", + serial=proxy_serial, + model=self.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + advertise_ip=self.bind_ip or "", + bind_ip=self.bind_ip or "", + ) + self._tasks.append( + asyncio.create_task( + run_with_logging(self._ssdp.start(), "SSDP"), + name=f"vp_{self.id}_ssdp", + ) + ) + + async def stop_proxy(self) -> None: + """Stop proxy mode services for this instance.""" + if self._proxy: + await self._proxy.stop() + self._proxy = None + if self._ssdp: + await self._ssdp.stop() + self._ssdp = None + if self._ssdp_proxy: + await self._ssdp_proxy.stop() + self._ssdp_proxy = None + await self._cancel_tasks() + + async def _cancel_tasks(self) -> None: + """Cancel all running tasks and wait for cleanup.""" + for task in self._tasks: + task.cancel() + if self._tasks: + try: + await asyncio.wait_for(asyncio.gather(*self._tasks, return_exceptions=True), timeout=1.0) + except TimeoutError: + pass + self._tasks = [] + + def get_status(self) -> dict: + """Get status for this instance.""" + status: dict = { "running": self.is_running, - "mode": self._mode, - "name": self.PRINTER_NAME, - "serial": self.printer_serial, - "model": self._model, - "model_name": VIRTUAL_PRINTER_MODELS.get(self._model, self._model), "pending_files": len(self._pending_files), } - - # Add proxy-specific status - if self._mode == "proxy": - status["target_printer_ip"] = self._target_printer_ip - if self._proxy: - proxy_status = self._proxy.get_status() - status["proxy"] = proxy_status - + if self.is_proxy and self._proxy: + status["proxy"] = self._proxy.get_status() return status +class VirtualPrinterManager: + """Multi-instance virtual printer registry and orchestrator. + + Every VP runs its own independent services on a dedicated bind IP. + """ + + def __init__(self): + self._session_factory: Callable | None = None + self._instances: dict[int, VirtualPrinterInstance] = {} + + # Directories + self._base_dir = app_settings.base_dir / "virtual_printer" + + # Ensure base directories exist + self._ensure_base_directories() + + def _ensure_base_directories(self) -> None: + """Create base directories at startup.""" + for dir_path in [self._base_dir, self._base_dir / "uploads", self._base_dir / "certs"]: + try: + dir_path.mkdir(parents=True, exist_ok=True) + except PermissionError: + logger.error( + f"Cannot create directory {dir_path}: Permission denied. " + f"For Docker: ensure the data volume is writable by the container user. " + f"For bare metal: run 'sudo chown -R $(whoami) {self._base_dir}'" + ) + + def set_session_factory(self, session_factory: Callable) -> None: + """Set the database session factory.""" + self._session_factory = session_factory + + @property + def is_enabled(self) -> bool: + """Check if any virtual printer is running.""" + return len(self._instances) > 0 + + async def sync_from_db(self) -> None: + """Load all VPs from DB, reconcile running state.""" + if not self._session_factory: + logger.warning("Cannot sync virtual printers: no session factory") + return + + from sqlalchemy import select + + from backend.app.models.printer import Printer + from backend.app.models.virtual_printer import VirtualPrinter + + async with self._session_factory() as db: + result = await db.execute( + select(VirtualPrinter).where(VirtualPrinter.enabled == True).order_by(VirtualPrinter.position) # noqa: E712 + ) + enabled_vps = result.scalars().all() + + # Stop instances that are no longer enabled or changed mode + enabled_ids = {vp.id for vp in enabled_vps} + for vp_id in list(self._instances.keys()): + if vp_id not in enabled_ids: + await self.remove_instance(vp_id) + + # Look up printer IPs for proxy VPs + proxy_vps = [vp for vp in enabled_vps if vp.mode == "proxy"] + proxy_ips: dict[int, tuple[str, str]] = {} + if proxy_vps: + async with self._session_factory() as db: + for pvp in proxy_vps: + if pvp.target_printer_id: + result = await db.execute(select(Printer).where(Printer.id == pvp.target_printer_id)) + printer = result.scalar_one_or_none() + if printer: + proxy_ips[pvp.id] = (printer.ip_address, printer.serial_number) + + # Start instances for all enabled VPs (skip already running) + for vp in enabled_vps: + if vp.id in self._instances: + continue + + if vp.mode == "proxy": + ip_info = proxy_ips.get(vp.id) + if not ip_info: + logger.warning("Proxy VP %s: target printer not found, skipping", vp.name) + continue + target_ip, target_serial = ip_info + instance = VirtualPrinterInstance( + vp_id=vp.id, + name=vp.name, + mode=vp.mode, + model=vp.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + access_code=vp.access_code or "", + serial_suffix=vp.serial_suffix, + target_printer_ip=target_ip, + target_printer_serial=target_serial, + bind_ip=vp.bind_ip or "", + remote_interface_ip=vp.remote_interface_ip or "", + base_dir=self._base_dir, + session_factory=self._session_factory, + ) + self._instances[vp.id] = instance + await instance.start_proxy() + logger.info("Started proxy VP: %s → %s", instance.name, target_ip) + else: + instance = VirtualPrinterInstance( + vp_id=vp.id, + name=vp.name, + mode=vp.mode, + model=vp.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + access_code=vp.access_code or "", + serial_suffix=vp.serial_suffix, + bind_ip=vp.bind_ip or "", + remote_interface_ip=vp.remote_interface_ip or "", + base_dir=self._base_dir, + session_factory=self._session_factory, + ) + self._instances[vp.id] = instance + await instance.start_server() + logger.info("Started server-mode VP: %s on %s", instance.name, vp.bind_ip) + + async def remove_instance(self, vp_id: int) -> None: + """Stop and remove a single VP instance.""" + instance = self._instances.pop(vp_id, None) + if instance: + if instance.is_proxy: + await instance.stop_proxy() + else: + await instance.stop_server() + logger.info("Removed VP instance: %s", instance.name) + + async def stop_all(self) -> None: + """Shutdown all virtual printer services.""" + logger.info("Stopping all virtual printer services...") + + for vp_id in list(self._instances.keys()): + await self.remove_instance(vp_id) + + logger.info("All virtual printer services stopped") + + def get_instance(self, vp_id: int) -> VirtualPrinterInstance | None: + """Get a running instance by ID.""" + return self._instances.get(vp_id) + + def get_all_status(self) -> list[dict]: + """Get status for all running instances.""" + return [ + { + "id": inst.id, + "name": inst.name, + "mode": inst.mode, + **inst.get_status(), + } + for inst in self._instances.values() + ] + + # -- Legacy single-printer compat -- + + def get_status(self) -> dict: + """Get status for first virtual printer (backward compat).""" + if self._instances: + first = next(iter(self._instances.values())) + return { + "enabled": True, + "running": first.is_running, + "mode": first.mode, + "name": first.name, + "serial": first.serial, + "model": first.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + "model_name": VIRTUAL_PRINTER_MODELS.get( + first.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + first.model or DEFAULT_VIRTUAL_PRINTER_MODEL, + ), + "pending_files": first.get_status().get("pending_files", 0), + **({"target_printer_ip": first.target_printer_ip} if first.is_proxy else {}), + **({"proxy": first.get_status().get("proxy", {})} if first.is_proxy else {}), + } + return { + "enabled": False, + "running": False, + "mode": "immediate", + "name": "Bambuddy", + "serial": "", + "model": DEFAULT_VIRTUAL_PRINTER_MODEL, + "model_name": VIRTUAL_PRINTER_MODELS[DEFAULT_VIRTUAL_PRINTER_MODEL], + "pending_files": 0, + } + + async def configure( + self, + enabled: bool, + access_code: str = "", + mode: str = "immediate", + model: str = "", + target_printer_ip: str = "", + target_printer_serial: str = "", + remote_interface_ip: str = "", + ) -> None: + """Legacy single-printer configure. Delegates to sync_from_db().""" + # This method is kept for backward compat with the settings endpoint. + # The actual work is done by sync_from_db() which reads from the DB. + await self.sync_from_db() + + # Global instance virtual_printer_manager = VirtualPrinterManager() diff --git a/backend/app/services/virtual_printer/mqtt_server.py b/backend/app/services/virtual_printer/mqtt_server.py index f5055dec8..74abdf958 100644 --- a/backend/app/services/virtual_printer/mqtt_server.py +++ b/backend/app/services/virtual_printer/mqtt_server.py @@ -16,6 +16,21 @@ logger = logging.getLogger(__name__) # Default MQTT port for Bambu printers (MQTT over TLS) MQTT_PORT = 8883 +# Model code → product_name for version response (must match what slicer expects) +MODEL_PRODUCT_NAMES = { + "3DPrinter-X1-Carbon": "X1 Carbon", + "3DPrinter-X1": "X1", + "C13": "X1E", + "C11": "P1P", + "C12": "P1S", + "N7": "P2S", + "N2S": "A1", + "N1": "A1 mini", + "O1D": "H2D", + "O1C": "H2C", + "O1S": "H2S", +} + class VirtualPrinterMQTTServer: """MQTT broker that accepts connections from slicers. @@ -168,13 +183,17 @@ class SimpleMQTTServer: key_path: Path, port: int = MQTT_PORT, on_print_command: Callable[[str, dict], None] | None = None, + model: str = "", + bind_address: str = "0.0.0.0", # nosec B104 ): self.serial = serial self.access_code = access_code + self.model = model self.cert_path = cert_path self.key_path = key_path self.port = port self.on_print_command = on_print_command + self.bind_address = bind_address self._running = False self._server = None self._clients: dict[str, asyncio.StreamWriter] = {} @@ -255,7 +274,7 @@ class SimpleMQTTServer: self._server = await asyncio.start_server( connection_handler, - "0.0.0.0", # nosec B104 + self.bind_address, self.port, ssl=ssl_context, ) @@ -594,7 +613,7 @@ class SimpleMQTTServer: } } - await self._publish_to_report(writer, status) + await self._publish_to_report(writer, status, self.serial) except OSError as e: logger.error("Failed to send status report: %s", e) @@ -602,6 +621,9 @@ class SimpleMQTTServer: async def _send_version_response(self, writer: asyncio.StreamWriter, sequence_id: str) -> None: """Send version info response to the slicer.""" try: + product_name = MODEL_PRODUCT_NAMES.get(self.model, self.model or "X1 Carbon") + serial = self.serial + # Build version response matching OrcaSlicer expectations # Required fields per module: name, product_name, sw_ver, sw_new_ver, sn, hw_ver, flag version_info = { @@ -611,55 +633,55 @@ class SimpleMQTTServer: "module": [ { "name": "ota", - "product_name": "X1 Carbon", + "product_name": product_name, "sw_ver": "01.07.00.00", "sw_new_ver": "", "hw_ver": "OTA", - "sn": self.serial, + "sn": serial, "flag": 0, }, { "name": "esp32", - "product_name": "X1 Carbon", + "product_name": product_name, "sw_ver": "01.07.22.25", "sw_new_ver": "", "hw_ver": "AP05", - "sn": self.serial, + "sn": serial, "flag": 0, }, { "name": "rv1126", - "product_name": "X1 Carbon", + "product_name": product_name, "sw_ver": "00.00.27.38", "sw_new_ver": "", "hw_ver": "AP05", - "sn": self.serial, + "sn": serial, "flag": 0, }, { "name": "th", - "product_name": "X1 Carbon", + "product_name": product_name, "sw_ver": "00.00.04.00", "sw_new_ver": "", "hw_ver": "TH07", - "sn": self.serial, + "sn": serial, "flag": 0, }, { "name": "mc", - "product_name": "X1 Carbon", + "product_name": product_name, "sw_ver": "00.00.10.00", "sw_new_ver": "", "hw_ver": "MC07", - "sn": self.serial, + "sn": serial, "flag": 0, }, ], } } - await self._publish_to_report(writer, version_info) - logger.info("Sent version response") + await self._publish_to_report(writer, version_info, serial) + logger.info("Sent version response (product_name=%s)", product_name) except OSError as e: logger.error("Failed to send version response: %s", e) @@ -673,9 +695,9 @@ class SimpleMQTTServer: self._current_file = filename self._prepare_percent = prepare_percent - async def _publish_to_report(self, writer: asyncio.StreamWriter, payload: dict) -> None: + async def _publish_to_report(self, writer: asyncio.StreamWriter, payload: dict, serial: str = "") -> None: """Publish a message on the device report topic.""" - topic = f"device/{self.serial}/report" + topic = f"device/{serial or self.serial}/report" message = json.dumps(payload) topic_bytes = topic.encode("utf-8") diff --git a/backend/app/services/virtual_printer/ssdp_server.py b/backend/app/services/virtual_printer/ssdp_server.py index 38469f2c5..8a8e20869 100644 --- a/backend/app/services/virtual_printer/ssdp_server.py +++ b/backend/app/services/virtual_printer/ssdp_server.py @@ -34,21 +34,24 @@ class VirtualPrinterSSDPServer: serial: str = "00M09A391800001", # X1C serial format for compatibility model: str = "BL-P001", # X1C model code for best compatibility advertise_ip: str = "", + bind_ip: str = "", ): """Initialize the SSDP server. Args: name: Display name shown in slicer discovery - serial: Unique serial number for this virtual printer (must match cert CN) - model: Model code (BL-P001=X1C, C11=P1S, O1D=H2D) + serial: Unique serial number + model: Model code advertise_ip: Override IP to advertise instead of auto-detecting + bind_ip: IP address to bind the SSDP socket to """ self.name = name self.serial = serial self.model = model + self._bind_ip = bind_ip self._running = False self._socket: socket.socket | None = None - self._local_ip: str | None = advertise_ip or None + self._local_ip: str | None = advertise_ip or bind_ip or None def _get_local_ip(self) -> str: """Get the local IP address to advertise.""" @@ -67,14 +70,8 @@ class VirtualPrinterSSDPServer: return "127.0.0.1" def _build_notify_message(self) -> bytes: - """Build SSDP NOTIFY message for periodic announcements. - - Format matches real Bambu printer SSDP broadcasts observed on the network. - Real printers use Host: 239.255.255.250:1990 (port 1990 in header). - """ + """Build SSDP NOTIFY message for periodic announcements.""" ip = self._get_local_ip() - # Match exact format of real Bambu printers (captured via tcpdump) - # Key: DevBind.bambu.com: free - tells slicer printer is NOT cloud-bound message = ( "NOTIFY * HTTP/1.1\r\n" f"Host: {SSDP_MULTICAST_ADDR}:1990\r\n" @@ -98,13 +95,8 @@ class VirtualPrinterSSDPServer: return message.encode() def _build_response_message(self) -> bytes: - """Build SSDP response message for M-SEARCH requests. - - Format matches real Bambu printer SSDP responses. - """ + """Build SSDP response message for M-SEARCH requests.""" ip = self._get_local_ip() - # Match format of real Bambu printers - # Key: DevBind.bambu.com: free - tells slicer printer is NOT cloud-bound message = ( "HTTP/1.1 200 OK\r\n" "Server: UPnP/1.0\r\n" @@ -147,11 +139,18 @@ class VirtualPrinterSSDPServer: # Set non-blocking mode self._socket.setblocking(False) - # Bind to SSDP port - self._socket.bind(("", SSDP_PORT)) + # Bind to SSDP port on specific interface (or all interfaces) + self._socket.bind((self._bind_ip or "", SSDP_PORT)) - # Join multicast group - mreq = struct.pack("4sl", socket.inet_aton(SSDP_MULTICAST_ADDR), socket.INADDR_ANY) + # Join multicast group (on specific interface if bind_ip is set) + if self._bind_ip: + mreq = struct.pack( + "4s4s", + socket.inet_aton(SSDP_MULTICAST_ADDR), + socket.inet_aton(self._bind_ip), + ) + else: + mreq = struct.pack("4sl", socket.inet_aton(SSDP_MULTICAST_ADDR), socket.INADDR_ANY) self._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) # Enable broadcast @@ -226,17 +225,16 @@ class VirtualPrinterSSDPServer: self._socket = None async def _send_notify(self) -> None: - """Send SSDP NOTIFY message via broadcast (like real Bambu printers).""" + """Send SSDP NOTIFY message via broadcast.""" if not self._socket: return try: msg = self._build_notify_message() - # Real Bambu printers broadcast to 255.255.255.255, not multicast self._socket.sendto(msg, (SSDP_BROADCAST_ADDR, SSDP_PORT)) logger.debug("Sent SSDP NOTIFY for %s", self.name) except OSError as e: - logger.debug("Failed to send NOTIFY: %s", e) + logger.debug("Failed to send NOTIFY for %s: %s", self.name, e) async def _send_byebye(self) -> None: """Send SSDP byebye message when shutting down.""" @@ -282,7 +280,7 @@ class VirtualPrinterSSDPServer: self._socket.sendto(response, addr) logger.info("Sent SSDP response to %s for virtual printer '%s'", addr[0], self.name) except OSError as e: - logger.debug("Failed to send SSDP response: %s", e) + logger.debug("Failed to send SSDP response for %s: %s", self.name, e) class SSDPProxy: diff --git a/backend/app/services/virtual_printer/tcp_proxy.py b/backend/app/services/virtual_printer/tcp_proxy.py index 6dc906ec3..2db32f6c0 100644 --- a/backend/app/services/virtual_printer/tcp_proxy.py +++ b/backend/app/services/virtual_printer/tcp_proxy.py @@ -81,6 +81,7 @@ class TLSProxy: server_key_path: Path, on_connect: Callable[[str], None] | None = None, on_disconnect: Callable[[str], None] | None = None, + bind_address: str = "0.0.0.0", # nosec B104 ): """Initialize the TLS proxy. @@ -93,6 +94,7 @@ class TLSProxy: server_key_path: Path to server private key on_connect: Optional callback when client connects (receives client_id) on_disconnect: Optional callback when client disconnects (receives client_id) + bind_address: IP address to bind to (default: all interfaces) """ self.name = name self.listen_port = listen_port @@ -102,6 +104,7 @@ class TLSProxy: self.server_key_path = server_key_path self.on_connect = on_connect self.on_disconnect = on_disconnect + self.bind_address = bind_address self._server: asyncio.Server | None = None self._running = False @@ -134,7 +137,7 @@ class TLSProxy: return logger.info( - f"Starting {self.name} TLS proxy: 0.0.0.0:{self.listen_port} → {self.target_host}:{self.target_port}" + f"Starting {self.name} TLS proxy: {self.bind_address}:{self.listen_port} → {self.target_host}:{self.target_port}" ) try: @@ -147,7 +150,7 @@ class TLSProxy: # Start server with TLS self._server = await asyncio.start_server( self._handle_client, - "0.0.0.0", # nosec B104 + self.bind_address, self.listen_port, ssl=self._server_ssl_context, ) @@ -355,6 +358,7 @@ class TCPProxy: target_port: int, on_connect: Callable[[str], None] | None = None, on_disconnect: Callable[[str], None] | None = None, + bind_address: str = "0.0.0.0", # nosec B104 ): self.name = name self.listen_port = listen_port @@ -362,6 +366,7 @@ class TCPProxy: self.target_port = target_port self.on_connect = on_connect self.on_disconnect = on_disconnect + self.bind_address = bind_address self._server: asyncio.Server | None = None self._running = False @@ -373,8 +378,9 @@ class TCPProxy: return logger.info( - "Starting %s TCP proxy: 0.0.0.0:%s → %s:%s", + "Starting %s TCP proxy: %s:%s → %s:%s", self.name, + self.bind_address, self.listen_port, self.target_host, self.target_port, @@ -385,7 +391,7 @@ class TCPProxy: self._server = await asyncio.start_server( self._handle_client, - "0.0.0.0", # nosec B104 + self.bind_address, self.listen_port, ) @@ -1053,6 +1059,7 @@ class SlicerProxyManager: cert_path: Path, key_path: Path, on_activity: Callable[[str, str], None] | None = None, + bind_address: str = "0.0.0.0", # nosec B104 ): """Initialize the slicer proxy manager. @@ -1061,11 +1068,13 @@ class SlicerProxyManager: cert_path: Path to server certificate key_path: Path to server private key on_activity: Optional callback for activity logging (name, message) + bind_address: IP address to bind proxy listeners to """ self.target_host = target_host self.cert_path = cert_path self.key_path = key_path self.on_activity = on_activity + self.bind_address = bind_address self._ftp_proxy: TLSProxy | None = None self._mqtt_proxy: TLSProxy | None = None @@ -1100,6 +1109,7 @@ class SlicerProxyManager: server_key_path=self.key_path, on_connect=lambda cid: self._log_activity("FTP", f"connected: {cid}"), on_disconnect=lambda cid: self._log_activity("FTP", f"disconnected: {cid}"), + bind_address=self.bind_address, ) self._mqtt_proxy = TLSProxy( @@ -1111,6 +1121,7 @@ class SlicerProxyManager: server_key_path=self.key_path, on_connect=lambda cid: self._log_activity("MQTT", f"connected: {cid}"), on_disconnect=lambda cid: self._log_activity("MQTT", f"disconnected: {cid}"), + bind_address=self.bind_address, ) # Bind/auth proxy (port 3000) - raw TCP, no TLS @@ -1121,6 +1132,7 @@ class SlicerProxyManager: target_port=self.PRINTER_BIND_PORT, on_connect=lambda cid: self._log_activity("Bind", f"connected: {cid}"), on_disconnect=lambda cid: self._log_activity("Bind", f"disconnected: {cid}"), + bind_address=self.bind_address, ) # Start as background tasks diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 5b6c2ed6d..23c4ef69e 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -4492,6 +4492,8 @@ export interface NetworkInterface { ip: string; netmask: string; subnet: string; + is_alias?: boolean; + label?: string; } export interface VirtualPrinterModels { @@ -4539,6 +4541,69 @@ export const virtualPrinterApi = { }, }; +// Multi Virtual Printer API +export interface VirtualPrinterConfig { + id: number; + name: string; + enabled: boolean; + mode: VirtualPrinterMode; + model: string | null; + model_name: string | null; + access_code_set: boolean; + serial: string; + target_printer_id: number | null; + bind_ip: string | null; + remote_interface_ip: string | null; + position: number; + status: { running: boolean; pending_files: number; proxy?: VirtualPrinterProxyStatus }; +} + +export interface VirtualPrinterListResponse { + printers: VirtualPrinterConfig[]; + models: Record; +} + +export const multiVirtualPrinterApi = { + list: () => request('/virtual-printers'), + + get: (id: number) => request(`/virtual-printers/${id}`), + + create: (data: { + name?: string; + enabled?: boolean; + mode?: string; + model?: string; + access_code?: string; + target_printer_id?: number; + bind_ip?: string; + remote_interface_ip?: string; + }) => + request('/virtual-printers', { + method: 'POST', + body: JSON.stringify(data), + }), + + update: (id: number, data: { + name?: string; + enabled?: boolean; + mode?: string; + model?: string; + access_code?: string; + target_printer_id?: number; + bind_ip?: string; + remote_interface_ip?: string; + }) => + request(`/virtual-printers/${id}`, { + method: 'PUT', + body: JSON.stringify(data), + }), + + remove: (id: number) => + request<{ detail: string; id: number }>(`/virtual-printers/${id}`, { + method: 'DELETE', + }), +}; + // Pending Uploads API export const pendingUploadsApi = { list: () => request('/pending-uploads/'), diff --git a/frontend/src/components/VirtualPrinterAddDialog.tsx b/frontend/src/components/VirtualPrinterAddDialog.tsx new file mode 100644 index 000000000..d795c5c97 --- /dev/null +++ b/frontend/src/components/VirtualPrinterAddDialog.tsx @@ -0,0 +1,156 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { Loader2, ChevronDown, ArrowRightLeft } from 'lucide-react'; +import { api, multiVirtualPrinterApi } from '../api/client'; +import { Card, CardContent } from './Card'; +import { Button } from './Button'; +import { useToast } from '../contexts/ToastContext'; + +type Mode = 'immediate' | 'review' | 'print_queue' | 'proxy'; + +const MODE_LABELS: Record = { + immediate: 'archive', + review: 'review', + print_queue: 'queue', + proxy: 'proxy', +}; + +interface VirtualPrinterAddDialogProps { + onClose: () => void; +} + +export function VirtualPrinterAddDialog({ onClose }: VirtualPrinterAddDialogProps) { + const { t } = useTranslation(); + const queryClient = useQueryClient(); + const { showToast } = useToast(); + + const [name, setName] = useState(''); + const [mode, setMode] = useState('immediate'); + const [targetPrinterId, setTargetPrinterId] = useState(null); + + const { data: printers } = useQuery({ + queryKey: ['printers'], + queryFn: api.getPrinters, + }); + + const createMutation = useMutation({ + mutationFn: () => + multiVirtualPrinterApi.create({ + name: name.trim() || 'Bambuddy', + mode, + target_printer_id: mode === 'proxy' ? (targetPrinterId ?? undefined) : undefined, + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['virtual-printers'] }); + showToast(t('virtualPrinter.toast.created')); + onClose(); + }, + onError: (error: Error) => { + showToast(error.message || t('virtualPrinter.toast.failedToCreate'), 'error'); + }, + }); + + return ( +
+ e.stopPropagation()} + > + +

{t('virtualPrinter.addDialog.title')}

+ + {/* Name */} +
+ + setName(e.target.value)} + placeholder="Bambuddy" + className="w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-2 text-white text-sm placeholder-bambu-gray" + autoFocus + /> +
+ + {/* Mode */} +
+ +
+ {(['immediate', 'review', 'print_queue', 'proxy'] as const).map((m) => ( + + ))} +
+
+ + {/* Target Printer - only for proxy mode */} + {mode === 'proxy' && ( +
+ +
+ + +
+
+ )} + +

+ {t('virtualPrinter.addDialog.hint')} +

+ + {/* Actions */} +
+ + +
+
+
+
+ ); +} diff --git a/frontend/src/components/VirtualPrinterCard.tsx b/frontend/src/components/VirtualPrinterCard.tsx new file mode 100644 index 000000000..0f6305893 --- /dev/null +++ b/frontend/src/components/VirtualPrinterCard.tsx @@ -0,0 +1,459 @@ +import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'; +import { + Loader2, Check, AlertTriangle, Eye, EyeOff, Info, + ChevronDown, ChevronRight, ArrowRightLeft, Trash2, +} from 'lucide-react'; +import { api, multiVirtualPrinterApi } from '../api/client'; +import type { VirtualPrinterConfig } from '../api/client'; +import { Card, CardContent } from './Card'; +import { Button } from './Button'; +import { ConfirmModal } from './ConfirmModal'; +import { useToast } from '../contexts/ToastContext'; + +type LocalMode = 'immediate' | 'review' | 'print_queue' | 'proxy'; + +const MODE_LABELS: Record = { + immediate: 'archive', + review: 'review', + print_queue: 'queue', + proxy: 'proxy', +}; + +interface VirtualPrinterCardProps { + printer: VirtualPrinterConfig; + models: Record; +} + +export function VirtualPrinterCard({ printer, models }: VirtualPrinterCardProps) { + const { t } = useTranslation(); + const queryClient = useQueryClient(); + const { showToast } = useToast(); + + const [expanded, setExpanded] = useState(false); + const [localEnabled, setLocalEnabled] = useState(printer.enabled); + const [localName, setLocalName] = useState(printer.name); + const [localAccessCode, setLocalAccessCode] = useState(''); + const [localMode, setLocalMode] = useState( + (printer.mode === 'queue' ? 'review' : printer.mode) as LocalMode + ); + const [localTargetPrinterId, setLocalTargetPrinterId] = useState(printer.target_printer_id); + const [localBindIp, setLocalBindIp] = useState(printer.bind_ip || ''); + const [localRemoteInterfaceIp, setLocalRemoteInterfaceIp] = useState(printer.remote_interface_ip || ''); + const [localModel, setLocalModel] = useState(printer.model || ''); + const [showAccessCode, setShowAccessCode] = useState(false); + const [pendingAction, setPendingAction] = useState(null); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + + // Sync local state when props change (e.g., after backend auto-disable) + useEffect(() => { + if (!pendingAction) { + setLocalEnabled(printer.enabled); + setLocalMode((printer.mode === 'queue' ? 'review' : printer.mode) as LocalMode); + setLocalName(printer.name); + setLocalTargetPrinterId(printer.target_printer_id); + setLocalBindIp(printer.bind_ip || ''); + setLocalRemoteInterfaceIp(printer.remote_interface_ip || ''); + setLocalModel(printer.model || ''); + } + }, [printer, pendingAction]); + + // Fetch printers for dropdown + const { data: printers } = useQuery({ + queryKey: ['printers'], + queryFn: api.getPrinters, + }); + + // Fetch network interfaces + const { data: networkInterfaces } = useQuery({ + queryKey: ['network-interfaces'], + queryFn: () => api.getNetworkInterfaces().then(res => res.interfaces), + }); + + const updateMutation = useMutation({ + mutationFn: (data: Parameters[1]) => + multiVirtualPrinterApi.update(printer.id, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['virtual-printers'] }); + showToast(t('virtualPrinter.toast.updated')); + setPendingAction(null); + }, + onError: (error: Error) => { + showToast(error.message || t('virtualPrinter.toast.failedToUpdate'), 'error'); + setLocalEnabled(printer.enabled); + setLocalMode((printer.mode === 'queue' ? 'review' : printer.mode) as LocalMode); + setLocalTargetPrinterId(printer.target_printer_id); + setLocalBindIp(printer.bind_ip || ''); + setPendingAction(null); + }, + }); + + const deleteMutation = useMutation({ + mutationFn: () => multiVirtualPrinterApi.remove(printer.id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['virtual-printers'] }); + showToast(t('virtualPrinter.toast.deleted')); + setShowDeleteConfirm(false); + }, + onError: (error: Error) => { + showToast(error.message || t('virtualPrinter.toast.failedToDelete'), 'error'); + setShowDeleteConfirm(false); + }, + }); + + const handleToggleEnabled = (e: React.MouseEvent) => { + e.stopPropagation(); + const newEnabled = !localEnabled; + if (newEnabled) { + if (!localBindIp) { + showToast(t('virtualPrinter.toast.bindIpRequired'), 'error'); + return; + } + if (localMode === 'proxy') { + if (!localTargetPrinterId) { + showToast(t('virtualPrinter.toast.targetPrinterRequired'), 'error'); + return; + } + } else { + if (!localAccessCode && !printer.access_code_set) { + showToast(t('virtualPrinter.toast.accessCodeRequired'), 'error'); + return; + } + } + } + setLocalEnabled(newEnabled); + setPendingAction('toggle'); + updateMutation.mutate({ enabled: newEnabled }); + }; + + const handleNameChange = () => { + if (!localName.trim()) return; + setPendingAction('name'); + updateMutation.mutate({ name: localName.trim() }); + }; + + const handleAccessCodeChange = () => { + if (!localAccessCode) { + showToast(t('virtualPrinter.toast.accessCodeEmpty'), 'error'); + return; + } + if (localAccessCode.length !== 8) { + showToast(t('virtualPrinter.toast.accessCodeLength'), 'error'); + return; + } + setPendingAction('accessCode'); + updateMutation.mutate({ access_code: localAccessCode }); + setLocalAccessCode(''); + }; + + const handleModeChange = (mode: LocalMode) => { + setLocalMode(mode); + setPendingAction('mode'); + updateMutation.mutate({ mode }); + }; + + const handleModelChange = (model: string) => { + setLocalModel(model); + setPendingAction('model'); + updateMutation.mutate({ model }); + }; + + const handleTargetPrinterChange = (printerId: number) => { + setLocalTargetPrinterId(printerId); + setPendingAction('targetPrinter'); + updateMutation.mutate({ target_printer_id: printerId }); + }; + + const handleRemoteInterfaceChange = (ip: string) => { + setLocalRemoteInterfaceIp(ip); + setPendingAction('remoteInterface'); + updateMutation.mutate({ remote_interface_ip: ip }); + }; + + const isRunning = printer.status?.running || false; + const modeLabel = t(`virtualPrinter.mode.${MODE_LABELS[localMode] || 'archive'}`); + const targetPrinterName = printers?.find(p => p.id === localTargetPrinterId)?.name; + + return ( + <> + + {/* Collapsed header - always visible, clickable to expand */} +
setExpanded(!expanded)} + > + + + {printer.name} + {modeLabel} + {printer.model_name && ( + {printer.model_name} + )} + {targetPrinterName && ( + + {localMode === 'proxy' && } + {targetPrinterName} + + )} + {localBindIp && ( + {localBindIp} + )} + {localRemoteInterfaceIp && ( + {localRemoteInterfaceIp} + )} +
e.stopPropagation()}> + +
+
+ + {/* Expanded content */} + {expanded && ( + +
+ + {/* Name + delete */} +
+ setLocalName(e.target.value)} + onBlur={handleNameChange} + onKeyDown={(e) => e.key === 'Enter' && handleNameChange()} + className="flex-1 text-sm text-white bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-1.5 focus:border-bambu-green focus:outline-none" + /> + {printer.serial} + +
+ + {/* Mode */} +
+
{t('virtualPrinter.mode.title')}
+
+ {(['immediate', 'review', 'print_queue', 'proxy'] as const).map((mode) => ( + + ))} +
+
+ + {/* Printer Model - for non-proxy modes */} + {localMode !== 'proxy' && ( +
+
{t('virtualPrinter.model.title')}
+

{t('virtualPrinter.model.description')}

+
+ + +
+
+ )} + + {/* Proxy mode: hint about using target printer's access code */} + {localMode === 'proxy' && ( +
+
+ +

+ {t('virtualPrinter.proxy.accessCodeHint')} +

+
+
+ )} + + {/* Access Code - only for non-proxy modes */} + {localMode !== 'proxy' && ( +
+
+
{t('virtualPrinter.accessCode.title')}
+ {printer.access_code_set ? ( + + + {t('virtualPrinter.accessCode.isSet')} + + ) : ( + + + {t('virtualPrinter.accessCode.notSet')} + + )} +
+
+
+ setLocalAccessCode(e.target.value)} + placeholder={printer.access_code_set ? t('virtualPrinter.accessCode.placeholderChange') : t('virtualPrinter.accessCode.placeholder')} + maxLength={8} + className="w-full bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-md px-3 py-1.5 text-white text-sm placeholder-bambu-gray pr-10 font-mono" + /> + +
+ +
+ {localAccessCode && ( +

+ + {t('virtualPrinter.accessCode.charCount', { count: localAccessCode.length })} + +

+ )} +
+ )} + + {/* Target Printer */} +
+
{t('virtualPrinter.targetPrinter.title')}
+
+ + +
+
+ + {/* Bind Interface */} +
+
{t('virtualPrinter.bindIp.title')}
+
+ + +
+

{t('virtualPrinter.bindIp.hint')}

+
+ + {/* Remote Interface - always visible for configuration */} +
+
+
{t('virtualPrinter.remoteInterface.title')}
+ {localRemoteInterfaceIp ? ( + + ) : ( + + )} +
+
+ + +
+
+ + )} + + + {showDeleteConfirm && ( + deleteMutation.mutate()} + onCancel={() => setShowDeleteConfirm(false)} + /> + )} + + + ); +} diff --git a/frontend/src/components/VirtualPrinterList.tsx b/frontend/src/components/VirtualPrinterList.tsx new file mode 100644 index 000000000..54b97a6cd --- /dev/null +++ b/frontend/src/components/VirtualPrinterList.tsx @@ -0,0 +1,128 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from '@tanstack/react-query'; +import { Loader2, Plus, Printer, ExternalLink, AlertTriangle, Info } from 'lucide-react'; +import { multiVirtualPrinterApi } from '../api/client'; +import { Card, CardContent } from './Card'; +import { Button } from './Button'; +import { VirtualPrinterCard } from './VirtualPrinterCard'; +import { VirtualPrinterAddDialog } from './VirtualPrinterAddDialog'; + +export function VirtualPrinterList() { + const { t } = useTranslation(); + const [showAddDialog, setShowAddDialog] = useState(false); + + const { data, isLoading } = useQuery({ + queryKey: ['virtual-printers'], + queryFn: multiVirtualPrinterApi.list, + refetchInterval: 10000, + }); + + if (isLoading) { + return ( + + + + + + ); + } + + const printers = data?.printers || []; + const models = data?.models || {}; + + return ( +
+ {/* Top row - Setup Required (25%) + How it works (75%) */} +
+ + +
+ +
+

{t('virtualPrinter.setupRequired.title')}

+

{t('virtualPrinter.setupRequired.description')}

+ + + {t('virtualPrinter.setupRequired.readGuide')} + +
+
+
+
+ + + +
+ +
+

{t('virtualPrinter.howItWorks.title')}

+

{t('virtualPrinter.howItWorks.intro')}

+
+
+

{t('virtualPrinter.mode.archive')}

+

{t('virtualPrinter.howItWorks.archiveDesc')}

+
+
+

{t('virtualPrinter.mode.review')}

+

{t('virtualPrinter.howItWorks.reviewDesc')}

+
+
+

{t('virtualPrinter.mode.queue')}

+

{t('virtualPrinter.howItWorks.queueDesc')}

+
+
+

{t('virtualPrinter.mode.proxy')}

+

{t('virtualPrinter.howItWorks.proxyDesc')}

+
+
+
+
+
+
+
+ + {/* Header with add button */} +
+
+ +

{t('virtualPrinter.list.title')}

+ ({printers.length}) +
+ +
+ + {/* Printer cards - 3 column grid */} + {printers.length === 0 ? ( + + + +

{t('virtualPrinter.list.empty')}

+ +
+
+ ) : ( +
+ {printers.map((printer) => ( + + ))} +
+ )} + + {showAddDialog && ( + setShowAddDialog(false)} /> + )} +
+ ); +} diff --git a/frontend/src/i18n/locales/de.ts b/frontend/src/i18n/locales/de.ts index 2c4b87003..e73bfbc26 100644 --- a/frontend/src/i18n/locales/de.ts +++ b/frontend/src/i18n/locales/de.ts @@ -2979,19 +2979,11 @@ export default { }, howItWorks: { title: 'So funktioniert es', - titleProxy: 'So funktioniert es (Proxy-Modus)', - step1: 'Schließe die Einrichtungsanleitung für deine Plattform ab', - step2: 'Aktiviere den virtuellen Drucker und setze einen Zugangscode', - step3: 'In Bambu Studio oder OrcaSlicer gehe zu "Drucker hinzufügen"', - step4: 'Der "Bambuddy"-Drucker sollte in der Erkennungsliste erscheinen', - step5: 'Verbinde mit dem von dir gesetzten Zugangscode', - step6: 'Wenn du zu Bambuddy "druckst", wird die 3MF-Datei stattdessen archiviert', - proxyStep1: 'Wähle den Zieldrucker (muss im LAN-Modus sein)', - proxyStep2: 'Bei Netzwerkübergreifend: Wähle die Slicer-Netzwerkschnittstelle', - proxyStep3: 'Aktiviere den Proxy - Drucker erscheint per SSDP in der Slicer-Erkennung', - proxyStep4: 'Verbinde mit dem Zugangscode des Druckers', - proxyStep5: 'Drucke wie gewohnt - der Datenverkehr wird über Bambuddy weitergeleitet', - proxyStep6: 'Kamera-Streaming erfordert NAT/IP-Weiterleitung (siehe Dokumentation)', + intro: 'Jeder virtuelle Drucker erscheint in Bambu Studio / OrcaSlicer über SSDP-Erkennung. Erstelle mehrere Drucker mit verschiedenen Modi:', + archiveDesc: '3MF-Dateien an diesen Drucker werden direkt im Archiv gespeichert.', + reviewDesc: 'Dateien werden zur Überprüfung zurückgehalten. Du genehmigst oder verwirfst jeden Upload vor der Archivierung.', + queueDesc: 'Dateien werden archiviert und automatisch zur Druckwarteschlange hinzugefügt (nicht zugewiesen).', + proxyDesc: 'Datenverkehr wird an einen echten Drucker weitergeleitet. Erfordert eine dedizierte IP pro Proxy (Interface-Alias).', }, status: { title: 'Status-Details', @@ -3011,8 +3003,37 @@ export default { failedToUpdate: 'Einstellungen konnten nicht aktualisiert werden', accessCodeRequired: 'Bitte zuerst einen Zugangscode setzen', targetPrinterRequired: 'Bitte zuerst einen Zieldrucker auswählen', + bindIpRequired: 'Bitte zuerst eine Bind-IP setzen', accessCodeEmpty: 'Zugangscode darf nicht leer sein', accessCodeLength: 'Zugangscode muss genau 8 Zeichen lang sein', + created: 'Virtueller Drucker erstellt', + failedToCreate: 'Virtueller Drucker konnte nicht erstellt werden', + deleted: 'Virtueller Drucker gelöscht', + failedToDelete: 'Virtueller Drucker konnte nicht gelöscht werden', + }, + list: { + title: 'Virtuelle Drucker', + add: 'Hinzufügen', + addFirst: 'Virtuellen Drucker hinzufügen', + empty: 'Keine virtuellen Drucker konfiguriert. Fügen Sie einen hinzu, um zu beginnen.', + }, + bindIp: { + title: 'Bind-Interface', + placeholder: 'Interface auswählen...', + hint: 'Netzwerkinterface, an das dieser virtuelle Drucker gebunden wird. Muss pro Drucker eindeutig sein.', + }, + proxy: { + accessCodeHint: 'Im Proxy-Modus den Zugangscode des Zieldruckers im Slicer verwenden. Die Verbindung wird transparent zum echten Drucker weitergeleitet.', + }, + addDialog: { + title: 'Virtuellen Drucker hinzufügen', + name: 'Name', + hint: 'Sie können Zugangscode, Zieldrucker und andere Einstellungen nach dem Erstellen konfigurieren.', + create: 'Erstellen', + }, + deleteConfirm: { + title: 'Virtuellen Drucker löschen', + message: 'Möchten Sie "{{name}}" wirklich löschen? Dies stoppt alle Dienste für diesen Drucker.', }, }, diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 36a4a9f43..0d0d23727 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -2984,19 +2984,11 @@ export default { }, howItWorks: { title: 'How it works', - titleProxy: 'How it works (Proxy Mode)', - step1: 'Complete the setup guide for your platform', - step2: 'Enable the virtual printer and set an access code', - step3: 'In Bambu Studio or OrcaSlicer, go to "Add Printer"', - step4: 'The "Bambuddy" printer should appear in the discovery list', - step5: 'Connect using the access code you set', - step6: 'When you "print" to Bambuddy, the 3MF file is archived instead', - proxyStep1: 'Select the target printer (must be in LAN mode)', - proxyStep2: 'For cross-network: select the slicer network interface', - proxyStep3: 'Enable the proxy - printer appears in slicer discovery via SSDP', - proxyStep4: 'Connect using the printer\'s access code', - proxyStep5: 'Print as normal - traffic is relayed through Bambuddy', - proxyStep6: 'Camera streaming requires NAT/IP forwarding (see docs)', + intro: 'Each virtual printer appears in Bambu Studio / OrcaSlicer via SSDP discovery. Create multiple printers with different modes:', + archiveDesc: '3MF files sent to this printer are saved directly to the archive.', + reviewDesc: 'Files are held for review. You approve or discard each upload before archiving.', + queueDesc: 'Files are archived and automatically added to the print queue (unassigned).', + proxyDesc: 'Traffic is relayed to a real printer. Requires a dedicated IP per proxy (interface alias).', }, status: { title: 'Status Details', @@ -3016,8 +3008,37 @@ export default { failedToUpdate: 'Failed to update settings', accessCodeRequired: 'Please set an access code first', targetPrinterRequired: 'Please select a target printer first', + bindIpRequired: 'Please set a bind IP first', accessCodeEmpty: 'Access code cannot be empty', accessCodeLength: 'Access code must be exactly 8 characters', + created: 'Virtual printer created', + failedToCreate: 'Failed to create virtual printer', + deleted: 'Virtual printer deleted', + failedToDelete: 'Failed to delete virtual printer', + }, + list: { + title: 'Virtual Printers', + add: 'Add', + addFirst: 'Add Virtual Printer', + empty: 'No virtual printers configured. Add one to get started.', + }, + bindIp: { + title: 'Bind Interface', + placeholder: 'Select interface...', + hint: 'Network interface for this virtual printer to bind to. Must be unique per printer.', + }, + proxy: { + accessCodeHint: 'In proxy mode, use your target printer\'s access code in the slicer. The connection is forwarded transparently to the real printer.', + }, + addDialog: { + title: 'Add Virtual Printer', + name: 'Name', + hint: 'You can configure access code, target printer, and other settings after creating.', + create: 'Create', + }, + deleteConfirm: { + title: 'Delete Virtual Printer', + message: 'Are you sure you want to delete "{{name}}"? This will stop all services for this printer.', }, }, diff --git a/frontend/src/i18n/locales/fr.ts b/frontend/src/i18n/locales/fr.ts index 08e10d7d8..b1648c8ce 100644 --- a/frontend/src/i18n/locales/fr.ts +++ b/frontend/src/i18n/locales/fr.ts @@ -2954,6 +2954,14 @@ export default { placeholder: 'Auto (défaut)...', hint: 'Force l\'IP annoncée via SSDP.', }, + bindIp: { + title: 'Interface réseau', + placeholder: 'Sélectionner interface...', + hint: 'Interface réseau sur laquelle cette imprimante virtuelle écoute. Doit être unique par imprimante.', + }, + proxy: { + accessCodeHint: 'En mode proxy, utilisez le code d\'accès de l\'imprimante cible dans le slicer. La connexion est transmise de manière transparente à l\'imprimante réelle.', + }, mode: { title: 'Mode', archive: 'Archiver', diff --git a/frontend/src/i18n/locales/it.ts b/frontend/src/i18n/locales/it.ts index 1afc233be..f725f317a 100644 --- a/frontend/src/i18n/locales/it.ts +++ b/frontend/src/i18n/locales/it.ts @@ -2675,6 +2675,14 @@ export default { placeholder: 'Rilevamento automatico (predefinito)...', hint: 'Sovrascrive l\'indirizzo IP pubblicizzato via SSDP e usato nel certificato TLS. Utile quando Bambuddy ha piu interfacce di rete.', }, + bindIp: { + title: 'Interfaccia di rete', + placeholder: 'Seleziona interfaccia...', + hint: 'Interfaccia di rete a cui questa stampante virtuale si collega. Deve essere unica per stampante.', + }, + proxy: { + accessCodeHint: 'In modalita proxy, usa il codice di accesso della stampante di destinazione nello slicer. La connessione viene inoltrata in modo trasparente alla stampante reale.', + }, mode: { title: 'Modalita', archive: 'Archivio', diff --git a/frontend/src/i18n/locales/ja.ts b/frontend/src/i18n/locales/ja.ts index 390012f88..cf1791daa 100644 --- a/frontend/src/i18n/locales/ja.ts +++ b/frontend/src/i18n/locales/ja.ts @@ -2818,20 +2818,12 @@ export default { hint: 'SSDPで広告され、TLS証明書に使用されるIPアドレスを上書きします。Bambuddyに複数のネットワークインターフェースがある場合に便利です。', }, howItWorks: { - step5: '設定したアクセスコードで接続する', - step6: 'Bambuddyに「印刷」すると、3MFファイルがアーカイブされる', - proxyStep1: 'ターゲットプリンターを選択(LANモードである必要があります)', - proxyStep2: 'クロスネットワーク時:スライサーネットワークインターフェースを選択', - proxyStep3: 'プロキシを有効化 - プリンターがSSDPでスライサー検出に表示されます', - proxyStep4: 'プリンターのアクセスコードで接続', - proxyStep5: '通常通り印刷 - トラフィックはBambuddyを経由して中継されます', - proxyStep6: 'カメラストリーミングにはNAT/IP転送が必要です(ドキュメント参照)', title: '仕組み', - titleProxy: '仕組み(プロキシモード)', - step1: 'プラットフォーム用のセットアップガイドを完了', - step2: '仮想プリンターを有効にしてアクセスコードを設定', - step3: 'Bambu StudioまたはOrcaSlicerで「プリンター追加」へ', - step4: '「Bambuddy」プリンターが検出リストに表示されます', + intro: '各仮想プリンターはSSDPディスカバリーを通じてBambu Studio / OrcaSlicerに表示されます。異なるモードで複数のプリンターを作成できます:', + archiveDesc: 'このプリンターに送信された3MFファイルはアーカイブに直接保存されます。', + reviewDesc: 'ファイルはレビュー用に保留されます。アーカイブ前に各アップロードを承認または破棄します。', + queueDesc: 'ファイルはアーカイブされ、自動的に印刷キューに追加されます(未割り当て)。', + proxyDesc: 'トラフィックは実際のプリンターに転送されます。プロキシごとに専用IPが必要です(インターフェースエイリアス)。', }, status: { mode: 'モード', @@ -2849,10 +2841,39 @@ export default { toast: { accessCodeRequired: '先にアクセスコードを設定してください', targetPrinterRequired: '先にターゲットプリンターを選択してください', + bindIpRequired: '先にバインドIPを設定してください', accessCodeEmpty: 'アクセスコードは空にできません', accessCodeLength: 'アクセスコードは8文字である必要があります', updated: '仮想プリンター設定を更新しました', failedToUpdate: '設定の更新に失敗しました', + created: '仮想プリンターを作成しました', + failedToCreate: '仮想プリンターの作成に失敗しました', + deleted: '仮想プリンターを削除しました', + failedToDelete: '仮想プリンターの削除に失敗しました', + }, + list: { + title: '仮想プリンター', + add: '追加', + addFirst: '仮想プリンターを追加', + empty: '仮想プリンターが設定されていません。追加して始めましょう。', + }, + bindIp: { + title: 'バインドインターフェース', + placeholder: 'インターフェースを選択...', + hint: 'この仮想プリンターがバインドするネットワークインターフェース。プリンターごとに一意である必要があります。', + }, + proxy: { + accessCodeHint: 'プロキシモードでは、スライサーにターゲットプリンターのアクセスコードを使用してください。接続は実際のプリンターに透過的に転送されます。', + }, + addDialog: { + title: '仮想プリンターを追加', + name: '名前', + hint: 'アクセスコード、ターゲットプリンター、その他の設定は作成後に設定できます。', + create: '作成', + }, + deleteConfirm: { + title: '仮想プリンターを削除', + message: '「{{name}}」を削除してもよろしいですか?このプリンターのすべてのサービスが停止されます。', }, title: '仮想プリンター', stopped: '停止', diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index b13788bc5..fc9daeac2 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -21,7 +21,7 @@ import { SpoolmanSettings } from '../components/SpoolmanSettings'; import { SpoolCatalogSettings } from '../components/SpoolCatalogSettings'; import { ColorCatalogSettings } from '../components/ColorCatalogSettings'; import { ExternalLinksSettings } from '../components/ExternalLinksSettings'; -import { VirtualPrinterSettings } from '../components/VirtualPrinterSettings'; +import { VirtualPrinterList } from '../components/VirtualPrinterList'; import { GitHubBackupSettings } from '../components/GitHubBackupSettings'; import { EmailSettings } from '../components/EmailSettings'; import { APIBrowser } from '../components/APIBrowser'; @@ -3330,7 +3330,7 @@ export function SettingsPage() { {/* Virtual Printer Tab */} {activeTab === 'virtual-printer' && ( - + )} {/* Filament Tab */} diff --git a/static/assets/index-BGInJFa3.css b/static/assets/index-BGInJFa3.css new file mode 100644 index 000000000..961f0add0 --- /dev/null +++ b/static/assets/index-BGInJFa3.css @@ -0,0 +1 @@ +@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap";@layer properties,theme,base,components,utilities;@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-950:oklch(25.8% .092 26.042);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-lime-500:oklch(76.8% .233 130.85);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-900:oklch(39.3% .095 152.535);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-700:oklch(50.8% .118 165.612);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-violet-500:oklch(60.6% .25 292.717);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-700:oklch(49.6% .265 301.924);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-500:oklch(55.4% .046 257.417);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--drop-shadow-md:0 3px 3px #0000001f;--drop-shadow-lg:0 4px 4px #00000026;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-sm:8px;--blur-2xl:40px;--aspect-video:16/9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bambu-green:var(--accent);--color-bambu-green-light:var(--accent-light);--color-bambu-green-dark:var(--accent-dark);--color-status-ok:var(--status-ok);--color-status-error:var(--status-error);--color-status-warning:var(--status-warning);--color-bambu-dark:var(--bg-primary);--color-bambu-dark-secondary:var(--bg-secondary);--color-bambu-dark-tertiary:var(--bg-tertiary);--color-bambu-gray:var(--text-muted);--color-bambu-gray-light:var(--text-secondary);--color-bambu-gray-dark:var(--text-tertiary)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.-top-1{top:calc(var(--spacing)*-1)}.-top-1\.5{top:calc(var(--spacing)*-1.5)}.top-0{top:calc(var(--spacing)*0)}.top-0\.5{top:calc(var(--spacing)*.5)}.top-1{top:calc(var(--spacing)*1)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.top-2\.5{top:calc(var(--spacing)*2.5)}.top-4{top:calc(var(--spacing)*4)}.top-8{top:calc(var(--spacing)*8)}.top-\[2px\]{top:2px}.top-\[3px\]{top:3px}.top-\[52px\]{top:52px}.top-full{top:100%}.-right-1{right:calc(var(--spacing)*-1)}.-right-1\.5{right:calc(var(--spacing)*-1.5)}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-1\.5{right:calc(var(--spacing)*1.5)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.right-6{right:calc(var(--spacing)*6)}.right-12{right:calc(var(--spacing)*12)}.right-\[5\.5rem\]{right:5.5rem}.right-full{right:100%}.-bottom-1{bottom:calc(var(--spacing)*-1)}.-bottom-2{bottom:calc(var(--spacing)*-2)}.-bottom-7{bottom:calc(var(--spacing)*-7)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-1{bottom:calc(var(--spacing)*1)}.bottom-2{bottom:calc(var(--spacing)*2)}.bottom-4{bottom:calc(var(--spacing)*4)}.bottom-6{bottom:calc(var(--spacing)*6)}.bottom-8{bottom:calc(var(--spacing)*8)}.bottom-full{bottom:100%}.-left-2{left:calc(var(--spacing)*-2)}.left-0{left:calc(var(--spacing)*0)}.left-0\.5{left:calc(var(--spacing)*.5)}.left-1{left:calc(var(--spacing)*1)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing)*2)}.left-2\.5{left:calc(var(--spacing)*2.5)}.left-3{left:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.left-10{left:calc(var(--spacing)*10)}.left-12{left:calc(var(--spacing)*12)}.left-\[2px\]{left:2px}.left-\[3px\]{left:3px}.left-full{left:100%}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-60,.z-\[60\]{z-index:60}.z-\[100\]{z-index:100}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.-m-4{margin:calc(var(--spacing)*-4)}.-mx-4{margin-inline:calc(var(--spacing)*-4)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.my-8{margin-block:calc(var(--spacing)*8)}.-mt-2{margin-top:calc(var(--spacing)*-2)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-14{margin-top:calc(var(--spacing)*14)}.mt-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-3{margin-right:calc(var(--spacing)*3)}.mr-4{margin-right:calc(var(--spacing)*4)}.mr-auto{margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.-ml-1{margin-left:calc(var(--spacing)*-1)}.-ml-2{margin-left:calc(var(--spacing)*-2)}.ml-0\.5{margin-left:calc(var(--spacing)*.5)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-6{margin-left:calc(var(--spacing)*6)}.ml-7{margin-left:calc(var(--spacing)*7)}.ml-9{margin-left:calc(var(--spacing)*9)}.ml-14{margin-left:calc(var(--spacing)*14)}.ml-16{margin-left:calc(var(--spacing)*16)}.ml-64{margin-left:calc(var(--spacing)*64)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-square{aspect-ratio:1}.aspect-video{aspect-ratio:var(--aspect-video)}.h-0{height:calc(var(--spacing)*0)}.h-0\.5{height:calc(var(--spacing)*.5)}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-20{height:calc(var(--spacing)*20)}.h-24{height:calc(var(--spacing)*24)}.h-48{height:calc(var(--spacing)*48)}.h-64{height:calc(var(--spacing)*64)}.h-80{height:calc(var(--spacing)*80)}.h-\[18px\]{height:18px}.h-\[38px\]{height:38px}.h-\[80vh\]{height:80vh}.h-\[160px\]{height:160px}.h-\[300px\]{height:300px}.h-\[calc\(100\%-40px\)\]{height:calc(100% - 40px)}.h-full{height:100%}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-80{max-height:calc(var(--spacing)*80)}.max-h-96{max-height:calc(var(--spacing)*96)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.max-h-\[400px\]{max-height:400px}.max-h-\[500px\]{max-height:500px}.max-h-\[600px\]{max-height:600px}.max-h-\[calc\(90vh-80px\)\]{max-height:calc(90vh - 80px)}.max-h-\[calc\(100vh-320px\)\]{max-height:calc(100vh - 320px)}.max-h-full{max-height:100%}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[44px\]{min-height:44px}.min-h-\[48px\]{min-height:48px}.min-h-\[80px\]{min-height:80px}.min-h-\[120px\]{min-height:120px}.min-h-\[300px\]{min-height:300px}.min-h-\[400px\]{min-height:400px}.min-h-\[calc\(100vh-64px\)\]{min-height:calc(100vh - 64px)}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-0{width:calc(var(--spacing)*0)}.w-0\.5{width:calc(var(--spacing)*.5)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-1\/3{width:33.3333%}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-4\.5{width:calc(var(--spacing)*4.5)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-40{width:calc(var(--spacing)*40)}.w-44{width:calc(var(--spacing)*44)}.w-48{width:calc(var(--spacing)*48)}.w-52{width:calc(var(--spacing)*52)}.w-56{width:calc(var(--spacing)*56)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-96{width:calc(var(--spacing)*96)}.w-\[560px\]{width:560px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-32{max-width:calc(var(--spacing)*32)}.max-w-\[40\%\]{max-width:40%}.max-w-\[50\%\]{max-width:50%}.max-w-\[80px\]{max-width:80px}.max-w-\[90vw\]{max-width:90vw}.max-w-\[100px\]{max-width:100px}.max-w-\[120px\]{max-width:120px}.max-w-\[150px\]{max-width:150px}.max-w-\[160px\]{max-width:160px}.max-w-\[200px\]{max-width:200px}.max-w-\[280px\]{max-width:280px}.max-w-\[600px\]{max-width:600px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-48{min-width:calc(var(--spacing)*48)}.min-w-\[16px\]{min-width:16px}.min-w-\[18px\]{min-width:18px}.min-w-\[32px\]{min-width:32px}.min-w-\[40px\]{min-width:40px}.min-w-\[48px\]{min-width:48px}.min-w-\[80px\]{min-width:80px}.min-w-\[100px\]{min-width:100px}.min-w-\[120px\]{min-width:120px}.min-w-\[140px\]{min-width:140px}.min-w-\[150px\]{min-width:150px}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.min-w-\[200px\]{min-width:200px}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-\[2_1_190px\]{flex:2 190px}.flex-shrink{flex-shrink:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0\.5{--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-1{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-3\.5{--tw-translate-x:calc(var(--spacing)*3.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing)*5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-6{--tw-translate-x:calc(var(--spacing)*6);translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-105{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-\[1\.02\]{scale:1.02}.-rotate-90{rotate:-90deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-grab{cursor:grab}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.cursor-se-resize{cursor:se-resize}.touch-manipulation{touch-action:manipulation}.resize{resize:both}.resize-none{resize:none}.resize-y{resize:vertical}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-\[auto_1fr_100px_100px_100px_80px\]{grid-template-columns:auto 1fr 100px 100px 100px 80px}.grid-cols-\[auto_1fr_120px_100px_100px_100px_80px\]{grid-template-columns:auto 1fr 120px 100px 100px 100px 80px}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}.gap-8{gap:calc(var(--spacing)*8)}.gap-\[3px\]{gap:3px}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-4{column-gap:calc(var(--spacing)*4)}.gap-x-6{column-gap:calc(var(--spacing)*6)}.gap-y-1{row-gap:calc(var(--spacing)*1)}.gap-y-2{row-gap:calc(var(--spacing)*2)}.gap-y-3{row-gap:calc(var(--spacing)*3)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-bambu-dark-tertiary>:not(:last-child)),:where(.divide-bambu-dark-tertiary\/30>:not(:last-child)){border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){:where(.divide-bambu-dark-tertiary\/30>:not(:last-child)){border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}:where(.divide-bambu-gray\/20>:not(:last-child)){border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){:where(.divide-bambu-gray\/20>:not(:last-child)){border-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.\!rounded-l-none{border-top-left-radius:0!important;border-bottom-left-radius:0!important}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-tl{border-top-left-radius:.25rem}.\!rounded-r-none{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-\[5px\]{border-top-style:var(--tw-border-style);border-top-width:5px}.border-t-\[6px\]{border-top-style:var(--tw-border-style);border-top-width:6px}.\!border-r-0{border-right-style:var(--tw-border-style)!important;border-right-width:0!important}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-\[5px\]{border-right-style:var(--tw-border-style);border-right-width:5px}.border-r-\[6px\]{border-right-style:var(--tw-border-style);border-right-width:6px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-b-\[6px\]{border-bottom-style:var(--tw-border-style);border-bottom-width:6px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-l-\[5px\]{border-left-style:var(--tw-border-style);border-left-width:5px}.border-l-\[6px\]{border-left-style:var(--tw-border-style);border-left-width:6px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.\!border-green-500{border-color:var(--color-green-500)!important}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.border-bambu-dark-tertiary,.border-bambu-dark-tertiary\/20{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/20{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)20%,transparent)}}.border-bambu-dark-tertiary\/30{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/30{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.border-bambu-dark-tertiary\/40{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/40{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)40%,transparent)}}.border-bambu-dark-tertiary\/50{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/50{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.border-bambu-gray,.border-bambu-gray\/20{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/20{border-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.border-bambu-gray\/30{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/30{border-color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.border-bambu-gray\/50{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/50{border-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.border-bambu-green,.border-bambu-green\/20{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/20{border-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.border-bambu-green\/30{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/30{border-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.border-bambu-green\/40{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/40{border-color:color-mix(in oklab,var(--color-bambu-green)40%,transparent)}}.border-bambu-green\/50{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/50{border-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.border-blue-400\/20{border-color:#54a2ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-400\/20{border-color:color-mix(in oklab,var(--color-blue-400)20%,transparent)}}.border-blue-500{border-color:var(--color-blue-500)}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.border-blue-500\/50{border-color:#3080ff80}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/50{border-color:color-mix(in oklab,var(--color-blue-500)50%,transparent)}}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400\/20{border-color:#99a1af33}@supports (color:color-mix(in lab,red,red)){.border-gray-400\/20{border-color:color-mix(in oklab,var(--color-gray-400)20%,transparent)}}.border-gray-500\/20{border-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/20{border-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.border-gray-700{border-color:var(--color-gray-700)}.border-green-300{border-color:var(--color-green-300)}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500)30%,transparent)}}.border-green-500\/50{border-color:#00c75880}@supports (color:color-mix(in lab,red,red)){.border-green-500\/50{border-color:color-mix(in oklab,var(--color-green-500)50%,transparent)}}.border-orange-400\/20{border-color:#ff8b1a33}@supports (color:color-mix(in lab,red,red)){.border-orange-400\/20{border-color:color-mix(in oklab,var(--color-orange-400)20%,transparent)}}.border-orange-400\/50{border-color:#ff8b1a80}@supports (color:color-mix(in lab,red,red)){.border-orange-400\/50{border-color:color-mix(in oklab,var(--color-orange-400)50%,transparent)}}.border-orange-500\/20{border-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/20{border-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.border-orange-500\/30{border-color:#fe6e004d}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/30{border-color:color-mix(in oklab,var(--color-orange-500)30%,transparent)}}.border-purple-400\/20{border-color:#c07eff33}@supports (color:color-mix(in lab,red,red)){.border-purple-400\/20{border-color:color-mix(in oklab,var(--color-purple-400)20%,transparent)}}.border-purple-500\/20{border-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/20{border-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500)30%,transparent)}}.border-red-300{border-color:var(--color-red-300)}.border-red-500{border-color:var(--color-red-500)}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500)50%,transparent)}}.border-red-800\/70{border-color:#9f0712b3}@supports (color:color-mix(in lab,red,red)){.border-red-800\/70{border-color:color-mix(in oklab,var(--color-red-800)70%,transparent)}}.border-slate-500{border-color:var(--color-slate-500)}.border-status-error\/20{border-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.border-status-error\/20{border-color:color-mix(in oklab,var(--color-status-error)20%,transparent)}}.border-status-ok,.border-status-ok\/20{border-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.border-status-ok\/20{border-color:color-mix(in oklab,var(--color-status-ok)20%,transparent)}}.border-status-warning\/20{border-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.border-status-warning\/20{border-color:color-mix(in oklab,var(--color-status-warning)20%,transparent)}}.border-transparent{border-color:#0000}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.border-white\/20{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.border-white\/20{border-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.border-white\/30{border-color:color-mix(in oklab,var(--color-white)30%,transparent)}}.border-yellow-400\/20{border-color:#fac80033}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/20{border-color:color-mix(in oklab,var(--color-yellow-400)20%,transparent)}}.border-yellow-400\/30{border-color:#fac8004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/30{border-color:color-mix(in oklab,var(--color-yellow-400)30%,transparent)}}.border-yellow-400\/50{border-color:#fac80080}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/50{border-color:color-mix(in oklab,var(--color-yellow-400)50%,transparent)}}.border-yellow-500{border-color:var(--color-yellow-500)}.border-yellow-500\/20{border-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/20{border-color:color-mix(in oklab,var(--color-yellow-500)20%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500)30%,transparent)}}.border-yellow-500\/50{border-color:#edb20080}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/50{border-color:color-mix(in oklab,var(--color-yellow-500)50%,transparent)}}.border-t-bambu-dark-tertiary{border-top-color:var(--color-bambu-dark-tertiary)}.border-t-transparent{border-top-color:#0000}.border-r-transparent{border-right-color:#0000}.border-b-bambu-dark-tertiary{border-bottom-color:var(--color-bambu-dark-tertiary)}.border-l-transparent{border-left-color:#0000}.border-l-yellow-500{border-left-color:var(--color-yellow-500)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/5{background-color:#f99c000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/5{background-color:color-mix(in oklab,var(--color-amber-500)5%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-amber-600\/20{background-color:#dd740033}@supports (color:color-mix(in lab,red,red)){.bg-amber-600\/20{background-color:color-mix(in oklab,var(--color-amber-600)20%,transparent)}}.bg-bambu-dark{background-color:var(--color-bambu-dark)}.bg-bambu-dark-secondary,.bg-bambu-dark-secondary\/30{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/30{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)30%,transparent)}}.bg-bambu-dark-secondary\/50{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/50{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)50%,transparent)}}.bg-bambu-dark-secondary\/60{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/60{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)60%,transparent)}}.bg-bambu-dark-secondary\/90{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/90{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)90%,transparent)}}.bg-bambu-dark-secondary\/95{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/95{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)95%,transparent)}}.bg-bambu-dark-tertiary,.bg-bambu-dark-tertiary\/20{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/20{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)20%,transparent)}}.bg-bambu-dark-tertiary\/30{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/30{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.bg-bambu-dark-tertiary\/40{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/40{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)40%,transparent)}}.bg-bambu-dark-tertiary\/50{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/50{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.bg-bambu-dark-tertiary\/70{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/70{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)70%,transparent)}}.bg-bambu-dark-tertiary\/80{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/80{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.bg-bambu-dark\/20{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/20{background-color:color-mix(in oklab,var(--color-bambu-dark)20%,transparent)}}.bg-bambu-dark\/30{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/30{background-color:color-mix(in oklab,var(--color-bambu-dark)30%,transparent)}}.bg-bambu-dark\/50{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/50{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.bg-bambu-dark\/80{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/80{background-color:color-mix(in oklab,var(--color-bambu-dark)80%,transparent)}}.bg-bambu-dark\/90{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/90{background-color:color-mix(in oklab,var(--color-bambu-dark)90%,transparent)}}.bg-bambu-gray\/10{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/10{background-color:color-mix(in oklab,var(--color-bambu-gray)10%,transparent)}}.bg-bambu-gray\/20{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/20{background-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.bg-bambu-gray\/30{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/30{background-color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.bg-bambu-gray\/50{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/50{background-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.bg-bambu-gray\/90{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/90{background-color:color-mix(in oklab,var(--color-bambu-gray)90%,transparent)}}.bg-bambu-green,.bg-bambu-green\/5{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/5{background-color:color-mix(in oklab,var(--color-bambu-green)5%,transparent)}}.bg-bambu-green\/10{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/10{background-color:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.bg-bambu-green\/20{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/20{background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.bg-bambu-green\/30{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/30{background-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.bg-bambu-green\/50{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/50{background-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.bg-bambu-green\/75{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/75{background-color:color-mix(in oklab,var(--color-bambu-green)75%,transparent)}}.bg-bambu-green\/90{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/90{background-color:color-mix(in oklab,var(--color-bambu-green)90%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab,red,red)){.bg-black\/30{background-color:color-mix(in oklab,var(--color-black)30%,transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.bg-black\/90{background-color:#000000e6}@supports (color:color-mix(in lab,red,red)){.bg-black\/90{background-color:color-mix(in oklab,var(--color-black)90%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-400\/10{background-color:#54a2ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-400\/10{background-color:color-mix(in oklab,var(--color-blue-400)10%,transparent)}}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\/5{background-color:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/5{background-color:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.bg-blue-500\/30{background-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/30{background-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.bg-blue-500\/90{background-color:#3080ffe6}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/90{background-color:color-mix(in oklab,var(--color-blue-500)90%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-blue-600\/20{background-color:#155dfc33}@supports (color:color-mix(in lab,red,red)){.bg-blue-600\/20{background-color:color-mix(in oklab,var(--color-blue-600)20%,transparent)}}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/10{background-color:color-mix(in oklab,var(--color-cyan-500)10%,transparent)}}.bg-cyan-500\/20{background-color:#00b7d733}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/20{background-color:color-mix(in oklab,var(--color-cyan-500)20%,transparent)}}.bg-cyan-600\/20{background-color:#0092b533}@supports (color:color-mix(in lab,red,red)){.bg-cyan-600\/20{background-color:color-mix(in oklab,var(--color-cyan-600)20%,transparent)}}.bg-emerald-500\/20{background-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-fuchsia-500{background-color:var(--color-fuchsia-500)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-400\/10{background-color:#99a1af1a}@supports (color:color-mix(in lab,red,red)){.bg-gray-400\/10{background-color:color-mix(in oklab,var(--color-gray-400)10%,transparent)}}.bg-gray-500{background-color:var(--color-gray-500)}.bg-gray-500\/20{background-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/20{background-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.bg-green-900\/50{background-color:#0d542b80}@supports (color:color-mix(in lab,red,red)){.bg-green-900\/50{background-color:color-mix(in oklab,var(--color-green-900)50%,transparent)}}.bg-indigo-500\/20{background-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/20{background-color:color-mix(in oklab,var(--color-indigo-500)20%,transparent)}}.bg-lime-500{background-color:var(--color-lime-500)}.bg-orange-400\/10{background-color:#ff8b1a1a}@supports (color:color-mix(in lab,red,red)){.bg-orange-400\/10{background-color:color-mix(in oklab,var(--color-orange-400)10%,transparent)}}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-orange-500\/20{background-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/20{background-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.bg-orange-600{background-color:var(--color-orange-600)}.bg-orange-600\/20{background-color:#f0510033}@supports (color:color-mix(in lab,red,red)){.bg-orange-600\/20{background-color:color-mix(in oklab,var(--color-orange-600)20%,transparent)}}.bg-purple-400\/10{background-color:#c07eff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-400\/10{background-color:color-mix(in oklab,var(--color-purple-400)10%,transparent)}}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.bg-purple-500\/80{background-color:#ac4bffcc}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/80{background-color:color-mix(in oklab,var(--color-purple-500)80%,transparent)}}.bg-purple-500\/90{background-color:#ac4bffe6}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/90{background-color:color-mix(in oklab,var(--color-purple-500)90%,transparent)}}.bg-purple-700{background-color:var(--color-purple-700)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500)5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-red-500\/15{background-color:#fb2c3626}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/15{background-color:color-mix(in oklab,var(--color-red-500)15%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.bg-red-500\/30{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/30{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.bg-red-500\/40{background-color:#fb2c3666}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/40{background-color:color-mix(in oklab,var(--color-red-500)40%,transparent)}}.bg-red-500\/80{background-color:#fb2c36cc}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/80{background-color:color-mix(in oklab,var(--color-red-500)80%,transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-red-950\/70{background-color:#460809b3}@supports (color:color-mix(in lab,red,red)){.bg-red-950\/70{background-color:color-mix(in oklab,var(--color-red-950)70%,transparent)}}.bg-rose-500\/20{background-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/20{background-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.bg-rose-600\/20{background-color:#e7004433}@supports (color:color-mix(in lab,red,red)){.bg-rose-600\/20{background-color:color-mix(in oklab,var(--color-rose-600)20%,transparent)}}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-500\/20{background-color:#00a5ef33}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/20{background-color:color-mix(in oklab,var(--color-sky-500)20%,transparent)}}.bg-status-error,.bg-status-error\/10{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/10{background-color:color-mix(in oklab,var(--color-status-error)10%,transparent)}}.bg-status-error\/20{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/20{background-color:color-mix(in oklab,var(--color-status-error)20%,transparent)}}.bg-status-error\/80{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/80{background-color:color-mix(in oklab,var(--color-status-error)80%,transparent)}}.bg-status-ok,.bg-status-ok\/10{background-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.bg-status-ok\/10{background-color:color-mix(in oklab,var(--color-status-ok)10%,transparent)}}.bg-status-ok\/20{background-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.bg-status-ok\/20{background-color:color-mix(in oklab,var(--color-status-ok)20%,transparent)}}.bg-status-warning\/10{background-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.bg-status-warning\/10{background-color:color-mix(in oklab,var(--color-status-warning)10%,transparent)}}.bg-status-warning\/20{background-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.bg-status-warning\/20{background-color:color-mix(in oklab,var(--color-status-warning)20%,transparent)}}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-500\/20{background-color:#00baa733}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/20{background-color:color-mix(in oklab,var(--color-teal-500)20%,transparent)}}.bg-transparent{background-color:#0000}.bg-violet-500{background-color:var(--color-violet-500)}.bg-white{background-color:var(--color-white)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab,red,red)){.bg-white\/20{background-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.bg-white\/50{background-color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.bg-white\/50{background-color:color-mix(in oklab,var(--color-white)50%,transparent)}}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-white\/70{background-color:color-mix(in oklab,var(--color-white)70%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-400\/10{background-color:#fac8001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-400\/10{background-color:color-mix(in oklab,var(--color-yellow-400)10%,transparent)}}.bg-yellow-400\/20{background-color:#fac80033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-400\/20{background-color:color-mix(in oklab,var(--color-yellow-400)20%,transparent)}}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500)5%,transparent)}}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500)20%,transparent)}}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-bambu-dark-secondary{--tw-gradient-from:var(--color-bambu-dark-secondary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-bambu-green\/10{--tw-gradient-from:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.from-bambu-green\/10{--tw-gradient-from:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.from-bambu-green\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-black\/80{--tw-gradient-from:#000c}@supports (color:color-mix(in lab,red,red)){.from-black\/80{--tw-gradient-from:color-mix(in oklab,var(--color-black)80%,transparent)}}.from-black\/80{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-blue-500\/5{--tw-gradient-from:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.from-blue-500\/5{--tw-gradient-from:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.from-blue-500\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-blue-500\/10{--tw-gradient-from:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.from-blue-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.from-blue-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-gray-500\/10{--tw-gradient-from:#6a72821a}@supports (color:color-mix(in lab,red,red)){.from-gray-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-gray-500)10%,transparent)}}.from-gray-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-purple-500\/10{--tw-gradient-from:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.from-purple-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.from-purple-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-white\/10{--tw-gradient-from:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.from-white\/10{--tw-gradient-from:color-mix(in oklab,var(--color-white)10%,transparent)}}.from-white\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-yellow-500\/10{--tw-gradient-from:#edb2001a}@supports (color:color-mix(in lab,red,red)){.from-yellow-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.from-yellow-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.via-black\/60{--tw-gradient-via:#0009}@supports (color:color-mix(in lab,red,red)){.via-black\/60{--tw-gradient-via:color-mix(in oklab,var(--color-black)60%,transparent)}}.via-black\/60{--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-bambu-dark-secondary{--tw-gradient-to:var(--color-bambu-dark-secondary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-bambu-dark-tertiary{--tw-gradient-to:var(--color-bambu-dark-tertiary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-cover{background-size:cover}.bg-center{background-position:50%}.fill-yellow-400{fill:var(--color-yellow-400)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.object-left{object-position:left}.p-0{padding:calc(var(--spacing)*0)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-12{padding:calc(var(--spacing)*12)}.\!px-1\.5{padding-inline:calc(var(--spacing)*1.5)!important}.\!px-3{padding-inline:calc(var(--spacing)*3)!important}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.py-16{padding-block:calc(var(--spacing)*16)}.py-20{padding-block:calc(var(--spacing)*20)}.py-24{padding-block:calc(var(--spacing)*24)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-1{padding-right:calc(var(--spacing)*1)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-4{padding-right:calc(var(--spacing)*4)}.pr-6{padding-right:calc(var(--spacing)*6)}.pr-7{padding-right:calc(var(--spacing)*7)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-7{padding-left:calc(var(--spacing)*7)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-9{padding-left:calc(var(--spacing)*9)}.pl-10{padding-left:calc(var(--spacing)*10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[7px\]{font-size:7px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.\!text-green-400{color:var(--color-green-400)!important}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-amber-300\/60{color:#ffd23699}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/60{color:color-mix(in oklab,var(--color-amber-300)60%,transparent)}}.text-amber-300\/70{color:#ffd236b3}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/70{color:color-mix(in oklab,var(--color-amber-300)70%,transparent)}}.text-amber-300\/80{color:#ffd236cc}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/80{color:color-mix(in oklab,var(--color-amber-300)80%,transparent)}}.text-amber-400{color:var(--color-amber-400)}.text-amber-400\/70{color:#fcbb00b3}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/70{color:color-mix(in oklab,var(--color-amber-400)70%,transparent)}}.text-amber-500{color:var(--color-amber-500)}.text-amber-500\/80{color:#f99c00cc}@supports (color:color-mix(in lab,red,red)){.text-amber-500\/80{color:color-mix(in oklab,var(--color-amber-500)80%,transparent)}}.text-amber-600{color:var(--color-amber-600)}.text-bambu-dark-tertiary{color:var(--color-bambu-dark-tertiary)}.text-bambu-gray{color:var(--color-bambu-gray)}.text-bambu-gray-dark{color:var(--color-bambu-gray-dark)}.text-bambu-gray-light{color:var(--color-bambu-gray-light)}.text-bambu-gray\/30{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/30{color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.text-bambu-gray\/40{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/40{color:color-mix(in oklab,var(--color-bambu-gray)40%,transparent)}}.text-bambu-gray\/50{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/50{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.text-bambu-gray\/60{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/60{color:color-mix(in oklab,var(--color-bambu-gray)60%,transparent)}}.text-bambu-gray\/70{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/70{color:color-mix(in oklab,var(--color-bambu-gray)70%,transparent)}}.text-bambu-gray\/80{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/80{color:color-mix(in oklab,var(--color-bambu-gray)80%,transparent)}}.text-bambu-green,.text-bambu-green\/70{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.text-bambu-green\/70{color:color-mix(in oklab,var(--color-bambu-green)70%,transparent)}}.text-black{color:var(--color-black)}.text-black\/80{color:#000c}@supports (color:color-mix(in lab,red,red)){.text-black\/80{color:color-mix(in oklab,var(--color-black)80%,transparent)}}.text-blue-300{color:var(--color-blue-300)}.text-blue-300\/70{color:#90c5ffb3}@supports (color:color-mix(in lab,red,red)){.text-blue-300\/70{color:color-mix(in oklab,var(--color-blue-300)70%,transparent)}}.text-blue-300\/80{color:#90c5ffcc}@supports (color:color-mix(in lab,red,red)){.text-blue-300\/80{color:color-mix(in oklab,var(--color-blue-300)80%,transparent)}}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-500\/70{color:#3080ffb3}@supports (color:color-mix(in lab,red,red)){.text-blue-500\/70{color:color-mix(in oklab,var(--color-blue-500)70%,transparent)}}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-emerald-400{color:var(--color-emerald-400)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-200{color:var(--color-green-200)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-500\/60{color:#00c75899}@supports (color:color-mix(in lab,red,red)){.text-green-500\/60{color:color-mix(in oklab,var(--color-green-500)60%,transparent)}}.text-green-600{color:var(--color-green-600)}.text-indigo-400{color:var(--color-indigo-400)}.text-orange-200{color:var(--color-orange-200)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-purple-300{color:var(--color-purple-300)}.text-purple-300\/70{color:#d9b3ffb3}@supports (color:color-mix(in lab,red,red)){.text-purple-300\/70{color:color-mix(in oklab,var(--color-purple-300)70%,transparent)}}.text-purple-400{color:var(--color-purple-400)}.text-purple-400\/80{color:#c07effcc}@supports (color:color-mix(in lab,red,red)){.text-purple-400\/80{color:color-mix(in oklab,var(--color-purple-400)80%,transparent)}}.text-purple-500{color:var(--color-purple-500)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-300\/80{color:#ffa3a3cc}@supports (color:color-mix(in lab,red,red)){.text-red-300\/80{color:color-mix(in oklab,var(--color-red-300)80%,transparent)}}.text-red-400{color:var(--color-red-400)}.text-red-400\/50{color:#ff656880}@supports (color:color-mix(in lab,red,red)){.text-red-400\/50{color:color-mix(in oklab,var(--color-red-400)50%,transparent)}}.text-red-400\/60{color:#ff656899}@supports (color:color-mix(in lab,red,red)){.text-red-400\/60{color:color-mix(in oklab,var(--color-red-400)60%,transparent)}}.text-red-400\/70{color:#ff6568b3}@supports (color:color-mix(in lab,red,red)){.text-red-400\/70{color:color-mix(in oklab,var(--color-red-400)70%,transparent)}}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-sky-400{color:var(--color-sky-400)}.text-slate-300{color:var(--color-slate-300)}.text-status-error{color:var(--color-status-error)}.text-status-ok{color:var(--color-status-ok)}.text-status-warning{color:var(--color-status-warning)}.text-teal-400{color:var(--color-teal-400)}.text-white{color:var(--color-white)}.text-white\/40{color:#fff6}@supports (color:color-mix(in lab,red,red)){.text-white\/40{color:color-mix(in oklab,var(--color-white)40%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white)60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white)70%,transparent)}}.text-white\/80{color:#fffc}@supports (color:color-mix(in lab,red,red)){.text-white\/80{color:color-mix(in oklab,var(--color-white)80%,transparent)}}.text-white\/90{color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.text-white\/90{color:color-mix(in oklab,var(--color-white)90%,transparent)}}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-200\/70{color:#fff085b3}@supports (color:color-mix(in lab,red,red)){.text-yellow-200\/70{color:color-mix(in oklab,var(--color-yellow-200)70%,transparent)}}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-500\/70{color:#edb200b3}@supports (color:color-mix(in lab,red,red)){.text-yellow-500\/70{color:color-mix(in oklab,var(--color-yellow-500)70%,transparent)}}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.placeholder-bambu-gray::placeholder{color:var(--color-bambu-gray)}.placeholder-bambu-gray-dark::placeholder{color:var(--color-bambu-gray-dark)}.placeholder-bambu-gray\/50::placeholder{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.placeholder-bambu-gray\/50::placeholder{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.accent-bambu-green{accent-color:var(--color-bambu-green)}.accent-green-500{accent-color:var(--color-green-500)}.opacity-0{opacity:0}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-bambu-green\/20{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.shadow-bambu-green\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-bambu-green\/25{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.shadow-bambu-green\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)25%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-bambu-green,.ring-bambu-green\/30{--tw-ring-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.ring-bambu-green\/30{--tw-ring-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.ring-blue-400\/50{--tw-ring-color:#54a2ff80}@supports (color:color-mix(in lab,red,red)){.ring-blue-400\/50{--tw-ring-color:color-mix(in oklab,var(--color-blue-400)50%,transparent)}}.ring-green-500{--tw-ring-color:var(--color-green-500)}.ring-white{--tw-ring-color:var(--color-white)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-bambu-dark{--tw-ring-offset-color:var(--color-bambu-dark)}.ring-offset-bambu-dark-secondary{--tw-ring-offset-color:var(--color-bambu-dark-secondary)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.blur-2xl{--tw-blur:blur(var(--blur-2xl));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.drop-shadow-md{--tw-drop-shadow-size:drop-shadow(0 3px 3px var(--tw-drop-shadow-color,#0000001f));--tw-drop-shadow:drop-shadow(var(--drop-shadow-md));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:bg-bambu-green\/20:is(:where(.group):hover *){background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-bambu-green\/20:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.group-hover\:text-bambu-gray:is(:where(.group):hover *){color:var(--color-bambu-gray)}.group-hover\:text-bambu-green:is(:where(.group):hover *){color:var(--color-bambu-green)}.group-hover\:opacity-50:is(:where(.group):hover *){opacity:.5}.group-hover\:opacity-100:is(:where(.group):hover *),.group-hover\/resize\:opacity-100:is(:where(.group\/resize):hover *){opacity:1}.group-hover\/thumb\:block:is(:where(.group\/thumb):hover *){display:block}}.peer-checked\:bg-bambu-green:is(:where(.peer):checked~*){background-color:var(--color-bambu-green)}.peer-focus\:outline-none:is(:where(.peer):focus~*){--tw-outline-style:none;outline-style:none}.placeholder\:text-bambu-gray::placeholder,.placeholder\:text-bambu-gray\/50::placeholder{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-bambu-gray\/50::placeholder{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:top-0:before{content:var(--tw-content);top:calc(var(--spacing)*0)}.before\:right-0:before{content:var(--tw-content);right:calc(var(--spacing)*0)}.before\:left-0:before{content:var(--tw-content);left:calc(var(--spacing)*0)}.before\:h-0\.5:before{content:var(--tw-content);height:calc(var(--spacing)*.5)}.before\:bg-bambu-green:before{content:var(--tw-content);background-color:var(--color-bambu-green)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:left-\[2px\]:after{content:var(--tw-content);left:2px}.after\:h-4:after{content:var(--tw-content);height:calc(var(--spacing)*4)}.after\:h-5:after{content:var(--tw-content);height:calc(var(--spacing)*5)}.after\:w-4:after{content:var(--tw-content);width:calc(var(--spacing)*4)}.after\:w-5:after{content:var(--tw-content);width:calc(var(--spacing)*5)}.after\:rounded-full:after{content:var(--tw-content);border-radius:3.40282e38px}.after\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);border-color:var(--color-gray-300)}.after\:bg-white:after{content:var(--tw-content);background-color:var(--color-white)}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.peer-checked\:after\:translate-x-full:is(:where(.peer):checked~*):after{content:var(--tw-content);--tw-translate-x:100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.peer-checked\:after\:border-white:is(:where(.peer):checked~*):after{content:var(--tw-content);border-color:var(--color-white)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:border-bambu-dark-tertiary\/80:hover{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-dark-tertiary\/80:hover{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.hover\:border-bambu-gray:hover{border-color:var(--color-bambu-gray)}.hover\:border-bambu-gray-dark:hover{border-color:var(--color-bambu-gray-dark)}.hover\:border-bambu-gray\/50:hover{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-gray\/50:hover{border-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.hover\:border-bambu-green:hover,.hover\:border-bambu-green\/50:hover{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-green\/50:hover{border-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.hover\:border-white:hover{border-color:var(--color-white)}.hover\:border-white\/40:hover{border-color:#fff6}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/40:hover{border-color:color-mix(in oklab,var(--color-white)40%,transparent)}}.hover\:\!bg-green-500\/20:hover{background-color:#00c75833!important}@supports (color:color-mix(in lab,red,red)){.hover\:\!bg-green-500\/20:hover{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)!important}}.hover\:bg-amber-500\/30:hover{background-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/30:hover{background-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.hover\:bg-bambu-dark:hover{background-color:var(--color-bambu-dark)}.hover\:bg-bambu-dark-secondary:hover,.hover\:bg-bambu-dark-secondary\/50:hover{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-secondary\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)50%,transparent)}}.hover\:bg-bambu-dark-tertiary:hover,.hover\:bg-bambu-dark-tertiary\/30:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/30:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.hover\:bg-bambu-dark-tertiary\/50:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.hover\:bg-bambu-dark-tertiary\/70:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/70:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)70%,transparent)}}.hover\:bg-bambu-dark-tertiary\/80:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.hover\:bg-bambu-dark\/50:hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.hover\:bg-bambu-dark\/80:hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-dark)80%,transparent)}}.hover\:bg-bambu-gray-dark:hover{background-color:var(--color-bambu-gray-dark)}.hover\:bg-bambu-green:hover{background-color:var(--color-bambu-green)}.hover\:bg-bambu-green-dark:hover{background-color:var(--color-bambu-green-dark)}.hover\:bg-bambu-green-light:hover{background-color:var(--color-bambu-green-light)}.hover\:bg-bambu-green\/10:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/10:hover{background-color:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.hover\:bg-bambu-green\/20:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/20:hover{background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.hover\:bg-bambu-green\/30:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/30:hover{background-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.hover\:bg-bambu-green\/50:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.hover\:bg-bambu-green\/80:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-green)80%,transparent)}}.hover\:bg-black\/40:hover{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/40:hover{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.hover\:bg-black\/70:hover{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/70:hover{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.hover\:bg-black\/80:hover{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/80:hover{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.hover\:bg-blue-500\/30:hover{background-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-500\/30:hover{background-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-green-500\/20:hover{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-500\/20:hover{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.hover\:bg-orange-400\/20:hover{background-color:#ff8b1a33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-orange-400\/20:hover{background-color:color-mix(in oklab,var(--color-orange-400)20%,transparent)}}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-red-400\/10:hover{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-400\/10:hover{background-color:color-mix(in oklab,var(--color-red-400)10%,transparent)}}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.hover\:bg-red-500\/20:hover{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/20:hover{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.hover\:bg-red-500\/30:hover{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/30:hover{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-white\/5:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/5:hover{background-color:color-mix(in oklab,var(--color-white)5%,transparent)}}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.hover\:bg-white\/80:hover{background-color:#fffc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/80:hover{background-color:color-mix(in oklab,var(--color-white)80%,transparent)}}.hover\:bg-yellow-500\/30:hover{background-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-500\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-500)30%,transparent)}}.hover\:bg-yellow-600:hover{background-color:var(--color-yellow-600)}.hover\:text-amber-300:hover{color:var(--color-amber-300)}.hover\:text-bambu-gray:hover{color:var(--color-bambu-gray)}.hover\:text-bambu-green:hover{color:var(--color-bambu-green)}.hover\:text-bambu-green-light:hover{color:var(--color-bambu-green-light)}.hover\:text-bambu-green\/70:hover{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:text-bambu-green\/70:hover{color:color-mix(in oklab,var(--color-bambu-green)70%,transparent)}}.hover\:text-bambu-green\/80:hover{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:text-bambu-green\/80:hover{color:color-mix(in oklab,var(--color-bambu-green)80%,transparent)}}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-orange-300:hover{color:var(--color-orange-300)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-white\/80:hover{color:#fffc}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/80:hover{color:color-mix(in oklab,var(--color-white)80%,transparent)}}.hover\:text-yellow-400:hover{color:var(--color-yellow-400)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-bambu-green\/5:hover{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-bambu-green\/5:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)5%,transparent)var(--tw-shadow-alpha),transparent)}}.hover\:shadow-bambu-green\/30:hover{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-bambu-green\/30:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)30%,transparent)var(--tw-shadow-alpha),transparent)}}}.focus\:border-bambu-green:focus{border-color:var(--color-bambu-green)}.focus\:border-red-500:focus{border-color:var(--color-red-500)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-bambu-gray:focus{--tw-ring-color:var(--color-bambu-gray)}.focus\:ring-bambu-green:focus,.focus\:ring-bambu-green\/50:focus{--tw-ring-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.focus\:ring-bambu-green\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-bambu-dark:focus{--tw-ring-offset-color:var(--color-bambu-dark)}.focus\:ring-offset-bambu-dark-secondary:focus{--tw-ring-offset-color:var(--color-bambu-dark-secondary)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media(hover:hover){.disabled\:hover\:bg-bambu-green:disabled:hover{background-color:var(--color-bambu-green)}}@media not all and (min-width:640px){.max-\[640px\]\:hidden{display:none}.max-\[640px\]\:w-full{width:100%}.max-\[640px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[640px\]\:flex-col{flex-direction:column}.max-\[640px\]\:flex-wrap{flex-wrap:wrap}.max-\[640px\]\:items-center{align-items:center}.max-\[640px\]\:items-start{align-items:flex-start}}@media not all and (min-width:550px){.max-\[550px\]\:order-1{order:1}.max-\[550px\]\:order-2{order:2}.max-\[550px\]\:order-3{order:3}.max-\[550px\]\:order-4{order:4}.max-\[550px\]\:mt-1{margin-top:calc(var(--spacing)*1)}.max-\[550px\]\:w-full{width:100%}.max-\[550px\]\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.max-\[550px\]\:flex-col{flex-direction:column}.max-\[550px\]\:flex-wrap{flex-wrap:wrap}.max-\[550px\]\:items-center{align-items:center}.max-\[550px\]\:items-start{align-items:flex-start}.max-\[550px\]\:justify-end{justify-content:flex-end}.max-\[550px\]\:gap-1\.5{gap:calc(var(--spacing)*1.5)}.max-\[550px\]\:gap-2{gap:calc(var(--spacing)*2)}.max-\[550px\]\:gap-3{gap:calc(var(--spacing)*3)}.max-\[550px\]\:self-start{align-self:flex-start}}@media(min-width:40rem){.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:mr-1{margin-right:calc(var(--spacing)*1)}.sm\:ml-auto{margin-left:auto}.sm\:block{display:block}.sm\:grid{display:grid}.sm\:inline{display:inline}.sm\:h-4{height:calc(var(--spacing)*4)}.sm\:w-4{width:calc(var(--spacing)*4)}.sm\:w-16{width:calc(var(--spacing)*16)}.sm\:w-40{width:calc(var(--spacing)*40)}.sm\:w-auto{width:auto}.sm\:max-w-xs{max-width:var(--container-xs)}.sm\:flex-1{flex:1}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-2{gap:calc(var(--spacing)*2)}.sm\:gap-3{gap:calc(var(--spacing)*3)}.sm\:gap-6{gap:calc(var(--spacing)*6)}.sm\:p-1\.5{padding:calc(var(--spacing)*1.5)}.sm\:p-3{padding:calc(var(--spacing)*3)}}@media(min-width:48rem){.md\:top-\[2px\]{top:2px}.md\:left-\[2px\]{left:2px}.md\:mx-0{margin-inline:calc(var(--spacing)*0)}.md\:block{display:block}.md\:inline{display:inline}.md\:h-4{height:calc(var(--spacing)*4)}.md\:h-5{height:calc(var(--spacing)*5)}.md\:h-8{height:calc(var(--spacing)*8)}.md\:min-h-0{min-height:calc(var(--spacing)*0)}.md\:w-4{width:calc(var(--spacing)*4)}.md\:w-8{width:calc(var(--spacing)*8)}.md\:w-9{width:calc(var(--spacing)*9)}.md\:min-w-\[200px\]{min-width:200px}.md\:flex-1{flex:1}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-wrap{flex-wrap:wrap}.md\:items-center{align-items:center}.md\:gap-4{gap:calc(var(--spacing)*4)}.md\:p-6{padding:calc(var(--spacing)*6)}.md\:p-8{padding:calc(var(--spacing)*8)}.md\:px-0{padding-inline:calc(var(--spacing)*0)}.md\:py-2{padding-block:calc(var(--spacing)*2)}.md\:pb-0{padding-bottom:calc(var(--spacing)*0)}}@media(min-width:64rem){.lg\:static{position:static}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-\[calc\(100vh-64px\)\]{height:calc(100vh - 64px)}.lg\:w-1\/3{width:33.3333%}.lg\:w-2\/3{width:66.6667%}.lg\:w-80{width:calc(var(--spacing)*80)}.lg\:w-\[480px\]{width:480px}.lg\:max-w-md{max-width:var(--container-md)}.lg\:max-w-sm{max-width:var(--container-sm)}.lg\:max-w-xl{max-width:var(--container-xl)}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:gap-6{gap:calc(var(--spacing)*6)}.lg\:gap-8{gap:calc(var(--spacing)*8)}.lg\:overflow-y-auto{overflow-y:auto}.lg\:p-8{padding:calc(var(--spacing)*8)}}@media(min-width:80rem){.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}@media(min-width:96rem){.\32xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}.dark\:border-bambu-dark-tertiary:where(.dark,.dark *),.dark\:border-bambu-dark-tertiary\/50:where(.dark,.dark *){border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.dark\:border-bambu-dark-tertiary\/50:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.dark\:border-bambu-green\/40:where(.dark,.dark *){border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:border-bambu-green\/40:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-bambu-green)40%,transparent)}}.dark\:border-gray-600:where(.dark,.dark *){border-color:var(--color-gray-600)}.dark\:border-red-500\/30:where(.dark,.dark *){border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.dark\:border-red-500\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.dark\:border-red-500\/40:where(.dark,.dark *){border-color:#fb2c3666}@supports (color:color-mix(in lab,red,red)){.dark\:border-red-500\/40:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-red-500)40%,transparent)}}.dark\:bg-amber-500\/10:where(.dark,.dark *){background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.dark\:bg-bambu-dark:where(.dark,.dark *){background-color:var(--color-bambu-dark)}.dark\:bg-bambu-dark-secondary:where(.dark,.dark *){background-color:var(--color-bambu-dark-secondary)}.dark\:bg-bambu-green\/20:where(.dark,.dark *){background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-bambu-green\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.dark\:bg-black\/80:where(.dark,.dark *){background-color:#000c}@supports (color:color-mix(in lab,red,red)){.dark\:bg-black\/80:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.dark\:bg-blue-500\/10:where(.dark,.dark *){background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.dark\:bg-blue-500\/20:where(.dark,.dark *){background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.dark\:bg-gray-500\/40:where(.dark,.dark *){background-color:#6a728266}@supports (color:color-mix(in lab,red,red)){.dark\:bg-gray-500\/40:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-gray-500)40%,transparent)}}.dark\:bg-gray-900:where(.dark,.dark *){background-color:var(--color-gray-900)}.dark\:bg-red-500\/10:where(.dark,.dark *){background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.dark\:bg-red-500\/20:where(.dark,.dark *){background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-500\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.dark\:text-amber-400:where(.dark,.dark *){color:var(--color-amber-400)}.dark\:text-bambu-gray:where(.dark,.dark *),.dark\:text-bambu-gray\/30:where(.dark,.dark *){color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-gray\/30:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.dark\:text-bambu-gray\/50:where(.dark,.dark *){color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-gray\/50:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.dark\:text-bambu-green:where(.dark,.dark *),.dark\:text-bambu-green\/60:where(.dark,.dark *){color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-green\/60:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-green)60%,transparent)}}.dark\:text-blue-200:where(.dark,.dark *){color:var(--color-blue-200)}.dark\:text-blue-200\/80:where(.dark,.dark *){color:#bedbffcc}@supports (color:color-mix(in lab,red,red)){.dark\:text-blue-200\/80:where(.dark,.dark *){color:color-mix(in oklab,var(--color-blue-200)80%,transparent)}}.dark\:text-blue-300:where(.dark,.dark *){color:var(--color-blue-300)}.dark\:text-blue-300\/60:where(.dark,.dark *){color:#90c5ff99}@supports (color:color-mix(in lab,red,red)){.dark\:text-blue-300\/60:where(.dark,.dark *){color:color-mix(in oklab,var(--color-blue-300)60%,transparent)}}.dark\:text-blue-400:where(.dark,.dark *){color:var(--color-blue-400)}.dark\:text-orange-200:where(.dark,.dark *){color:var(--color-orange-200)}.dark\:text-orange-400:where(.dark,.dark *){color:var(--color-orange-400)}.dark\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.dark\:text-red-400\/70:where(.dark,.dark *){color:#ff6568b3}@supports (color:color-mix(in lab,red,red)){.dark\:text-red-400\/70:where(.dark,.dark *){color:color-mix(in oklab,var(--color-red-400)70%,transparent)}}.dark\:text-white:where(.dark,.dark *){color:var(--color-white)}.dark\:text-yellow-200:where(.dark,.dark *){color:var(--color-yellow-200)}.dark\:text-yellow-200\/70:where(.dark,.dark *){color:#fff085b3}@supports (color:color-mix(in lab,red,red)){.dark\:text-yellow-200\/70:where(.dark,.dark *){color:color-mix(in oklab,var(--color-yellow-200)70%,transparent)}}@media(hover:hover){.dark\:hover\:bg-bambu-dark-tertiary:where(.dark,.dark *):hover{background-color:var(--color-bambu-dark-tertiary)}.dark\:hover\:bg-bambu-dark\/50:where(.dark,.dark *):hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-bambu-dark\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.dark\:hover\:bg-red-500\/30:where(.dark,.dark *):hover{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-red-500\/30:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.dark\:hover\:text-white:where(.dark,.dark *):hover{color:var(--color-white)}}.\[\&\:\:-webkit-slider-thumb\]\:h-3::-webkit-slider-thumb{height:calc(var(--spacing)*3)}.\[\&\:\:-webkit-slider-thumb\]\:w-3::-webkit-slider-thumb{width:calc(var(--spacing)*3)}.\[\&\:\:-webkit-slider-thumb\]\:cursor-pointer::-webkit-slider-thumb{cursor:pointer}.\[\&\:\:-webkit-slider-thumb\]\:appearance-none::-webkit-slider-thumb{appearance:none}.\[\&\:\:-webkit-slider-thumb\]\:rounded-full::-webkit-slider-thumb{border-radius:3.40282e38px}.\[\&\:\:-webkit-slider-thumb\]\:bg-bambu-green::-webkit-slider-thumb{background-color:var(--color-bambu-green)}}:root{--accent:#00ae42;--accent-light:#00c64d;--accent-dark:#009438;--status-ok:#22c55e;--status-error:#ef4444;--status-warning:#f59e0b;--bg-primary:#f5f5f5;--bg-secondary:#fff;--bg-tertiary:#e5e5e5;--text-primary:#1a1a1a;--text-secondary:#4a4a4a;--text-muted:#6b6b6b;--text-tertiary:gray;--border-color:#d4d4d4;--card-shadow:0 2px 8px #00000014;--glow-color:transparent;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,system-ui,sans-serif;font-weight:400;line-height:1.5}.dark{--bg-primary:#1a1a1a;--bg-secondary:#2d2d2d;--bg-tertiary:#3d3d3d;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:gray;--text-tertiary:#4a4a4a;--border-color:#3d3d3d;--card-shadow:0 4px 16px #0006}.bg-warm{--bg-primary:#faf8f5;--bg-secondary:#fffefa;--bg-tertiary:#e8e4dd;--text-primary:#2d2a26;--text-secondary:#5c5750;--text-muted:#7a756c;--text-tertiary:#9a9590;--border-color:#d8d4cc}.bg-cool{--bg-primary:#f0f4f8;--bg-secondary:#fff;--bg-tertiary:#dce4ec;--text-primary:#1a2530;--text-secondary:#4a5568;--text-muted:#6b7a8a;--text-tertiary:#8a9aaa;--border-color:#c8d4e0}.dark.bg-neutral{--bg-primary:#1a1a1a;--bg-secondary:#2d2d2d;--bg-tertiary:#3d3d3d;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:gray;--text-tertiary:#4a4a4a;--border-color:#3d3d3d}.dark.bg-warm{--bg-primary:#1c1a18;--bg-secondary:#2e2a26;--bg-tertiary:#3e3a36;--text-primary:#f5f0ea;--text-secondary:#b0a898;--text-muted:#8a8278;--text-tertiary:#5a5248;--border-color:#3e3a36}.dark.bg-cool{--bg-primary:#181c20;--bg-secondary:#262c32;--bg-tertiary:#363e46;--text-primary:#f0f4f8;--text-secondary:#98a8b8;--text-muted:#788898;--text-tertiary:#4a5a6a;--border-color:#363e46}.dark.bg-oled{--bg-primary:#000;--bg-secondary:#141414;--bg-tertiary:#1f1f1f;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:#707070;--text-tertiary:#404040;--border-color:#2a2a2a}.dark.bg-slate{--bg-primary:#0f172a;--bg-secondary:#1e293b;--bg-tertiary:#334155;--text-primary:#f1f5f9;--text-secondary:#94a3b8;--text-muted:#64748b;--text-tertiary:#475569;--border-color:#334155}.dark.bg-forest{--bg-primary:#121a16;--bg-secondary:#1c2a22;--bg-tertiary:#2a3d30;--text-primary:#e8f5ec;--text-secondary:#8aa894;--text-muted:#6a8874;--text-tertiary:#4a6854;--border-color:#2a3d30}.printer-control-buttons-container{container-type:inline-size}@container (max-width:220px){.printer-control-buttons{flex-direction:column;align-items:stretch}.printer-control-buttons>button{width:100%}}.style-glow{--card-shadow:0 2px 8px #00000014,0 0 25px var(--accent)}@supports (color:color-mix(in lab,red,red)){.style-glow{--card-shadow:0 2px 8px #00000014,0 0 25px color-mix(in srgb,var(--accent)12%,transparent)}}.dark.style-glow{--card-shadow:0 4px 20px #00000080,0 0 40px var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark.style-glow{--card-shadow:0 4px 20px #00000080,0 0 40px color-mix(in srgb,var(--accent)15%,transparent)}}.style-vibrant{--card-shadow:0 8px 30px #00000026,0 2px 8px #0000001a}.dark.style-vibrant{--card-shadow:0 10px 40px #0009,0 4px 12px #0006}.accent-green{--accent:#00ae42;--accent-light:#00c64d;--accent-dark:#009438}.accent-teal{--accent:#14b8a6;--accent-light:#2dd4bf;--accent-dark:#0d9488}.accent-blue{--accent:#3b82f6;--accent-light:#60a5fa;--accent-dark:#2563eb}.accent-orange{--accent:#f97316;--accent-light:#fb923c;--accent-dark:#ea580c}.accent-purple{--accent:#8b5cf6;--accent-light:#a78bfa;--accent-dark:#7c3aed}.accent-red{--accent:#ef4444;--accent-light:#f87171;--accent-dark:#dc2626}body{background-color:var(--bg-primary);color:var(--text-primary);min-height:100vh;margin:0;transition:background-color .2s,color .2s}#root{min-height:100vh}.text-white{color:var(--text-primary)}.bg-bambu-dark,.bg-bambu-dark-secondary,.bg-bambu-dark-tertiary,.border-bambu-dark-tertiary{transition:background-color .2s,border-color .2s}@keyframes slide-in{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}.animate-slide-in{animation:.2s ease-out slide-in}.icon-theme{opacity:.5}.dark .icon-theme{filter:invert();opacity:.4}.icon-green{filter:invert(48%)sepia(89%)saturate(459%)hue-rotate(93deg)brightness(95%)contrast(92%);opacity:1}.icon-heating{filter:invert(50%)sepia()saturate(1000%)hue-rotate(360deg)brightness()contrast();opacity:1}.dark .jogpad-theme{filter:brightness(.35)contrast(1.2)}.ams-empty-slot{background:repeating-linear-gradient(45deg,#444,#444 2px,#222 2px 4px)}.dark .ams-empty-slot{background:repeating-linear-gradient(45deg,#555,#555 2px,#333 2px 4px)}.touch-manipulation{touch-action:manipulation}.safe-area-bottom{padding-bottom:env(safe-area-inset-bottom,0)}.safe-area-top{padding-top:env(safe-area-inset-top,0)}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}@keyframes slide-in-left{0%{transform:translate(-100%)}to{transform:translate(0)}}.animate-slide-in-left{animation:.3s ease-out slide-in-left}.card-shadow{box-shadow:var(--card-shadow)}.calendar-scroll{scrollbar-width:thin;scrollbar-color:var(--text-muted)transparent}@supports (color:color-mix(in lab,red,red)){.calendar-scroll{scrollbar-color:color-mix(in srgb,var(--text-muted)60%,transparent)transparent}}.calendar-scroll::-webkit-scrollbar{width:8px}.calendar-scroll::-webkit-scrollbar-track{background:0 0}.calendar-scroll::-webkit-scrollbar-thumb{background-color:var(--text-muted)}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb{background-color:color-mix(in srgb,var(--text-muted)60%,transparent)}}.calendar-scroll::-webkit-scrollbar-thumb{border:2px solid var(--bg-secondary);border-radius:999px}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb{border:2px solid color-mix(in srgb,var(--bg-secondary)70%,transparent)}}.calendar-scroll::-webkit-scrollbar-thumb:hover{background-color:var(--text-muted)}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb,var(--text-muted)80%,transparent)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}} diff --git a/static/assets/index-DF7TfzH1.css b/static/assets/index-DF7TfzH1.css deleted file mode 100644 index 245cdd70a..000000000 --- a/static/assets/index-DF7TfzH1.css +++ /dev/null @@ -1 +0,0 @@ -@import"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap";@layer properties,theme,base,components,utilities;@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-100:oklch(93.6% .032 17.717);--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-red-800:oklch(44.4% .177 26.899);--color-red-950:oklch(25.8% .092 26.042);--color-orange-200:oklch(90.1% .076 70.697);--color-orange-300:oklch(83.7% .128 66.29);--color-orange-400:oklch(75% .183 55.934);--color-orange-500:oklch(70.5% .213 47.604);--color-orange-600:oklch(64.6% .222 41.116);--color-orange-700:oklch(55.3% .195 38.402);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-500:oklch(76.9% .188 70.08);--color-amber-600:oklch(66.6% .179 58.318);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-300:oklch(90.5% .182 98.111);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-lime-500:oklch(76.8% .233 130.85);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-300:oklch(87.1% .15 154.449);--color-green-400:oklch(79.2% .209 151.711);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-900:oklch(39.3% .095 152.535);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-700:oklch(50.8% .118 165.612);--color-teal-400:oklch(77.7% .152 181.912);--color-teal-500:oklch(70.4% .14 182.503);--color-cyan-300:oklch(86.5% .127 207.078);--color-cyan-400:oklch(78.9% .154 211.53);--color-cyan-500:oklch(71.5% .143 215.221);--color-cyan-600:oklch(60.9% .126 221.723);--color-sky-400:oklch(74.6% .16 232.661);--color-sky-500:oklch(68.5% .169 237.323);--color-blue-50:oklch(97% .014 254.604);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-indigo-400:oklch(67.3% .182 276.935);--color-indigo-500:oklch(58.5% .233 277.117);--color-violet-500:oklch(60.6% .25 292.717);--color-purple-300:oklch(82.7% .119 306.383);--color-purple-400:oklch(71.4% .203 305.504);--color-purple-500:oklch(62.7% .265 303.9);--color-purple-700:oklch(49.6% .265 301.924);--color-fuchsia-500:oklch(66.7% .295 322.15);--color-rose-300:oklch(81% .117 11.638);--color-rose-400:oklch(71.2% .194 13.428);--color-rose-500:oklch(64.5% .246 16.439);--color-rose-600:oklch(58.6% .253 17.585);--color-slate-300:oklch(86.9% .022 252.894);--color-slate-500:oklch(55.4% .046 257.417);--color-gray-50:oklch(98.5% .002 247.839);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-sm:24rem;--container-md:28rem;--container-lg:32rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--container-5xl:64rem;--container-6xl:72rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--text-3xl:1.875rem;--text-3xl--line-height: 1.2 ;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-wide:.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-tight:1.25;--radius-sm:.25rem;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--radius-2xl:1rem;--drop-shadow-md:0 3px 3px #0000001f;--drop-shadow-lg:0 4px 4px #00000026;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--blur-sm:8px;--blur-2xl:40px;--aspect-video:16/9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bambu-green:var(--accent);--color-bambu-green-light:var(--accent-light);--color-bambu-green-dark:var(--accent-dark);--color-status-ok:var(--status-ok);--color-status-error:var(--status-error);--color-status-warning:var(--status-warning);--color-bambu-dark:var(--bg-primary);--color-bambu-dark-secondary:var(--bg-secondary);--color-bambu-dark-tertiary:var(--bg-tertiary);--color-bambu-gray:var(--text-muted);--color-bambu-gray-light:var(--text-secondary);--color-bambu-gray-dark:var(--text-tertiary)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.-top-1{top:calc(var(--spacing)*-1)}.-top-1\.5{top:calc(var(--spacing)*-1.5)}.top-0{top:calc(var(--spacing)*0)}.top-0\.5{top:calc(var(--spacing)*.5)}.top-1{top:calc(var(--spacing)*1)}.top-1\.5{top:calc(var(--spacing)*1.5)}.top-1\/2{top:50%}.top-2{top:calc(var(--spacing)*2)}.top-2\.5{top:calc(var(--spacing)*2.5)}.top-4{top:calc(var(--spacing)*4)}.top-8{top:calc(var(--spacing)*8)}.top-\[2px\]{top:2px}.top-\[3px\]{top:3px}.top-\[52px\]{top:52px}.top-full{top:100%}.-right-1{right:calc(var(--spacing)*-1)}.-right-1\.5{right:calc(var(--spacing)*-1.5)}.right-0{right:calc(var(--spacing)*0)}.right-1{right:calc(var(--spacing)*1)}.right-1\.5{right:calc(var(--spacing)*1.5)}.right-2{right:calc(var(--spacing)*2)}.right-3{right:calc(var(--spacing)*3)}.right-4{right:calc(var(--spacing)*4)}.right-6{right:calc(var(--spacing)*6)}.right-12{right:calc(var(--spacing)*12)}.right-\[5\.5rem\]{right:5.5rem}.right-full{right:100%}.-bottom-1{bottom:calc(var(--spacing)*-1)}.-bottom-2{bottom:calc(var(--spacing)*-2)}.-bottom-7{bottom:calc(var(--spacing)*-7)}.bottom-0{bottom:calc(var(--spacing)*0)}.bottom-1{bottom:calc(var(--spacing)*1)}.bottom-2{bottom:calc(var(--spacing)*2)}.bottom-4{bottom:calc(var(--spacing)*4)}.bottom-6{bottom:calc(var(--spacing)*6)}.bottom-8{bottom:calc(var(--spacing)*8)}.bottom-full{bottom:100%}.-left-2{left:calc(var(--spacing)*-2)}.left-0{left:calc(var(--spacing)*0)}.left-1{left:calc(var(--spacing)*1)}.left-1\/2{left:50%}.left-2{left:calc(var(--spacing)*2)}.left-2\.5{left:calc(var(--spacing)*2.5)}.left-3{left:calc(var(--spacing)*3)}.left-4{left:calc(var(--spacing)*4)}.left-10{left:calc(var(--spacing)*10)}.left-12{left:calc(var(--spacing)*12)}.left-\[2px\]{left:2px}.left-\[3px\]{left:3px}.left-full{left:100%}.z-0{z-index:0}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-60,.z-\[60\]{z-index:60}.z-\[100\]{z-index:100}.col-span-1{grid-column:span 1/span 1}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-4{grid-column:span 4/span 4}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.-m-4{margin:calc(var(--spacing)*-4)}.-mx-4{margin-inline:calc(var(--spacing)*-4)}.mx-1{margin-inline:calc(var(--spacing)*1)}.mx-2{margin-inline:calc(var(--spacing)*2)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.my-1{margin-block:calc(var(--spacing)*1)}.my-8{margin-block:calc(var(--spacing)*8)}.-mt-2{margin-top:calc(var(--spacing)*-2)}.mt-0\.5{margin-top:calc(var(--spacing)*.5)}.mt-1{margin-top:calc(var(--spacing)*1)}.mt-1\.5{margin-top:calc(var(--spacing)*1.5)}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-3{margin-top:calc(var(--spacing)*3)}.mt-4{margin-top:calc(var(--spacing)*4)}.mt-6{margin-top:calc(var(--spacing)*6)}.mt-8{margin-top:calc(var(--spacing)*8)}.mt-14{margin-top:calc(var(--spacing)*14)}.mt-auto{margin-top:auto}.mr-1{margin-right:calc(var(--spacing)*1)}.mr-2{margin-right:calc(var(--spacing)*2)}.mr-3{margin-right:calc(var(--spacing)*3)}.mr-4{margin-right:calc(var(--spacing)*4)}.mr-auto{margin-right:auto}.-mb-px{margin-bottom:-1px}.mb-0\.5{margin-bottom:calc(var(--spacing)*.5)}.mb-1{margin-bottom:calc(var(--spacing)*1)}.mb-1\.5{margin-bottom:calc(var(--spacing)*1.5)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-5{margin-bottom:calc(var(--spacing)*5)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.mb-8{margin-bottom:calc(var(--spacing)*8)}.-ml-1{margin-left:calc(var(--spacing)*-1)}.-ml-2{margin-left:calc(var(--spacing)*-2)}.ml-0\.5{margin-left:calc(var(--spacing)*.5)}.ml-1{margin-left:calc(var(--spacing)*1)}.ml-1\.5{margin-left:calc(var(--spacing)*1.5)}.ml-2{margin-left:calc(var(--spacing)*2)}.ml-3{margin-left:calc(var(--spacing)*3)}.ml-4{margin-left:calc(var(--spacing)*4)}.ml-6{margin-left:calc(var(--spacing)*6)}.ml-7{margin-left:calc(var(--spacing)*7)}.ml-9{margin-left:calc(var(--spacing)*9)}.ml-14{margin-left:calc(var(--spacing)*14)}.ml-16{margin-left:calc(var(--spacing)*16)}.ml-64{margin-left:calc(var(--spacing)*64)}.ml-auto{margin-left:auto}.line-clamp-1{-webkit-line-clamp:1;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-square{aspect-ratio:1}.aspect-video{aspect-ratio:var(--aspect-video)}.h-0{height:calc(var(--spacing)*0)}.h-0\.5{height:calc(var(--spacing)*.5)}.h-1{height:calc(var(--spacing)*1)}.h-1\.5{height:calc(var(--spacing)*1.5)}.h-2{height:calc(var(--spacing)*2)}.h-2\.5{height:calc(var(--spacing)*2.5)}.h-3{height:calc(var(--spacing)*3)}.h-3\.5{height:calc(var(--spacing)*3.5)}.h-4{height:calc(var(--spacing)*4)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-7{height:calc(var(--spacing)*7)}.h-8{height:calc(var(--spacing)*8)}.h-10{height:calc(var(--spacing)*10)}.h-12{height:calc(var(--spacing)*12)}.h-14{height:calc(var(--spacing)*14)}.h-16{height:calc(var(--spacing)*16)}.h-20{height:calc(var(--spacing)*20)}.h-24{height:calc(var(--spacing)*24)}.h-48{height:calc(var(--spacing)*48)}.h-64{height:calc(var(--spacing)*64)}.h-80{height:calc(var(--spacing)*80)}.h-\[18px\]{height:18px}.h-\[38px\]{height:38px}.h-\[80vh\]{height:80vh}.h-\[160px\]{height:160px}.h-\[300px\]{height:300px}.h-\[calc\(100\%-40px\)\]{height:calc(100% - 40px)}.h-full{height:100%}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing)*40)}.max-h-48{max-height:calc(var(--spacing)*48)}.max-h-60{max-height:calc(var(--spacing)*60)}.max-h-64{max-height:calc(var(--spacing)*64)}.max-h-80{max-height:calc(var(--spacing)*80)}.max-h-96{max-height:calc(var(--spacing)*96)}.max-h-\[60vh\]{max-height:60vh}.max-h-\[80vh\]{max-height:80vh}.max-h-\[85vh\]{max-height:85vh}.max-h-\[90vh\]{max-height:90vh}.max-h-\[300px\]{max-height:300px}.max-h-\[400px\]{max-height:400px}.max-h-\[500px\]{max-height:500px}.max-h-\[600px\]{max-height:600px}.max-h-\[calc\(90vh-80px\)\]{max-height:calc(90vh - 80px)}.max-h-\[calc\(100vh-320px\)\]{max-height:calc(100vh - 320px)}.max-h-full{max-height:100%}.min-h-0{min-height:calc(var(--spacing)*0)}.min-h-\[44px\]{min-height:44px}.min-h-\[48px\]{min-height:48px}.min-h-\[80px\]{min-height:80px}.min-h-\[120px\]{min-height:120px}.min-h-\[300px\]{min-height:300px}.min-h-\[400px\]{min-height:400px}.min-h-\[calc\(100vh-64px\)\]{min-height:calc(100vh - 64px)}.min-h-full{min-height:100%}.min-h-screen{min-height:100vh}.w-0{width:calc(var(--spacing)*0)}.w-0\.5{width:calc(var(--spacing)*.5)}.w-1\.5{width:calc(var(--spacing)*1.5)}.w-1\/3{width:33.3333%}.w-2{width:calc(var(--spacing)*2)}.w-2\.5{width:calc(var(--spacing)*2.5)}.w-3{width:calc(var(--spacing)*3)}.w-3\.5{width:calc(var(--spacing)*3.5)}.w-4{width:calc(var(--spacing)*4)}.w-4\.5{width:calc(var(--spacing)*4.5)}.w-5{width:calc(var(--spacing)*5)}.w-6{width:calc(var(--spacing)*6)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-9{width:calc(var(--spacing)*9)}.w-10{width:calc(var(--spacing)*10)}.w-11{width:calc(var(--spacing)*11)}.w-12{width:calc(var(--spacing)*12)}.w-14{width:calc(var(--spacing)*14)}.w-16{width:calc(var(--spacing)*16)}.w-20{width:calc(var(--spacing)*20)}.w-24{width:calc(var(--spacing)*24)}.w-32{width:calc(var(--spacing)*32)}.w-36{width:calc(var(--spacing)*36)}.w-40{width:calc(var(--spacing)*40)}.w-44{width:calc(var(--spacing)*44)}.w-48{width:calc(var(--spacing)*48)}.w-52{width:calc(var(--spacing)*52)}.w-56{width:calc(var(--spacing)*56)}.w-64{width:calc(var(--spacing)*64)}.w-72{width:calc(var(--spacing)*72)}.w-96{width:calc(var(--spacing)*96)}.w-\[560px\]{width:560px}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.w-px{width:1px}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-5xl{max-width:var(--container-5xl)}.max-w-6xl{max-width:var(--container-6xl)}.max-w-32{max-width:calc(var(--spacing)*32)}.max-w-\[40\%\]{max-width:40%}.max-w-\[50\%\]{max-width:50%}.max-w-\[80px\]{max-width:80px}.max-w-\[90vw\]{max-width:90vw}.max-w-\[100px\]{max-width:100px}.max-w-\[120px\]{max-width:120px}.max-w-\[150px\]{max-width:150px}.max-w-\[160px\]{max-width:160px}.max-w-\[200px\]{max-width:200px}.max-w-\[280px\]{max-width:280px}.max-w-\[600px\]{max-width:600px}.max-w-full{max-width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-sm{max-width:var(--container-sm)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing)*0)}.min-w-48{min-width:calc(var(--spacing)*48)}.min-w-\[16px\]{min-width:16px}.min-w-\[18px\]{min-width:18px}.min-w-\[32px\]{min-width:32px}.min-w-\[40px\]{min-width:40px}.min-w-\[48px\]{min-width:48px}.min-w-\[80px\]{min-width:80px}.min-w-\[100px\]{min-width:100px}.min-w-\[120px\]{min-width:120px}.min-w-\[140px\]{min-width:140px}.min-w-\[150px\]{min-width:150px}.min-w-\[160px\]{min-width:160px}.min-w-\[180px\]{min-width:180px}.min-w-\[200px\]{min-width:200px}.min-w-full{min-width:100%}.flex-1{flex:1}.flex-\[2_1_190px\]{flex:2 190px}.flex-shrink{flex-shrink:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-x-full{--tw-translate-x:-100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0{--tw-translate-x:calc(var(--spacing)*0);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-0\.5{--tw-translate-x:calc(var(--spacing)*.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-1{--tw-translate-x:calc(var(--spacing)*1);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-3\.5{--tw-translate-x:calc(var(--spacing)*3.5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-4{--tw-translate-x:calc(var(--spacing)*4);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-5{--tw-translate-x:calc(var(--spacing)*5);translate:var(--tw-translate-x)var(--tw-translate-y)}.translate-x-6{--tw-translate-x:calc(var(--spacing)*6);translate:var(--tw-translate-x)var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.scale-105{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.scale-\[1\.02\]{scale:1.02}.-rotate-90{rotate:-90deg}.rotate-90{rotate:90deg}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-ew-resize{cursor:ew-resize}.cursor-grab{cursor:grab}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.cursor-row-resize{cursor:row-resize}.cursor-se-resize{cursor:se-resize}.touch-manipulation{touch-action:manipulation}.resize{resize:both}.resize-none{resize:none}.resize-y{resize:vertical}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-12{grid-template-columns:repeat(12,minmax(0,1fr))}.grid-cols-\[auto_1fr_100px_100px_100px_80px\]{grid-template-columns:auto 1fr 100px 100px 100px 80px}.grid-cols-\[auto_1fr_120px_100px_100px_100px_80px\]{grid-template-columns:auto 1fr 120px 100px 100px 100px 80px}.flex-col{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0{gap:calc(var(--spacing)*0)}.gap-0\.5{gap:calc(var(--spacing)*.5)}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}.gap-8{gap:calc(var(--spacing)*8)}.gap-\[3px\]{gap:3px}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2.5)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2.5)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-8>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*8)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*8)*calc(1 - var(--tw-space-y-reverse)))}.gap-x-4{column-gap:calc(var(--spacing)*4)}.gap-x-6{column-gap:calc(var(--spacing)*6)}.gap-y-1{row-gap:calc(var(--spacing)*1)}.gap-y-2{row-gap:calc(var(--spacing)*2)}.gap-y-3{row-gap:calc(var(--spacing)*3)}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px*var(--tw-divide-y-reverse));border-bottom-width:calc(1px*calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-bambu-dark-tertiary>:not(:last-child)),:where(.divide-bambu-dark-tertiary\/30>:not(:last-child)){border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){:where(.divide-bambu-dark-tertiary\/30>:not(:last-child)){border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}:where(.divide-bambu-gray\/20>:not(:last-child)){border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){:where(.divide-bambu-gray\/20>:not(:last-child)){border-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-none{border-radius:0}.rounded-sm{border-radius:var(--radius-sm)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-t-lg{border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg)}.rounded-t-xl{border-top-left-radius:var(--radius-xl);border-top-right-radius:var(--radius-xl)}.\!rounded-l-none{border-top-left-radius:0!important;border-bottom-left-radius:0!important}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-l-lg{border-top-left-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.rounded-tl{border-top-left-radius:.25rem}.\!rounded-r-none{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-r-lg{border-top-right-radius:var(--radius-lg);border-bottom-right-radius:var(--radius-lg)}.rounded-b-lg{border-bottom-right-radius:var(--radius-lg);border-bottom-left-radius:var(--radius-lg)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-4{border-style:var(--tw-border-style);border-width:4px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-\[5px\]{border-top-style:var(--tw-border-style);border-top-width:5px}.border-t-\[6px\]{border-top-style:var(--tw-border-style);border-top-width:6px}.\!border-r-0{border-right-style:var(--tw-border-style)!important;border-right-width:0!important}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-r-\[5px\]{border-right-style:var(--tw-border-style);border-right-width:5px}.border-r-\[6px\]{border-right-style:var(--tw-border-style);border-right-width:6px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-b-\[6px\]{border-bottom-style:var(--tw-border-style);border-bottom-width:6px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-l-\[5px\]{border-left-style:var(--tw-border-style);border-left-width:5px}.border-l-\[6px\]{border-left-style:var(--tw-border-style);border-left-width:6px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.\!border-green-500{border-color:var(--color-green-500)!important}.border-amber-500\/20{border-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/20{border-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.border-amber-500\/30{border-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.border-amber-500\/30{border-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.border-bambu-dark-tertiary,.border-bambu-dark-tertiary\/20{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/20{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)20%,transparent)}}.border-bambu-dark-tertiary\/30{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/30{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.border-bambu-dark-tertiary\/40{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/40{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)40%,transparent)}}.border-bambu-dark-tertiary\/50{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.border-bambu-dark-tertiary\/50{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.border-bambu-gray,.border-bambu-gray\/20{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/20{border-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.border-bambu-gray\/30{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/30{border-color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.border-bambu-gray\/50{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.border-bambu-gray\/50{border-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.border-bambu-green,.border-bambu-green\/20{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/20{border-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.border-bambu-green\/30{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/30{border-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.border-bambu-green\/40{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/40{border-color:color-mix(in oklab,var(--color-bambu-green)40%,transparent)}}.border-bambu-green\/50{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.border-bambu-green\/50{border-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.border-blue-400\/20{border-color:#54a2ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-400\/20{border-color:color-mix(in oklab,var(--color-blue-400)20%,transparent)}}.border-blue-500{border-color:var(--color-blue-500)}.border-blue-500\/20{border-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/20{border-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.border-blue-500\/30{border-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/30{border-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.border-blue-500\/50{border-color:#3080ff80}@supports (color:color-mix(in lab,red,red)){.border-blue-500\/50{border-color:color-mix(in oklab,var(--color-blue-500)50%,transparent)}}.border-gray-200{border-color:var(--color-gray-200)}.border-gray-300{border-color:var(--color-gray-300)}.border-gray-400\/20{border-color:#99a1af33}@supports (color:color-mix(in lab,red,red)){.border-gray-400\/20{border-color:color-mix(in oklab,var(--color-gray-400)20%,transparent)}}.border-gray-500\/20{border-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.border-gray-500\/20{border-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.border-gray-700{border-color:var(--color-gray-700)}.border-green-300{border-color:var(--color-green-300)}.border-green-500\/30{border-color:#00c7584d}@supports (color:color-mix(in lab,red,red)){.border-green-500\/30{border-color:color-mix(in oklab,var(--color-green-500)30%,transparent)}}.border-green-500\/50{border-color:#00c75880}@supports (color:color-mix(in lab,red,red)){.border-green-500\/50{border-color:color-mix(in oklab,var(--color-green-500)50%,transparent)}}.border-orange-400\/20{border-color:#ff8b1a33}@supports (color:color-mix(in lab,red,red)){.border-orange-400\/20{border-color:color-mix(in oklab,var(--color-orange-400)20%,transparent)}}.border-orange-400\/50{border-color:#ff8b1a80}@supports (color:color-mix(in lab,red,red)){.border-orange-400\/50{border-color:color-mix(in oklab,var(--color-orange-400)50%,transparent)}}.border-orange-500\/20{border-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/20{border-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.border-orange-500\/30{border-color:#fe6e004d}@supports (color:color-mix(in lab,red,red)){.border-orange-500\/30{border-color:color-mix(in oklab,var(--color-orange-500)30%,transparent)}}.border-purple-400\/20{border-color:#c07eff33}@supports (color:color-mix(in lab,red,red)){.border-purple-400\/20{border-color:color-mix(in oklab,var(--color-purple-400)20%,transparent)}}.border-purple-500\/20{border-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/20{border-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.border-purple-500\/30{border-color:#ac4bff4d}@supports (color:color-mix(in lab,red,red)){.border-purple-500\/30{border-color:color-mix(in oklab,var(--color-purple-500)30%,transparent)}}.border-red-300{border-color:var(--color-red-300)}.border-red-500{border-color:var(--color-red-500)}.border-red-500\/20{border-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.border-red-500\/20{border-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.border-red-500\/30{border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.border-red-500\/30{border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.border-red-500\/50{border-color:#fb2c3680}@supports (color:color-mix(in lab,red,red)){.border-red-500\/50{border-color:color-mix(in oklab,var(--color-red-500)50%,transparent)}}.border-red-800\/70{border-color:#9f0712b3}@supports (color:color-mix(in lab,red,red)){.border-red-800\/70{border-color:color-mix(in oklab,var(--color-red-800)70%,transparent)}}.border-slate-500{border-color:var(--color-slate-500)}.border-status-error\/20{border-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.border-status-error\/20{border-color:color-mix(in oklab,var(--color-status-error)20%,transparent)}}.border-status-ok,.border-status-ok\/20{border-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.border-status-ok\/20{border-color:color-mix(in oklab,var(--color-status-ok)20%,transparent)}}.border-status-warning\/20{border-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.border-status-warning\/20{border-color:color-mix(in oklab,var(--color-status-warning)20%,transparent)}}.border-transparent{border-color:#0000}.border-white\/10{border-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.border-white\/10{border-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.border-white\/20{border-color:#fff3}@supports (color:color-mix(in lab,red,red)){.border-white\/20{border-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.border-white\/30{border-color:#ffffff4d}@supports (color:color-mix(in lab,red,red)){.border-white\/30{border-color:color-mix(in oklab,var(--color-white)30%,transparent)}}.border-yellow-400\/20{border-color:#fac80033}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/20{border-color:color-mix(in oklab,var(--color-yellow-400)20%,transparent)}}.border-yellow-400\/30{border-color:#fac8004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/30{border-color:color-mix(in oklab,var(--color-yellow-400)30%,transparent)}}.border-yellow-400\/50{border-color:#fac80080}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/50{border-color:color-mix(in oklab,var(--color-yellow-400)50%,transparent)}}.border-yellow-500{border-color:var(--color-yellow-500)}.border-yellow-500\/20{border-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/20{border-color:color-mix(in oklab,var(--color-yellow-500)20%,transparent)}}.border-yellow-500\/30{border-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/30{border-color:color-mix(in oklab,var(--color-yellow-500)30%,transparent)}}.border-yellow-500\/50{border-color:#edb20080}@supports (color:color-mix(in lab,red,red)){.border-yellow-500\/50{border-color:color-mix(in oklab,var(--color-yellow-500)50%,transparent)}}.border-t-bambu-dark-tertiary{border-top-color:var(--color-bambu-dark-tertiary)}.border-t-transparent{border-top-color:#0000}.border-r-transparent{border-right-color:#0000}.border-b-bambu-dark-tertiary{border-bottom-color:var(--color-bambu-dark-tertiary)}.border-l-transparent{border-left-color:#0000}.border-l-yellow-500{border-left-color:var(--color-yellow-500)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-500{background-color:var(--color-amber-500)}.bg-amber-500\/5{background-color:#f99c000d}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/5{background-color:color-mix(in oklab,var(--color-amber-500)5%,transparent)}}.bg-amber-500\/10{background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/10{background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.bg-amber-500\/20{background-color:#f99c0033}@supports (color:color-mix(in lab,red,red)){.bg-amber-500\/20{background-color:color-mix(in oklab,var(--color-amber-500)20%,transparent)}}.bg-amber-600\/20{background-color:#dd740033}@supports (color:color-mix(in lab,red,red)){.bg-amber-600\/20{background-color:color-mix(in oklab,var(--color-amber-600)20%,transparent)}}.bg-bambu-dark{background-color:var(--color-bambu-dark)}.bg-bambu-dark-secondary,.bg-bambu-dark-secondary\/30{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/30{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)30%,transparent)}}.bg-bambu-dark-secondary\/50{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/50{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)50%,transparent)}}.bg-bambu-dark-secondary\/60{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/60{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)60%,transparent)}}.bg-bambu-dark-secondary\/90{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/90{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)90%,transparent)}}.bg-bambu-dark-secondary\/95{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-secondary\/95{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)95%,transparent)}}.bg-bambu-dark-tertiary,.bg-bambu-dark-tertiary\/20{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/20{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)20%,transparent)}}.bg-bambu-dark-tertiary\/30{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/30{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.bg-bambu-dark-tertiary\/40{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/40{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)40%,transparent)}}.bg-bambu-dark-tertiary\/50{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/50{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.bg-bambu-dark-tertiary\/70{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/70{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)70%,transparent)}}.bg-bambu-dark-tertiary\/80{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark-tertiary\/80{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.bg-bambu-dark\/20{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/20{background-color:color-mix(in oklab,var(--color-bambu-dark)20%,transparent)}}.bg-bambu-dark\/30{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/30{background-color:color-mix(in oklab,var(--color-bambu-dark)30%,transparent)}}.bg-bambu-dark\/50{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/50{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.bg-bambu-dark\/80{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/80{background-color:color-mix(in oklab,var(--color-bambu-dark)80%,transparent)}}.bg-bambu-dark\/90{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-dark\/90{background-color:color-mix(in oklab,var(--color-bambu-dark)90%,transparent)}}.bg-bambu-gray\/10{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/10{background-color:color-mix(in oklab,var(--color-bambu-gray)10%,transparent)}}.bg-bambu-gray\/20{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/20{background-color:color-mix(in oklab,var(--color-bambu-gray)20%,transparent)}}.bg-bambu-gray\/30{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/30{background-color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.bg-bambu-gray\/50{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/50{background-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.bg-bambu-gray\/90{background-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-gray\/90{background-color:color-mix(in oklab,var(--color-bambu-gray)90%,transparent)}}.bg-bambu-green,.bg-bambu-green\/5{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/5{background-color:color-mix(in oklab,var(--color-bambu-green)5%,transparent)}}.bg-bambu-green\/10{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/10{background-color:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.bg-bambu-green\/20{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/20{background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.bg-bambu-green\/30{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/30{background-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.bg-bambu-green\/50{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/50{background-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.bg-bambu-green\/75{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/75{background-color:color-mix(in oklab,var(--color-bambu-green)75%,transparent)}}.bg-bambu-green\/90{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.bg-bambu-green\/90{background-color:color-mix(in oklab,var(--color-bambu-green)90%,transparent)}}.bg-black{background-color:var(--color-black)}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab,red,red)){.bg-black\/30{background-color:color-mix(in oklab,var(--color-black)30%,transparent)}}.bg-black\/40{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.bg-black\/40{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab,red,red)){.bg-black\/60{background-color:color-mix(in oklab,var(--color-black)60%,transparent)}}.bg-black\/70{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.bg-black\/70{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.bg-black\/80{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.bg-black\/80{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.bg-black\/90{background-color:#000000e6}@supports (color:color-mix(in lab,red,red)){.bg-black\/90{background-color:color-mix(in oklab,var(--color-black)90%,transparent)}}.bg-blue-50{background-color:var(--color-blue-50)}.bg-blue-100{background-color:var(--color-blue-100)}.bg-blue-400{background-color:var(--color-blue-400)}.bg-blue-400\/10{background-color:#54a2ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-400\/10{background-color:color-mix(in oklab,var(--color-blue-400)10%,transparent)}}.bg-blue-500{background-color:var(--color-blue-500)}.bg-blue-500\/5{background-color:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/5{background-color:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.bg-blue-500\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/10{background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.bg-blue-500\/20{background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/20{background-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.bg-blue-500\/30{background-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/30{background-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.bg-blue-500\/90{background-color:#3080ffe6}@supports (color:color-mix(in lab,red,red)){.bg-blue-500\/90{background-color:color-mix(in oklab,var(--color-blue-500)90%,transparent)}}.bg-blue-600{background-color:var(--color-blue-600)}.bg-blue-600\/20{background-color:#155dfc33}@supports (color:color-mix(in lab,red,red)){.bg-blue-600\/20{background-color:color-mix(in oklab,var(--color-blue-600)20%,transparent)}}.bg-cyan-500{background-color:var(--color-cyan-500)}.bg-cyan-500\/10{background-color:#00b7d71a}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/10{background-color:color-mix(in oklab,var(--color-cyan-500)10%,transparent)}}.bg-cyan-500\/20{background-color:#00b7d733}@supports (color:color-mix(in lab,red,red)){.bg-cyan-500\/20{background-color:color-mix(in oklab,var(--color-cyan-500)20%,transparent)}}.bg-cyan-600\/20{background-color:#0092b533}@supports (color:color-mix(in lab,red,red)){.bg-cyan-600\/20{background-color:color-mix(in oklab,var(--color-cyan-600)20%,transparent)}}.bg-emerald-500\/20{background-color:#00bb7f33}@supports (color:color-mix(in lab,red,red)){.bg-emerald-500\/20{background-color:color-mix(in oklab,var(--color-emerald-500)20%,transparent)}}.bg-emerald-700{background-color:var(--color-emerald-700)}.bg-fuchsia-500{background-color:var(--color-fuchsia-500)}.bg-gray-50{background-color:var(--color-gray-50)}.bg-gray-100{background-color:var(--color-gray-100)}.bg-gray-400{background-color:var(--color-gray-400)}.bg-gray-400\/10{background-color:#99a1af1a}@supports (color:color-mix(in lab,red,red)){.bg-gray-400\/10{background-color:color-mix(in oklab,var(--color-gray-400)10%,transparent)}}.bg-gray-500{background-color:var(--color-gray-500)}.bg-gray-500\/20{background-color:#6a728233}@supports (color:color-mix(in lab,red,red)){.bg-gray-500\/20{background-color:color-mix(in oklab,var(--color-gray-500)20%,transparent)}}.bg-gray-800{background-color:var(--color-gray-800)}.bg-gray-900{background-color:var(--color-gray-900)}.bg-green-100{background-color:var(--color-green-100)}.bg-green-400{background-color:var(--color-green-400)}.bg-green-500{background-color:var(--color-green-500)}.bg-green-500\/10{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/10{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.bg-green-500\/20{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.bg-green-500\/20{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.bg-green-900\/50{background-color:#0d542b80}@supports (color:color-mix(in lab,red,red)){.bg-green-900\/50{background-color:color-mix(in oklab,var(--color-green-900)50%,transparent)}}.bg-indigo-500\/20{background-color:#625fff33}@supports (color:color-mix(in lab,red,red)){.bg-indigo-500\/20{background-color:color-mix(in oklab,var(--color-indigo-500)20%,transparent)}}.bg-lime-500{background-color:var(--color-lime-500)}.bg-orange-400\/10{background-color:#ff8b1a1a}@supports (color:color-mix(in lab,red,red)){.bg-orange-400\/10{background-color:color-mix(in oklab,var(--color-orange-400)10%,transparent)}}.bg-orange-500{background-color:var(--color-orange-500)}.bg-orange-500\/10{background-color:#fe6e001a}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/10{background-color:color-mix(in oklab,var(--color-orange-500)10%,transparent)}}.bg-orange-500\/20{background-color:#fe6e0033}@supports (color:color-mix(in lab,red,red)){.bg-orange-500\/20{background-color:color-mix(in oklab,var(--color-orange-500)20%,transparent)}}.bg-orange-600{background-color:var(--color-orange-600)}.bg-orange-600\/20{background-color:#f0510033}@supports (color:color-mix(in lab,red,red)){.bg-orange-600\/20{background-color:color-mix(in oklab,var(--color-orange-600)20%,transparent)}}.bg-purple-400\/10{background-color:#c07eff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-400\/10{background-color:color-mix(in oklab,var(--color-purple-400)10%,transparent)}}.bg-purple-500{background-color:var(--color-purple-500)}.bg-purple-500\/10{background-color:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/10{background-color:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.bg-purple-500\/20{background-color:#ac4bff33}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/20{background-color:color-mix(in oklab,var(--color-purple-500)20%,transparent)}}.bg-purple-500\/80{background-color:#ac4bffcc}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/80{background-color:color-mix(in oklab,var(--color-purple-500)80%,transparent)}}.bg-purple-500\/90{background-color:#ac4bffe6}@supports (color:color-mix(in lab,red,red)){.bg-purple-500\/90{background-color:color-mix(in oklab,var(--color-purple-500)90%,transparent)}}.bg-purple-700{background-color:var(--color-purple-700)}.bg-red-50{background-color:var(--color-red-50)}.bg-red-100{background-color:var(--color-red-100)}.bg-red-400{background-color:var(--color-red-400)}.bg-red-500{background-color:var(--color-red-500)}.bg-red-500\/5{background-color:#fb2c360d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/5{background-color:color-mix(in oklab,var(--color-red-500)5%,transparent)}}.bg-red-500\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/10{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.bg-red-500\/15{background-color:#fb2c3626}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/15{background-color:color-mix(in oklab,var(--color-red-500)15%,transparent)}}.bg-red-500\/20{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/20{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.bg-red-500\/30{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/30{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.bg-red-500\/40{background-color:#fb2c3666}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/40{background-color:color-mix(in oklab,var(--color-red-500)40%,transparent)}}.bg-red-500\/80{background-color:#fb2c36cc}@supports (color:color-mix(in lab,red,red)){.bg-red-500\/80{background-color:color-mix(in oklab,var(--color-red-500)80%,transparent)}}.bg-red-600{background-color:var(--color-red-600)}.bg-red-950\/70{background-color:#460809b3}@supports (color:color-mix(in lab,red,red)){.bg-red-950\/70{background-color:color-mix(in oklab,var(--color-red-950)70%,transparent)}}.bg-rose-500\/20{background-color:#ff235733}@supports (color:color-mix(in lab,red,red)){.bg-rose-500\/20{background-color:color-mix(in oklab,var(--color-rose-500)20%,transparent)}}.bg-rose-600\/20{background-color:#e7004433}@supports (color:color-mix(in lab,red,red)){.bg-rose-600\/20{background-color:color-mix(in oklab,var(--color-rose-600)20%,transparent)}}.bg-sky-500{background-color:var(--color-sky-500)}.bg-sky-500\/20{background-color:#00a5ef33}@supports (color:color-mix(in lab,red,red)){.bg-sky-500\/20{background-color:color-mix(in oklab,var(--color-sky-500)20%,transparent)}}.bg-status-error,.bg-status-error\/10{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/10{background-color:color-mix(in oklab,var(--color-status-error)10%,transparent)}}.bg-status-error\/20{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/20{background-color:color-mix(in oklab,var(--color-status-error)20%,transparent)}}.bg-status-error\/80{background-color:var(--color-status-error)}@supports (color:color-mix(in lab,red,red)){.bg-status-error\/80{background-color:color-mix(in oklab,var(--color-status-error)80%,transparent)}}.bg-status-ok,.bg-status-ok\/10{background-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.bg-status-ok\/10{background-color:color-mix(in oklab,var(--color-status-ok)10%,transparent)}}.bg-status-ok\/20{background-color:var(--color-status-ok)}@supports (color:color-mix(in lab,red,red)){.bg-status-ok\/20{background-color:color-mix(in oklab,var(--color-status-ok)20%,transparent)}}.bg-status-warning\/10{background-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.bg-status-warning\/10{background-color:color-mix(in oklab,var(--color-status-warning)10%,transparent)}}.bg-status-warning\/20{background-color:var(--color-status-warning)}@supports (color:color-mix(in lab,red,red)){.bg-status-warning\/20{background-color:color-mix(in oklab,var(--color-status-warning)20%,transparent)}}.bg-teal-500{background-color:var(--color-teal-500)}.bg-teal-500\/20{background-color:#00baa733}@supports (color:color-mix(in lab,red,red)){.bg-teal-500\/20{background-color:color-mix(in oklab,var(--color-teal-500)20%,transparent)}}.bg-transparent{background-color:#0000}.bg-violet-500{background-color:var(--color-violet-500)}.bg-white{background-color:var(--color-white)}.bg-white\/20{background-color:#fff3}@supports (color:color-mix(in lab,red,red)){.bg-white\/20{background-color:color-mix(in oklab,var(--color-white)20%,transparent)}}.bg-white\/50{background-color:#ffffff80}@supports (color:color-mix(in lab,red,red)){.bg-white\/50{background-color:color-mix(in oklab,var(--color-white)50%,transparent)}}.bg-white\/70{background-color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.bg-white\/70{background-color:color-mix(in oklab,var(--color-white)70%,transparent)}}.bg-white\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.bg-white\/90{background-color:color-mix(in oklab,var(--color-white)90%,transparent)}}.bg-yellow-400{background-color:var(--color-yellow-400)}.bg-yellow-400\/10{background-color:#fac8001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-400\/10{background-color:color-mix(in oklab,var(--color-yellow-400)10%,transparent)}}.bg-yellow-400\/20{background-color:#fac80033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-400\/20{background-color:color-mix(in oklab,var(--color-yellow-400)20%,transparent)}}.bg-yellow-500{background-color:var(--color-yellow-500)}.bg-yellow-500\/5{background-color:#edb2000d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/5{background-color:color-mix(in oklab,var(--color-yellow-500)5%,transparent)}}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-yellow-500\/20{background-color:#edb20033}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/20{background-color:color-mix(in oklab,var(--color-yellow-500)20%,transparent)}}.bg-gradient-to-b{--tw-gradient-position:to bottom in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-br{--tw-gradient-position:to bottom right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-r{--tw-gradient-position:to right in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.bg-gradient-to-t{--tw-gradient-position:to top in oklab;background-image:linear-gradient(var(--tw-gradient-stops))}.from-bambu-dark-secondary{--tw-gradient-from:var(--color-bambu-dark-secondary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-bambu-green\/10{--tw-gradient-from:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.from-bambu-green\/10{--tw-gradient-from:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.from-bambu-green\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-black\/80{--tw-gradient-from:#000c}@supports (color:color-mix(in lab,red,red)){.from-black\/80{--tw-gradient-from:color-mix(in oklab,var(--color-black)80%,transparent)}}.from-black\/80{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-blue-500\/5{--tw-gradient-from:#3080ff0d}@supports (color:color-mix(in lab,red,red)){.from-blue-500\/5{--tw-gradient-from:color-mix(in oklab,var(--color-blue-500)5%,transparent)}}.from-blue-500\/5{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-blue-500\/10{--tw-gradient-from:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.from-blue-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.from-blue-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-gray-500\/10{--tw-gradient-from:#6a72821a}@supports (color:color-mix(in lab,red,red)){.from-gray-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-gray-500)10%,transparent)}}.from-gray-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-purple-500\/10{--tw-gradient-from:#ac4bff1a}@supports (color:color-mix(in lab,red,red)){.from-purple-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-purple-500)10%,transparent)}}.from-purple-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-white\/10{--tw-gradient-from:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.from-white\/10{--tw-gradient-from:color-mix(in oklab,var(--color-white)10%,transparent)}}.from-white\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.from-yellow-500\/10{--tw-gradient-from:#edb2001a}@supports (color:color-mix(in lab,red,red)){.from-yellow-500\/10{--tw-gradient-from:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.from-yellow-500\/10{--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.via-black\/60{--tw-gradient-via:#0009}@supports (color:color-mix(in lab,red,red)){.via-black\/60{--tw-gradient-via:color-mix(in oklab,var(--color-black)60%,transparent)}}.via-black\/60{--tw-gradient-via-stops:var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-via)var(--tw-gradient-via-position),var(--tw-gradient-to)var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-via-stops)}.to-bambu-dark-secondary{--tw-gradient-to:var(--color-bambu-dark-secondary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-bambu-dark-tertiary{--tw-gradient-to:var(--color-bambu-dark-tertiary);--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.to-transparent{--tw-gradient-to:transparent;--tw-gradient-stops:var(--tw-gradient-via-stops,var(--tw-gradient-position),var(--tw-gradient-from)var(--tw-gradient-from-position),var(--tw-gradient-to)var(--tw-gradient-to-position))}.bg-cover{background-size:cover}.bg-center{background-position:50%}.fill-yellow-400{fill:var(--color-yellow-400)}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.object-left{object-position:left}.p-0{padding:calc(var(--spacing)*0)}.p-0\.5{padding:calc(var(--spacing)*.5)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-2\.5{padding:calc(var(--spacing)*2.5)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-5{padding:calc(var(--spacing)*5)}.p-6{padding:calc(var(--spacing)*6)}.p-8{padding:calc(var(--spacing)*8)}.p-12{padding:calc(var(--spacing)*12)}.\!px-1\.5{padding-inline:calc(var(--spacing)*1.5)!important}.\!px-3{padding-inline:calc(var(--spacing)*3)!important}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-2\.5{padding-inline:calc(var(--spacing)*2.5)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-2\.5{padding-block:calc(var(--spacing)*2.5)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.py-6{padding-block:calc(var(--spacing)*6)}.py-8{padding-block:calc(var(--spacing)*8)}.py-12{padding-block:calc(var(--spacing)*12)}.py-16{padding-block:calc(var(--spacing)*16)}.py-20{padding-block:calc(var(--spacing)*20)}.py-24{padding-block:calc(var(--spacing)*24)}.pt-0{padding-top:calc(var(--spacing)*0)}.pt-1{padding-top:calc(var(--spacing)*1)}.pt-2{padding-top:calc(var(--spacing)*2)}.pt-3{padding-top:calc(var(--spacing)*3)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-1{padding-right:calc(var(--spacing)*1)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-4{padding-right:calc(var(--spacing)*4)}.pr-6{padding-right:calc(var(--spacing)*6)}.pr-7{padding-right:calc(var(--spacing)*7)}.pr-8{padding-right:calc(var(--spacing)*8)}.pr-10{padding-right:calc(var(--spacing)*10)}.pb-1{padding-bottom:calc(var(--spacing)*1)}.pb-2{padding-bottom:calc(var(--spacing)*2)}.pb-4{padding-bottom:calc(var(--spacing)*4)}.pl-2{padding-left:calc(var(--spacing)*2)}.pl-4{padding-left:calc(var(--spacing)*4)}.pl-6{padding-left:calc(var(--spacing)*6)}.pl-7{padding-left:calc(var(--spacing)*7)}.pl-8{padding-left:calc(var(--spacing)*8)}.pl-9{padding-left:calc(var(--spacing)*9)}.pl-10{padding-left:calc(var(--spacing)*10)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[7px\]{font-size:7px}.text-\[8px\]{font-size:8px}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.leading-none{--tw-leading:1;line-height:1}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.\!text-green-400{color:var(--color-green-400)!important}.text-amber-200{color:var(--color-amber-200)}.text-amber-300{color:var(--color-amber-300)}.text-amber-300\/60{color:#ffd23699}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/60{color:color-mix(in oklab,var(--color-amber-300)60%,transparent)}}.text-amber-300\/70{color:#ffd236b3}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/70{color:color-mix(in oklab,var(--color-amber-300)70%,transparent)}}.text-amber-300\/80{color:#ffd236cc}@supports (color:color-mix(in lab,red,red)){.text-amber-300\/80{color:color-mix(in oklab,var(--color-amber-300)80%,transparent)}}.text-amber-400{color:var(--color-amber-400)}.text-amber-400\/70{color:#fcbb00b3}@supports (color:color-mix(in lab,red,red)){.text-amber-400\/70{color:color-mix(in oklab,var(--color-amber-400)70%,transparent)}}.text-amber-500{color:var(--color-amber-500)}.text-amber-500\/80{color:#f99c00cc}@supports (color:color-mix(in lab,red,red)){.text-amber-500\/80{color:color-mix(in oklab,var(--color-amber-500)80%,transparent)}}.text-amber-600{color:var(--color-amber-600)}.text-bambu-dark-tertiary{color:var(--color-bambu-dark-tertiary)}.text-bambu-gray{color:var(--color-bambu-gray)}.text-bambu-gray-dark{color:var(--color-bambu-gray-dark)}.text-bambu-gray-light{color:var(--color-bambu-gray-light)}.text-bambu-gray\/30{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/30{color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.text-bambu-gray\/40{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/40{color:color-mix(in oklab,var(--color-bambu-gray)40%,transparent)}}.text-bambu-gray\/50{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/50{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.text-bambu-gray\/60{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/60{color:color-mix(in oklab,var(--color-bambu-gray)60%,transparent)}}.text-bambu-gray\/70{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/70{color:color-mix(in oklab,var(--color-bambu-gray)70%,transparent)}}.text-bambu-gray\/80{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.text-bambu-gray\/80{color:color-mix(in oklab,var(--color-bambu-gray)80%,transparent)}}.text-bambu-green,.text-bambu-green\/70{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.text-bambu-green\/70{color:color-mix(in oklab,var(--color-bambu-green)70%,transparent)}}.text-black{color:var(--color-black)}.text-black\/80{color:#000c}@supports (color:color-mix(in lab,red,red)){.text-black\/80{color:color-mix(in oklab,var(--color-black)80%,transparent)}}.text-blue-300{color:var(--color-blue-300)}.text-blue-300\/70{color:#90c5ffb3}@supports (color:color-mix(in lab,red,red)){.text-blue-300\/70{color:color-mix(in oklab,var(--color-blue-300)70%,transparent)}}.text-blue-300\/80{color:#90c5ffcc}@supports (color:color-mix(in lab,red,red)){.text-blue-300\/80{color:color-mix(in oklab,var(--color-blue-300)80%,transparent)}}.text-blue-400{color:var(--color-blue-400)}.text-blue-500{color:var(--color-blue-500)}.text-blue-500\/70{color:#3080ffb3}@supports (color:color-mix(in lab,red,red)){.text-blue-500\/70{color:color-mix(in oklab,var(--color-blue-500)70%,transparent)}}.text-blue-600{color:var(--color-blue-600)}.text-blue-700{color:var(--color-blue-700)}.text-cyan-300{color:var(--color-cyan-300)}.text-cyan-400{color:var(--color-cyan-400)}.text-emerald-400{color:var(--color-emerald-400)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.text-gray-700{color:var(--color-gray-700)}.text-gray-800{color:var(--color-gray-800)}.text-gray-900{color:var(--color-gray-900)}.text-green-200{color:var(--color-green-200)}.text-green-300{color:var(--color-green-300)}.text-green-400{color:var(--color-green-400)}.text-green-500{color:var(--color-green-500)}.text-green-500\/60{color:#00c75899}@supports (color:color-mix(in lab,red,red)){.text-green-500\/60{color:color-mix(in oklab,var(--color-green-500)60%,transparent)}}.text-green-600{color:var(--color-green-600)}.text-indigo-400{color:var(--color-indigo-400)}.text-orange-200{color:var(--color-orange-200)}.text-orange-300{color:var(--color-orange-300)}.text-orange-400{color:var(--color-orange-400)}.text-orange-500{color:var(--color-orange-500)}.text-orange-600{color:var(--color-orange-600)}.text-orange-700{color:var(--color-orange-700)}.text-purple-300{color:var(--color-purple-300)}.text-purple-300\/70{color:#d9b3ffb3}@supports (color:color-mix(in lab,red,red)){.text-purple-300\/70{color:color-mix(in oklab,var(--color-purple-300)70%,transparent)}}.text-purple-400{color:var(--color-purple-400)}.text-purple-400\/80{color:#c07effcc}@supports (color:color-mix(in lab,red,red)){.text-purple-400\/80{color:color-mix(in oklab,var(--color-purple-400)80%,transparent)}}.text-purple-500{color:var(--color-purple-500)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-red-300\/80{color:#ffa3a3cc}@supports (color:color-mix(in lab,red,red)){.text-red-300\/80{color:color-mix(in oklab,var(--color-red-300)80%,transparent)}}.text-red-400{color:var(--color-red-400)}.text-red-400\/50{color:#ff656880}@supports (color:color-mix(in lab,red,red)){.text-red-400\/50{color:color-mix(in oklab,var(--color-red-400)50%,transparent)}}.text-red-400\/60{color:#ff656899}@supports (color:color-mix(in lab,red,red)){.text-red-400\/60{color:color-mix(in oklab,var(--color-red-400)60%,transparent)}}.text-red-400\/70{color:#ff6568b3}@supports (color:color-mix(in lab,red,red)){.text-red-400\/70{color:color-mix(in oklab,var(--color-red-400)70%,transparent)}}.text-red-500{color:var(--color-red-500)}.text-red-600{color:var(--color-red-600)}.text-rose-300{color:var(--color-rose-300)}.text-rose-400{color:var(--color-rose-400)}.text-sky-400{color:var(--color-sky-400)}.text-slate-300{color:var(--color-slate-300)}.text-status-error{color:var(--color-status-error)}.text-status-ok{color:var(--color-status-ok)}.text-status-warning{color:var(--color-status-warning)}.text-teal-400{color:var(--color-teal-400)}.text-white{color:var(--color-white)}.text-white\/40{color:#fff6}@supports (color:color-mix(in lab,red,red)){.text-white\/40{color:color-mix(in oklab,var(--color-white)40%,transparent)}}.text-white\/60{color:#fff9}@supports (color:color-mix(in lab,red,red)){.text-white\/60{color:color-mix(in oklab,var(--color-white)60%,transparent)}}.text-white\/70{color:#ffffffb3}@supports (color:color-mix(in lab,red,red)){.text-white\/70{color:color-mix(in oklab,var(--color-white)70%,transparent)}}.text-white\/80{color:#fffc}@supports (color:color-mix(in lab,red,red)){.text-white\/80{color:color-mix(in oklab,var(--color-white)80%,transparent)}}.text-white\/90{color:#ffffffe6}@supports (color:color-mix(in lab,red,red)){.text-white\/90{color:color-mix(in oklab,var(--color-white)90%,transparent)}}.text-yellow-200{color:var(--color-yellow-200)}.text-yellow-200\/70{color:#fff085b3}@supports (color:color-mix(in lab,red,red)){.text-yellow-200\/70{color:color-mix(in oklab,var(--color-yellow-200)70%,transparent)}}.text-yellow-300{color:var(--color-yellow-300)}.text-yellow-400{color:var(--color-yellow-400)}.text-yellow-500{color:var(--color-yellow-500)}.text-yellow-500\/70{color:#edb200b3}@supports (color:color-mix(in lab,red,red)){.text-yellow-500\/70{color:color-mix(in oklab,var(--color-yellow-500)70%,transparent)}}.text-yellow-600{color:var(--color-yellow-600)}.text-yellow-700{color:var(--color-yellow-700)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,)var(--tw-slashed-zero,)var(--tw-numeric-figure,)var(--tw-numeric-spacing,)var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.placeholder-bambu-gray::placeholder{color:var(--color-bambu-gray)}.placeholder-bambu-gray-dark::placeholder{color:var(--color-bambu-gray-dark)}.placeholder-bambu-gray\/50::placeholder{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.placeholder-bambu-gray\/50::placeholder{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.accent-bambu-green{accent-color:var(--color-bambu-green)}.accent-green-500{accent-color:var(--color-green-500)}.opacity-0{opacity:0}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-100{opacity:1}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a),0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-bambu-green\/20{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.shadow-bambu-green\/20{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)20%,transparent)var(--tw-shadow-alpha),transparent)}}.shadow-bambu-green\/25{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.shadow-bambu-green\/25{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)25%,transparent)var(--tw-shadow-alpha),transparent)}}.ring-bambu-green,.ring-bambu-green\/30{--tw-ring-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.ring-bambu-green\/30{--tw-ring-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.ring-blue-400\/50{--tw-ring-color:#54a2ff80}@supports (color:color-mix(in lab,red,red)){.ring-blue-400\/50{--tw-ring-color:color-mix(in oklab,var(--color-blue-400)50%,transparent)}}.ring-green-500{--tw-ring-color:var(--color-green-500)}.ring-white{--tw-ring-color:var(--color-white)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-2{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.ring-offset-bambu-dark{--tw-ring-offset-color:var(--color-bambu-dark)}.ring-offset-bambu-dark-secondary{--tw-ring-offset-color:var(--color-bambu-dark-secondary)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.blur-2xl{--tw-blur:blur(var(--blur-2xl));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.drop-shadow-lg{--tw-drop-shadow-size:drop-shadow(0 4px 4px var(--tw-drop-shadow-color,#00000026));--tw-drop-shadow:drop-shadow(var(--drop-shadow-lg));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.drop-shadow-md{--tw-drop-shadow-size:drop-shadow(0 3px 3px var(--tw-drop-shadow-color,#0000001f));--tw-drop-shadow:drop-shadow(var(--drop-shadow-md));filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,)var(--tw-backdrop-brightness,)var(--tw-backdrop-contrast,)var(--tw-backdrop-grayscale,)var(--tw-backdrop-hue-rotate,)var(--tw-backdrop-invert,)var(--tw-backdrop-opacity,)var(--tw-backdrop-saturate,)var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.group-hover\:bg-bambu-green\/20:is(:where(.group):hover *){background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.group-hover\:bg-bambu-green\/20:is(:where(.group):hover *){background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.group-hover\:text-bambu-gray:is(:where(.group):hover *){color:var(--color-bambu-gray)}.group-hover\:text-bambu-green:is(:where(.group):hover *){color:var(--color-bambu-green)}.group-hover\:opacity-50:is(:where(.group):hover *){opacity:.5}.group-hover\:opacity-100:is(:where(.group):hover *),.group-hover\/resize\:opacity-100:is(:where(.group\/resize):hover *){opacity:1}.group-hover\/thumb\:block:is(:where(.group\/thumb):hover *){display:block}}.peer-checked\:bg-bambu-green:is(:where(.peer):checked~*){background-color:var(--color-bambu-green)}.peer-focus\:outline-none:is(:where(.peer):focus~*){--tw-outline-style:none;outline-style:none}.placeholder\:text-bambu-gray::placeholder,.placeholder\:text-bambu-gray\/50::placeholder{color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.placeholder\:text-bambu-gray\/50::placeholder{color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:top-0:before{content:var(--tw-content);top:calc(var(--spacing)*0)}.before\:right-0:before{content:var(--tw-content);right:calc(var(--spacing)*0)}.before\:left-0:before{content:var(--tw-content);left:calc(var(--spacing)*0)}.before\:h-0\.5:before{content:var(--tw-content);height:calc(var(--spacing)*.5)}.before\:bg-bambu-green:before{content:var(--tw-content);background-color:var(--color-bambu-green)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:top-\[2px\]:after{content:var(--tw-content);top:2px}.after\:left-\[2px\]:after{content:var(--tw-content);left:2px}.after\:h-4:after{content:var(--tw-content);height:calc(var(--spacing)*4)}.after\:h-5:after{content:var(--tw-content);height:calc(var(--spacing)*5)}.after\:w-4:after{content:var(--tw-content);width:calc(var(--spacing)*4)}.after\:w-5:after{content:var(--tw-content);width:calc(var(--spacing)*5)}.after\:rounded-full:after{content:var(--tw-content);border-radius:3.40282e38px}.after\:border:after{content:var(--tw-content);border-style:var(--tw-border-style);border-width:1px}.after\:border-gray-300:after{content:var(--tw-content);border-color:var(--color-gray-300)}.after\:bg-white:after{content:var(--tw-content);background-color:var(--color-white)}.after\:transition-all:after{content:var(--tw-content);transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.peer-checked\:after\:translate-x-full:is(:where(.peer):checked~*):after{content:var(--tw-content);--tw-translate-x:100%;translate:var(--tw-translate-x)var(--tw-translate-y)}.peer-checked\:after\:border-white:is(:where(.peer):checked~*):after{content:var(--tw-content);border-color:var(--color-white)}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}@media(hover:hover){.hover\:scale-105:hover{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:border-bambu-dark-tertiary\/80:hover{border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-dark-tertiary\/80:hover{border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.hover\:border-bambu-gray:hover{border-color:var(--color-bambu-gray)}.hover\:border-bambu-gray-dark:hover{border-color:var(--color-bambu-gray-dark)}.hover\:border-bambu-gray\/50:hover{border-color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-gray\/50:hover{border-color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.hover\:border-bambu-green:hover,.hover\:border-bambu-green\/50:hover{border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:border-bambu-green\/50:hover{border-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.hover\:border-white:hover{border-color:var(--color-white)}.hover\:border-white\/40:hover{border-color:#fff6}@supports (color:color-mix(in lab,red,red)){.hover\:border-white\/40:hover{border-color:color-mix(in oklab,var(--color-white)40%,transparent)}}.hover\:\!bg-green-500\/20:hover{background-color:#00c75833!important}@supports (color:color-mix(in lab,red,red)){.hover\:\!bg-green-500\/20:hover{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)!important}}.hover\:bg-amber-500\/30:hover{background-color:#f99c004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-amber-500\/30:hover{background-color:color-mix(in oklab,var(--color-amber-500)30%,transparent)}}.hover\:bg-bambu-dark:hover{background-color:var(--color-bambu-dark)}.hover\:bg-bambu-dark-secondary:hover,.hover\:bg-bambu-dark-secondary\/50:hover{background-color:var(--color-bambu-dark-secondary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-secondary\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-secondary)50%,transparent)}}.hover\:bg-bambu-dark-tertiary:hover,.hover\:bg-bambu-dark-tertiary\/30:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/30:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)30%,transparent)}}.hover\:bg-bambu-dark-tertiary\/50:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.hover\:bg-bambu-dark-tertiary\/70:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/70:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)70%,transparent)}}.hover\:bg-bambu-dark-tertiary\/80:hover{background-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark-tertiary\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)80%,transparent)}}.hover\:bg-bambu-dark\/50:hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.hover\:bg-bambu-dark\/80:hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-dark\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-dark)80%,transparent)}}.hover\:bg-bambu-gray-dark:hover{background-color:var(--color-bambu-gray-dark)}.hover\:bg-bambu-green:hover{background-color:var(--color-bambu-green)}.hover\:bg-bambu-green-dark:hover{background-color:var(--color-bambu-green-dark)}.hover\:bg-bambu-green-light:hover{background-color:var(--color-bambu-green-light)}.hover\:bg-bambu-green\/10:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/10:hover{background-color:color-mix(in oklab,var(--color-bambu-green)10%,transparent)}}.hover\:bg-bambu-green\/20:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/20:hover{background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.hover\:bg-bambu-green\/30:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/30:hover{background-color:color-mix(in oklab,var(--color-bambu-green)30%,transparent)}}.hover\:bg-bambu-green\/50:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/50:hover{background-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.hover\:bg-bambu-green\/80:hover{background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:bg-bambu-green\/80:hover{background-color:color-mix(in oklab,var(--color-bambu-green)80%,transparent)}}.hover\:bg-black\/40:hover{background-color:#0006}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/40:hover{background-color:color-mix(in oklab,var(--color-black)40%,transparent)}}.hover\:bg-black\/70:hover{background-color:#000000b3}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/70:hover{background-color:color-mix(in oklab,var(--color-black)70%,transparent)}}.hover\:bg-black\/80:hover{background-color:#000c}@supports (color:color-mix(in lab,red,red)){.hover\:bg-black\/80:hover{background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.hover\:bg-blue-500\/30:hover{background-color:#3080ff4d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-500\/30:hover{background-color:color-mix(in oklab,var(--color-blue-500)30%,transparent)}}.hover\:bg-gray-50:hover{background-color:var(--color-gray-50)}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:bg-gray-700:hover{background-color:var(--color-gray-700)}.hover\:bg-green-500\/20:hover{background-color:#00c75833}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-500\/20:hover{background-color:color-mix(in oklab,var(--color-green-500)20%,transparent)}}.hover\:bg-orange-400\/20:hover{background-color:#ff8b1a33}@supports (color:color-mix(in lab,red,red)){.hover\:bg-orange-400\/20:hover{background-color:color-mix(in oklab,var(--color-orange-400)20%,transparent)}}.hover\:bg-red-200:hover{background-color:var(--color-red-200)}.hover\:bg-red-400\/10:hover{background-color:#ff65681a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-400\/10:hover{background-color:color-mix(in oklab,var(--color-red-400)10%,transparent)}}.hover\:bg-red-500\/10:hover{background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/10:hover{background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.hover\:bg-red-500\/20:hover{background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/20:hover{background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.hover\:bg-red-500\/30:hover{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-500\/30:hover{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.hover\:bg-red-600:hover{background-color:var(--color-red-600)}.hover\:bg-red-700:hover{background-color:var(--color-red-700)}.hover\:bg-white\/5:hover{background-color:#ffffff0d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/5:hover{background-color:color-mix(in oklab,var(--color-white)5%,transparent)}}.hover\:bg-white\/10:hover{background-color:#ffffff1a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/10:hover{background-color:color-mix(in oklab,var(--color-white)10%,transparent)}}.hover\:bg-white\/80:hover{background-color:#fffc}@supports (color:color-mix(in lab,red,red)){.hover\:bg-white\/80:hover{background-color:color-mix(in oklab,var(--color-white)80%,transparent)}}.hover\:bg-yellow-500\/30:hover{background-color:#edb2004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-500\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-500)30%,transparent)}}.hover\:bg-yellow-600:hover{background-color:var(--color-yellow-600)}.hover\:text-amber-300:hover{color:var(--color-amber-300)}.hover\:text-bambu-gray:hover{color:var(--color-bambu-gray)}.hover\:text-bambu-green:hover{color:var(--color-bambu-green)}.hover\:text-bambu-green-light:hover{color:var(--color-bambu-green-light)}.hover\:text-bambu-green\/70:hover{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:text-bambu-green\/70:hover{color:color-mix(in oklab,var(--color-bambu-green)70%,transparent)}}.hover\:text-bambu-green\/80:hover{color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:text-bambu-green\/80:hover{color:color-mix(in oklab,var(--color-bambu-green)80%,transparent)}}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}.hover\:text-orange-300:hover{color:var(--color-orange-300)}.hover\:text-red-300:hover{color:var(--color-red-300)}.hover\:text-red-400:hover{color:var(--color-red-400)}.hover\:text-white:hover{color:var(--color-white)}.hover\:text-white\/80:hover{color:#fffc}@supports (color:color-mix(in lab,red,red)){.hover\:text-white\/80:hover{color:color-mix(in oklab,var(--color-white)80%,transparent)}}.hover\:text-yellow-400:hover{color:var(--color-yellow-400)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.hover\:shadow-bambu-green\/5:hover{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-bambu-green\/5:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)5%,transparent)var(--tw-shadow-alpha),transparent)}}.hover\:shadow-bambu-green\/30:hover{--tw-shadow-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.hover\:shadow-bambu-green\/30:hover{--tw-shadow-color:color-mix(in oklab,color-mix(in oklab,var(--color-bambu-green)30%,transparent)var(--tw-shadow-alpha),transparent)}}}.focus\:border-bambu-green:focus{border-color:var(--color-bambu-green)}.focus\:border-red-500:focus{border-color:var(--color-red-500)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-bambu-gray:focus{--tw-ring-color:var(--color-bambu-gray)}.focus\:ring-bambu-green:focus,.focus\:ring-bambu-green\/50:focus{--tw-ring-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.focus\:ring-bambu-green\/50:focus{--tw-ring-color:color-mix(in oklab,var(--color-bambu-green)50%,transparent)}}.focus\:ring-red-500:focus{--tw-ring-color:var(--color-red-500)}.focus\:ring-offset-0:focus{--tw-ring-offset-width:0px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,)0 0 0 var(--tw-ring-offset-width)var(--tw-ring-offset-color)}.focus\:ring-offset-bambu-dark:focus{--tw-ring-offset-color:var(--color-bambu-dark)}.focus\:ring-offset-bambu-dark-secondary:focus{--tw-ring-offset-color:var(--color-bambu-dark-secondary)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.active\:cursor-grabbing:active{cursor:grabbing}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}.disabled\:opacity-50:disabled{opacity:.5}@media(hover:hover){.disabled\:hover\:bg-bambu-green:disabled:hover{background-color:var(--color-bambu-green)}}@media not all and (min-width:640px){.max-\[640px\]\:hidden{display:none}.max-\[640px\]\:w-full{width:100%}.max-\[640px\]\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.max-\[640px\]\:flex-col{flex-direction:column}.max-\[640px\]\:flex-wrap{flex-wrap:wrap}.max-\[640px\]\:items-center{align-items:center}.max-\[640px\]\:items-start{align-items:flex-start}}@media not all and (min-width:550px){.max-\[550px\]\:order-1{order:1}.max-\[550px\]\:order-2{order:2}.max-\[550px\]\:order-3{order:3}.max-\[550px\]\:order-4{order:4}.max-\[550px\]\:mt-1{margin-top:calc(var(--spacing)*1)}.max-\[550px\]\:w-full{width:100%}.max-\[550px\]\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.max-\[550px\]\:flex-col{flex-direction:column}.max-\[550px\]\:flex-wrap{flex-wrap:wrap}.max-\[550px\]\:items-center{align-items:center}.max-\[550px\]\:items-start{align-items:flex-start}.max-\[550px\]\:justify-end{justify-content:flex-end}.max-\[550px\]\:gap-1\.5{gap:calc(var(--spacing)*1.5)}.max-\[550px\]\:gap-2{gap:calc(var(--spacing)*2)}.max-\[550px\]\:gap-3{gap:calc(var(--spacing)*3)}.max-\[550px\]\:self-start{align-self:flex-start}}@media(min-width:40rem){.sm\:mt-0{margin-top:calc(var(--spacing)*0)}.sm\:mr-1{margin-right:calc(var(--spacing)*1)}.sm\:ml-auto{margin-left:auto}.sm\:block{display:block}.sm\:grid{display:grid}.sm\:inline{display:inline}.sm\:h-4{height:calc(var(--spacing)*4)}.sm\:w-4{width:calc(var(--spacing)*4)}.sm\:w-16{width:calc(var(--spacing)*16)}.sm\:w-40{width:calc(var(--spacing)*40)}.sm\:w-auto{width:auto}.sm\:max-w-xs{max-width:var(--container-xs)}.sm\:flex-1{flex:1}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-2{gap:calc(var(--spacing)*2)}.sm\:gap-3{gap:calc(var(--spacing)*3)}.sm\:gap-6{gap:calc(var(--spacing)*6)}.sm\:p-1\.5{padding:calc(var(--spacing)*1.5)}.sm\:p-3{padding:calc(var(--spacing)*3)}}@media(min-width:48rem){.md\:top-\[2px\]{top:2px}.md\:left-\[2px\]{left:2px}.md\:mx-0{margin-inline:calc(var(--spacing)*0)}.md\:block{display:block}.md\:inline{display:inline}.md\:h-4{height:calc(var(--spacing)*4)}.md\:h-5{height:calc(var(--spacing)*5)}.md\:h-8{height:calc(var(--spacing)*8)}.md\:min-h-0{min-height:calc(var(--spacing)*0)}.md\:w-4{width:calc(var(--spacing)*4)}.md\:w-8{width:calc(var(--spacing)*8)}.md\:w-9{width:calc(var(--spacing)*9)}.md\:min-w-\[200px\]{min-width:200px}.md\:flex-1{flex:1}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-wrap{flex-wrap:wrap}.md\:items-center{align-items:center}.md\:gap-4{gap:calc(var(--spacing)*4)}.md\:p-6{padding:calc(var(--spacing)*6)}.md\:p-8{padding:calc(var(--spacing)*8)}.md\:px-0{padding-inline:calc(var(--spacing)*0)}.md\:py-2{padding-block:calc(var(--spacing)*2)}.md\:pb-0{padding-bottom:calc(var(--spacing)*0)}}@media(min-width:64rem){.lg\:static{position:static}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-\[calc\(100vh-64px\)\]{height:calc(100vh - 64px)}.lg\:w-1\/3{width:33.3333%}.lg\:w-2\/3{width:66.6667%}.lg\:w-80{width:calc(var(--spacing)*80)}.lg\:w-\[480px\]{width:480px}.lg\:max-w-md{max-width:var(--container-md)}.lg\:max-w-sm{max-width:var(--container-sm)}.lg\:max-w-xl{max-width:var(--container-xl)}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:gap-6{gap:calc(var(--spacing)*6)}.lg\:gap-8{gap:calc(var(--spacing)*8)}.lg\:overflow-y-auto{overflow-y:auto}.lg\:p-8{padding:calc(var(--spacing)*8)}}@media(min-width:80rem){.xl\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}@media(min-width:96rem){.\32xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}.dark\:border-bambu-dark-tertiary:where(.dark,.dark *),.dark\:border-bambu-dark-tertiary\/50:where(.dark,.dark *){border-color:var(--color-bambu-dark-tertiary)}@supports (color:color-mix(in lab,red,red)){.dark\:border-bambu-dark-tertiary\/50:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-bambu-dark-tertiary)50%,transparent)}}.dark\:border-bambu-green\/40:where(.dark,.dark *){border-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:border-bambu-green\/40:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-bambu-green)40%,transparent)}}.dark\:border-gray-600:where(.dark,.dark *){border-color:var(--color-gray-600)}.dark\:border-red-500\/30:where(.dark,.dark *){border-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.dark\:border-red-500\/30:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.dark\:border-red-500\/40:where(.dark,.dark *){border-color:#fb2c3666}@supports (color:color-mix(in lab,red,red)){.dark\:border-red-500\/40:where(.dark,.dark *){border-color:color-mix(in oklab,var(--color-red-500)40%,transparent)}}.dark\:bg-amber-500\/10:where(.dark,.dark *){background-color:#f99c001a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-amber-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-amber-500)10%,transparent)}}.dark\:bg-bambu-dark:where(.dark,.dark *){background-color:var(--color-bambu-dark)}.dark\:bg-bambu-dark-secondary:where(.dark,.dark *){background-color:var(--color-bambu-dark-secondary)}.dark\:bg-bambu-green\/20:where(.dark,.dark *){background-color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:bg-bambu-green\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-bambu-green)20%,transparent)}}.dark\:bg-black\/80:where(.dark,.dark *){background-color:#000c}@supports (color:color-mix(in lab,red,red)){.dark\:bg-black\/80:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-black)80%,transparent)}}.dark\:bg-blue-500\/10:where(.dark,.dark *){background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-500)10%,transparent)}}.dark\:bg-blue-500\/20:where(.dark,.dark *){background-color:#3080ff33}@supports (color:color-mix(in lab,red,red)){.dark\:bg-blue-500\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-blue-500)20%,transparent)}}.dark\:bg-gray-500\/40:where(.dark,.dark *){background-color:#6a728266}@supports (color:color-mix(in lab,red,red)){.dark\:bg-gray-500\/40:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-gray-500)40%,transparent)}}.dark\:bg-gray-900:where(.dark,.dark *){background-color:var(--color-gray-900)}.dark\:bg-red-500\/10:where(.dark,.dark *){background-color:#fb2c361a}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-500\/10:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-500)10%,transparent)}}.dark\:bg-red-500\/20:where(.dark,.dark *){background-color:#fb2c3633}@supports (color:color-mix(in lab,red,red)){.dark\:bg-red-500\/20:where(.dark,.dark *){background-color:color-mix(in oklab,var(--color-red-500)20%,transparent)}}.dark\:text-amber-400:where(.dark,.dark *){color:var(--color-amber-400)}.dark\:text-bambu-gray:where(.dark,.dark *),.dark\:text-bambu-gray\/30:where(.dark,.dark *){color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-gray\/30:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-gray)30%,transparent)}}.dark\:text-bambu-gray\/50:where(.dark,.dark *){color:var(--color-bambu-gray)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-gray\/50:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-gray)50%,transparent)}}.dark\:text-bambu-green:where(.dark,.dark *),.dark\:text-bambu-green\/60:where(.dark,.dark *){color:var(--color-bambu-green)}@supports (color:color-mix(in lab,red,red)){.dark\:text-bambu-green\/60:where(.dark,.dark *){color:color-mix(in oklab,var(--color-bambu-green)60%,transparent)}}.dark\:text-blue-200:where(.dark,.dark *){color:var(--color-blue-200)}.dark\:text-blue-200\/80:where(.dark,.dark *){color:#bedbffcc}@supports (color:color-mix(in lab,red,red)){.dark\:text-blue-200\/80:where(.dark,.dark *){color:color-mix(in oklab,var(--color-blue-200)80%,transparent)}}.dark\:text-blue-300:where(.dark,.dark *){color:var(--color-blue-300)}.dark\:text-blue-300\/60:where(.dark,.dark *){color:#90c5ff99}@supports (color:color-mix(in lab,red,red)){.dark\:text-blue-300\/60:where(.dark,.dark *){color:color-mix(in oklab,var(--color-blue-300)60%,transparent)}}.dark\:text-blue-400:where(.dark,.dark *){color:var(--color-blue-400)}.dark\:text-orange-200:where(.dark,.dark *){color:var(--color-orange-200)}.dark\:text-orange-400:where(.dark,.dark *){color:var(--color-orange-400)}.dark\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.dark\:text-red-400\/70:where(.dark,.dark *){color:#ff6568b3}@supports (color:color-mix(in lab,red,red)){.dark\:text-red-400\/70:where(.dark,.dark *){color:color-mix(in oklab,var(--color-red-400)70%,transparent)}}.dark\:text-white:where(.dark,.dark *){color:var(--color-white)}.dark\:text-yellow-200:where(.dark,.dark *){color:var(--color-yellow-200)}.dark\:text-yellow-200\/70:where(.dark,.dark *){color:#fff085b3}@supports (color:color-mix(in lab,red,red)){.dark\:text-yellow-200\/70:where(.dark,.dark *){color:color-mix(in oklab,var(--color-yellow-200)70%,transparent)}}@media(hover:hover){.dark\:hover\:bg-bambu-dark-tertiary:where(.dark,.dark *):hover{background-color:var(--color-bambu-dark-tertiary)}.dark\:hover\:bg-bambu-dark\/50:where(.dark,.dark *):hover{background-color:var(--color-bambu-dark)}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-bambu-dark\/50:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-bambu-dark)50%,transparent)}}.dark\:hover\:bg-red-500\/30:where(.dark,.dark *):hover{background-color:#fb2c364d}@supports (color:color-mix(in lab,red,red)){.dark\:hover\:bg-red-500\/30:where(.dark,.dark *):hover{background-color:color-mix(in oklab,var(--color-red-500)30%,transparent)}}.dark\:hover\:text-white:where(.dark,.dark *):hover{color:var(--color-white)}}.\[\&\:\:-webkit-slider-thumb\]\:h-3::-webkit-slider-thumb{height:calc(var(--spacing)*3)}.\[\&\:\:-webkit-slider-thumb\]\:w-3::-webkit-slider-thumb{width:calc(var(--spacing)*3)}.\[\&\:\:-webkit-slider-thumb\]\:cursor-pointer::-webkit-slider-thumb{cursor:pointer}.\[\&\:\:-webkit-slider-thumb\]\:appearance-none::-webkit-slider-thumb{appearance:none}.\[\&\:\:-webkit-slider-thumb\]\:rounded-full::-webkit-slider-thumb{border-radius:3.40282e38px}.\[\&\:\:-webkit-slider-thumb\]\:bg-bambu-green::-webkit-slider-thumb{background-color:var(--color-bambu-green)}}:root{--accent:#00ae42;--accent-light:#00c64d;--accent-dark:#009438;--status-ok:#22c55e;--status-error:#ef4444;--status-warning:#f59e0b;--bg-primary:#f5f5f5;--bg-secondary:#fff;--bg-tertiary:#e5e5e5;--text-primary:#1a1a1a;--text-secondary:#4a4a4a;--text-muted:#6b6b6b;--text-tertiary:gray;--border-color:#d4d4d4;--card-shadow:0 2px 8px #00000014;--glow-color:transparent;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:Inter,system-ui,sans-serif;font-weight:400;line-height:1.5}.dark{--bg-primary:#1a1a1a;--bg-secondary:#2d2d2d;--bg-tertiary:#3d3d3d;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:gray;--text-tertiary:#4a4a4a;--border-color:#3d3d3d;--card-shadow:0 4px 16px #0006}.bg-warm{--bg-primary:#faf8f5;--bg-secondary:#fffefa;--bg-tertiary:#e8e4dd;--text-primary:#2d2a26;--text-secondary:#5c5750;--text-muted:#7a756c;--text-tertiary:#9a9590;--border-color:#d8d4cc}.bg-cool{--bg-primary:#f0f4f8;--bg-secondary:#fff;--bg-tertiary:#dce4ec;--text-primary:#1a2530;--text-secondary:#4a5568;--text-muted:#6b7a8a;--text-tertiary:#8a9aaa;--border-color:#c8d4e0}.dark.bg-neutral{--bg-primary:#1a1a1a;--bg-secondary:#2d2d2d;--bg-tertiary:#3d3d3d;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:gray;--text-tertiary:#4a4a4a;--border-color:#3d3d3d}.dark.bg-warm{--bg-primary:#1c1a18;--bg-secondary:#2e2a26;--bg-tertiary:#3e3a36;--text-primary:#f5f0ea;--text-secondary:#b0a898;--text-muted:#8a8278;--text-tertiary:#5a5248;--border-color:#3e3a36}.dark.bg-cool{--bg-primary:#181c20;--bg-secondary:#262c32;--bg-tertiary:#363e46;--text-primary:#f0f4f8;--text-secondary:#98a8b8;--text-muted:#788898;--text-tertiary:#4a5a6a;--border-color:#363e46}.dark.bg-oled{--bg-primary:#000;--bg-secondary:#141414;--bg-tertiary:#1f1f1f;--text-primary:#fff;--text-secondary:#a0a0a0;--text-muted:#707070;--text-tertiary:#404040;--border-color:#2a2a2a}.dark.bg-slate{--bg-primary:#0f172a;--bg-secondary:#1e293b;--bg-tertiary:#334155;--text-primary:#f1f5f9;--text-secondary:#94a3b8;--text-muted:#64748b;--text-tertiary:#475569;--border-color:#334155}.dark.bg-forest{--bg-primary:#121a16;--bg-secondary:#1c2a22;--bg-tertiary:#2a3d30;--text-primary:#e8f5ec;--text-secondary:#8aa894;--text-muted:#6a8874;--text-tertiary:#4a6854;--border-color:#2a3d30}.printer-control-buttons-container{container-type:inline-size}@container (max-width:220px){.printer-control-buttons{flex-direction:column;align-items:stretch}.printer-control-buttons>button{width:100%}}.style-glow{--card-shadow:0 2px 8px #00000014,0 0 25px var(--accent)}@supports (color:color-mix(in lab,red,red)){.style-glow{--card-shadow:0 2px 8px #00000014,0 0 25px color-mix(in srgb,var(--accent)12%,transparent)}}.dark.style-glow{--card-shadow:0 4px 20px #00000080,0 0 40px var(--accent)}@supports (color:color-mix(in lab,red,red)){.dark.style-glow{--card-shadow:0 4px 20px #00000080,0 0 40px color-mix(in srgb,var(--accent)15%,transparent)}}.style-vibrant{--card-shadow:0 8px 30px #00000026,0 2px 8px #0000001a}.dark.style-vibrant{--card-shadow:0 10px 40px #0009,0 4px 12px #0006}.accent-green{--accent:#00ae42;--accent-light:#00c64d;--accent-dark:#009438}.accent-teal{--accent:#14b8a6;--accent-light:#2dd4bf;--accent-dark:#0d9488}.accent-blue{--accent:#3b82f6;--accent-light:#60a5fa;--accent-dark:#2563eb}.accent-orange{--accent:#f97316;--accent-light:#fb923c;--accent-dark:#ea580c}.accent-purple{--accent:#8b5cf6;--accent-light:#a78bfa;--accent-dark:#7c3aed}.accent-red{--accent:#ef4444;--accent-light:#f87171;--accent-dark:#dc2626}body{background-color:var(--bg-primary);color:var(--text-primary);min-height:100vh;margin:0;transition:background-color .2s,color .2s}#root{min-height:100vh}.text-white{color:var(--text-primary)}.bg-bambu-dark,.bg-bambu-dark-secondary,.bg-bambu-dark-tertiary,.border-bambu-dark-tertiary{transition:background-color .2s,border-color .2s}@keyframes slide-in{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}.animate-slide-in{animation:.2s ease-out slide-in}.icon-theme{opacity:.5}.dark .icon-theme{filter:invert();opacity:.4}.icon-green{filter:invert(48%)sepia(89%)saturate(459%)hue-rotate(93deg)brightness(95%)contrast(92%);opacity:1}.icon-heating{filter:invert(50%)sepia()saturate(1000%)hue-rotate(360deg)brightness()contrast();opacity:1}.dark .jogpad-theme{filter:brightness(.35)contrast(1.2)}.ams-empty-slot{background:repeating-linear-gradient(45deg,#444,#444 2px,#222 2px 4px)}.dark .ams-empty-slot{background:repeating-linear-gradient(45deg,#555,#555 2px,#333 2px 4px)}.touch-manipulation{touch-action:manipulation}.safe-area-bottom{padding-bottom:env(safe-area-inset-bottom,0)}.safe-area-top{padding-top:env(safe-area-inset-top,0)}.scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}@keyframes slide-in-left{0%{transform:translate(-100%)}to{transform:translate(0)}}.animate-slide-in-left{animation:.3s ease-out slide-in-left}.card-shadow{box-shadow:var(--card-shadow)}.calendar-scroll{scrollbar-width:thin;scrollbar-color:var(--text-muted)transparent}@supports (color:color-mix(in lab,red,red)){.calendar-scroll{scrollbar-color:color-mix(in srgb,var(--text-muted)60%,transparent)transparent}}.calendar-scroll::-webkit-scrollbar{width:8px}.calendar-scroll::-webkit-scrollbar-track{background:0 0}.calendar-scroll::-webkit-scrollbar-thumb{background-color:var(--text-muted)}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb{background-color:color-mix(in srgb,var(--text-muted)60%,transparent)}}.calendar-scroll::-webkit-scrollbar-thumb{border:2px solid var(--bg-secondary);border-radius:999px}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb{border:2px solid color-mix(in srgb,var(--bg-secondary)70%,transparent)}}.calendar-scroll::-webkit-scrollbar-thumb:hover{background-color:var(--text-muted)}@supports (color:color-mix(in lab,red,red)){.calendar-scroll::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb,var(--text-muted)80%,transparent)}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-gradient-position{syntax:"*";inherits:false}@property --tw-gradient-from{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-via{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-to{syntax:"";inherits:false;initial-value:#0000}@property --tw-gradient-stops{syntax:"*";inherits:false}@property --tw-gradient-via-stops{syntax:"*";inherits:false}@property --tw-gradient-from-position{syntax:"";inherits:false;initial-value:0%}@property --tw-gradient-via-position{syntax:"";inherits:false;initial-value:50%}@property --tw-gradient-to-position{syntax:"";inherits:false;initial-value:100%}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}} diff --git a/static/assets/index-D8lA4NE5.js b/static/assets/index-_Fo-S_XR.js similarity index 66% rename from static/assets/index-D8lA4NE5.js rename to static/assets/index-_Fo-S_XR.js index d685dbc9c..72bcf3fd7 100644 --- a/static/assets/index-D8lA4NE5.js +++ b/static/assets/index-_Fo-S_XR.js @@ -1,18 +1,18 @@ -function joe(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(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 s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var l_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ec(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var zT={exports:{}},ry={};var GU;function Doe(){if(GU)return ry;GU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return ry.Fragment=e,ry.jsx=n,ry.jsxs=n,ry}var qU;function Roe(){return qU||(qU=1,zT.exports=Doe()),zT.exports}var a=Roe(),UT={exports:{}},kn={};var WU;function Foe(){if(WU)return kn;WU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),h=Symbol.for("react.activity"),f=Symbol.iterator;function p($){return $===null||typeof $!="object"?null:($=f&&$[f]||$["@@iterator"],typeof $=="function"?$:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},v=Object.assign,b={};function y($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}y.prototype.isReactComponent={},y.prototype.setState=function($,X){if(typeof $!="object"&&typeof $!="function"&&$!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,$,X,"setState")},y.prototype.forceUpdate=function($){this.updater.enqueueForceUpdate(this,$,"forceUpdate")};function S(){}S.prototype=y.prototype;function N($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}var A=N.prototype=new S;A.constructor=N,v(A,y.prototype),A.isPureReactComponent=!0;var C=Array.isArray;function T(){}var k={H:null,A:null,T:null,S:null},j=Object.prototype.hasOwnProperty;function E($,X,ee){var J=ee.ref;return{$$typeof:t,type:$,key:X,ref:J!==void 0?J:null,props:ee}}function P($,X){return E($.type,X,$.props)}function U($){return typeof $=="object"&&$!==null&&$.$$typeof===t}function O($){var X={"=":"=0",":":"=2"};return"$"+$.replace(/[=:]/g,function(ee){return X[ee]})}var B=/\/+/g;function R($,X){return typeof $=="object"&&$!==null&&$.key!=null?O(""+$.key):X.toString(36)}function G($){switch($.status){case"fulfilled":return $.value;case"rejected":throw $.reason;default:switch(typeof $.status=="string"?$.then(T,T):($.status="pending",$.then(function(X){$.status==="pending"&&($.status="fulfilled",$.value=X)},function(X){$.status==="pending"&&($.status="rejected",$.reason=X)})),$.status){case"fulfilled":return $.value;case"rejected":throw $.reason}}throw $}function q($,X,ee,J,_e){var pe=typeof $;(pe==="undefined"||pe==="boolean")&&($=null);var me=!1;if($===null)me=!0;else switch(pe){case"bigint":case"string":case"number":me=!0;break;case"object":switch($.$$typeof){case t:case e:me=!0;break;case d:return me=$._init,q(me($._payload),X,ee,J,_e)}}if(me)return _e=_e($),me=J===""?"."+R($,0):J,C(_e)?(ee="",me!=null&&(ee=me.replace(B,"$&/")+"/"),q(_e,X,ee,"",function(ce){return ce})):_e!=null&&(U(_e)&&(_e=P(_e,ee+(_e.key==null||$&&$.key===_e.key?"":(""+_e.key).replace(B,"$&/")+"/")+me)),X.push(_e)),1;me=0;var je=J===""?".":J+":";if(C($))for(var de=0;de<$.length;de++)J=$[de],pe=je+R(J,de),me+=q(J,X,ee,pe,_e);else if(de=p($),typeof de=="function")for($=de.call($),de=0;!(J=$.next()).done;)J=J.value,pe=je+R(J,de++),me+=q(J,X,ee,pe,_e);else if(pe==="object"){if(typeof $.then=="function")return q(G($),X,ee,J,_e);throw X=String($),Error("Objects are not valid as a React child (found: "+(X==="[object Object]"?"object with keys {"+Object.keys($).join(", ")+"}":X)+"). If you meant to render a collection of children, use an array instead.")}return me}function z($,X,ee){if($==null)return $;var J=[],_e=0;return q($,J,"","",function(pe){return X.call(ee,pe,_e++)}),J}function K($){if($._status===-1){var X=$._result;X=X(),X.then(function(ee){($._status===0||$._status===-1)&&($._status=1,$._result=ee)},function(ee){($._status===0||$._status===-1)&&($._status=2,$._result=ee)}),$._status===-1&&($._status=0,$._result=X)}if($._status===1)return $._result.default;throw $._result}var Y=typeof reportError=="function"?reportError:function($){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var X=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof $=="object"&&$!==null&&typeof $.message=="string"?String($.message):String($),error:$});if(!window.dispatchEvent(X))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",$);return}console.error($)},oe={map:z,forEach:function($,X,ee){z($,function(){X.apply(this,arguments)},ee)},count:function($){var X=0;return z($,function(){X++}),X},toArray:function($){return z($,function(X){return X})||[]},only:function($){if(!U($))throw Error("React.Children.only expected to receive a single React element child.");return $}};return kn.Activity=h,kn.Children=oe,kn.Component=y,kn.Fragment=n,kn.Profiler=i,kn.PureComponent=N,kn.StrictMode=r,kn.Suspense=c,kn.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=k,kn.__COMPILER_RUNTIME={__proto__:null,c:function($){return k.H.useMemoCache($)}},kn.cache=function($){return function(){return $.apply(null,arguments)}},kn.cacheSignal=function(){return null},kn.cloneElement=function($,X,ee){if($==null)throw Error("The argument must be a React element, but you passed "+$+".");var J=v({},$.props),_e=$.key;if(X!=null)for(pe in X.key!==void 0&&(_e=""+X.key),X)!j.call(X,pe)||pe==="key"||pe==="__self"||pe==="__source"||pe==="ref"&&X.ref===void 0||(J[pe]=X[pe]);var pe=arguments.length-2;if(pe===1)J.children=ee;else if(1>>1,oe=q[Y];if(0>>1;Y<$;){var X=2*(Y+1)-1,ee=q[X],J=X+1,_e=q[J];if(0>i(ee,K))Ji(_e,ee)?(q[Y]=_e,q[J]=K,Y=J):(q[Y]=ee,q[X]=K,Y=X);else if(Ji(_e,K))q[Y]=_e,q[J]=K,Y=J;else break e}}return z}function i(q,z){var K=q.sortIndex-z.sortIndex;return K!==0?K:q.id-z.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,h=null,f=3,p=!1,x=!1,v=!1,b=!1,y=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function A(q){for(var z=n(u);z!==null;){if(z.callback===null)r(u);else if(z.startTime<=q)r(u),z.sortIndex=z.expirationTime,e(c,z);else break;z=n(u)}}function C(q){if(v=!1,A(q),!x)if(n(c)!==null)x=!0,T||(T=!0,O());else{var z=n(u);z!==null&&G(C,z.startTime-q)}}var T=!1,k=-1,j=5,E=-1;function P(){return b?!0:!(t.unstable_now()-Eq&&P());){var Y=h.callback;if(typeof Y=="function"){h.callback=null,f=h.priorityLevel;var oe=Y(h.expirationTime<=q);if(q=t.unstable_now(),typeof oe=="function"){h.callback=oe,A(q),z=!0;break t}h===n(c)&&r(c),A(q)}else r(c);h=n(c)}if(h!==null)z=!0;else{var $=n(u);$!==null&&G(C,$.startTime-q),z=!1}}break e}finally{h=null,f=K,p=!1}z=void 0}}finally{z?O():T=!1}}}var O;if(typeof N=="function")O=function(){N(U)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,R=B.port2;B.port1.onmessage=U,O=function(){R.postMessage(null)}}else O=function(){y(U,0)};function G(q,z){k=y(function(){q(t.unstable_now())},z)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125Y?(q.sortIndex=K,e(u,q),n(c)===null&&q===n(u)&&(v?(S(k),k=-1):v=!0,G(C,K-Y))):(q.sortIndex=oe,e(c,q),x||p||(x=!0,T||(T=!0,O()))),q},t.unstable_shouldYield=P,t.unstable_wrapCallback=function(q){var z=f;return function(){var K=f;f=z;try{return q.apply(this,arguments)}finally{f=K}}}})($T)),$T}var YU;function Ioe(){return YU||(YU=1,HT.exports=Ooe()),HT.exports}var VT={exports:{}},ss={};var QU;function zoe(){if(QU)return ss;QU=1;var t=Bb();function e(c){var u="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),VT.exports=zoe(),VT.exports}var JU;function Uoe(){if(JU)return iy;JU=1;var t=Ioe(),e=Bb(),n=bK();function r(m){var g="https://react.dev/errors/"+m;if(1oe||(m.current=Y[oe],Y[oe]=null,oe--)}function ee(m,g){oe++,Y[oe]=m.current,m.current=g}var J=$(null),_e=$(null),pe=$(null),me=$(null);function je(m,g){switch(ee(pe,g),ee(_e,m),ee(J,null),g.nodeType){case 9:case 11:m=(m=g.documentElement)&&(m=m.namespaceURI)?fU(m):0;break;default:if(m=g.tagName,g=g.namespaceURI)g=fU(g),m=pU(g,m);else switch(m){case"svg":m=1;break;case"math":m=2;break;default:m=0}}X(J),ee(J,m)}function de(){X(J),X(_e),X(pe)}function ce(m){m.memoizedState!==null&&ee(me,m);var g=J.current,_=pU(g,m.type);g!==_&&(ee(_e,m),ee(J,_))}function Se(m){_e.current===m&&(X(J),X(_e)),me.current===m&&(X(me),Jx._currentValue=K)}var ie,Fe;function V(m){if(ie===void 0)try{throw Error()}catch(_){var g=_.stack.trim().match(/\n( *(at )?)/);ie=g&&g[1]||"",Fe=-1<_.stack.indexOf(` +function Roe(t,e){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}))}(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 s of i)if(s.type==="childList")for(const o of s.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&r(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const s={};return i.integrity&&(s.integrity=i.integrity),i.referrerPolicy&&(s.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?s.credentials="include":i.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function r(i){if(i.ep)return;i.ep=!0;const s=n(i);fetch(i.href,s)}})();var l_=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function ec(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var zT={exports:{}},ry={};var WU;function Foe(){if(WU)return ry;WU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.fragment");function n(r,i,s){var o=null;if(s!==void 0&&(o=""+s),i.key!==void 0&&(o=""+i.key),"key"in i){s={};for(var l in i)l!=="key"&&(s[l]=i[l])}else s=i;return i=s.ref,{$$typeof:t,type:r,key:o,ref:i!==void 0?i:null,props:s}}return ry.Fragment=e,ry.jsx=n,ry.jsxs=n,ry}var KU;function Loe(){return KU||(KU=1,zT.exports=Foe()),zT.exports}var a=Loe(),UT={exports:{}},kn={};var XU;function Ooe(){if(XU)return kn;XU=1;var t=Symbol.for("react.transitional.element"),e=Symbol.for("react.portal"),n=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),i=Symbol.for("react.profiler"),s=Symbol.for("react.consumer"),o=Symbol.for("react.context"),l=Symbol.for("react.forward_ref"),c=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),h=Symbol.for("react.activity"),f=Symbol.iterator;function p($){return $===null||typeof $!="object"?null:($=f&&$[f]||$["@@iterator"],typeof $=="function"?$:null)}var x={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},y=Object.assign,b={};function v($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}v.prototype.isReactComponent={},v.prototype.setState=function($,X){if(typeof $!="object"&&typeof $!="function"&&$!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,$,X,"setState")},v.prototype.forceUpdate=function($){this.updater.enqueueForceUpdate(this,$,"forceUpdate")};function S(){}S.prototype=v.prototype;function N($,X,ee){this.props=$,this.context=X,this.refs=b,this.updater=ee||x}var A=N.prototype=new S;A.constructor=N,y(A,v.prototype),A.isPureReactComponent=!0;var C=Array.isArray;function T(){}var k={H:null,A:null,T:null,S:null},j=Object.prototype.hasOwnProperty;function E($,X,ee){var J=ee.ref;return{$$typeof:t,type:$,key:X,ref:J!==void 0?J:null,props:ee}}function P($,X){return E($.type,X,$.props)}function z($){return typeof $=="object"&&$!==null&&$.$$typeof===t}function O($){var X={"=":"=0",":":"=2"};return"$"+$.replace(/[=:]/g,function(ee){return X[ee]})}var B=/\/+/g;function R($,X){return typeof $=="object"&&$!==null&&$.key!=null?O(""+$.key):X.toString(36)}function G($){switch($.status){case"fulfilled":return $.value;case"rejected":throw $.reason;default:switch(typeof $.status=="string"?$.then(T,T):($.status="pending",$.then(function(X){$.status==="pending"&&($.status="fulfilled",$.value=X)},function(X){$.status==="pending"&&($.status="rejected",$.reason=X)})),$.status){case"fulfilled":return $.value;case"rejected":throw $.reason}}throw $}function q($,X,ee,J,_e){var pe=typeof $;(pe==="undefined"||pe==="boolean")&&($=null);var ae=!1;if($===null)ae=!0;else switch(pe){case"bigint":case"string":case"number":ae=!0;break;case"object":switch($.$$typeof){case t:case e:ae=!0;break;case d:return ae=$._init,q(ae($._payload),X,ee,J,_e)}}if(ae)return _e=_e($),ae=J===""?"."+R($,0):J,C(_e)?(ee="",ae!=null&&(ee=ae.replace(B,"$&/")+"/"),q(_e,X,ee,"",function(ue){return ue})):_e!=null&&(z(_e)&&(_e=P(_e,ee+(_e.key==null||$&&$.key===_e.key?"":(""+_e.key).replace(B,"$&/")+"/")+ae)),X.push(_e)),1;ae=0;var Pe=J===""?".":J+":";if(C($))for(var he=0;he<$.length;he++)J=$[he],pe=Pe+R(J,he),ae+=q(J,X,ee,pe,_e);else if(he=p($),typeof he=="function")for($=he.call($),he=0;!(J=$.next()).done;)J=J.value,pe=Pe+R(J,he++),ae+=q(J,X,ee,pe,_e);else if(pe==="object"){if(typeof $.then=="function")return q(G($),X,ee,J,_e);throw X=String($),Error("Objects are not valid as a React child (found: "+(X==="[object Object]"?"object with keys {"+Object.keys($).join(", ")+"}":X)+"). If you meant to render a collection of children, use an array instead.")}return ae}function U($,X,ee){if($==null)return $;var J=[],_e=0;return q($,J,"","",function(pe){return X.call(ee,pe,_e++)}),J}function K($){if($._status===-1){var X=$._result;X=X(),X.then(function(ee){($._status===0||$._status===-1)&&($._status=1,$._result=ee)},function(ee){($._status===0||$._status===-1)&&($._status=2,$._result=ee)}),$._status===-1&&($._status=0,$._result=X)}if($._status===1)return $._result.default;throw $._result}var Y=typeof reportError=="function"?reportError:function($){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var X=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof $=="object"&&$!==null&&typeof $.message=="string"?String($.message):String($),error:$});if(!window.dispatchEvent(X))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",$);return}console.error($)},oe={map:U,forEach:function($,X,ee){U($,function(){X.apply(this,arguments)},ee)},count:function($){var X=0;return U($,function(){X++}),X},toArray:function($){return U($,function(X){return X})||[]},only:function($){if(!z($))throw Error("React.Children.only expected to receive a single React element child.");return $}};return kn.Activity=h,kn.Children=oe,kn.Component=v,kn.Fragment=n,kn.Profiler=i,kn.PureComponent=N,kn.StrictMode=r,kn.Suspense=c,kn.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=k,kn.__COMPILER_RUNTIME={__proto__:null,c:function($){return k.H.useMemoCache($)}},kn.cache=function($){return function(){return $.apply(null,arguments)}},kn.cacheSignal=function(){return null},kn.cloneElement=function($,X,ee){if($==null)throw Error("The argument must be a React element, but you passed "+$+".");var J=y({},$.props),_e=$.key;if(X!=null)for(pe in X.key!==void 0&&(_e=""+X.key),X)!j.call(X,pe)||pe==="key"||pe==="__self"||pe==="__source"||pe==="ref"&&X.ref===void 0||(J[pe]=X[pe]);var pe=arguments.length-2;if(pe===1)J.children=ee;else if(1>>1,oe=q[Y];if(0>>1;Y<$;){var X=2*(Y+1)-1,ee=q[X],J=X+1,_e=q[J];if(0>i(ee,K))Ji(_e,ee)?(q[Y]=_e,q[J]=K,Y=J):(q[Y]=ee,q[X]=K,Y=X);else if(Ji(_e,K))q[Y]=_e,q[J]=K,Y=J;else break e}}return U}function i(q,U){var K=q.sortIndex-U.sortIndex;return K!==0?K:q.id-U.id}if(t.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var s=performance;t.unstable_now=function(){return s.now()}}else{var o=Date,l=o.now();t.unstable_now=function(){return o.now()-l}}var c=[],u=[],d=1,h=null,f=3,p=!1,x=!1,y=!1,b=!1,v=typeof setTimeout=="function"?setTimeout:null,S=typeof clearTimeout=="function"?clearTimeout:null,N=typeof setImmediate<"u"?setImmediate:null;function A(q){for(var U=n(u);U!==null;){if(U.callback===null)r(u);else if(U.startTime<=q)r(u),U.sortIndex=U.expirationTime,e(c,U);else break;U=n(u)}}function C(q){if(y=!1,A(q),!x)if(n(c)!==null)x=!0,T||(T=!0,O());else{var U=n(u);U!==null&&G(C,U.startTime-q)}}var T=!1,k=-1,j=5,E=-1;function P(){return b?!0:!(t.unstable_now()-Eq&&P());){var Y=h.callback;if(typeof Y=="function"){h.callback=null,f=h.priorityLevel;var oe=Y(h.expirationTime<=q);if(q=t.unstable_now(),typeof oe=="function"){h.callback=oe,A(q),U=!0;break t}h===n(c)&&r(c),A(q)}else r(c);h=n(c)}if(h!==null)U=!0;else{var $=n(u);$!==null&&G(C,$.startTime-q),U=!1}}break e}finally{h=null,f=K,p=!1}U=void 0}}finally{U?O():T=!1}}}var O;if(typeof N=="function")O=function(){N(z)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,R=B.port2;B.port1.onmessage=z,O=function(){R.postMessage(null)}}else O=function(){v(z,0)};function G(q,U){k=v(function(){q(t.unstable_now())},U)}t.unstable_IdlePriority=5,t.unstable_ImmediatePriority=1,t.unstable_LowPriority=4,t.unstable_NormalPriority=3,t.unstable_Profiling=null,t.unstable_UserBlockingPriority=2,t.unstable_cancelCallback=function(q){q.callback=null},t.unstable_forceFrameRate=function(q){0>q||125Y?(q.sortIndex=K,e(u,q),n(c)===null&&q===n(u)&&(y?(S(k),k=-1):y=!0,G(C,K-Y))):(q.sortIndex=oe,e(c,q),x||p||(x=!0,T||(T=!0,O()))),q},t.unstable_shouldYield=P,t.unstable_wrapCallback=function(q){var U=f;return function(){var K=f;f=U;try{return q.apply(this,arguments)}finally{f=K}}}})($T)),$T}var ZU;function Uoe(){return ZU||(ZU=1,HT.exports=zoe()),HT.exports}var VT={exports:{}},ss={};var JU;function Boe(){if(JU)return ss;JU=1;var t=Bb();function e(c){var u="https://react.dev/errors/"+c;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(t)}catch(e){console.error(e)}}return t(),VT.exports=Boe(),VT.exports}var tB;function Hoe(){if(tB)return iy;tB=1;var t=Uoe(),e=Bb(),n=yK();function r(m){var g="https://react.dev/errors/"+m;if(1oe||(m.current=Y[oe],Y[oe]=null,oe--)}function ee(m,g){oe++,Y[oe]=m.current,m.current=g}var J=$(null),_e=$(null),pe=$(null),ae=$(null);function Pe(m,g){switch(ee(pe,g),ee(_e,m),ee(J,null),g.nodeType){case 9:case 11:m=(m=g.documentElement)&&(m=m.namespaceURI)?gU(m):0;break;default:if(m=g.tagName,g=g.namespaceURI)g=gU(g),m=bU(g,m);else switch(m){case"svg":m=1;break;case"math":m=2;break;default:m=0}}X(J),ee(J,m)}function he(){X(J),X(_e),X(pe)}function ue(m){m.memoizedState!==null&&ee(ae,m);var g=J.current,_=bU(g,m.type);g!==_&&(ee(_e,m),ee(J,_))}function Se(m){_e.current===m&&(X(J),X(_e)),ae.current===m&&(X(ae),Jx._currentValue=K)}var ie,Fe;function V(m){if(ie===void 0)try{throw Error()}catch(_){var g=_.stack.trim().match(/\n( *(at )?)/);ie=g&&g[1]||"",Fe=-1<_.stack.indexOf(` at`)?" ()":-1<_.stack.indexOf("@")?"@unknown:0:0":""}return` -`+ie+m+Fe}var W=!1;function ue(m,g){if(!m||W)return"";W=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var M={DetermineComponentFrameRoot:function(){try{if(g){var Ct=function(){throw Error()};if(Object.defineProperty(Ct.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Ct,[])}catch(gt){var mt=gt}Reflect.construct(m,[],Ct)}else{try{Ct.call()}catch(gt){mt=gt}m.call(Ct.prototype)}}else{try{throw Error()}catch(gt){mt=gt}(Ct=m())&&typeof Ct.catch=="function"&&Ct.catch(function(){})}}catch(gt){if(gt&&mt&&typeof gt.stack=="string")return[gt.stack,mt.stack]}return[null,null]}};M.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var F=Object.getOwnPropertyDescriptor(M.DetermineComponentFrameRoot,"name");F&&F.configurable&&Object.defineProperty(M.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var I=M.DetermineComponentFrameRoot(),te=I[0],ke=I[1];if(te&&ke){var Be=te.split(` +`+ie+m+Fe}var W=!1;function de(m,g){if(!m||W)return"";W=!0;var _=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var M={DetermineComponentFrameRoot:function(){try{if(g){var Ct=function(){throw Error()};if(Object.defineProperty(Ct.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(Ct,[])}catch(gt){var mt=gt}Reflect.construct(m,[],Ct)}else{try{Ct.call()}catch(gt){mt=gt}m.call(Ct.prototype)}}else{try{throw Error()}catch(gt){mt=gt}(Ct=m())&&typeof Ct.catch=="function"&&Ct.catch(function(){})}}catch(gt){if(gt&&mt&&typeof gt.stack=="string")return[gt.stack,mt.stack]}return[null,null]}};M.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var F=Object.getOwnPropertyDescriptor(M.DetermineComponentFrameRoot,"name");F&&F.configurable&&Object.defineProperty(M.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var I=M.DetermineComponentFrameRoot(),te=I[0],ke=I[1];if(te&&ke){var Be=te.split(` `),ht=ke.split(` `);for(F=M=0;MF||Be[M]!==ht[F]){var yt=` -`+Be[M].replace(" at new "," at ");return m.displayName&&yt.includes("")&&(yt=yt.replace("",m.displayName)),yt}while(1<=M&&0<=F);break}}}finally{W=!1,Error.prepareStackTrace=_}return(_=m?m.displayName||m.name:"")?V(_):""}function Q(m,g){switch(m.tag){case 26:case 27:case 5:return V(m.type);case 16:return V("Lazy");case 13:return m.child!==g&&g!==null?V("Suspense Fallback"):V("Suspense");case 19:return V("SuspenseList");case 0:case 15:return ue(m.type,!1);case 11:return ue(m.type.render,!1);case 1:return ue(m.type,!0);case 31:return V("Activity");default:return""}}function se(m){try{var g="",_=null;do g+=Q(m,_),_=m,m=m.return;while(m);return g}catch(M){return` +`+Be[M].replace(" at new "," at ");return m.displayName&&yt.includes("")&&(yt=yt.replace("",m.displayName)),yt}while(1<=M&&0<=F);break}}}finally{W=!1,Error.prepareStackTrace=_}return(_=m?m.displayName||m.name:"")?V(_):""}function Q(m,g){switch(m.tag){case 26:case 27:case 5:return V(m.type);case 16:return V("Lazy");case 13:return m.child!==g&&g!==null?V("Suspense Fallback"):V("Suspense");case 19:return V("SuspenseList");case 0:case 15:return de(m.type,!1);case 11:return de(m.type.render,!1);case 1:return de(m.type,!0);case 31:return V("Activity");default:return""}}function le(m){try{var g="",_=null;do g+=Q(m,_),_=m,m=m.return;while(m);return g}catch(M){return` Error generating stack: `+M.message+` -`+M.stack}}var Z=Object.prototype.hasOwnProperty,Ne=t.unstable_scheduleCallback,ve=t.unstable_cancelCallback,fe=t.unstable_shouldYield,L=t.unstable_requestPaint,D=t.unstable_now,H=t.unstable_getCurrentPriorityLevel,he=t.unstable_ImmediatePriority,xe=t.unstable_UserBlockingPriority,be=t.unstable_NormalPriority,Ue=t.unstable_LowPriority,Oe=t.unstable_IdlePriority,Qe=t.log,rt=t.unstable_setDisableYieldValue,we=null,ye=null;function le(m){if(typeof Qe=="function"&&rt(m),ye&&typeof ye.setStrictMode=="function")try{ye.setStrictMode(we,m)}catch{}}var Le=Math.clz32?Math.clz32:Ae,Me=Math.log,$e=Math.LN2;function Ae(m){return m>>>=0,m===0?32:31-(Me(m)/$e|0)|0}var Je=256,Pe=262144,Xe=4194304;function De(m){var g=m&42;if(g!==0)return g;switch(m&-m){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return m&261888;case 262144:case 524288:case 1048576:case 2097152:return m&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return m&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return m}}function Re(m,g,_){var M=m.pendingLanes;if(M===0)return 0;var F=0,I=m.suspendedLanes,te=m.pingedLanes;m=m.warmLanes;var ke=M&134217727;return ke!==0?(M=ke&~I,M!==0?F=De(M):(te&=ke,te!==0?F=De(te):_||(_=ke&~m,_!==0&&(F=De(_))))):(ke=M&~I,ke!==0?F=De(ke):te!==0?F=De(te):_||(_=M&~m,_!==0&&(F=De(_)))),F===0?0:g!==0&&g!==F&&(g&I)===0&&(I=F&-F,_=g&-g,I>=_||I===32&&(_&4194048)!==0)?g:F}function qe(m,g){return(m.pendingLanes&~(m.suspendedLanes&~m.pingedLanes)&g)===0}function pt(m,g){switch(m){case 1:case 2:case 4:case 8:case 64:return g+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return g+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function At(){var m=Xe;return Xe<<=1,(Xe&62914560)===0&&(Xe=4194304),m}function Rt(m){for(var g=[],_=0;31>_;_++)g.push(m);return g}function Wt(m,g){m.pendingLanes|=g,g!==268435456&&(m.suspendedLanes=0,m.pingedLanes=0,m.warmLanes=0)}function Tt(m,g,_,M,F,I){var te=m.pendingLanes;m.pendingLanes=_,m.suspendedLanes=0,m.pingedLanes=0,m.warmLanes=0,m.expiredLanes&=_,m.entangledLanes&=_,m.errorRecoveryDisabledLanes&=_,m.shellSuspendCounter=0;var ke=m.entanglements,Be=m.expirationTimes,ht=m.hiddenUpdates;for(_=te&~_;0<_;){var yt=31-Le(_),Ct=1<"u")return null;try{return m.activeElement||m.body}catch{return m.body}}var Qr=/[\n"\\]/g;function gn(m){return m.replace(Qr,function(g){return"\\"+g.charCodeAt(0).toString(16)+" "})}function Zr(m,g,_,M,F,I,te,ke){m.name="",te!=null&&typeof te!="function"&&typeof te!="symbol"&&typeof te!="boolean"?m.type=te:m.removeAttribute("type"),g!=null?te==="number"?(g===0&&m.value===""||m.value!=g)&&(m.value=""+Mn(g)):m.value!==""+Mn(g)&&(m.value=""+Mn(g)):te!=="submit"&&te!=="reset"||m.removeAttribute("value"),g!=null?vi(m,te,Mn(g)):_!=null?vi(m,te,Mn(_)):M!=null&&m.removeAttribute("value"),F==null&&I!=null&&(m.defaultChecked=!!I),F!=null&&(m.checked=F&&typeof F!="function"&&typeof F!="symbol"),ke!=null&&typeof ke!="function"&&typeof ke!="symbol"&&typeof ke!="boolean"?m.name=""+Mn(ke):m.removeAttribute("name")}function Kn(m,g,_,M,F,I,te,ke){if(I!=null&&typeof I!="function"&&typeof I!="symbol"&&typeof I!="boolean"&&(m.type=I),g!=null||_!=null){if(!(I!=="submit"&&I!=="reset"||g!=null)){sn(m);return}_=_!=null?""+Mn(_):"",g=g!=null?""+Mn(g):_,ke||g===m.value||(m.value=g),m.defaultValue=g}M=M??F,M=typeof M!="function"&&typeof M!="symbol"&&!!M,m.checked=ke?m.checked:!!M,m.defaultChecked=!!M,te!=null&&typeof te!="function"&&typeof te!="symbol"&&typeof te!="boolean"&&(m.name=te),sn(m)}function vi(m,g,_){g==="number"&&Mr(m.ownerDocument)===m||m.defaultValue===""+_||(m.defaultValue=""+_)}function mi(m,g,_,M){if(m=m.options,g){g={};for(var F=0;F<_.length;F++)g["$"+_[F]]=!0;for(_=0;_"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),vr=!1;if(Ur)try{var Cl={};Object.defineProperty(Cl,"passive",{get:function(){vr=!0}}),window.addEventListener("test",Cl,Cl),window.removeEventListener("test",Cl,Cl)}catch{vr=!1}var Ma=null,oa=null,ic=null;function Br(){if(ic)return ic;var m,g=oa,_=g.length,M,F="value"in Ma?Ma.value:Ma.textContent,I=F.length;for(m=0;m<_&&g[m]===F[m];m++);var te=_-m;for(M=1;M<=te&&g[_-M]===F[I-M];M++);return ic=F.slice(m,1=vx),wI=" ",_I=!1;function SI(m,g){switch(m){case"keyup":return ese.indexOf(g.keyCode)!==-1;case"keydown":return g.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function NI(m){return m=m.detail,typeof m=="object"&&"data"in m?m.data:null}var fp=!1;function nse(m,g){switch(m){case"compositionend":return NI(g);case"keypress":return g.which!==32?null:(_I=!0,wI);case"textInput":return m=g.data,m===wI&&_I?null:m;default:return null}}function rse(m,g){if(fp)return m==="compositionend"||!GM&&SI(m,g)?(m=Br(),ic=oa=Ma=null,fp=!1,m):null;switch(m){case"paste":return null;case"keypress":if(!(g.ctrlKey||g.altKey||g.metaKey)||g.ctrlKey&&g.altKey){if(g.char&&1=g)return{node:_,offset:g-m};m=M}e:{for(;_;){if(_.nextSibling){_=_.nextSibling;break e}_=_.parentNode}_=void 0}_=jI(_)}}function RI(m,g){return m&&g?m===g?!0:m&&m.nodeType===3?!1:g&&g.nodeType===3?RI(m,g.parentNode):"contains"in m?m.contains(g):m.compareDocumentPosition?!!(m.compareDocumentPosition(g)&16):!1:!1}function FI(m){m=m!=null&&m.ownerDocument!=null&&m.ownerDocument.defaultView!=null?m.ownerDocument.defaultView:window;for(var g=Mr(m.document);g instanceof m.HTMLIFrameElement;){try{var _=typeof g.contentWindow.location.href=="string"}catch{_=!1}if(_)m=g.contentWindow;else break;g=Mr(m.document)}return g}function KM(m){var g=m&&m.nodeName&&m.nodeName.toLowerCase();return g&&(g==="input"&&(m.type==="text"||m.type==="search"||m.type==="tel"||m.type==="url"||m.type==="password")||g==="textarea"||m.contentEditable==="true")}var dse=Ur&&"documentMode"in document&&11>=document.documentMode,pp=null,XM=null,Nx=null,YM=!1;function LI(m,g,_){var M=_.window===_?_.document:_.nodeType===9?_:_.ownerDocument;YM||pp==null||pp!==Mr(M)||(M=pp,"selectionStart"in M&&KM(M)?M={start:M.selectionStart,end:M.selectionEnd}:(M=(M.ownerDocument&&M.ownerDocument.defaultView||window).getSelection(),M={anchorNode:M.anchorNode,anchorOffset:M.anchorOffset,focusNode:M.focusNode,focusOffset:M.focusOffset}),Nx&&Sx(Nx,M)||(Nx=M,M=Ww(XM,"onSelect"),0>=te,F-=te,ac=1<<32-Le(g)+F|_<Rn?(tr=en,en=null):tr=en.sibling;var dr=mt(nt,en,ut[Rn],St);if(dr===null){en===null&&(en=tr);break}m&&en&&dr.alternate===null&&g(nt,en),We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr,en=tr}if(Rn===ut.length)return _(nt,en),rr&&Zc(nt,Rn),an;if(en===null){for(;RnRn?(tr=en,en=null):tr=en.sibling;var Vd=mt(nt,en,dr.value,St);if(Vd===null){en===null&&(en=tr);break}m&&en&&Vd.alternate===null&&g(nt,en),We=I(Vd,We,Rn),ur===null?an=Vd:ur.sibling=Vd,ur=Vd,en=tr}if(dr.done)return _(nt,en),rr&&Zc(nt,Rn),an;if(en===null){for(;!dr.done;Rn++,dr=ut.next())dr=Ct(nt,dr.value,St),dr!==null&&(We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr);return rr&&Zc(nt,Rn),an}for(en=M(en);!dr.done;Rn++,dr=ut.next())dr=gt(en,nt,Rn,dr.value,St),dr!==null&&(m&&dr.alternate!==null&&en.delete(dr.key===null?Rn:dr.key),We=I(dr,We,Rn),ur===null?an=dr:ur.sibling=dr,ur=dr);return m&&en.forEach(function(Poe){return g(nt,Poe)}),rr&&Zc(nt,Rn),an}function Er(nt,We,ut,St){if(typeof ut=="object"&&ut!==null&&ut.type===v&&ut.key===null&&(ut=ut.props.children),typeof ut=="object"&&ut!==null){switch(ut.$$typeof){case p:e:{for(var an=ut.key;We!==null;){if(We.key===an){if(an=ut.type,an===v){if(We.tag===7){_(nt,We.sibling),St=F(We,ut.props.children),St.return=nt,nt=St;break e}}else if(We.elementType===an||typeof an=="object"&&an!==null&&an.$$typeof===j&&Nm(an)===We.type){_(nt,We.sibling),St=F(We,ut.props),Ex(St,ut),St.return=nt,nt=St;break e}_(nt,We);break}else g(nt,We);We=We.sibling}ut.type===v?(St=ym(ut.props.children,nt.mode,St,ut.key),St.return=nt,nt=St):(St=uw(ut.type,ut.key,ut.props,null,nt.mode,St),Ex(St,ut),St.return=nt,nt=St)}return te(nt);case x:e:{for(an=ut.key;We!==null;){if(We.key===an)if(We.tag===4&&We.stateNode.containerInfo===ut.containerInfo&&We.stateNode.implementation===ut.implementation){_(nt,We.sibling),St=F(We,ut.children||[]),St.return=nt,nt=St;break e}else{_(nt,We);break}else g(nt,We);We=We.sibling}St=rA(ut,nt.mode,St),St.return=nt,nt=St}return te(nt);case j:return ut=Nm(ut),Er(nt,We,ut,St)}if(G(ut))return Qt(nt,We,ut,St);if(O(ut)){if(an=O(ut),typeof an!="function")throw Error(r(150));return ut=an.call(ut),mn(nt,We,ut,St)}if(typeof ut.then=="function")return Er(nt,We,bw(ut),St);if(ut.$$typeof===N)return Er(nt,We,mw(nt,ut),St);xw(nt,ut)}return typeof ut=="string"&&ut!==""||typeof ut=="number"||typeof ut=="bigint"?(ut=""+ut,We!==null&&We.tag===6?(_(nt,We.sibling),St=F(We,ut),St.return=nt,nt=St):(_(nt,We),St=nA(ut,nt.mode,St),St.return=nt,nt=St),te(nt)):_(nt,We)}return function(nt,We,ut,St){try{Tx=0;var an=Er(nt,We,ut,St);return Cp=null,an}catch(en){if(en===kp||en===pw)throw en;var ur=fo(29,en,null,nt.mode);return ur.lanes=St,ur.return=nt,ur}}}var Cm=a5(!0),s5=a5(!1),Md=!1;function pA(m){m.updateQueue={baseState:m.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function gA(m,g){m=m.updateQueue,g.updateQueue===m&&(g.updateQueue={baseState:m.baseState,firstBaseUpdate:m.firstBaseUpdate,lastBaseUpdate:m.lastBaseUpdate,shared:m.shared,callbacks:null})}function Ad(m){return{lane:m,tag:0,payload:null,callback:null,next:null}}function Td(m,g,_){var M=m.updateQueue;if(M===null)return null;if(M=M.shared,(pr&2)!==0){var F=M.pending;return F===null?g.next=g:(g.next=F.next,F.next=g),M.pending=g,g=cw(m),$I(m,null,_),g}return lw(m,M,g,_),cw(m)}function Px(m,g,_){if(g=g.updateQueue,g!==null&&(g=g.shared,(_&4194048)!==0)){var M=g.lanes;M&=m.pendingLanes,_|=M,g.lanes=_,xt(m,_)}}function bA(m,g){var _=m.updateQueue,M=m.alternate;if(M!==null&&(M=M.updateQueue,_===M)){var F=null,I=null;if(_=_.firstBaseUpdate,_!==null){do{var te={lane:_.lane,tag:_.tag,payload:_.payload,callback:null,next:null};I===null?F=I=te:I=I.next=te,_=_.next}while(_!==null);I===null?F=I=g:I=I.next=g}else F=I=g;_={baseState:M.baseState,firstBaseUpdate:F,lastBaseUpdate:I,shared:M.shared,callbacks:M.callbacks},m.updateQueue=_;return}m=_.lastBaseUpdate,m===null?_.firstBaseUpdate=g:m.next=g,_.lastBaseUpdate=g}var xA=!1;function jx(){if(xA){var m=Np;if(m!==null)throw m}}function Dx(m,g,_,M){xA=!1;var F=m.updateQueue;Md=!1;var I=F.firstBaseUpdate,te=F.lastBaseUpdate,ke=F.shared.pending;if(ke!==null){F.shared.pending=null;var Be=ke,ht=Be.next;Be.next=null,te===null?I=ht:te.next=ht,te=Be;var yt=m.alternate;yt!==null&&(yt=yt.updateQueue,ke=yt.lastBaseUpdate,ke!==te&&(ke===null?yt.firstBaseUpdate=ht:ke.next=ht,yt.lastBaseUpdate=Be))}if(I!==null){var Ct=F.baseState;te=0,yt=ht=Be=null,ke=I;do{var mt=ke.lane&-536870913,gt=mt!==ke.lane;if(gt?(er&mt)===mt:(M&mt)===mt){mt!==0&&mt===Sp&&(xA=!0),yt!==null&&(yt=yt.next={lane:0,tag:ke.tag,payload:ke.payload,callback:null,next:null});e:{var Qt=m,mn=ke;mt=g;var Er=_;switch(mn.tag){case 1:if(Qt=mn.payload,typeof Qt=="function"){Ct=Qt.call(Er,Ct,mt);break e}Ct=Qt;break e;case 3:Qt.flags=Qt.flags&-65537|128;case 0:if(Qt=mn.payload,mt=typeof Qt=="function"?Qt.call(Er,Ct,mt):Qt,mt==null)break e;Ct=h({},Ct,mt);break e;case 2:Md=!0}}mt=ke.callback,mt!==null&&(m.flags|=64,gt&&(m.flags|=8192),gt=F.callbacks,gt===null?F.callbacks=[mt]:gt.push(mt))}else gt={lane:mt,tag:ke.tag,payload:ke.payload,callback:ke.callback,next:null},yt===null?(ht=yt=gt,Be=Ct):yt=yt.next=gt,te|=mt;if(ke=ke.next,ke===null){if(ke=F.shared.pending,ke===null)break;gt=ke,ke=gt.next,gt.next=null,F.lastBaseUpdate=gt,F.shared.pending=null}}while(!0);yt===null&&(Be=Ct),F.baseState=Be,F.firstBaseUpdate=ht,F.lastBaseUpdate=yt,I===null&&(F.shared.lanes=0),Rd|=te,m.lanes=te,m.memoizedState=Ct}}function o5(m,g){if(typeof m!="function")throw Error(r(191,m));m.call(g)}function l5(m,g){var _=m.callbacks;if(_!==null)for(m.callbacks=null,m=0;m<_.length;m++)o5(_[m],g)}var Mp=$(null),yw=$(0);function c5(m,g){m=cu,ee(yw,m),ee(Mp,g),cu=m|g.baseLanes}function yA(){ee(yw,cu),ee(Mp,Mp.current)}function vA(){cu=yw.current,X(Mp),X(yw)}var po=$(null),Vo=null;function Ed(m){var g=m.alternate;ee(Ii,Ii.current&1),ee(po,m),Vo===null&&(g===null||Mp.current!==null||g.memoizedState!==null)&&(Vo=m)}function wA(m){ee(Ii,Ii.current),ee(po,m),Vo===null&&(Vo=m)}function u5(m){m.tag===22?(ee(Ii,Ii.current),ee(po,m),Vo===null&&(Vo=m)):Pd()}function Pd(){ee(Ii,Ii.current),ee(po,po.current)}function go(m){X(po),Vo===m&&(Vo=null),X(Ii)}var Ii=$(0);function vw(m){for(var g=m;g!==null;){if(g.tag===13){var _=g.memoizedState;if(_!==null&&(_=_.dehydrated,_===null||MT(_)||AT(_)))return g}else if(g.tag===19&&(g.memoizedProps.revealOrder==="forwards"||g.memoizedProps.revealOrder==="backwards"||g.memoizedProps.revealOrder==="unstable_legacy-backwards"||g.memoizedProps.revealOrder==="together")){if((g.flags&128)!==0)return g}else if(g.child!==null){g.child.return=g,g=g.child;continue}if(g===m)break;for(;g.sibling===null;){if(g.return===null||g.return===m)return null;g=g.return}g.sibling.return=g.return,g=g.sibling}return null}var tu=0,En=null,Ar=null,Wi=null,ww=!1,Ap=!1,Mm=!1,_w=0,Rx=0,Tp=null,wse=0;function Ni(){throw Error(r(321))}function _A(m,g){if(g===null)return!1;for(var _=0;_I?I:8;var te=q.T,ke={};q.T=ke,OA(m,!1,g,_);try{var Be=F(),ht=q.S;if(ht!==null&&ht(ke,Be),Be!==null&&typeof Be=="object"&&typeof Be.then=="function"){var yt=vse(Be,M);Lx(m,g,yt,yo(m))}else Lx(m,g,M,yo(m))}catch(Ct){Lx(m,g,{then:function(){},status:"rejected",reason:Ct},yo())}finally{z.p=I,te!==null&&ke.types!==null&&(te.types=ke.types),q.T=te}}function Cse(){}function FA(m,g,_,M){if(m.tag!==5)throw Error(r(476));var F=U5(m).queue;z5(m,F,g,K,_===null?Cse:function(){return B5(m),_(M)})}function U5(m){var g=m.memoizedState;if(g!==null)return g;g={memoizedState:K,baseState:K,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:nu,lastRenderedState:K},next:null};var _={};return g.next={memoizedState:_,baseState:_,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:nu,lastRenderedState:_},next:null},m.memoizedState=g,m=m.alternate,m!==null&&(m.memoizedState=g),g}function B5(m){var g=U5(m);g.next===null&&(g=m.alternate.memoizedState),Lx(m,g.next.queue,{},yo())}function LA(){return $a(Jx)}function H5(){return zi().memoizedState}function $5(){return zi().memoizedState}function Mse(m){for(var g=m.return;g!==null;){switch(g.tag){case 24:case 3:var _=yo();m=Ad(_);var M=Td(g,m,_);M!==null&&(qs(M,g,_),Px(M,g,_)),g={cache:dA()},m.payload=g;return}g=g.return}}function Ase(m,g,_){var M=yo();_={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null},Aw(m)?G5(g,_):(_=eA(m,g,_,M),_!==null&&(qs(_,m,M),q5(_,g,M)))}function V5(m,g,_){var M=yo();Lx(m,g,_,M)}function Lx(m,g,_,M){var F={lane:M,revertLane:0,gesture:null,action:_,hasEagerState:!1,eagerState:null,next:null};if(Aw(m))G5(g,F);else{var I=m.alternate;if(m.lanes===0&&(I===null||I.lanes===0)&&(I=g.lastRenderedReducer,I!==null))try{var te=g.lastRenderedState,ke=I(te,_);if(F.hasEagerState=!0,F.eagerState=ke,mo(ke,te))return lw(m,g,F,0),Hr===null&&ow(),!1}catch{}if(_=eA(m,g,F,M),_!==null)return qs(_,m,M),q5(_,g,M),!0}return!1}function OA(m,g,_,M){if(M={lane:2,revertLane:pT(),gesture:null,action:M,hasEagerState:!1,eagerState:null,next:null},Aw(m)){if(g)throw Error(r(479))}else g=eA(m,_,M,2),g!==null&&qs(g,m,2)}function Aw(m){var g=m.alternate;return m===En||g!==null&&g===En}function G5(m,g){Ap=ww=!0;var _=m.pending;_===null?g.next=g:(g.next=_.next,_.next=g),m.pending=g}function q5(m,g,_){if((_&4194048)!==0){var M=g.lanes;M&=m.pendingLanes,_|=M,g.lanes=_,xt(m,_)}}var Ox={readContext:$a,use:Nw,useCallback:Ni,useContext:Ni,useEffect:Ni,useImperativeHandle:Ni,useLayoutEffect:Ni,useInsertionEffect:Ni,useMemo:Ni,useReducer:Ni,useRef:Ni,useState:Ni,useDebugValue:Ni,useDeferredValue:Ni,useTransition:Ni,useSyncExternalStore:Ni,useId:Ni,useHostTransitionStatus:Ni,useFormState:Ni,useActionState:Ni,useOptimistic:Ni,useMemoCache:Ni,useCacheRefresh:Ni};Ox.useEffectEvent=Ni;var W5={readContext:$a,use:Nw,useCallback:function(m,g){return Cs().memoizedState=[m,g===void 0?null:g],m},useContext:$a,useEffect:E5,useImperativeHandle:function(m,g,_){_=_!=null?_.concat([m]):null,Cw(4194308,4,R5.bind(null,g,m),_)},useLayoutEffect:function(m,g){return Cw(4194308,4,m,g)},useInsertionEffect:function(m,g){Cw(4,2,m,g)},useMemo:function(m,g){var _=Cs();g=g===void 0?null:g;var M=m();if(Mm){le(!0);try{m()}finally{le(!1)}}return _.memoizedState=[M,g],M},useReducer:function(m,g,_){var M=Cs();if(_!==void 0){var F=_(g);if(Mm){le(!0);try{_(g)}finally{le(!1)}}}else F=g;return M.memoizedState=M.baseState=F,m={pending:null,lanes:0,dispatch:null,lastRenderedReducer:m,lastRenderedState:F},M.queue=m,m=m.dispatch=Ase.bind(null,En,m),[M.memoizedState,m]},useRef:function(m){var g=Cs();return m={current:m},g.memoizedState=m},useState:function(m){m=EA(m);var g=m.queue,_=V5.bind(null,En,g);return g.dispatch=_,[m.memoizedState,_]},useDebugValue:DA,useDeferredValue:function(m,g){var _=Cs();return RA(_,m,g)},useTransition:function(){var m=EA(!1);return m=z5.bind(null,En,m.queue,!0,!1),Cs().memoizedState=m,[!1,m]},useSyncExternalStore:function(m,g,_){var M=En,F=Cs();if(rr){if(_===void 0)throw Error(r(407));_=_()}else{if(_=g(),Hr===null)throw Error(r(349));(er&127)!==0||f5(M,g,_)}F.memoizedState=_;var I={value:_,getSnapshot:g};return F.queue=I,E5(g5.bind(null,M,I,m),[m]),M.flags|=2048,Ep(9,{destroy:void 0},p5.bind(null,M,I,_,g),null),_},useId:function(){var m=Cs(),g=Hr.identifierPrefix;if(rr){var _=sc,M=ac;_=(M&~(1<<32-Le(M)-1)).toString(32)+_,g="_"+g+"R_"+_,_=_w++,0<_&&(g+="H"+_.toString(32)),g+="_"}else _=wse++,g="_"+g+"r_"+_.toString(32)+"_";return m.memoizedState=g},useHostTransitionStatus:LA,useFormState:k5,useActionState:k5,useOptimistic:function(m){var g=Cs();g.memoizedState=g.baseState=m;var _={pending:null,lanes:0,dispatch:null,lastRenderedReducer:null,lastRenderedState:null};return g.queue=_,g=OA.bind(null,En,!0,_),_.dispatch=g,[m,g]},useMemoCache:MA,useCacheRefresh:function(){return Cs().memoizedState=Mse.bind(null,En)},useEffectEvent:function(m){var g=Cs(),_={impl:m};return g.memoizedState=_,function(){if((pr&2)!==0)throw Error(r(440));return _.impl.apply(void 0,arguments)}}},IA={readContext:$a,use:Nw,useCallback:L5,useContext:$a,useEffect:jA,useImperativeHandle:F5,useInsertionEffect:j5,useLayoutEffect:D5,useMemo:O5,useReducer:kw,useRef:T5,useState:function(){return kw(nu)},useDebugValue:DA,useDeferredValue:function(m,g){var _=zi();return I5(_,Ar.memoizedState,m,g)},useTransition:function(){var m=kw(nu)[0],g=zi().memoizedState;return[typeof m=="boolean"?m:Fx(m),g]},useSyncExternalStore:m5,useId:H5,useHostTransitionStatus:LA,useFormState:C5,useActionState:C5,useOptimistic:function(m,g){var _=zi();return y5(_,Ar,m,g)},useMemoCache:MA,useCacheRefresh:$5};IA.useEffectEvent=P5;var K5={readContext:$a,use:Nw,useCallback:L5,useContext:$a,useEffect:jA,useImperativeHandle:F5,useInsertionEffect:j5,useLayoutEffect:D5,useMemo:O5,useReducer:TA,useRef:T5,useState:function(){return TA(nu)},useDebugValue:DA,useDeferredValue:function(m,g){var _=zi();return Ar===null?RA(_,m,g):I5(_,Ar.memoizedState,m,g)},useTransition:function(){var m=TA(nu)[0],g=zi().memoizedState;return[typeof m=="boolean"?m:Fx(m),g]},useSyncExternalStore:m5,useId:H5,useHostTransitionStatus:LA,useFormState:A5,useActionState:A5,useOptimistic:function(m,g){var _=zi();return Ar!==null?y5(_,Ar,m,g):(_.baseState=m,[m,_.queue.dispatch])},useMemoCache:MA,useCacheRefresh:$5};K5.useEffectEvent=P5;function zA(m,g,_,M){g=m.memoizedState,_=_(M,g),_=_==null?g:h({},g,_),m.memoizedState=_,m.lanes===0&&(m.updateQueue.baseState=_)}var UA={enqueueSetState:function(m,g,_){m=m._reactInternals;var M=yo(),F=Ad(M);F.payload=g,_!=null&&(F.callback=_),g=Td(m,F,M),g!==null&&(qs(g,m,M),Px(g,m,M))},enqueueReplaceState:function(m,g,_){m=m._reactInternals;var M=yo(),F=Ad(M);F.tag=1,F.payload=g,_!=null&&(F.callback=_),g=Td(m,F,M),g!==null&&(qs(g,m,M),Px(g,m,M))},enqueueForceUpdate:function(m,g){m=m._reactInternals;var _=yo(),M=Ad(_);M.tag=2,g!=null&&(M.callback=g),g=Td(m,M,_),g!==null&&(qs(g,m,_),Px(g,m,_))}};function X5(m,g,_,M,F,I,te){return m=m.stateNode,typeof m.shouldComponentUpdate=="function"?m.shouldComponentUpdate(M,I,te):g.prototype&&g.prototype.isPureReactComponent?!Sx(_,M)||!Sx(F,I):!0}function Y5(m,g,_,M){m=g.state,typeof g.componentWillReceiveProps=="function"&&g.componentWillReceiveProps(_,M),typeof g.UNSAFE_componentWillReceiveProps=="function"&&g.UNSAFE_componentWillReceiveProps(_,M),g.state!==m&&UA.enqueueReplaceState(g,g.state,null)}function Am(m,g){var _=g;if("ref"in g){_={};for(var M in g)M!=="ref"&&(_[M]=g[M])}if(m=m.defaultProps){_===g&&(_=h({},_));for(var F in m)_[F]===void 0&&(_[F]=m[F])}return _}function Q5(m){sw(m)}function Z5(m){console.error(m)}function J5(m){sw(m)}function Tw(m,g){try{var _=m.onUncaughtError;_(g.value,{componentStack:g.stack})}catch(M){setTimeout(function(){throw M})}}function ez(m,g,_){try{var M=m.onCaughtError;M(_.value,{componentStack:_.stack,errorBoundary:g.tag===1?g.stateNode:null})}catch(F){setTimeout(function(){throw F})}}function BA(m,g,_){return _=Ad(_),_.tag=3,_.payload={element:null},_.callback=function(){Tw(m,g)},_}function tz(m){return m=Ad(m),m.tag=3,m}function nz(m,g,_,M){var F=_.type.getDerivedStateFromError;if(typeof F=="function"){var I=M.value;m.payload=function(){return F(I)},m.callback=function(){ez(g,_,M)}}var te=_.stateNode;te!==null&&typeof te.componentDidCatch=="function"&&(m.callback=function(){ez(g,_,M),typeof F!="function"&&(Fd===null?Fd=new Set([this]):Fd.add(this));var ke=M.stack;this.componentDidCatch(M.value,{componentStack:ke!==null?ke:""})})}function Tse(m,g,_,M,F){if(_.flags|=32768,M!==null&&typeof M=="object"&&typeof M.then=="function"){if(g=_.alternate,g!==null&&_p(g,_,F,!0),_=po.current,_!==null){switch(_.tag){case 31:case 13:return Vo===null?Bw():_.alternate===null&&ki===0&&(ki=3),_.flags&=-257,_.flags|=65536,_.lanes=F,M===gw?_.flags|=16384:(g=_.updateQueue,g===null?_.updateQueue=new Set([M]):g.add(M),hT(m,M,F)),!1;case 22:return _.flags|=65536,M===gw?_.flags|=16384:(g=_.updateQueue,g===null?(g={transitions:null,markerInstances:null,retryQueue:new Set([M])},_.updateQueue=g):(_=g.retryQueue,_===null?g.retryQueue=new Set([M]):_.add(M)),hT(m,M,F)),!1}throw Error(r(435,_.tag))}return hT(m,M,F),Bw(),!1}if(rr)return g=po.current,g!==null?((g.flags&65536)===0&&(g.flags|=256),g.flags|=65536,g.lanes=F,M!==sA&&(m=Error(r(422),{cause:M}),Cx(Uo(m,_)))):(M!==sA&&(g=Error(r(423),{cause:M}),Cx(Uo(g,_))),m=m.current.alternate,m.flags|=65536,F&=-F,m.lanes|=F,M=Uo(M,_),F=BA(m.stateNode,M,F),bA(m,F),ki!==4&&(ki=2)),!1;var I=Error(r(520),{cause:M});if(I=Uo(I,_),Gx===null?Gx=[I]:Gx.push(I),ki!==4&&(ki=2),g===null)return!0;M=Uo(M,_),_=g;do{switch(_.tag){case 3:return _.flags|=65536,m=F&-F,_.lanes|=m,m=BA(_.stateNode,M,m),bA(_,m),!1;case 1:if(g=_.type,I=_.stateNode,(_.flags&128)===0&&(typeof g.getDerivedStateFromError=="function"||I!==null&&typeof I.componentDidCatch=="function"&&(Fd===null||!Fd.has(I))))return _.flags|=65536,F&=-F,_.lanes|=F,F=tz(F),nz(F,m,_,M),bA(_,F),!1}_=_.return}while(_!==null);return!1}var HA=Error(r(461)),Ki=!1;function Va(m,g,_,M){g.child=m===null?s5(g,null,_,M):Cm(g,m.child,_,M)}function rz(m,g,_,M,F){_=_.render;var I=g.ref;if("ref"in M){var te={};for(var ke in M)ke!=="ref"&&(te[ke]=M[ke])}else te=M;return _m(g),M=SA(m,g,_,te,I,F),ke=NA(),m!==null&&!Ki?(kA(m,g,F),ru(m,g,F)):(rr&&ke&&iA(g),g.flags|=1,Va(m,g,M,F),g.child)}function iz(m,g,_,M,F){if(m===null){var I=_.type;return typeof I=="function"&&!tA(I)&&I.defaultProps===void 0&&_.compare===null?(g.tag=15,g.type=I,az(m,g,I,M,F)):(m=uw(_.type,null,M,g,g.mode,F),m.ref=g.ref,m.return=g,g.child=m)}if(I=m.child,!YA(m,F)){var te=I.memoizedProps;if(_=_.compare,_=_!==null?_:Sx,_(te,M)&&m.ref===g.ref)return ru(m,g,F)}return g.flags|=1,m=Qc(I,M),m.ref=g.ref,m.return=g,g.child=m}function az(m,g,_,M,F){if(m!==null){var I=m.memoizedProps;if(Sx(I,M)&&m.ref===g.ref)if(Ki=!1,g.pendingProps=M=I,YA(m,F))(m.flags&131072)!==0&&(Ki=!0);else return g.lanes=m.lanes,ru(m,g,F)}return $A(m,g,_,M,F)}function sz(m,g,_,M){var F=M.children,I=m!==null?m.memoizedState:null;if(m===null&&g.stateNode===null&&(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),M.mode==="hidden"){if((g.flags&128)!==0){if(I=I!==null?I.baseLanes|_:_,m!==null){for(M=g.child=m.child,F=0;M!==null;)F=F|M.lanes|M.childLanes,M=M.sibling;M=F&~I}else M=0,g.child=null;return oz(m,g,I,_,M)}if((_&536870912)!==0)g.memoizedState={baseLanes:0,cachePool:null},m!==null&&fw(g,I!==null?I.cachePool:null),I!==null?c5(g,I):yA(),u5(g);else return M=g.lanes=536870912,oz(m,g,I!==null?I.baseLanes|_:_,_,M)}else I!==null?(fw(g,I.cachePool),c5(g,I),Pd(),g.memoizedState=null):(m!==null&&fw(g,null),yA(),Pd());return Va(m,g,F,_),g.child}function Ix(m,g){return m!==null&&m.tag===22||g.stateNode!==null||(g.stateNode={_visibility:1,_pendingMarkers:null,_retryCache:null,_transitions:null}),g.sibling}function oz(m,g,_,M,F){var I=mA();return I=I===null?null:{parent:qi._currentValue,pool:I},g.memoizedState={baseLanes:_,cachePool:I},m!==null&&fw(g,null),yA(),u5(g),m!==null&&_p(m,g,M,!0),g.childLanes=F,null}function Ew(m,g){return g=jw({mode:g.mode,children:g.children},m.mode),g.ref=m.ref,m.child=g,g.return=m,g}function lz(m,g,_){return Cm(g,m.child,null,_),m=Ew(g,g.pendingProps),m.flags|=2,go(g),g.memoizedState=null,m}function Ese(m,g,_){var M=g.pendingProps,F=(g.flags&128)!==0;if(g.flags&=-129,m===null){if(rr){if(M.mode==="hidden")return m=Ew(g,M),g.lanes=536870912,Ix(null,m);if(wA(g),(m=Jr)?(m=vU(m,$o),m=m!==null&&m.data==="&"?m:null,m!==null&&(g.memoizedState={dehydrated:m,treeContext:Sd!==null?{id:ac,overflow:sc}:null,retryLane:536870912,hydrationErrors:null},_=GI(m),_.return=g,g.child=_,Ha=g,Jr=null)):m=null,m===null)throw kd(g);return g.lanes=536870912,null}return Ew(g,M)}var I=m.memoizedState;if(I!==null){var te=I.dehydrated;if(wA(g),F)if(g.flags&256)g.flags&=-257,g=lz(m,g,_);else if(g.memoizedState!==null)g.child=m.child,g.flags|=128,g=null;else throw Error(r(558));else if(Ki||_p(m,g,_,!1),F=(_&m.childLanes)!==0,Ki||F){if(M=Hr,M!==null&&(te=wt(M,_),te!==0&&te!==I.retryLane))throw I.retryLane=te,xm(m,te),qs(M,m,te),HA;Bw(),g=lz(m,g,_)}else m=I.treeContext,Jr=Go(te.nextSibling),Ha=g,rr=!0,Nd=null,$o=!1,m!==null&&KI(g,m),g=Ew(g,M),g.flags|=4096;return g}return m=Qc(m.child,{mode:M.mode,children:M.children}),m.ref=g.ref,g.child=m,m.return=g,m}function Pw(m,g){var _=g.ref;if(_===null)m!==null&&m.ref!==null&&(g.flags|=4194816);else{if(typeof _!="function"&&typeof _!="object")throw Error(r(284));(m===null||m.ref!==_)&&(g.flags|=4194816)}}function $A(m,g,_,M,F){return _m(g),_=SA(m,g,_,M,void 0,F),M=NA(),m!==null&&!Ki?(kA(m,g,F),ru(m,g,F)):(rr&&M&&iA(g),g.flags|=1,Va(m,g,_,F),g.child)}function cz(m,g,_,M,F,I){return _m(g),g.updateQueue=null,_=h5(g,M,_,F),d5(m),M=NA(),m!==null&&!Ki?(kA(m,g,I),ru(m,g,I)):(rr&&M&&iA(g),g.flags|=1,Va(m,g,_,I),g.child)}function uz(m,g,_,M,F){if(_m(g),g.stateNode===null){var I=xp,te=_.contextType;typeof te=="object"&&te!==null&&(I=$a(te)),I=new _(M,I),g.memoizedState=I.state!==null&&I.state!==void 0?I.state:null,I.updater=UA,g.stateNode=I,I._reactInternals=g,I=g.stateNode,I.props=M,I.state=g.memoizedState,I.refs={},pA(g),te=_.contextType,I.context=typeof te=="object"&&te!==null?$a(te):xp,I.state=g.memoizedState,te=_.getDerivedStateFromProps,typeof te=="function"&&(zA(g,_,te,M),I.state=g.memoizedState),typeof _.getDerivedStateFromProps=="function"||typeof I.getSnapshotBeforeUpdate=="function"||typeof I.UNSAFE_componentWillMount!="function"&&typeof I.componentWillMount!="function"||(te=I.state,typeof I.componentWillMount=="function"&&I.componentWillMount(),typeof I.UNSAFE_componentWillMount=="function"&&I.UNSAFE_componentWillMount(),te!==I.state&&UA.enqueueReplaceState(I,I.state,null),Dx(g,M,I,F),jx(),I.state=g.memoizedState),typeof I.componentDidMount=="function"&&(g.flags|=4194308),M=!0}else if(m===null){I=g.stateNode;var ke=g.memoizedProps,Be=Am(_,ke);I.props=Be;var ht=I.context,yt=_.contextType;te=xp,typeof yt=="object"&&yt!==null&&(te=$a(yt));var Ct=_.getDerivedStateFromProps;yt=typeof Ct=="function"||typeof I.getSnapshotBeforeUpdate=="function",ke=g.pendingProps!==ke,yt||typeof I.UNSAFE_componentWillReceiveProps!="function"&&typeof I.componentWillReceiveProps!="function"||(ke||ht!==te)&&Y5(g,I,M,te),Md=!1;var mt=g.memoizedState;I.state=mt,Dx(g,M,I,F),jx(),ht=g.memoizedState,ke||mt!==ht||Md?(typeof Ct=="function"&&(zA(g,_,Ct,M),ht=g.memoizedState),(Be=Md||X5(g,_,Be,M,mt,ht,te))?(yt||typeof I.UNSAFE_componentWillMount!="function"&&typeof I.componentWillMount!="function"||(typeof I.componentWillMount=="function"&&I.componentWillMount(),typeof I.UNSAFE_componentWillMount=="function"&&I.UNSAFE_componentWillMount()),typeof I.componentDidMount=="function"&&(g.flags|=4194308)):(typeof I.componentDidMount=="function"&&(g.flags|=4194308),g.memoizedProps=M,g.memoizedState=ht),I.props=M,I.state=ht,I.context=te,M=Be):(typeof I.componentDidMount=="function"&&(g.flags|=4194308),M=!1)}else{I=g.stateNode,gA(m,g),te=g.memoizedProps,yt=Am(_,te),I.props=yt,Ct=g.pendingProps,mt=I.context,ht=_.contextType,Be=xp,typeof ht=="object"&&ht!==null&&(Be=$a(ht)),ke=_.getDerivedStateFromProps,(ht=typeof ke=="function"||typeof I.getSnapshotBeforeUpdate=="function")||typeof I.UNSAFE_componentWillReceiveProps!="function"&&typeof I.componentWillReceiveProps!="function"||(te!==Ct||mt!==Be)&&Y5(g,I,M,Be),Md=!1,mt=g.memoizedState,I.state=mt,Dx(g,M,I,F),jx();var gt=g.memoizedState;te!==Ct||mt!==gt||Md||m!==null&&m.dependencies!==null&&hw(m.dependencies)?(typeof ke=="function"&&(zA(g,_,ke,M),gt=g.memoizedState),(yt=Md||X5(g,_,yt,M,mt,gt,Be)||m!==null&&m.dependencies!==null&&hw(m.dependencies))?(ht||typeof I.UNSAFE_componentWillUpdate!="function"&&typeof I.componentWillUpdate!="function"||(typeof I.componentWillUpdate=="function"&&I.componentWillUpdate(M,gt,Be),typeof I.UNSAFE_componentWillUpdate=="function"&&I.UNSAFE_componentWillUpdate(M,gt,Be)),typeof I.componentDidUpdate=="function"&&(g.flags|=4),typeof I.getSnapshotBeforeUpdate=="function"&&(g.flags|=1024)):(typeof I.componentDidUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=4),typeof I.getSnapshotBeforeUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=1024),g.memoizedProps=M,g.memoizedState=gt),I.props=M,I.state=gt,I.context=Be,M=yt):(typeof I.componentDidUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=4),typeof I.getSnapshotBeforeUpdate!="function"||te===m.memoizedProps&&mt===m.memoizedState||(g.flags|=1024),M=!1)}return I=M,Pw(m,g),M=(g.flags&128)!==0,I||M?(I=g.stateNode,_=M&&typeof _.getDerivedStateFromError!="function"?null:I.render(),g.flags|=1,m!==null&&M?(g.child=Cm(g,m.child,null,F),g.child=Cm(g,null,_,F)):Va(m,g,_,F),g.memoizedState=I.state,m=g.child):m=ru(m,g,F),m}function dz(m,g,_,M){return vm(),g.flags|=256,Va(m,g,_,M),g.child}var VA={dehydrated:null,treeContext:null,retryLane:0,hydrationErrors:null};function GA(m){return{baseLanes:m,cachePool:e5()}}function qA(m,g,_){return m=m!==null?m.childLanes&~_:0,g&&(m|=xo),m}function hz(m,g,_){var M=g.pendingProps,F=!1,I=(g.flags&128)!==0,te;if((te=I)||(te=m!==null&&m.memoizedState===null?!1:(Ii.current&2)!==0),te&&(F=!0,g.flags&=-129),te=(g.flags&32)!==0,g.flags&=-33,m===null){if(rr){if(F?Ed(g):Pd(),(m=Jr)?(m=vU(m,$o),m=m!==null&&m.data!=="&"?m:null,m!==null&&(g.memoizedState={dehydrated:m,treeContext:Sd!==null?{id:ac,overflow:sc}:null,retryLane:536870912,hydrationErrors:null},_=GI(m),_.return=g,g.child=_,Ha=g,Jr=null)):m=null,m===null)throw kd(g);return AT(m)?g.lanes=32:g.lanes=536870912,null}var ke=M.children;return M=M.fallback,F?(Pd(),F=g.mode,ke=jw({mode:"hidden",children:ke},F),M=ym(M,F,_,null),ke.return=g,M.return=g,ke.sibling=M,g.child=ke,M=g.child,M.memoizedState=GA(_),M.childLanes=qA(m,te,_),g.memoizedState=VA,Ix(null,M)):(Ed(g),WA(g,ke))}var Be=m.memoizedState;if(Be!==null&&(ke=Be.dehydrated,ke!==null)){if(I)g.flags&256?(Ed(g),g.flags&=-257,g=KA(m,g,_)):g.memoizedState!==null?(Pd(),g.child=m.child,g.flags|=128,g=null):(Pd(),ke=M.fallback,F=g.mode,M=jw({mode:"visible",children:M.children},F),ke=ym(ke,F,_,null),ke.flags|=2,M.return=g,ke.return=g,M.sibling=ke,g.child=M,Cm(g,m.child,null,_),M=g.child,M.memoizedState=GA(_),M.childLanes=qA(m,te,_),g.memoizedState=VA,g=Ix(null,M));else if(Ed(g),AT(ke)){if(te=ke.nextSibling&&ke.nextSibling.dataset,te)var ht=te.dgst;te=ht,M=Error(r(419)),M.stack="",M.digest=te,Cx({value:M,source:null,stack:null}),g=KA(m,g,_)}else if(Ki||_p(m,g,_,!1),te=(_&m.childLanes)!==0,Ki||te){if(te=Hr,te!==null&&(M=wt(te,_),M!==0&&M!==Be.retryLane))throw Be.retryLane=M,xm(m,M),qs(te,m,M),HA;MT(ke)||Bw(),g=KA(m,g,_)}else MT(ke)?(g.flags|=192,g.child=m.child,g=null):(m=Be.treeContext,Jr=Go(ke.nextSibling),Ha=g,rr=!0,Nd=null,$o=!1,m!==null&&KI(g,m),g=WA(g,M.children),g.flags|=4096);return g}return F?(Pd(),ke=M.fallback,F=g.mode,Be=m.child,ht=Be.sibling,M=Qc(Be,{mode:"hidden",children:M.children}),M.subtreeFlags=Be.subtreeFlags&65011712,ht!==null?ke=Qc(ht,ke):(ke=ym(ke,F,_,null),ke.flags|=2),ke.return=g,M.return=g,M.sibling=ke,g.child=M,Ix(null,M),M=g.child,ke=m.child.memoizedState,ke===null?ke=GA(_):(F=ke.cachePool,F!==null?(Be=qi._currentValue,F=F.parent!==Be?{parent:Be,pool:Be}:F):F=e5(),ke={baseLanes:ke.baseLanes|_,cachePool:F}),M.memoizedState=ke,M.childLanes=qA(m,te,_),g.memoizedState=VA,Ix(m.child,M)):(Ed(g),_=m.child,m=_.sibling,_=Qc(_,{mode:"visible",children:M.children}),_.return=g,_.sibling=null,m!==null&&(te=g.deletions,te===null?(g.deletions=[m],g.flags|=16):te.push(m)),g.child=_,g.memoizedState=null,_)}function WA(m,g){return g=jw({mode:"visible",children:g},m.mode),g.return=m,m.child=g}function jw(m,g){return m=fo(22,m,null,g),m.lanes=0,m}function KA(m,g,_){return Cm(g,m.child,null,_),m=WA(g,g.pendingProps.children),m.flags|=2,g.memoizedState=null,m}function mz(m,g,_){m.lanes|=g;var M=m.alternate;M!==null&&(M.lanes|=g),cA(m.return,g,_)}function XA(m,g,_,M,F,I){var te=m.memoizedState;te===null?m.memoizedState={isBackwards:g,rendering:null,renderingStartTime:0,last:M,tail:_,tailMode:F,treeForkCount:I}:(te.isBackwards=g,te.rendering=null,te.renderingStartTime=0,te.last=M,te.tail=_,te.tailMode=F,te.treeForkCount=I)}function fz(m,g,_){var M=g.pendingProps,F=M.revealOrder,I=M.tail;M=M.children;var te=Ii.current,ke=(te&2)!==0;if(ke?(te=te&1|2,g.flags|=128):te&=1,ee(Ii,te),Va(m,g,M,_),M=rr?kx:0,!ke&&m!==null&&(m.flags&128)!==0)e:for(m=g.child;m!==null;){if(m.tag===13)m.memoizedState!==null&&mz(m,_,g);else if(m.tag===19)mz(m,_,g);else if(m.child!==null){m.child.return=m,m=m.child;continue}if(m===g)break e;for(;m.sibling===null;){if(m.return===null||m.return===g)break e;m=m.return}m.sibling.return=m.return,m=m.sibling}switch(F){case"forwards":for(_=g.child,F=null;_!==null;)m=_.alternate,m!==null&&vw(m)===null&&(F=_),_=_.sibling;_=F,_===null?(F=g.child,g.child=null):(F=_.sibling,_.sibling=null),XA(g,!1,F,_,I,M);break;case"backwards":case"unstable_legacy-backwards":for(_=null,F=g.child,g.child=null;F!==null;){if(m=F.alternate,m!==null&&vw(m)===null){g.child=F;break}m=F.sibling,F.sibling=_,_=F,F=m}XA(g,!0,_,null,I,M);break;case"together":XA(g,!1,null,null,void 0,M);break;default:g.memoizedState=null}return g.child}function ru(m,g,_){if(m!==null&&(g.dependencies=m.dependencies),Rd|=g.lanes,(_&g.childLanes)===0)if(m!==null){if(_p(m,g,_,!1),(_&g.childLanes)===0)return null}else return null;if(m!==null&&g.child!==m.child)throw Error(r(153));if(g.child!==null){for(m=g.child,_=Qc(m,m.pendingProps),g.child=_,_.return=g;m.sibling!==null;)m=m.sibling,_=_.sibling=Qc(m,m.pendingProps),_.return=g;_.sibling=null}return g.child}function YA(m,g){return(m.lanes&g)!==0?!0:(m=m.dependencies,!!(m!==null&&hw(m)))}function Pse(m,g,_){switch(g.tag){case 3:je(g,g.stateNode.containerInfo),Cd(g,qi,m.memoizedState.cache),vm();break;case 27:case 5:ce(g);break;case 4:je(g,g.stateNode.containerInfo);break;case 10:Cd(g,g.type,g.memoizedProps.value);break;case 31:if(g.memoizedState!==null)return g.flags|=128,wA(g),null;break;case 13:var M=g.memoizedState;if(M!==null)return M.dehydrated!==null?(Ed(g),g.flags|=128,null):(_&g.child.childLanes)!==0?hz(m,g,_):(Ed(g),m=ru(m,g,_),m!==null?m.sibling:null);Ed(g);break;case 19:var F=(m.flags&128)!==0;if(M=(_&g.childLanes)!==0,M||(_p(m,g,_,!1),M=(_&g.childLanes)!==0),F){if(M)return fz(m,g,_);g.flags|=128}if(F=g.memoizedState,F!==null&&(F.rendering=null,F.tail=null,F.lastEffect=null),ee(Ii,Ii.current),M)break;return null;case 22:return g.lanes=0,sz(m,g,_,g.pendingProps);case 24:Cd(g,qi,m.memoizedState.cache)}return ru(m,g,_)}function pz(m,g,_){if(m!==null)if(m.memoizedProps!==g.pendingProps)Ki=!0;else{if(!YA(m,_)&&(g.flags&128)===0)return Ki=!1,Pse(m,g,_);Ki=(m.flags&131072)!==0}else Ki=!1,rr&&(g.flags&1048576)!==0&&WI(g,kx,g.index);switch(g.lanes=0,g.tag){case 16:e:{var M=g.pendingProps;if(m=Nm(g.elementType),g.type=m,typeof m=="function")tA(m)?(M=Am(m,M),g.tag=1,g=uz(null,g,m,M,_)):(g.tag=0,g=$A(null,g,m,M,_));else{if(m!=null){var F=m.$$typeof;if(F===A){g.tag=11,g=rz(null,g,m,M,_);break e}else if(F===k){g.tag=14,g=iz(null,g,m,M,_);break e}}throw g=R(m)||m,Error(r(306,g,""))}}return g;case 0:return $A(m,g,g.type,g.pendingProps,_);case 1:return M=g.type,F=Am(M,g.pendingProps),uz(m,g,M,F,_);case 3:e:{if(je(g,g.stateNode.containerInfo),m===null)throw Error(r(387));M=g.pendingProps;var I=g.memoizedState;F=I.element,gA(m,g),Dx(g,M,null,_);var te=g.memoizedState;if(M=te.cache,Cd(g,qi,M),M!==I.cache&&uA(g,[qi],_,!0),jx(),M=te.element,I.isDehydrated)if(I={element:M,isDehydrated:!1,cache:te.cache},g.updateQueue.baseState=I,g.memoizedState=I,g.flags&256){g=dz(m,g,M,_);break e}else if(M!==F){F=Uo(Error(r(424)),g),Cx(F),g=dz(m,g,M,_);break e}else for(m=g.stateNode.containerInfo,m.nodeType===9?m=m.body:m=m.nodeName==="HTML"?m.ownerDocument.body:m,Jr=Go(m.firstChild),Ha=g,rr=!0,Nd=null,$o=!0,_=s5(g,null,M,_),g.child=_;_;)_.flags=_.flags&-3|4096,_=_.sibling;else{if(vm(),M===F){g=ru(m,g,_);break e}Va(m,g,M,_)}g=g.child}return g;case 26:return Pw(m,g),m===null?(_=CU(g.type,null,g.pendingProps,null))?g.memoizedState=_:rr||(_=g.type,m=g.pendingProps,M=Kw(pe.current).createElement(_),M[Pn]=g,M[cr]=m,Ga(M,_,m),Ve(M),g.stateNode=M):g.memoizedState=CU(g.type,m.memoizedProps,g.pendingProps,m.memoizedState),null;case 27:return ce(g),m===null&&rr&&(M=g.stateNode=SU(g.type,g.pendingProps,pe.current),Ha=g,$o=!0,F=Jr,zd(g.type)?(TT=F,Jr=Go(M.firstChild)):Jr=F),Va(m,g,g.pendingProps.children,_),Pw(m,g),m===null&&(g.flags|=4194304),g.child;case 5:return m===null&&rr&&((F=M=Jr)&&(M=ooe(M,g.type,g.pendingProps,$o),M!==null?(g.stateNode=M,Ha=g,Jr=Go(M.firstChild),$o=!1,F=!0):F=!1),F||kd(g)),ce(g),F=g.type,I=g.pendingProps,te=m!==null?m.memoizedProps:null,M=I.children,NT(F,I)?M=null:te!==null&&NT(F,te)&&(g.flags|=32),g.memoizedState!==null&&(F=SA(m,g,_se,null,null,_),Jx._currentValue=F),Pw(m,g),Va(m,g,M,_),g.child;case 6:return m===null&&rr&&((m=_=Jr)&&(_=loe(_,g.pendingProps,$o),_!==null?(g.stateNode=_,Ha=g,Jr=null,m=!0):m=!1),m||kd(g)),null;case 13:return hz(m,g,_);case 4:return je(g,g.stateNode.containerInfo),M=g.pendingProps,m===null?g.child=Cm(g,null,M,_):Va(m,g,M,_),g.child;case 11:return rz(m,g,g.type,g.pendingProps,_);case 7:return Va(m,g,g.pendingProps,_),g.child;case 8:return Va(m,g,g.pendingProps.children,_),g.child;case 12:return Va(m,g,g.pendingProps.children,_),g.child;case 10:return M=g.pendingProps,Cd(g,g.type,M.value),Va(m,g,M.children,_),g.child;case 9:return F=g.type._context,M=g.pendingProps.children,_m(g),F=$a(F),M=M(F),g.flags|=1,Va(m,g,M,_),g.child;case 14:return iz(m,g,g.type,g.pendingProps,_);case 15:return az(m,g,g.type,g.pendingProps,_);case 19:return fz(m,g,_);case 31:return Ese(m,g,_);case 22:return sz(m,g,_,g.pendingProps);case 24:return _m(g),M=$a(qi),m===null?(F=mA(),F===null&&(F=Hr,I=dA(),F.pooledCache=I,I.refCount++,I!==null&&(F.pooledCacheLanes|=_),F=I),g.memoizedState={parent:M,cache:F},pA(g),Cd(g,qi,F)):((m.lanes&_)!==0&&(gA(m,g),Dx(g,null,null,_),jx()),F=m.memoizedState,I=g.memoizedState,F.parent!==M?(F={parent:M,cache:M},g.memoizedState=F,g.lanes===0&&(g.memoizedState=g.updateQueue.baseState=F),Cd(g,qi,M)):(M=I.cache,Cd(g,qi,M),M!==F.cache&&uA(g,[qi],_,!0))),Va(m,g,g.pendingProps.children,_),g.child;case 29:throw g.pendingProps}throw Error(r(156,g.tag))}function iu(m){m.flags|=4}function QA(m,g,_,M,F){if((g=(m.mode&32)!==0)&&(g=!1),g){if(m.flags|=16777216,(F&335544128)===F)if(m.stateNode.complete)m.flags|=8192;else if(Hz())m.flags|=8192;else throw km=gw,fA}else m.flags&=-16777217}function gz(m,g){if(g.type!=="stylesheet"||(g.state.loading&4)!==0)m.flags&=-16777217;else if(m.flags|=16777216,!PU(g))if(Hz())m.flags|=8192;else throw km=gw,fA}function Dw(m,g){g!==null&&(m.flags|=4),m.flags&16384&&(g=m.tag!==22?At():536870912,m.lanes|=g,Rp|=g)}function zx(m,g){if(!rr)switch(m.tailMode){case"hidden":g=m.tail;for(var _=null;g!==null;)g.alternate!==null&&(_=g),g=g.sibling;_===null?m.tail=null:_.sibling=null;break;case"collapsed":_=m.tail;for(var M=null;_!==null;)_.alternate!==null&&(M=_),_=_.sibling;M===null?g||m.tail===null?m.tail=null:m.tail.sibling=null:M.sibling=null}}function ei(m){var g=m.alternate!==null&&m.alternate.child===m.child,_=0,M=0;if(g)for(var F=m.child;F!==null;)_|=F.lanes|F.childLanes,M|=F.subtreeFlags&65011712,M|=F.flags&65011712,F.return=m,F=F.sibling;else for(F=m.child;F!==null;)_|=F.lanes|F.childLanes,M|=F.subtreeFlags,M|=F.flags,F.return=m,F=F.sibling;return m.subtreeFlags|=M,m.childLanes=_,g}function jse(m,g,_){var M=g.pendingProps;switch(aA(g),g.tag){case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return ei(g),null;case 1:return ei(g),null;case 3:return _=g.stateNode,M=null,m!==null&&(M=m.memoizedState.cache),g.memoizedState.cache!==M&&(g.flags|=2048),eu(qi),de(),_.pendingContext&&(_.context=_.pendingContext,_.pendingContext=null),(m===null||m.child===null)&&(wp(g)?iu(g):m===null||m.memoizedState.isDehydrated&&(g.flags&256)===0||(g.flags|=1024,oA())),ei(g),null;case 26:var F=g.type,I=g.memoizedState;return m===null?(iu(g),I!==null?(ei(g),gz(g,I)):(ei(g),QA(g,F,null,M,_))):I?I!==m.memoizedState?(iu(g),ei(g),gz(g,I)):(ei(g),g.flags&=-16777217):(m=m.memoizedProps,m!==M&&iu(g),ei(g),QA(g,F,m,M,_)),null;case 27:if(Se(g),_=pe.current,F=g.type,m!==null&&g.stateNode!=null)m.memoizedProps!==M&&iu(g);else{if(!M){if(g.stateNode===null)throw Error(r(166));return ei(g),null}m=J.current,wp(g)?XI(g):(m=SU(F,M,_),g.stateNode=m,iu(g))}return ei(g),null;case 5:if(Se(g),F=g.type,m!==null&&g.stateNode!=null)m.memoizedProps!==M&&iu(g);else{if(!M){if(g.stateNode===null)throw Error(r(166));return ei(g),null}if(I=J.current,wp(g))XI(g);else{var te=Kw(pe.current);switch(I){case 1:I=te.createElementNS("http://www.w3.org/2000/svg",F);break;case 2:I=te.createElementNS("http://www.w3.org/1998/Math/MathML",F);break;default:switch(F){case"svg":I=te.createElementNS("http://www.w3.org/2000/svg",F);break;case"math":I=te.createElementNS("http://www.w3.org/1998/Math/MathML",F);break;case"script":I=te.createElement("div"),I.innerHTML=" - + +