From 5d0a48505afb6857771db6e78a83b8621c91eaed Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 25 Sep 2026 20:50:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(widget):=20Widget=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E3=80=81loader=20=E4=B8=8E=E5=8F=B3=E4=B8=8B=E8=A7=92=E6=82=AC?= =?UTF-8?q?=E6=B5=AE=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 生成可嵌入站点的 loader 与悬浮按钮,支持展开收起和移动端;附独立宿主测试页。 --- backend/app/api/widget.py | 704 + backend/app/api/widget_apps.py | 337 + backend/app/core/crawler_policy.py | 88 + backend/app/services/domain_verification.py | 188 + backend/app/services/widget_identity.py | 95 + backend/assets/widget.css | 1 + backend/assets/widget.js | 12778 ++++++++++++++++ chattermate-test/README.md | 69 + chattermate-test/index.html | 769 + chattermate-test/package-lock.json | 912 ++ chattermate-test/package.json | 12 + chattermate-test/server.js | 34 + chattermate-test/ui-options.html | 148 + chattermate-test/ui-test.html | 169 + frontend/public/apple-touch-icon-180x180.png | Bin 0 -> 4896 bytes .../public/assets/avatars/custom-agent.svg | 4 + .../public/assets/avatars/general-agent.svg | 4 + .../public/assets/avatars/sales-agent.svg | 1 + .../public/assets/avatars/support-agent.svg | 1 + frontend/public/assets/avatars/tech-agent.svg | 1 + frontend/public/assets/favicon.ico | Bin 0 -> 15086 bytes frontend/public/assets/favicon.svg | 6 + frontend/public/assets/images/logo.svg | 18 + frontend/public/assets/images/logo_dark.svg | 20 + frontend/public/config.dev.js | 32 + frontend/public/config.js | 30 + frontend/public/favicon.ico | Bin 0 -> 15086 bytes frontend/public/favicon.svg | 6 + frontend/public/images/logo.svg | 6 + frontend/public/meta-oauth-callback.html | 29 + frontend/public/notification-badge-96.png | Bin 0 -> 516 bytes frontend/public/pwa-192x192.png | Bin 0 -> 6503 bytes frontend/public/pwa-512x512.png | Bin 0 -> 17620 bytes frontend/public/pwa-maskable-512x512.png | Bin 0 -> 12739 bytes frontend/public/robots.txt | 2 + frontend/public/webclient/chattermate.min.js | 888 ++ .../src/components/widget-app/ApiKeyModal.vue | 208 + .../components/widget-app/WidgetAppForm.vue | 204 + .../components/widget-app/WidgetAppList.vue | 859 ++ frontend/src/composables/useCrawlCheck.ts | 66 + frontend/src/composables/useWidgetApps.ts | 272 + .../src/composables/useWidgetCustomization.ts | 101 + frontend/src/composables/useWidgetFiles.ts | 534 + frontend/src/composables/useWidgetStyles.ts | 77 + frontend/src/utils/widgetEmbed.ts | 78 + frontend/src/webclient/AskAiPanel.vue | 645 + frontend/src/webclient/NewChatConfirm.vue | 132 + frontend/src/webclient/WidgetBuilder.vue | 7036 +++++++++ frontend/src/webclient/chattermate.js | 2377 +++ frontend/src/webclient/host-bridge.ts | 100 + frontend/src/webclient/markdown.ts | 38 + frontend/src/webclient/new-chat.ts | 29 + frontend/src/webclient/widget-env.ts | 64 + frontend/src/webclient/widget-surface.css | 243 + frontend/src/webclient/widget-theme.ts | 206 + frontend/src/webclient/widget.css | 1389 ++ frontend/src/webclient/widget.ts | 76 + 57 files changed, 32086 insertions(+) create mode 100644 backend/app/api/widget.py create mode 100644 backend/app/api/widget_apps.py create mode 100644 backend/app/core/crawler_policy.py create mode 100644 backend/app/services/domain_verification.py create mode 100644 backend/app/services/widget_identity.py create mode 100644 backend/assets/widget.css create mode 100644 backend/assets/widget.js create mode 100644 chattermate-test/README.md create mode 100644 chattermate-test/index.html create mode 100644 chattermate-test/package-lock.json create mode 100644 chattermate-test/package.json create mode 100644 chattermate-test/server.js create mode 100644 chattermate-test/ui-options.html create mode 100644 chattermate-test/ui-test.html create mode 100644 frontend/public/apple-touch-icon-180x180.png create mode 100644 frontend/public/assets/avatars/custom-agent.svg create mode 100644 frontend/public/assets/avatars/general-agent.svg create mode 100644 frontend/public/assets/avatars/sales-agent.svg create mode 100644 frontend/public/assets/avatars/support-agent.svg create mode 100644 frontend/public/assets/avatars/tech-agent.svg create mode 100644 frontend/public/assets/favicon.ico create mode 100644 frontend/public/assets/favicon.svg create mode 100644 frontend/public/assets/images/logo.svg create mode 100644 frontend/public/assets/images/logo_dark.svg create mode 100644 frontend/public/config.dev.js create mode 100644 frontend/public/config.js create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/images/logo.svg create mode 100644 frontend/public/meta-oauth-callback.html create mode 100644 frontend/public/notification-badge-96.png create mode 100644 frontend/public/pwa-192x192.png create mode 100644 frontend/public/pwa-512x512.png create mode 100644 frontend/public/pwa-maskable-512x512.png create mode 100644 frontend/public/robots.txt create mode 100644 frontend/public/webclient/chattermate.min.js create mode 100644 frontend/src/components/widget-app/ApiKeyModal.vue create mode 100644 frontend/src/components/widget-app/WidgetAppForm.vue create mode 100644 frontend/src/components/widget-app/WidgetAppList.vue create mode 100644 frontend/src/composables/useCrawlCheck.ts create mode 100644 frontend/src/composables/useWidgetApps.ts create mode 100644 frontend/src/composables/useWidgetCustomization.ts create mode 100644 frontend/src/composables/useWidgetFiles.ts create mode 100644 frontend/src/composables/useWidgetStyles.ts create mode 100644 frontend/src/utils/widgetEmbed.ts create mode 100644 frontend/src/webclient/AskAiPanel.vue create mode 100644 frontend/src/webclient/NewChatConfirm.vue create mode 100644 frontend/src/webclient/WidgetBuilder.vue create mode 100644 frontend/src/webclient/chattermate.js create mode 100644 frontend/src/webclient/host-bridge.ts create mode 100644 frontend/src/webclient/markdown.ts create mode 100644 frontend/src/webclient/new-chat.ts create mode 100644 frontend/src/webclient/widget-env.ts create mode 100644 frontend/src/webclient/widget-surface.css create mode 100644 frontend/src/webclient/widget-theme.ts create mode 100644 frontend/src/webclient/widget.css create mode 100644 frontend/src/webclient/widget.ts diff --git a/backend/app/api/widget.py b/backend/app/api/widget.py new file mode 100644 index 0000000..8641e95 --- /dev/null +++ b/backend/app/api/widget.py @@ -0,0 +1,704 @@ +""" +Copyright 2024-2026 ChatterMate + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Request, Response, Header, Query, status +from fastapi.responses import HTMLResponse +from sqlalchemy.orm import Session +from typing import List, Optional +from uuid import UUID +from datetime import datetime +from pathlib import Path +import json +from urllib.parse import urlparse + +from app.models.agent import Agent +from app.models.customer import Customer +from app.models.widget import Widget +from app.models.user import User + +from app.core.auth import get_current_user +from app.database import get_db +from app.repositories.widget import WidgetRepository +from app.repositories.agent import AgentRepository +from app.core.security import create_conversation_token, verify_conversation_token +from app.services import conversation_token +from app.utils.client_ip import forwarded_client_ip +from app.repositories.customer import CustomerRepository +from app.models.schemas.widget import WidgetCreate, WidgetResponse +from app.core.logger import get_logger +from app.repositories.session_to_agent import SessionToAgentRepository, session_in_scope +from app.services.contact_capture import CONTACT_EMAIL_META_KEY +from app.services.widget_identity import may_stand_for +from app.models.session_to_agent import SessionStatus +from app.core.config import settings +from app.core.s3 import get_s3_signed_url +from app.utils.business_hours import is_within_business_hours + +router = APIRouter() +logger = get_logger(__name__) + +# The enterprise module can record where each widget is live; without it, +# nothing is recorded. +try: + from app.enterprise.services import widget_install +except ImportError: + widget_install = None + + +def _identity_expired() -> HTTPException: + """401 telling the widget its visitor's identity lapsed, not that it never had one. + + The embed loader answers this by asking the host page for a fresh token + (`tokenProvider`) and retrying, so an identified visitor is never silently + re-created as an anonymous customer. + """ + return HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail={ + "code": conversation_token.IDENTITY_EXPIRED_CODE, + "message": "Conversation token expired. Issue a new one with /generate-token.", + }, + ) + + +_ASSETS_DIR = Path(settings.WIDGET_ASSETS_DIR) + + +def _asset_version() -> str: + """Cache-buster for the widget bundle URLs. + + /assets/widget.js is served without a Cache-Control header and its URL never + changes, so browsers fall back to heuristic caching and can keep serving an old + bundle for hours after a release — the widget silently runs last week's code. + Stamping the build's mtime+size onto the URL makes a new build a new URL. + Recomputed per request (a stat is microseconds) so a rebuild takes effect + without restarting the backend. + """ + try: + stat = (_ASSETS_DIR / "widget.js").stat() + return f"{int(stat.st_mtime)}-{stat.st_size}" + except OSError: + # Assets served from somewhere else (CDN, dev server) — nothing to stamp. + return "" + + +def _widget_html_response(html: str) -> HTMLResponse: + """The widget HTML embeds a conversation token and the current customization, + and points at a versioned bundle URL. Caching it would hand a visitor someone + else's token from a shared cache, and would keep pinning them to a stale widget + build after a release.""" + return HTMLResponse(html, headers={"Cache-Control": "no-store"}) + + +# BACKEND_URL defaults to http://localhost:8000 and neither compose file sets +# it, so a self-hosted install that never overrode it used to serve every visitor +# a widget whose socket dialled the visitor's own machine (#315). Loopback can +# never be right for a browser on someone else's computer, so we fall back to the +# origin the widget HTML was actually fetched over. +_LOOPBACK_HOSTS = frozenset({"localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"}) + +_warned_about_loopback_backend_url = False + + +def _is_loopback(url: str) -> bool: + host = urlparse(url).hostname + return host is not None and host.lower() in _LOOPBACK_HOSTS + + +def _public_origin(request: Optional[Request]) -> Optional[str]: + """Scheme and host this request reached us on, as the browser sees them. + + Reads the forwarding headers our nginx sets rather than ``request.url``, which + only reflects them when the ASGI server is started with proxy headers enabled. + """ + if request is None: + return None + + # Several proxies in a chain append to these, and the browser's own value is + # the first entry. + forwarded_host = request.headers.get("x-forwarded-host") + host = (forwarded_host or request.headers.get("host") or "").split(",")[0].strip() + if not host: + return None + + forwarded_proto = request.headers.get("x-forwarded-proto") + if forwarded_proto: + scheme = forwarded_proto.split(",")[0].strip() + elif forwarded_host: + # A proxy that rewrites the host but not the protocol (Traefik and some + # Caddy setups, unlike our nginx) leaves request.url.scheme reading "http" + # for the proxy's own hop. Trusting that would hand an https page a ws:// + # socket the browser refuses as mixed content, so assume the hop was TLS. + scheme = "https" + else: + scheme = request.url.scheme or "https" + + return f"{scheme}://{host}" + + +def _widget_runtime_config(request: Optional[Request] = None) -> dict: + """Runtime API/WS URLs to hand the widget iframe as ``window.APP_CONFIG``. + + Derived from ``BACKEND_URL`` so a self-hosted widget talks to the configured + backend instead of the vendor cloud. On the hosted deployment BACKEND_URL is + ``https://api.chattermate.chat``, which yields exactly the widget's baked-in + defaults, so injecting this is a no-op there. + """ + global _warned_about_loopback_backend_url + + api_base = settings.BACKEND_URL.rstrip("/") + if _is_loopback(api_base): + derived = _public_origin(request) + if derived and not _is_loopback(derived): + if not _warned_about_loopback_backend_url: + _warned_about_loopback_backend_url = True + logger.warning( + "BACKEND_URL is %s, which no visitor's browser can reach. " + "Serving widgets against %s instead, taken from the request. " + "Set BACKEND_URL to this install's public API URL.", + api_base, derived, + ) + api_base = derived + + if api_base.startswith("https://"): + ws_url = "wss://" + api_base[len("https://"):] + elif api_base.startswith("http://"): + ws_url = "ws://" + api_base[len("http://"):] + else: + ws_url = api_base + return {"API_URL": f"{api_base}/api/v1", "WS_URL": ws_url} + +@router.post("", response_model=WidgetResponse) +def create_new_widget( + widget: WidgetCreate, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + """Create a new widget for the organization""" + widget_repo = WidgetRepository(db) + return widget_repo.create_widget(widget, current_user.organization_id) + + +def _note_install(db: Session, widget: Widget, request: Request, background_tasks: BackgroundTasks) -> None: + """Note where the widget is live (or blocked), after the response is + sent. Never lets a tracking error break the widget itself.""" + if widget_install is None: + return + try: + sighting = widget_install.claim_write(db, widget, request.headers.get("origin"), forwarded_client_ip(request)) + if sighting: + background_tasks.add_task(widget_install.record, widget.id, sighting) + except Exception: + logger.exception(f"Install tracking failed for widget {widget.id}") + + +@router.get("/{widget_id}/data", response_class=HTMLResponse) +async def get_widget_ui( + widget_id: str, + request: Request, + response: Response, + background_tasks: BackgroundTasks, + authorization: Optional[str] = Header(None), + source: Optional[str] = Query(None), + db: Session = Depends(get_db) +): + """Get widget UI and handle customer authentication""" + + widget = db.query(Widget).filter(Widget.id == widget_id).first() + if not widget: + raise HTTPException(status_code=404, detail="Widget not found") + + _note_install(db, widget, request, background_tasks) + + # Get agent data + agent_repo = AgentRepository(db) + agent = agent_repo.get_by_id(widget.agent_id) + if not agent: + raise HTTPException(status_code=404, detail="Agent not found") + + # What the header may honestly claim. Only a human-handled agent needs the + # business-hours check: while the AI answers, it answers at 3am too. + presence = { + 'mode': 'ai' if agent.ai_replies_enabled else 'human', + 'available': True if agent.ai_replies_enabled + else is_within_business_hours(agent.organization), + } + + require_token_auth = getattr(agent, 'require_token_auth', False) + customer_id = None + token = None + token_payload = None + + # Try to validate existing token if provided + if authorization and authorization.startswith('Bearer '): + token = authorization.split(' ')[1] + state = conversation_token.inspect(token, widget_id) + if state.is_live: + token_payload = state.payload + customer_id = state.payload.get("sub") + elif state.identity_lost: + # A lapsed token that named a customer means a signed-in visitor whose + # identity just ran out. Say so instead of rendering the frame as a + # brand-new anonymous visitor - the loader re-identifies and retries. + raise _identity_expired() + else: + token = None # Invalid token + + # SECURITY: If token auth is required, valid token MUST be provided + if require_token_auth: + if not token or customer_id is None: + logger.warning(f"Widget UI request denied: require_token_auth=true but no valid token for widget_id={widget_id}") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Authentication required. Token must be obtained from /api/v1/generate-token endpoint with valid API key." + ) + # Has valid token - return widget with existing token + return _widget_html_response(await get_widget_html( + widget_id=widget_id, + agent_name=agent.display_name or agent.name, + agent_customization=agent.customization, + customer_id=customer_id, + initial_token=token, + agent_workflow=bool(agent.use_workflow and agent.active_workflow_id), + allow_attachments=agent.allow_attachments, + ai_replies_enabled=agent.ai_replies_enabled, + presence=presence, + request=request + )) + + # Token auth NOT required - allow anonymous access + # If we have a valid token, use it; otherwise create a new one + if token and customer_id: + return _widget_html_response(await get_widget_html( + widget_id=widget_id, + agent_name=agent.display_name or agent.name, + agent_customization=agent.customization, + customer_id=customer_id, + initial_token=token, + agent_workflow=bool(agent.use_workflow and agent.active_workflow_id), + allow_attachments=agent.allow_attachments, + ai_replies_enabled=agent.ai_replies_enabled, + presence=presence, + request=request + )) + + # No valid token - create new one for anonymous access. A live token that + # names no customer yet keeps its conversation key. + token = _mint_widget_token(widget_id, _conversation_key(token_payload), source=source) + + return _widget_html_response(await get_widget_html( + widget_id=widget_id, + agent_name=agent.display_name or agent.name, + agent_customization=agent.customization, + customer_id=customer_id, + initial_token=token, + agent_workflow=bool(agent.use_workflow and agent.active_workflow_id), + allow_attachments=agent.allow_attachments, + ai_replies_enabled=agent.ai_replies_enabled, + presence=presence, + request=request + )) + +async def get_widget_html(widget_id: str, agent_name: str, agent_customization: dict, customer_id: Optional[str] = None, initial_token: Optional[str] = None, agent_workflow: bool = False, allow_attachments: bool = False, ai_replies_enabled: bool = True, + presence: Optional[dict] = None, request: Optional[Request] = None) -> str: + """Generate widget HTML with embedded data""" + import html + widget_url = settings.VITE_WIDGET_URL + version = _asset_version() + asset_query = f"?v={version}" if version else "" + + # Convert AgentCustomization to dict if it's a model instance + customization_dict = {} + if agent_customization: + # Get signed URL for photo if using S3 + photo_url = agent_customization.photo_url + if settings.S3_FILE_STORAGE and photo_url: + photo_url = await get_s3_signed_url(photo_url) + + customization_dict = { + "chat_background_color": agent_customization.chat_background_color, + "chat_text_color": agent_customization.chat_text_color, + "chat_bubble_color": agent_customization.chat_bubble_color, + "accent_color": agent_customization.accent_color, + "font_family": agent_customization.font_family, + "photo_url": photo_url, + "chat_style": agent_customization.chat_style.value if agent_customization.chat_style else "CHATBOT", + "widget_position": agent_customization.widget_position.value if agent_customization.widget_position else "FLOATING", + "welcome_title": agent_customization.welcome_title, + "welcome_subtitle": agent_customization.welcome_subtitle, + "welcome_message": agent_customization.welcome_message, + "chat_initiation_messages": agent_customization.chat_initiation_messages or [], + "quick_actions": agent_customization.quick_actions or [], + "show_citations": agent_customization.show_citations, + "collect_email": agent_customization.collect_email, + # A human-only agent never produces an AI reply, so disclosing one + # would be untrue — same reason the widget hides it after takeover. + "show_ai_disclaimer": agent_customization.show_ai_disclaimer and ai_replies_enabled, + "allow_new_chat": agent_customization.allow_new_chat, + "customization_metadata": agent_customization.customization_metadata or {} + } + + + return f""" + + + + + + Chat Widget + + + + + + + + + +
+ + + """ + +async def get_human_agent_session_info(db: Session, customer_id: str, scope: str) -> dict: + """Human agent assigned to the customer's open conversation this token may reach""" + human_agent_info = {} + session_repo = SessionToAgentRepository(db) + sessions = session_repo.get_customer_sessions(customer_id, SessionStatus.OPEN, scope=scope) + + if sessions and len(sessions) > 0: + session_model,user_full_name, user_profile_pic = sessions[0] + + if user_full_name: # If there's a human agent assigned + # HumanAgentResponse signs human_agent_profile_pic on serialization. + + # Get human agent info from session + human_agent_info = { + "human_agent_name": user_full_name, + "human_agent_profile_pic": user_profile_pic, + } + + return human_agent_info + + +def _conversation_key(token_payload: Optional[dict]) -> str: + """Keep the browser's conversation key across re-mints, or start one. + + Never an account key: only our backend mints those, for one signed-in + dashboard account, and every token the widget issues is unverified. + """ + key = (token_payload or {}).get(conversation_token.CONVERSATION_KEY_CLAIM) + if key and not conversation_token.is_account_key(key): + return key + return conversation_token.new_conversation_key() + + +def _mint_widget_token(widget_id: str, conversation_key: str, customer_id: Optional[str] = None, + source: Optional[str] = None, token_payload: Optional[dict] = None) -> str: + """A conversation token the widget issues itself - unverified, so always scoped. + + Prefers a fresh `source` (first-visit Explore flow) over one the visitor's + earlier token carried. + """ + extra_data = {conversation_token.CONVERSATION_KEY_CLAIM: conversation_key} + source = source or (token_payload or {}).get("source") + if widget_id == settings.EXPLORE_WIDGET_ID and source: + extra_data["source"] = source + return create_conversation_token(widget_id=widget_id, customer_id=customer_id, **extra_data) + + +def _anonymous_customer(customer_repo: CustomerRepository, widget: Widget, conversation_key: str, + claimed_email: Optional[str] = None) -> Customer: + """A customer of the visitor's own. An address they gave that belongs to + someone they can't stand for is kept where the inbox shows it.""" + meta_data = {CONTACT_EMAIL_META_KEY: claimed_email} if claimed_email else None + return customer_repo.create_customer( + CustomerRepository.anonymous_email(), widget.organization_id, + meta_data=meta_data, conversation_key=conversation_key) + + +def _widget_data_response(widget: Widget, agent: Agent, customer_id: Optional[str], human_agent_info: dict, + token: Optional[str] = None) -> dict: + """GET /widgets/{id}'s answer; `token` only when the visitor needs a new one.""" + # photo_url is signed by AgentCustomizationResponse on serialization. + data = { + "id": widget.id, + "organization_id": widget.organization_id, + "customer_id": customer_id, + "human_agent": human_agent_info, + "agent": { + "id": agent.id, + "name": agent.name, + "display_name": agent.display_name, + "customization": agent.customization, + "workflow": bool(agent.use_workflow and agent.active_workflow_id), + "allow_attachments": agent.allow_attachments + } + } + if token: + data["token"] = token + return data + + +@router.get("/{widget_id}", response_model=WidgetResponse) +async def get_widget_data( + widget_id: str, + response: Response, + email: Optional[str] = None, + source: Optional[str] = Query(None), + authorization: Optional[str] = Header(None), + db: Session = Depends(get_db) +): + """Get widget data including agent customization""" + logger.info(f"Getting widget data for widget_id {widget_id}, email {email}, has_token {bool(authorization)}") + # Binds this visitor to a customer: a stored copy would skip that and leave + # the widget holding the page's anonymous token. + response.headers["Cache-Control"] = "no-store" + + widget = db.query(Widget).filter(Widget.id == widget_id).first() + if not widget: + raise HTTPException(status_code=404, detail="Widget not found") + + agent_repo = AgentRepository(db) + agent = agent_repo.get_by_id(widget.agent_id) + if not agent: + raise HTTPException(status_code=404, detail="Agent not found") + + require_token_auth = getattr(agent, 'require_token_auth', False) + customer_id = None + token = None + token_payload = None + scope = None + + # Try to validate existing token if provided + if authorization and authorization.startswith('Bearer '): + token = authorization.split(' ')[1] + state = conversation_token.inspect(token, widget_id) + if state.is_live: + try: + scope = conversation_token.conversation_scope(state.payload) + token_payload = state.payload + customer_id = state.payload.get("sub") + except ValueError: + token = None + elif state.identity_lost: + # Identity ran out rather than never existing: minting a fresh anonymous + # customer here is what used to turn a signed-in visitor into "Anonymous" + # in the inbox the moment they started a new chat. + raise _identity_expired() + else: + token = None + + # SECURITY: If token auth required, must have valid token with customer_id + if require_token_auth: + if not token or customer_id is None: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized - Token required") + + # Check if agent has workflow enabled + agent_has_workflow = bool(agent.use_workflow and agent.active_workflow_id) + + # Check if agent has ASK_ANYTHING chat style + is_ask_anything_style = (agent.customization and + agent.customization.chat_style and + agent.customization.chat_style.value == "ASK_ANYTHING") + + # Email is optional unless the agent explicitly opts into collecting it before chat. + # When not collecting email we bypass the gate (anonymous customer + token), like + # ASK_ANYTHING / workflow agents already do. NOTE: this runs AFTER the require_token_auth + # 401 check above, so token-protected widgets are unaffected. + email_bypass = not bool(agent.customization and getattr(agent.customization, 'collect_email', False)) + + customer_repo = CustomerRepository(db) + conversation_key = _conversation_key(token_payload) + # Judged on the customer the socket will act as - the merge survivor - or + # the widget would be told yes here and refused on every connect. + customer = customer_repo.resolve_merged(customer_id) if customer_id not in (None, "None") else None + + if customer is not None: + # Once the embedding app signs this person in, a token the widget issued + # (from an email someone typed) may no longer name them - whoever claimed + # the address first would otherwise keep a token for the signed-in user. + # Hand that browser a customer of its own and keep the address it gave. + if not may_stand_for(customer, scope): + logger.info(f"Widget {widget_id}: unverified token names a signed-in customer; starting an anonymous one") + anonymous = _anonymous_customer(customer_repo, widget, conversation_key, claimed_email=customer.email) + new_token = _mint_widget_token(widget_id, conversation_key, customer_id=str(anonymous.id), + source=source, token_payload=token_payload) + return _widget_data_response(widget, agent, str(anonymous.id), {}, new_token) + + logger.debug(f"Using existing token for customer_id: {customer_id}") + human_agent_info = await get_human_agent_session_info(db, str(customer.id), scope) + return _widget_data_response(widget, agent, customer_id, human_agent_info) + + # A token-protected widget only ever runs on tokens its site issued: one whose + # customer is gone is no reason to issue a widget token of our own. + if require_token_auth: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized - Token required") + + # No customer yet - or the token's one no longer exists, which would otherwise + # refuse every connect while this answer kept the token. For workflow agents, + # ASK_ANYTHING style, or email-optional agents, create one with a blank email. + # Only email-collecting regular agents require email up front. + if not (email or agent_has_workflow or is_ask_anything_style or email_bypass): + # Regular agent without email/customer_id - return 401 + raise HTTPException(status_code=401, detail="Unauthorized") + + # The email is only what the visitor typed. It may still name the existing + # customer - one person in the inbox - because the token minted below is + # scoped to this browser's conversations, not to theirs. Never a person the + # embedding app signed in, though: only its own token may stand for them. + existing = customer_repo.get_customer_by_email(email, widget.organization_id) if email else None + if existing is not None and not existing.is_authenticated: + customer = existing + elif existing is not None: + customer = _anonymous_customer(customer_repo, widget, conversation_key, claimed_email=email) + elif email: + customer = customer_repo.create_customer(email, widget.organization_id, conversation_key=conversation_key) + else: + customer = _anonymous_customer(customer_repo, widget, conversation_key) + + human_agent_info = await get_human_agent_session_info(db, str(customer.id), conversation_key) + new_token = _mint_widget_token(widget_id, conversation_key, customer_id=str(customer.id), + source=source, token_payload=token_payload) + return _widget_data_response(widget, agent, str(customer.id), human_agent_info, new_token) + + +@router.post("/{widget_id}/end-chat") +async def end_chat_acknowledgment( + widget_id: str, + session_id: str, + reason: Optional[str] = None, + description: Optional[str] = None, + authorization: Optional[str] = Header(None), + db: Session = Depends(get_db) +): + """Acknowledge end_chat event from widget and close session on backend""" + try: + logger.info(f"End chat request received: widget_id={widget_id}, session_id={session_id}, reason={reason}, has_token={bool(authorization)}") + + # A conversation token is REQUIRED. This used to validate the token only + # when one was sent, so a request with no Authorization header fell + # straight through and closed any session by id, unauthenticated. + if not authorization or not authorization.startswith('Bearer '): + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") + + try: + token_data = verify_conversation_token(authorization.split(' ')[1]) + except Exception: + token_data = None + if not token_data: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + + customer_id = token_data.get("sub") + if token_data.get("widget_id") != widget_id: + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Widget mismatch") + try: + scope = conversation_token.conversation_scope(token_data) + except ValueError: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") + + # The token proves who the caller is, not that this session is theirs — + # without this any visitor could close another customer's conversation, + # or another browser's conversation of a customer it only claimed to be. + session_repo = SessionToAgentRepository(db) + session = session_repo.get_session(session_id) + if not session or str(session.customer_id) != str(customer_id) or not session_in_scope(session, scope): + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found") + + success = session_repo.close_session( + session_id=session_id, + reason=reason, + description=description + ) + + if success: + logger.info(f"End chat acknowledged: widget_id={widget_id}, session_id={session_id}, reason={reason}") + return { + "success": True, + "message": "Chat session closed", + "session_id": session_id, + "closed_at": datetime.utcnow().isoformat() + } + else: + logger.warning(f"Failed to close session: widget_id={widget_id}, session_id={session_id}") + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Session not found") + + except HTTPException: + raise + except Exception as e: + logger.error(f"Error in end_chat_acknowledgment: {str(e)}") + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to process end chat") + + +@router.get("", response_model=List[WidgetResponse]) +def list_widgets( + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + """List all widgets for the organization""" + widget_repo = WidgetRepository(db) + return widget_repo.get_widgets(current_user.organization_id) + + +@router.get("/agent/{agent_id}", response_model=List[WidgetResponse]) +def get_widgets_by_agent( + agent_id: UUID, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + """Get all widgets for a specific agent""" + widget_repo = WidgetRepository(db) + widgets = widget_repo.get_widgets_by_agent(agent_id) + + # Verify the agent belongs to the user's organization + agent_repo = AgentRepository(db) + agent = agent_repo.get_by_id(agent_id) + if not agent or str(agent.organization_id) != str(current_user.organization_id): + raise HTTPException(status_code=404, detail="Agent not found") + + return widgets + + +@router.delete("/{widget_id}") +def remove_widget( + widget_id: str, + db: Session = Depends(get_db), + current_user=Depends(get_current_user) +): + """Delete a widget""" + widget_repo = WidgetRepository(db) + widget = widget_repo.get_widget(widget_id) + if not widget or widget.organization_id != current_user.organization_id: + raise HTTPException(status_code=404, detail="Widget not found") + widget_repo.delete_widget(widget_id) + return {"message": "Widget deleted"} \ No newline at end of file diff --git a/backend/app/api/widget_apps.py b/backend/app/api/widget_apps.py new file mode 100644 index 0000000..cf42271 --- /dev/null +++ b/backend/app/api/widget_apps.py @@ -0,0 +1,337 @@ +""" +Copyright 2024-2026 ChatterMate + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from fastapi import APIRouter, Depends, HTTPException, status +from sqlalchemy.orm import Session +from app.database import get_db +from app.models.user import User +from app.core.auth import require_permissions +from app.repositories.widget_app import WidgetAppRepository +from app.models.schemas.widget_app import ( + WidgetAppCreate, + WidgetAppUpdate, + WidgetAppResponse, + WidgetAppWithKeyResponse, + WidgetAppListResponse +) +from uuid import UUID +from app.core.logger import get_logger + +# Enterprise feature check +try: + from app.enterprise.repositories.plan import PlanRepository + from app.enterprise.services.feature_access import require_accessible_subscription + HAS_ENTERPRISE = True +except ImportError: + HAS_ENTERPRISE = False + +router = APIRouter() +logger = get_logger(__name__) + + +def check_widget_app_feature_access(current_user: User, db: Session): + """Check if user has access to widget apps feature (requires pro plan)""" + if not HAS_ENTERPRISE: + return # Allow access in non-enterprise mode + + # Accessible subscription = active/trial/past-due-in-period OR cancelled-but- + # still-in-paid-period. Raises 403 when the org has no accessible plan. + subscription = require_accessible_subscription(db, current_user.organization_id) + plan_repo = PlanRepository(db) + + # Check if api_access feature is available in the plan (required for widget apps) + if not plan_repo.check_feature_availability(str(subscription.plan_id), 'api_access'): + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Widget Apps feature is not available in your current plan. Please upgrade to Pro to access this feature." + ) + + +@router.post("", response_model=WidgetAppWithKeyResponse, status_code=status.HTTP_201_CREATED) +async def create_widget_app( + app_data: WidgetAppCreate, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + Create a new widget app with generated API key. + + **IMPORTANT:** The API key is shown only once in this response. + Save it securely - it cannot be retrieved later. + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + + # Create app (returns app and plain key) + app, plain_key = repo.create_app( + organization_id=current_user.organization_id, + created_by=current_user.id, + name=app_data.name, + description=app_data.description + ) + + # Return app with key (shown only once) + return WidgetAppWithKeyResponse( + id=app.id, + name=app.name, + description=app.description, + organization_id=app.organization_id, + created_by=app.created_by, + is_active=app.is_active, + created_at=app.created_at, + updated_at=app.updated_at, + api_key=plain_key # ONLY time key is exposed + ) + + except Exception as e: + logger.exception(f"Error creating widget app: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to create widget app" + ) + + +@router.get("", response_model=WidgetAppListResponse) +async def list_widget_apps( + include_inactive: bool = False, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + List all widget apps for current organization. + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + apps = repo.get_apps_by_organization( + organization_id=current_user.organization_id, + include_inactive=include_inactive + ) + + return WidgetAppListResponse( + total=len(apps), + apps=[WidgetAppResponse.from_orm(app) for app in apps] + ) + + except Exception as e: + logger.exception(f"Error listing widget apps: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to list widget apps" + ) + + +@router.get("/{app_id}", response_model=WidgetAppResponse) +async def get_widget_app( + app_id: UUID, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + Get widget app details by ID. + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + app = repo.get_app_by_id( + app_id=app_id, + organization_id=current_user.organization_id + ) + + if not app: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Widget app {app_id} not found" + ) + + return WidgetAppResponse.from_orm(app) + + except HTTPException: + raise + except Exception as e: + logger.exception(f"Error getting widget app: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to get widget app" + ) + + +@router.patch("/{app_id}", response_model=WidgetAppResponse) +async def update_widget_app( + app_id: UUID, + update_data: WidgetAppUpdate, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + Update widget app (name, description, active status). + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + app = repo.update_app( + app_id=app_id, + organization_id=current_user.organization_id, + name=update_data.name, + description=update_data.description, + is_active=update_data.is_active + ) + + if not app: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Widget app {app_id} not found" + ) + + return WidgetAppResponse.from_orm(app) + + except HTTPException: + raise + except Exception as e: + logger.exception(f"Error updating widget app: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to update widget app" + ) + + +@router.delete("/{app_id}", status_code=status.HTTP_204_NO_CONTENT) +async def delete_widget_app( + app_id: UUID, + hard_delete: bool = False, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + Delete widget app (soft delete by default, hard delete if hard_delete=true). + + Soft delete (default): Sets is_active=False, keeps record for audit trail. + Hard delete: Permanently removes record (use with caution). + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + + if hard_delete: + success = repo.delete_app(app_id, current_user.organization_id) + else: + success = repo.deactivate_app(app_id, current_user.organization_id) + + if not success: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Widget app {app_id} not found" + ) + + # Invalidate cache if we have the key prefix (can't do this without key) + # Cache will naturally expire after TTL + + return None # 204 No Content + + except HTTPException: + raise + except Exception as e: + logger.exception(f"Error deleting widget app: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to delete widget app" + ) + + +@router.post("/{app_id}/regenerate-key", response_model=WidgetAppWithKeyResponse) +async def regenerate_api_key( + app_id: UUID, + current_user: User = Depends(require_permissions("manage_organization")), + db: Session = Depends(get_db) +): + """ + Regenerate API key for widget app. + + **IMPORTANT:** The new API key is shown only once in this response. + Save it securely - it cannot be retrieved later. + The old key will be immediately invalidated. + + Requires: manage_organization permission (admin only) + Requires: Pro plan with widget_apps feature + """ + try: + # Check widget apps feature access + check_widget_app_feature_access(current_user, db) + + repo = WidgetAppRepository(db) + + result = repo.regenerate_api_key( + app_id=app_id, + organization_id=current_user.organization_id + ) + + if not result: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Widget app {app_id} not found" + ) + + app, new_plain_key = result + + # Invalidate cache (old key is now invalid) + # Note: We don't have the old key here, so cache will expire naturally + + return WidgetAppWithKeyResponse( + id=app.id, + name=app.name, + description=app.description, + organization_id=app.organization_id, + created_by=app.created_by, + is_active=app.is_active, + created_at=app.created_at, + updated_at=app.updated_at, + api_key=new_plain_key # New key shown only once + ) + + except HTTPException: + raise + except Exception as e: + logger.exception(f"Error regenerating API key: {str(e)}") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Failed to regenerate API key" + ) diff --git a/backend/app/core/crawler_policy.py b/backend/app/core/crawler_policy.py new file mode 100644 index 0000000..a7f3a7e --- /dev/null +++ b/backend/app/core/crawler_policy.py @@ -0,0 +1,88 @@ +""" +Copyright 2024-2026 ChatterMate + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Keep crawlers out of the API origin. + +The API host had no robots.txt at all, so search engines were free to crawl +(and then report as 404s) endpoints that were never pages: /api/v1/... routes +and the YOUR_WIDGET_ID placeholder our docs use in examples. + +Two halves, because they do different jobs. robots.txt stops the fetch; +X-Robots-Tag stops a URL discovered elsewhere from being indexed anyway. +""" + +from fastapi import FastAPI +from fastapi.responses import PlainTextResponse + +from app.core.config import settings + +NOINDEX_HEADER = (b"x-robots-tag", b"noindex") + + +def api_robots_txt() -> str: + """The API origin's robots.txt body. + + Disallows everything — except, under path dispatch, the help center. That + mode (the self-host default) serves public help-center pages from + {BACKEND_URL}/help/{slug} on this very origin, and those are meant to be + indexed; a bare "Disallow: /" would quietly de-index every self-hosted + help center. Cloud runs subdomain mode, where nothing on this origin is a + page and the carve-out is absent. + + The Allow goes FIRST. RFC 9309 crawlers take the most specific match and + would get this right either way, but ones that take the first match read + a leading "Disallow: /" and stop — skipping the very pages the carve-out + exists to keep crawlable. + """ + lines = ["User-agent: *"] + if settings.HELP_CENTER_PUBLIC_MODE == "path": + lines.append("Allow: /help/") + lines.append("Disallow: /") + return "\n".join(lines) + "\n" + + +class NoIndexHeaderMiddleware: + """Stamp ``X-Robots-Tag: noindex`` on every response under ``prefix``. + + Pure ASGI rather than BaseHTTPMiddleware: this fronts every API request, + and BaseHTTPMiddleware would pull streaming responses through a queue for + the sake of one header. + """ + + def __init__(self, app, prefix: str): + self.app = app + self.prefix = prefix + + async def __call__(self, scope, receive, send): + if scope["type"] != "http" or not scope.get("path", "").startswith(self.prefix): + await self.app(scope, receive, send) + return + + async def send_with_noindex(message): + if message["type"] == "http.response.start": + message["headers"] = [*(message.get("headers") or []), NOINDEX_HEADER] + await send(message) + + await self.app(scope, receive, send_with_noindex) + + +def install_crawler_policy(app: FastAPI) -> None: + """Register the robots.txt route and the noindex header middleware.""" + + @app.get("/robots.txt", response_class=PlainTextResponse, include_in_schema=False) + async def robots_txt(): + return PlainTextResponse(api_robots_txt()) + + app.add_middleware(NoIndexHeaderMiddleware, prefix=settings.API_V1_STR) diff --git a/backend/app/services/domain_verification.py b/backend/app/services/domain_verification.py new file mode 100644 index 0000000..6450133 --- /dev/null +++ b/backend/app/services/domain_verification.py @@ -0,0 +1,188 @@ +""" +Copyright 2024-2026 ChatterMate + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Custom-domain verification for the public help center. + +The customer proves control of the domain with a TXT record +(_chattermate. = cm-verify=) and routes traffic with a CNAME +to HELP_CENTER_CNAME_TARGET (A/AAAA records matching HELP_CENTER_TARGET_IPS +are accepted for providers that flatten CNAMEs). Both must pass before the +host dispatcher will ever serve the domain. SSL turns active once an HTTPS +probe against /healthz succeeds with a valid certificate. +""" + +import os +import secrets +from datetime import datetime, timezone + +from sqlalchemy.orm import Session + +from app.core.config import settings +from app.core.logger import get_logger +from app.models.help_center import HelpCenterSettings, SSLStatus +from app.repositories.help_center import HelpCenterRepository + +logger = get_logger(__name__) + +DNS_LIFETIME_SECONDS = 5.0 +SSL_PROBE_TIMEOUT_SECONDS = 10.0 +TXT_RECORD_PREFIX = "_chattermate" +# Public resolvers for verification lookups. The container's own resolver is +# Docker's embedded DNS (127.0.0.11) for service discovery — it negatively +# caches names that didn't exist when first queried (so a TXT added *after* the +# customer set up the domain keeps returning NXDOMAIN). Query public DNS +# directly, and let self-hosters override if their network blocks these. +_PUBLIC_DNS_RESOLVERS = [ + ip.strip() + for ip in os.getenv("DOMAIN_VERIFY_DNS_RESOLVERS", "1.1.1.1,8.8.8.8,9.9.9.9").split(",") + if ip.strip() +] + + +def generate_verification_token() -> str: + return secrets.token_hex(16) + + +def txt_record_host(domain: str) -> str: + return f"{TXT_RECORD_PREFIX}.{domain}" + + +def expected_txt_value(row: HelpCenterSettings) -> str: + return f"cm-verify={row.domain_verification_token}" + + +def _resolver(): + import dns.resolver + + resolver = dns.resolver.Resolver() + if _PUBLIC_DNS_RESOLVERS: + resolver.nameservers = _PUBLIC_DNS_RESOLVERS + resolver.lifetime = DNS_LIFETIME_SECONDS + return resolver + + +def check_txt_record(row: HelpCenterSettings) -> bool: + """True when the verification TXT record is present.""" + import dns.resolver + + expected = expected_txt_value(row) + try: + answers = _resolver().resolve(txt_record_host(row.custom_domain), "TXT") + except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer, dns.resolver.NoNameservers, + dns.resolver.LifetimeTimeout): + return False + for record in answers: + value = b"".join(record.strings).decode("utf-8", errors="replace") + if value.strip() == expected: + return True + return False + + +def check_cname_record(domain: str) -> bool: + """True when the domain points at us: CNAME to the target, or (for + CNAME-flattening providers) A/AAAA records matching the target IPs.""" + import dns.resolver + + target = settings.HELP_CENTER_CNAME_TARGET.rstrip(".").lower() + try: + answers = _resolver().resolve(domain, "CNAME") + return any(str(record.target).rstrip(".").lower() == target for record in answers) + except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.LifetimeTimeout): + return False + except dns.resolver.NoAnswer: + pass # no CNAME — fall through to flattened A/AAAA comparison + if not settings.HELP_CENTER_TARGET_IPS: + return False + resolved = set() + for rtype in ("A", "AAAA"): + try: + resolved.update(str(r) for r in _resolver().resolve(domain, rtype)) + except Exception: + continue + return bool(resolved) and resolved <= settings.HELP_CENTER_TARGET_IPS + + +def probe_ssl(domain: str) -> bool: + """HTTPS reachability + certificate validation, resolving via PUBLIC DNS. + The container's own resolver (Docker embedded DNS) negatively caches a + custom domain that didn't exist before its cert was provisioned — so httpx + (which uses the system resolver) would keep failing. Resolve the A record + ourselves and connect to that IP with SNI = the domain, which also verifies + the served cert is actually issued for this domain.""" + import socket + import ssl + + try: + ip = str(_resolver().resolve(domain, "A")[0]) + except Exception as e: + logger.info(f"SSL probe DNS for {domain} not ready: {e}") + return False + try: + ctx = ssl.create_default_context() + ctx.minimum_version = ssl.TLSVersion.TLSv1_2 + with socket.create_connection((ip, 443), timeout=SSL_PROBE_TIMEOUT_SECONDS) as sock: + with ctx.wrap_socket(sock, server_hostname=domain) as tls: + tls.sendall( + f"GET /healthz HTTP/1.1\r\nHost: {domain}\r\nConnection: close\r\n\r\n".encode() + ) + status_line = tls.recv(128).split(b"\r\n", 1)[0] + return b" 200 " in status_line + except (OSError, ssl.SSLError) as e: + logger.info(f"SSL probe for {domain} not ready: {e}") + return False + + +def set_custom_domain(db: Session, row: HelpCenterSettings, domain: str) -> HelpCenterSettings: + """Claim a (normalized) domain: fresh token, verification state reset.""" + base_suffix = f".{settings.HELP_CENTER_BASE_DOMAIN}" + if domain == settings.HELP_CENTER_BASE_DOMAIN or domain.endswith(base_suffix): + raise ValueError(f"{settings.HELP_CENTER_BASE_DOMAIN} subdomains are assigned automatically and cannot be claimed.") + row.custom_domain = domain + row.domain_verification_token = generate_verification_token() + row.txt_record_verified = False + row.cname_record_verified = False + row.ssl_status = SSLStatus.NONE + row.domain_verified_at = None + return HelpCenterRepository(db).update(row) + + +def clear_custom_domain(db: Session, row: HelpCenterSettings) -> HelpCenterSettings: + row.custom_domain = None + row.domain_verification_token = None + row.txt_record_verified = False + row.cname_record_verified = False + row.ssl_status = SSLStatus.NONE + row.domain_verified_at = None + return HelpCenterRepository(db).update(row) + + +def verify_custom_domain(db: Session, row: HelpCenterSettings) -> HelpCenterSettings: + """Run both DNS checks (and the SSL probe once verified) and persist the + outcome. Blocking (DNS/HTTP I/O) — callers run it off the event loop.""" + if not row.custom_domain: + raise ValueError("No custom domain configured.") + row.txt_record_verified = check_txt_record(row) + row.cname_record_verified = check_cname_record(row.custom_domain) + if row.domain_verified: + if not row.domain_verified_at: + row.domain_verified_at = datetime.now(timezone.utc) + if row.ssl_status != SSLStatus.ACTIVE.value: + row.ssl_status = ( + SSLStatus.ACTIVE if probe_ssl(row.custom_domain) else SSLStatus.PENDING + ) + else: + row.domain_verified_at = None + row.ssl_status = SSLStatus.NONE + return HelpCenterRepository(db).update(row) diff --git a/backend/app/services/widget_identity.py b/backend/app/services/widget_identity.py new file mode 100644 index 0000000..380d61a --- /dev/null +++ b/backend/app/services/widget_identity.py @@ -0,0 +1,95 @@ +""" +Copyright 2024-2026 ChatterMate + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +What a widget conversation token may do to the customer it names. + +A widget visitor can be bound to a customer by an email they only typed, so the +token naming a customer is not proof of being them. Conversations are already +fenced by the token's scope (see conversation_token.conversation_scope); this +module fences the customer row itself - its contact details, merges, and whether +an unverified token may stand for a person the embedding app has signed in. +""" + +from typing import Optional +from uuid import UUID + +from sqlalchemy.orm import Session + +from app.models.customer import Customer +from app.models.session_to_agent import SessionToAgent +from app.repositories.customer import CustomerRepository +from app.repositories.session_to_agent import SessionToAgentRepository +from app.services.conversation_token import ( + LEGACY_SCOPE_PREFIX, + is_verified_scope, + scope_for_session_key, +) + + +def can_act_as(db: Session, customer: Optional[Customer], scope: Optional[str]) -> bool: + """True when a token with `scope` may change `customer` as if it were them. + + A verified token always may. An unverified one only for a customer its own + scope created - "no conversations elsewhere" is not ownership: a customer + made by a ticket or an import has none - and only while every conversation + of that customer is still its own. + """ + if customer is None or not scope: + return False + if is_verified_scope(scope): + return True + if customer.is_authenticated or not _created_by(customer, scope): + return False + return not SessionToAgentRepository(db).customer_has_sessions_outside_scope(customer.id, scope) + + +def _created_by(customer: Customer, scope: str) -> bool: + if customer.conversation_key: + return customer.conversation_key == scope + # Customers from before conversation keys: the anonymous row a legacy + # widget token created for itself is the only kind such a token can own. + return scope.startswith(LEGACY_SCOPE_PREFIX) and CustomerRepository.is_placeholder_email(customer.email) + + +def may_stand_for(customer: Optional[Customer], scope: Optional[str]) -> bool: + """True when an unverified token may keep naming `customer` at all. + + Once the embedding app has signed a person in, only a token our side vouched + for may name them - otherwise whoever claimed the address first would keep a + token for the signed-in user's row. + """ + if customer is None or not scope: + return False + return is_verified_scope(scope) or not customer.is_authenticated + + +def trusts_identity_in_conversation( + db: Session, customer: Optional[Customer], conversation: Optional[SessionToAgent] +) -> bool: + """True when the visitor in `conversation` may be told, or change, what we + know of `customer`. + + Reciting a stored name or email back is how a visitor who only typed someone + else's address would learn who they are. Unknown conversation: nothing. + """ + if conversation is None: + return False + return can_act_as(db, customer, scope_for_session_key(conversation.conversation_key)) + + +def trusts_identity_in_session(db: Session, customer: Optional[Customer], session_id: UUID | str | None) -> bool: + """trusts_identity_in_conversation, for a caller holding only the session id.""" + conversation = SessionToAgentRepository(db).get_session(session_id) if session_id else None + return trusts_identity_in_conversation(db, customer, conversation) diff --git a/backend/assets/widget.css b/backend/assets/widget.css new file mode 100644 index 0000000..b79bfed --- /dev/null +++ b/backend/assets/widget.css @@ -0,0 +1 @@ +.new-chat-confirm[data-v-6c78f353]{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:10px 14px;border-bottom:1px solid var(--cm-hairline, rgba(0, 0, 0, .07));background:var(--cm-agent-bg, rgba(0, 0, 0, .03));font-family:var(--cm-body-font, inherit)}.new-chat-confirm__question[data-v-6c78f353]{margin:0;font-size:12.5px;line-height:1.35;color:var(--cm-text, inherit)}.new-chat-confirm__actions[data-v-6c78f353]{display:flex;gap:8px;margin-left:auto}.new-chat-confirm__button[data-v-6c78f353]{padding:5px 11px;border-radius:var(--cm-field-radius, 8px);border:1px solid var(--cm-border, rgba(0, 0, 0, .12));background:transparent;color:var(--cm-text, inherit);font-family:inherit;font-size:12px;font-weight:500;cursor:pointer;transition:opacity .15s ease}.new-chat-confirm__button--primary[data-v-6c78f353]{border-color:transparent;background:var(--cm-accent, #f34611);color:var(--cm-on-accent, #ffffff)}.new-chat-confirm__button[data-v-6c78f353]:disabled{opacity:.5;cursor:default}.new-chat-confirm__button[data-v-6c78f353]:not(:disabled):hover{opacity:.85}@media (pointer: coarse){.new-chat-confirm[data-v-6c78f353]{padding:10px 12px}.new-chat-confirm__button[data-v-6c78f353]{min-height:40px;padding:8px 14px}}@media (prefers-reduced-motion: reduce){.new-chat-confirm__button[data-v-6c78f353]{transition:none}}.askai[data-v-edea9534]{display:flex;flex-direction:column;height:100%;min-height:0;background:var(--cm-card);color:var(--cm-text);font-family:var(--cm-body-font, inherit)}.askai__bar[data-v-edea9534]{display:flex;align-items:center;gap:10px;padding:14px 14px 14px 16px;border-bottom:1px solid var(--cm-border);flex-shrink:0}.askai__bar-icon[data-v-edea9534]{color:var(--cm-accent);flex-shrink:0}.askai__input[data-v-edea9534]{flex:1;min-width:0;border:none;outline:none;background:transparent;color:var(--cm-text);font-size:16px;font-family:inherit;padding:2px 0}.askai__input[data-v-edea9534]::placeholder{color:var(--cm-muted)}.askai__input[data-v-edea9534]:disabled{opacity:.6}.askai__new[data-v-edea9534]{position:relative;display:inline-flex;align-items:center;justify-content:center;width:28px;height:26px;border:1px solid var(--cm-border);background:transparent;border-radius:6px;cursor:pointer;color:var(--cm-muted);font-family:inherit;flex-shrink:0;transition:color .15s ease,border-color .15s ease}.askai__new[data-v-edea9534]:hover:not(:disabled){color:var(--cm-text);border-color:var(--cm-accent)}.askai__new[data-v-edea9534]:disabled{opacity:.5;cursor:default}.askai__new--armed[data-v-edea9534]{color:var(--cm-text);border-color:var(--cm-accent)}.askai__close[data-v-edea9534]{border:1px solid var(--cm-border);background:transparent;border-radius:6px;padding:3px 7px;cursor:pointer;color:var(--cm-muted);flex-shrink:0;transition:color .15s ease,border-color .15s ease}.askai__close[data-v-edea9534]:hover{color:var(--cm-text);border-color:var(--cm-accent)}.askai__kbd[data-v-edea9534]{font-size:11px;font-weight:600;letter-spacing:.02em;font-family:inherit}.askai__body[data-v-edea9534]{flex:1;min-height:0;overflow-y:auto;scrollbar-gutter:stable;padding:18px 20px 22px}.askai__content[data-v-edea9534]{display:flow-root}.askai__intro[data-v-edea9534]{margin-bottom:18px}.askai__title[data-v-edea9534]{margin:0 0 4px;font-size:19px;font-weight:600;letter-spacing:-.01em}.askai__subtitle[data-v-edea9534]{margin:0;font-size:14px;line-height:1.5;color:var(--cm-muted)}.askai__label[data-v-edea9534]{display:block;margin:0 0 8px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--cm-muted)}.askai__suggestions[data-v-edea9534]{display:flex;flex-direction:column;gap:6px}.askai__suggestion[data-v-edea9534]{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;text-align:left;padding:11px 13px;border:1px solid var(--cm-border);border-radius:10px;background:var(--cm-agent-bg);color:var(--cm-text);font-size:14px;font-family:inherit;cursor:pointer;transition:border-color .15s ease,transform .15s ease}.askai__suggestion[data-v-edea9534]:hover:not(:disabled){border-color:var(--cm-accent);transform:translate(2px)}.askai__suggestion[data-v-edea9534]:disabled{opacity:.55;cursor:default}.askai__suggestion svg[data-v-edea9534]{color:var(--cm-muted);flex-shrink:0}.askai__turn+.askai__turn[data-v-edea9534]{margin-top:20px}.askai__question[data-v-edea9534]{margin:0 0 10px;font-size:16px;font-weight:600;line-height:1.4;letter-spacing:-.01em}.askai__answer[data-v-edea9534]{font-size:14.5px;line-height:1.65;color:var(--cm-text);overflow-wrap:break-word}.askai__answer--streaming[data-v-edea9534]>:last-child:after{content:"";display:inline-block;width:6px;height:1em;margin-left:2px;vertical-align:-.15em;background:var(--cm-accent, currentColor);animation:askai-blink-edea9534 1s steps(1) infinite}@keyframes askai-blink-edea9534{0%,50%{opacity:1}51%,to{opacity:0}}.askai__answer[data-v-edea9534] p{margin:0 0 10px}.askai__answer[data-v-edea9534] p:last-child{margin-bottom:0}.askai__answer[data-v-edea9534] ul,.askai__answer[data-v-edea9534] ol{margin:0 0 10px;padding-left:20px}.askai__answer[data-v-edea9534] li{margin-bottom:4px}.askai__answer[data-v-edea9534] a{color:var(--cm-accent)}.askai__answer[data-v-edea9534] code{background:var(--cm-agent-bg);padding:1px 5px;border-radius:4px;font-size:.9em}.askai__answer[data-v-edea9534] pre{background:var(--cm-agent-bg);padding:12px;border-radius:8px;overflow-x:auto}.askai__answer[data-v-edea9534] pre code{background:none;padding:0}.askai__answer[data-v-edea9534] table{display:block;max-width:100%;overflow-x:auto}.askai__sources[data-v-edea9534]{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:12px}.askai__sources .askai__label[data-v-edea9534]{margin:0 2px 0 0}.askai__source[data-v-edea9534]{font-size:12px;padding:3px 9px;border-radius:999px;border:1px solid var(--cm-border);color:var(--cm-muted);max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.askai__thinking[data-v-edea9534]{display:flex;align-items:center;gap:5px;margin-top:16px;color:var(--cm-muted);font-size:13px}.askai__thinking-text[data-v-edea9534]{margin-left:4px}.askai__dot[data-v-edea9534]{width:5px;height:5px;border-radius:50%;background:var(--cm-accent);animation:askai-pulse-edea9534 1.4s ease-in-out infinite}.askai__dot[data-v-edea9534]:nth-child(2){animation-delay:.18s}.askai__dot[data-v-edea9534]:nth-child(3){animation-delay:.36s}@keyframes askai-pulse-edea9534{0%,to{opacity:.35;transform:translateY(0)}50%{opacity:1;transform:translateY(-2px)}}@media (prefers-reduced-motion: reduce){.askai__dot[data-v-edea9534]{animation:none}.askai__suggestion[data-v-edea9534]:hover:not(:disabled){transform:none}.askai__answer--streaming[data-v-edea9534]>:last-child:after{display:none}}.askai__system[data-v-edea9534]{margin:0;font-size:13px;font-style:italic;color:var(--cm-muted)}.askai__foot[data-v-edea9534]{display:flex;align-items:center;justify-content:center;flex-wrap:wrap;gap:4px 10px;margin:0;padding:10px 16px 12px;border-top:1px solid var(--cm-border);font-size:11.5px;text-align:center;color:var(--cm-muted);flex-shrink:0}.askai__brand[data-v-edea9534]{color:inherit;text-decoration:none;opacity:.8}.askai__brand[data-v-edea9534]:hover{text-decoration:underline}@media (max-width: 768px){.askai__body[data-v-edea9534]{padding:16px 16px 20px}.askai__input[data-v-edea9534]{font-size:16px}}.chat-container.cm-surface{font-family:var(--cm-body-font);color:var(--cm-text)}.chat-container.cm-surface .chat-panel{background:var(--cm-card);color:var(--cm-text)}.chat-container.cm-surface .chat-header{padding:14px 16px;border-bottom:1px solid var(--cm-hairline);background:transparent}.chat-container.cm-surface .header-info h3{color:var(--cm-text);font-family:var(--cm-body-font);font-size:14.5px;font-weight:600;line-height:1.15}.chat-container.cm-surface .cm-presence,.chat-container.cm-surface .status-text{color:var(--cm-presence, var(--cm-accent));font-size:11.5px}.chat-container.cm-surface .status-indicator.online{background:var(--cm-presence, var(--cm-accent));box-shadow:none}.chat-container.cm-surface .header-orb,.chat-container.cm-surface .header-avatar{width:34px;height:34px;border-radius:var(--cm-avatar-radius);border:none;box-shadow:none;object-fit:cover;flex-shrink:0}.chat-container.cm-surface .cm-msg-avatar,.chat-container.cm-surface .cm-msg-avatar-orb,.chat-container.cm-surface .cm-msg-avatar-img,.chat-container.cm-surface .cm-welcome-orb,.chat-container.cm-surface .cm-welcome-avatar{width:28px;height:28px;border-radius:var(--cm-avatar-radius);border:none;box-shadow:none;flex-shrink:0;display:block}.chat-container.cm-surface .cm-msg-avatar-img,.chat-container.cm-surface .cm-welcome-avatar{object-fit:cover}.chat-container.cm-surface .message{max-width:100%;gap:9px;align-items:flex-start}.chat-container.cm-surface .message-bubble{padding:10px 14px;font-size:14px;line-height:1.5;box-shadow:none;max-width:84%}.chat-container.cm-surface .message-bubble p{margin:0}.chat-container.cm-surface .message-bubble p+p{margin-top:.5em}.chat-container.cm-surface .message-bubble a{color:inherit;text-decoration:underline;text-underline-offset:2px;font-weight:600;cursor:pointer}.chat-container.cm-surface .message-bubble a:hover{opacity:.82}.chat-container.cm-surface .message-bubble pre{max-width:100%;overflow-x:auto;margin:.5em 0;padding:var(--space-sm, 12px);border-radius:8px;background:color-mix(in srgb,currentColor 8%,transparent);-webkit-overflow-scrolling:touch;scrollbar-width:thin}.chat-container.cm-surface .message-bubble pre code{white-space:pre;overflow-wrap:normal;background:none;padding:0}.chat-container.cm-surface .message-bubble code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.9em;background:color-mix(in srgb,currentColor 8%,transparent);padding:1px 5px;border-radius:6px}.chat-container.cm-surface .message-bubble table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;margin:.5em 0}.chat-container.cm-surface .message-bubble th,.chat-container.cm-surface .message-bubble td{border:1px solid color-mix(in srgb,currentColor 20%,transparent);padding:4px 8px}.chat-container.cm-surface .agent-message .message-bubble,.chat-container.cm-surface .assistant-message .message-bubble{background:var(--cm-agent-bg);color:var(--cm-text);border:1px solid var(--cm-hairline);border-radius:var(--cm-bubble-tail) var(--cm-bubble) var(--cm-bubble) var(--cm-bubble)}.chat-container.cm-surface .user-message .message-bubble{background:var(--cm-accent);color:var(--cm-on-accent);border:none;border-radius:var(--cm-bubble) var(--cm-bubble) var(--cm-bubble-tail) var(--cm-bubble)}.chat-container.cm-surface .cm-quick-action{padding:8px 14px;border-radius:999px;border:1px solid color-mix(in srgb,var(--cm-accent) 66%,transparent);background:color-mix(in srgb,var(--cm-accent) 18%,transparent);color:var(--cm-text);font-family:var(--cm-body-font);font-size:12.5px;font-weight:600;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.chat-container.cm-surface .cm-quick-action:hover:not(:disabled){background:var(--cm-accent);color:var(--cm-on-accent);border-color:var(--cm-accent)}.chat-container.cm-surface.theme-terminal .cm-quick-action{border-radius:6px}.chat-container.cm-surface .chat-input{background:transparent;border-top:1px solid var(--cm-hairline)}.chat-container.cm-surface .message-input input,.chat-container.cm-surface .email-input input{border-radius:var(--cm-field-radius);border:1px solid color-mix(in srgb,var(--cm-accent) 30%,var(--cm-hairline));background:color-mix(in srgb,var(--cm-text) 6%,transparent);color:var(--cm-text);font-family:var(--cm-body-font);font-size:14px}.chat-container.cm-surface .message-input input::placeholder,.chat-container.cm-surface .email-input input::placeholder{color:var(--cm-muted);opacity:1}.chat-container.cm-surface .message-input input:focus,.chat-container.cm-surface .email-input input:focus{border-color:var(--cm-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--cm-accent) 22%,transparent)}.chat-container.cm-surface .send-button{width:42px;min-width:42px;height:42px;flex-shrink:0;border:none;border-radius:var(--cm-field-radius);background:var(--cm-accent);color:var(--cm-on-accent);cursor:pointer}.chat-container.cm-surface .powered-by,.chat-container.cm-surface .powered-by-welcome{background:transparent;border-top:none;color:var(--cm-muted);font-family:var(--cm-body-font)}.chat-container.cm-surface .cm-powered-prefix{color:var(--cm-muted);opacity:1}.chat-container.cm-surface .cm-brand{color:var(--cm-text);font-weight:700}.chat-container.cm-surface.theme-terminal,.chat-container.cm-surface.theme-terminal .chat-panel,.chat-container.cm-surface.theme-terminal .header-info h3,.chat-container.cm-surface.theme-terminal .message-bubble,.chat-container.cm-surface.theme-terminal .cm-quick-action,.chat-container.cm-surface.theme-terminal .message-input input,.chat-container.cm-surface.theme-terminal .powered-by{font-family:var(--cm-body-font)}.chat-container.cm-surface.theme-glass .chat-panel{backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}.chat-container[data-v-1393b388]{width:100%;height:100%;display:flex;flex-direction:column;background:transparent;overflow:hidden;position:relative;border-radius:var(--radius-lg);border:none;box-shadow:none;transition:opacity .22s ease,transform .22s ease;font-family:var(--cm-body-font, "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif)}.chat-container .welcome-title[data-v-1393b388]{font-family:var(--cm-body-font, "Instrument Sans", system-ui, sans-serif);font-weight:700}.chat-container .citation-label[data-v-1393b388],.chat-container .citation-chip[data-v-1393b388],.chat-container .reading-label[data-v-1393b388],.chat-container .system-message .message-bubble[data-v-1393b388]{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.chat-container[data-v-1393b388]:after{content:"";position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;border-radius:var(--radius-lg);border:1px solid var(--cm-border, rgba(0, 0, 0, .08))}.chat-container.collapsed[data-v-1393b388]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.chat-panel[data-v-1393b388]{background:var(--background-base);display:flex;flex-direction:column;height:100%;transition:all .3s ease;border-radius:0}.chat-header[data-v-1393b388]{padding:var(--space-md);display:flex;justify-content:space-between;align-items:center;position:relative}.header-orb[data-v-1393b388]{width:34px;height:34px;border-radius:var(--cm-avatar-radius, 50%);flex-shrink:0}.cm-header-sheen[data-v-1393b388]{position:absolute;top:0;left:0;right:0;height:2px;background-size:200% 100%;animation:cm-sheen-1393b388 4.5s linear infinite;opacity:.75;pointer-events:none}@keyframes cm-sheen-1393b388{0%{background-position:200% 0}to{background-position:-200% 0}}@media (prefers-reduced-motion: reduce){.cm-header-sheen[data-v-1393b388]{animation:none!important}}.header-content[data-v-1393b388]{display:flex;align-items:center;gap:var(--space-sm)}.header-actions[data-v-1393b388]{display:flex;align-items:center;gap:6px;flex-shrink:0}.header-minimize[data-v-1393b388]{width:32px;height:32px;border-radius:9px;border:none;background:#7f7f7f1f;color:inherit;cursor:pointer;font-size:17px;line-height:1;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .15s ease}.header-minimize[data-v-1393b388]:hover{background:#7f7f7f38}.header-new-chat[data-v-1393b388]{width:32px;height:32px;border-radius:9px;border:none;background:#7f7f7f1f;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;transition:background .15s ease}.header-new-chat[data-v-1393b388]:hover:not(:disabled){background:#7f7f7f38}.header-new-chat.armed[data-v-1393b388]{background:color-mix(in srgb,currentColor 20%,transparent)}.header-new-chat[data-v-1393b388]:disabled{opacity:.5;cursor:default}.header-menu-container[data-v-1393b388]{position:relative;margin-left:auto}.header-menu-btn[data-v-1393b388]{background:transparent;border:none;cursor:pointer;padding:6px;border-radius:6px;color:var(--text-secondary, #6b7280);display:flex;align-items:center;justify-content:center;transition:all .2s ease}.header-menu-btn[data-v-1393b388]:hover{background:var(--background-secondary, #f3f4f6);color:var(--text-primary, #1f2937)}.header-menu-btn[data-v-1393b388]:focus{outline:2px solid var(--primary-color, #3b82f6);outline-offset:2px}.header-dropdown-menu[data-v-1393b388]{position:absolute;top:100%;right:0;margin-top:4px;background:var(--background-base, white);border:1px solid var(--border-color, #e5e7eb);border-radius:8px;box-shadow:0 4px 12px #00000026;min-width:140px;z-index:1000;overflow:hidden}.dropdown-item[data-v-1393b388]{display:flex;align-items:center;gap:8px;width:100%;padding:10px 14px;background:transparent;border:none;cursor:pointer;font-size:var(--text-sm, 14px);color:var(--text-primary, #1f2937);text-align:left;transition:all .15s ease}.dropdown-item[data-v-1393b388]:hover:not(:disabled){background:var(--background-secondary, #f3f4f6)}.dropdown-item[data-v-1393b388]:disabled{opacity:.5;cursor:not-allowed}.dropdown-item.end-chat-btn[data-v-1393b388]{color:var(--error-color, #ef4444)}.dropdown-item.end-chat-btn[data-v-1393b388]:hover:not(:disabled){background:#ef44441a}.dropdown-item svg[data-v-1393b388]{flex-shrink:0}.header-avatar[data-v-1393b388]{width:34px;height:34px;border-radius:var(--cm-avatar-radius, 50%);object-fit:cover;border:none}.header-info[data-v-1393b388]{display:flex;flex-direction:column;gap:2px}.header-info h3[data-v-1393b388]{margin:0;font-size:var(--text-md);font-weight:600;line-height:1.2}.status[data-v-1393b388]{display:flex;align-items:center;gap:var(--space-xs);font-size:var(--text-sm)}.status-indicator[data-v-1393b388]{width:8px;height:8px;border-radius:50%;background:var(--error-color);box-shadow:0 0 0 2px #ef444433;animation:pulse-offline-1393b388 2s ease-in-out infinite}@keyframes pulse-offline-1393b388{0%,to{opacity:1}50%{opacity:.5}}.status-indicator.online[data-v-1393b388]{background:var(--success-color);box-shadow:0 0 0 2px #10b98133;animation:pulse-online-1393b388 2s ease-in-out infinite}.status-indicator.away[data-v-1393b388]{background:var(--cm-muted, #9ca3af);box-shadow:0 0 0 2px #9ca3af33;animation:none}.cm-presence[data-v-1393b388]{color:var(--cm-presence, var(--cm-muted, #C9F24E));font-size:11.5px}@keyframes pulse-online-1393b388{0%,to{box-shadow:0 0 0 2px #10b98133}50%{box-shadow:0 0 0 4px #10b9814d}}.chat-messages[data-v-1393b388]{flex:1;overflow-y:auto;padding:var(--space-md);display:flex;flex-direction:column;gap:var(--space-md);-webkit-overflow-scrolling:touch;scroll-behavior:smooth;margin-top:var(--space-sm);scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--cm-accent, #C9F24E) 40%,transparent) transparent}.chat-messages[data-v-1393b388]::-webkit-scrollbar{width:8px}.chat-messages[data-v-1393b388]::-webkit-scrollbar-track{background:transparent}.chat-messages[data-v-1393b388]::-webkit-scrollbar-thumb{background-color:color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent);border-radius:999px;border:2px solid transparent;background-clip:padding-box}.chat-messages[data-v-1393b388]::-webkit-scrollbar-thumb:hover{background-color:color-mix(in srgb,var(--cm-accent, #C9F24E) 60%,transparent)}.message[data-v-1393b388]{display:flex;gap:var(--space-sm);max-width:85%;align-items:flex-start;margin-bottom:var(--space-md);animation:slideIn-1393b388 .3s ease-out}@keyframes slideIn-1393b388{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.message-avatar[data-v-1393b388]{width:24px;height:24px;border-radius:50%;object-fit:cover;margin-top:4px}.cm-msg-avatar[data-v-1393b388]{width:28px;height:28px;flex-shrink:0}.cm-msg-avatar-orb[data-v-1393b388],.cm-msg-avatar-img[data-v-1393b388]{width:28px;height:28px;border-radius:var(--cm-avatar-radius, 50%);display:block}.cm-msg-avatar-img[data-v-1393b388]{object-fit:cover}.message-bubble[data-v-1393b388]{padding:10px 14px;border-radius:var(--cm-bubble, 16px);font-size:14px;line-height:1.45;box-shadow:0 2px 8px #00000014;max-width:85%;transition:all .2s ease;position:relative;overflow-wrap:anywhere}.message-bubble[data-v-1393b388]:hover{box-shadow:0 4px 12px #0000001f}.user-message[data-v-1393b388]{align-self:flex-end;flex-direction:row-reverse}.user-message .message-bubble[data-v-1393b388]{border-radius:var(--cm-bubble, 16px) var(--cm-bubble, 16px) var(--cm-bubble-tail, 5px) var(--cm-bubble, 16px);background:linear-gradient(135deg,var(--accent-solid) 0%,color-mix(in srgb,var(--accent-solid) 90%,black) 100%)}.assistant-message .message-bubble[data-v-1393b388],.agent-message .message-bubble[data-v-1393b388]{border-radius:var(--cm-bubble-tail, 5px) var(--cm-bubble, 16px) var(--cm-bubble, 16px) var(--cm-bubble, 16px);background-color:#fff;border:1px solid rgba(0,0,0,.06)}.message-bubble p[data-v-1393b388]{margin:0}.message-bubble p+p[data-v-1393b388]{margin-top:.5em}.message-bubble ul[data-v-1393b388],.message-bubble ol[data-v-1393b388]{margin:.4em 0;padding-left:1.2em}.chat-input[data-v-1393b388]{padding:var(--space-md);border-top:1px solid color-mix(in srgb,currentColor 12%,transparent);display:flex;flex-direction:column;gap:var(--space-sm)}.email-input[data-v-1393b388]{width:100%}.email-input input[data-v-1393b388],.message-input input[data-v-1393b388]{width:100%;box-sizing:border-box;padding:var(--space-sm) var(--space-md);border:1.5px solid rgba(127,127,127,.3);border:1.5px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent);border-radius:var(--cm-field-radius, 12px);background:#7f7f7f14;background:color-mix(in srgb,currentColor 7%,transparent);color:inherit;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.chat-input input[data-v-1393b388]::placeholder{color:currentColor;opacity:.5}.email-input input[data-v-1393b388]:focus,.message-input input[data-v-1393b388]:focus{border-color:var(--cm-accent, #C9F24E);box-shadow:0 0 0 3px color-mix(in srgb,var(--cm-accent, #C9F24E) 22%,transparent)}.email-input input.invalid[data-v-1393b388]{border-color:var(--error-color)}.email-input input.invalid[data-v-1393b388]:focus{outline-color:var(--error-color)}.message-input[data-v-1393b388]{display:flex;gap:var(--space-sm)}.send-button[data-v-1393b388]{display:flex;align-items:center;justify-content:center;padding:0;min-width:42px;width:42px;height:42px;flex-shrink:0;border:none;border-radius:var(--cm-field-radius, 12px);cursor:pointer;color:#fff}.loading[data-v-1393b388]{display:flex;gap:4px;padding:12px 16px}.dot[data-v-1393b388]{width:8px;height:8px;background:currentColor;border-radius:50%;opacity:.6;animation:bounce-1393b388 1.4s infinite ease-in-out}.dot[data-v-1393b388]:nth-child(1){animation-delay:-.32s}.dot[data-v-1393b388]:nth-child(2){animation-delay:-.16s}.ai-disclaimer[data-v-1393b388]{text-align:center;padding:0 var(--space-md) 2px;font-size:.6875rem;line-height:1.3;color:var(--cm-muted)}.ai-disclaimer+.powered-by[data-v-1393b388]{padding-top:0}.powered-by[data-v-1393b388]{text-align:center;padding:var(--space-xs);font-size:.75rem;border-top:none;background:transparent;margin-top:auto;display:flex;align-items:center;justify-content:center;gap:6px}.cm-powered-prefix[data-v-1393b388]{color:var(--cm-muted)}.cm-brand[data-v-1393b388]{font-weight:700}.cm-powered-link[data-v-1393b388]{color:inherit;text-decoration:none;cursor:pointer}.cm-powered-link:hover .cm-brand[data-v-1393b388]{text-decoration:underline}.new-conversation-section[data-v-1393b388]{padding:var(--space-md);border-top:1px solid color-mix(in srgb,currentColor 12%,transparent);display:flex;justify-content:center;align-items:center}.conversation-ended-message[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;gap:var(--space-md);text-align:center;width:100%}.ended-text[data-v-1393b388]{margin:0;font-size:var(--text-sm);color:var(--text-muted);font-weight:500}.start-new-conversation-button[data-v-1393b388]{padding:var(--space-sm) var(--space-lg);border:none;border-radius:var(--radius-lg);font-size:var(--text-sm);font-weight:600;color:#fff;cursor:pointer;transition:all .2s ease;min-width:200px;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 4px #0000001a}.start-new-conversation-button[data-v-1393b388]:hover{opacity:.9;transform:translateY(-1px);box-shadow:0 4px 8px #00000026}.start-new-conversation-button[data-v-1393b388]:active{transform:translateY(0);box-shadow:0 2px 4px #0000001a}.error-alert[data-v-1393b388]{position:absolute;top:0;left:0;right:0;padding:12px;text-align:center;color:#fff;z-index:100;animation:slideDown-1393b388 .3s ease-out;border-radius:24px 24px 0 0}.auth-error-alert[data-v-1393b388]{position:absolute;top:0;left:0;right:0;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;background:linear-gradient(135deg,#dc2626,#991b1b);color:#fff;z-index:200;border-radius:24px;animation:slideDown-1393b388 .3s ease-out;text-align:center}.auth-error-header[data-v-1393b388]{display:flex;align-items:center;justify-content:center;gap:8px;margin-bottom:12px;font-size:16px;font-weight:600}.auth-error-icon[data-v-1393b388]{width:24px;height:24px;flex-shrink:0}.auth-error-message[data-v-1393b388]{font-size:14px;line-height:1.5;margin:12px 0;opacity:.95}.auth-error-refresh-btn[data-v-1393b388]{margin-top:16px;padding:8px 16px;background-color:#fff3;border:1px solid rgba(255,255,255,.4);border-radius:6px;color:#fff;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.auth-error-refresh-btn[data-v-1393b388]:hover{background-color:#ffffff4d;border-color:#fff9;transform:translateY(-1px)}.auth-error-refresh-btn[data-v-1393b388]:active{transform:translateY(0);background-color:#fff3}.auth-error-overlay[data-v-1393b388]{width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,#f8fafc,#f1f5f9);border-radius:var(--radius-lg);overflow:hidden;position:relative;font-family:var(--cm-body-font, "Instrument Sans", system-ui, -apple-system, "Segoe UI", sans-serif)}.auth-error-card[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px 24px;text-align:center;position:relative;z-index:1;max-width:90%}.auth-error-card .auth-error-header[data-v-1393b388]{flex-direction:column;gap:16px;margin-bottom:0}.auth-error-card .auth-error-icon[data-v-1393b388]{width:40px;height:40px;padding:16px;box-sizing:content-box;color:#dc2626;background:linear-gradient(135deg,#fee2e2,#fecaca);border-radius:20px;box-shadow:0 8px 24px #dc262626}.auth-error-card h2[data-v-1393b388]{font-size:20px;font-weight:600;color:#1e293b;margin:0;letter-spacing:-.01em}.auth-error-card .auth-error-message[data-v-1393b388]{font-size:14px;line-height:1.6;color:#64748b;margin:12px 0 24px;max-width:280px;opacity:1}.auth-error-card .auth-error-refresh-btn[data-v-1393b388]{margin-top:0;padding:11px 24px;background:#1e293b;border:none;border-radius:10px;color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:background .15s ease,transform .1s ease}.auth-error-card .auth-error-refresh-btn[data-v-1393b388]:hover{background:#0f172a;transform:translateY(-1px)}.auth-error-card .auth-error-refresh-btn[data-v-1393b388]:active{transform:translateY(0);background:#1e293b}.auth-error-only-container[data-v-1393b388]{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:linear-gradient(135deg,#dc2626,#991b1b);border-radius:24px;overflow:hidden;box-shadow:0 20px 40px #00000026}.auth-error-alert-blocking[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px 24px;color:#fff;z-index:9999;text-align:center;animation:slideDown-1393b388 .3s ease-out;max-width:90%}.auth-error-alert-blocking .auth-error-header[data-v-1393b388]{margin-bottom:16px}.auth-error-alert-blocking .auth-error-message[data-v-1393b388]{font-size:14px;line-height:1.6;margin:16px 0;opacity:.95;color:#f9fafb}.chat-container.collapsed .auth-error-alert[data-v-1393b388]{display:none}@keyframes slideDown-1393b388{0%{transform:translateY(-100%)}to{transform:translateY(0)}}.chat-container.collapsed .error-alert[data-v-1393b388]{display:none}@media (max-width: 768px){.chat-container[data-v-1393b388],.chat-container.collapsed[data-v-1393b388]{width:100vw!important;height:100vh!important;height:100dvh!important;border-radius:0!important;position:fixed!important;top:0!important;left:0!important;bottom:0!important;right:0!important;max-width:100vw!important;max-height:100vh!important;max-height:100dvh!important}.chat-panel[data-v-1393b388]{height:100%;border-radius:0}.chat-messages[data-v-1393b388]{padding:var(--space-sm)}.chat-toggle[data-v-1393b388]{width:48px;height:48px;font-size:14px}.new-conversation-section[data-v-1393b388]{padding:var(--space-sm)}.conversation-ended-message[data-v-1393b388]{gap:var(--space-sm)}.ended-text[data-v-1393b388]{font-size:var(--text-xs)}.start-new-conversation-button[data-v-1393b388]{padding:var(--space-xs) var(--space-md);font-size:var(--text-xs);min-width:160px;border-radius:var(--radius-md)}}.loading-history[data-v-1393b388]{display:flex;justify-content:center;align-items:center;padding:var(--space-sm);position:absolute;top:0;left:0;right:0;background:#ffffffe6;z-index:10}.loading-spinner[data-v-1393b388]{display:flex;gap:4px}.message-info[data-v-1393b388]{font-size:.75rem;margin-top:4px;display:flex;align-items:center;gap:4px}.agent-name[data-v-1393b388]{color:var(--text-muted);font-size:.75rem;font-weight:500;opacity:.8}.message-time[data-v-1393b388]{font-size:.75rem;opacity:.7;margin-top:4px;text-align:right}.typing-indicator[data-v-1393b388]{display:flex;gap:4px;padding:4px 16px;margin-top:var(--space-md)}.cm-typing-bubble[data-v-1393b388]{align-self:flex-start;display:inline-flex;align-items:center;gap:5px;padding:13px 16px;border-radius:var(--cm-bubble-tail, 5px) var(--cm-bubble, 16px) var(--cm-bubble, 16px) var(--cm-bubble, 16px)}.cm-typing-dot[data-v-1393b388]{width:7px;height:7px;border-radius:50%;background:currentColor;opacity:.45;animation:cm-bounce-1393b388 1.2s ease-in-out infinite}.cm-typing-dot[data-v-1393b388]:nth-child(2){animation-delay:.16s}.cm-typing-dot[data-v-1393b388]:nth-child(3){animation-delay:.32s}@keyframes cm-bounce-1393b388{0%,80%,to{transform:translateY(0);opacity:.4}40%{transform:translateY(-5px);opacity:1}}.typing-indicator.reading-indicator[data-v-1393b388]{align-items:center;gap:8px;padding:12px 16px;color:var(--text-muted, #6b7280);font-size:.78rem}.reading-bars[data-v-1393b388]{display:inline-flex;align-items:flex-end;gap:3px;height:14px}.reading-bars span[data-v-1393b388]{width:3px;height:14px;border-radius:2px;background:var(--primary-color, currentColor);transform-origin:bottom;transform:scaleY(.35);animation:cm-reading-bar-1393b388 1s ease-in-out infinite}.reading-bars span[data-v-1393b388]:nth-child(2){animation-delay:.15s}.reading-bars span[data-v-1393b388]:nth-child(3){animation-delay:.3s}.reading-label[data-v-1393b388]{opacity:.85}@keyframes cm-reading-bar-1393b388{0%,to{transform:scaleY(.35)}50%{transform:scaleY(1)}}.message-col[data-v-1393b388]{display:flex;flex-direction:column;align-items:flex-start;min-width:0;flex:1 1 auto}.user-message .message-col[data-v-1393b388]{align-items:flex-end}.citation-chips[data-v-1393b388]{display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin-top:6px;padding-left:2px;max-width:100%;animation:cm-msg-in-1393b388 .4s ease both}.citation-label[data-v-1393b388]{font-size:.66rem;text-transform:uppercase;letter-spacing:.06em;color:var(--cm-muted, currentColor);opacity:.85}.citation-chip[data-v-1393b388]{font-size:.72rem;line-height:1.3;padding:3px 10px;border-radius:999px;background:#7f7f7f14;background:color-mix(in srgb,var(--cm-accent, #C9F24E) 9%,transparent);border:1px solid rgba(127,127,127,.22);border-color:color-mix(in srgb,var(--cm-accent, #C9F24E) 26%,transparent);color:var(--cm-text, inherit);max-width:240px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.chat-messages .message[data-v-1393b388]{animation:cm-msg-in-1393b388 .32s cubic-bezier(.2,.7,.2,1) both}.chat-panel[data-v-1393b388]{animation:cm-panel-in-1393b388 .34s cubic-bezier(.2,.7,.2,1) both}@keyframes cm-msg-in-1393b388{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}@keyframes cm-panel-in-1393b388{0%{opacity:0;transform:translateY(10px) scale(.99)}to{opacity:1;transform:none}}.message-streaming[data-v-1393b388]{word-break:break-word}.message-streaming[data-v-1393b388]>:last-child:after{content:"";display:inline-block;width:6px;height:1em;margin-left:2px;vertical-align:-.15em;background:var(--cm-accent, currentColor);animation:cm-blink-1393b388 1s steps(1) infinite}@keyframes cm-blink-1393b388{0%,49%{opacity:1}50%,to{opacity:0}}.cm-welcome-block[data-v-1393b388]{display:flex;flex-direction:column;gap:12px;animation:cm-msg-in-1393b388 .4s ease both}.cm-welcome-row[data-v-1393b388]{display:flex;gap:9px;align-items:flex-start}.cm-welcome-orb[data-v-1393b388],.cm-welcome-avatar[data-v-1393b388]{width:28px;height:28px;border-radius:var(--cm-avatar-radius, 50%);flex-shrink:0}.cm-welcome-avatar[data-v-1393b388]{object-fit:cover}.cm-welcome-bubble[data-v-1393b388]{max-width:84%}.cm-quick-actions-bar[data-v-1393b388]{display:flex;flex-wrap:wrap;gap:8px;padding:0 16px 12px}.cm-quick-action[data-v-1393b388]{padding:8px 14px;border-radius:999px;border:1px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 66%,transparent);background:color-mix(in srgb,var(--cm-accent, #C9F24E) 18%,transparent);color:inherit;font:inherit;font-size:.8rem;font-weight:600;cursor:pointer;transition:background .15s ease,color .15s ease,border-color .15s ease}.cm-quick-action[data-v-1393b388]:hover:not(:disabled){background:var(--cm-accent, #C9F24E);color:#0b0c10;border-color:var(--cm-accent, #C9F24E)}.cm-quick-action[data-v-1393b388]:disabled{opacity:.55;cursor:default}.cm-email-gate[data-v-1393b388]{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:10px;padding:24px 22px;animation:cm-msg-in-1393b388 .4s ease both}.cm-email-gate-orb[data-v-1393b388]{width:48px;height:48px;border-radius:50%;margin-bottom:4px}.cm-email-gate-title[data-v-1393b388]{margin:0;font-family:Space Grotesk,system-ui,sans-serif;font-size:1.05rem;font-weight:600}.cm-email-gate-text[data-v-1393b388]{margin:0 0 6px;font-size:.85rem;opacity:.7;max-width:240px}.cm-email-gate-input[data-v-1393b388]{width:100%;max-width:280px;box-sizing:border-box;padding:var(--space-sm) var(--space-md);border-radius:var(--radius-lg);border:1.5px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent);background:color-mix(in srgb,currentColor 7%,transparent);color:inherit;font:inherit;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.cm-email-gate-input[data-v-1393b388]::placeholder{color:currentColor;opacity:.5}.cm-email-gate-input[data-v-1393b388]:focus{border-color:var(--cm-accent, #C9F24E);box-shadow:0 0 0 3px color-mix(in srgb,var(--cm-accent, #C9F24E) 22%,transparent)}.cm-email-gate-input.invalid[data-v-1393b388]{border-color:var(--error-color, #e5484d)}.cm-email-gate-error[data-v-1393b388]{margin:0;font-size:.78rem;color:var(--error-color, #e5484d)}.cm-email-gate-btn[data-v-1393b388]{margin-top:6px;padding:10px 22px;border:none;border-radius:999px;font:inherit;font-weight:600;font-size:.9rem;cursor:pointer;transition:opacity .15s ease,transform .15s ease}.cm-email-gate-btn[data-v-1393b388]:disabled{opacity:.6;cursor:default}.cm-email-gate-btn[data-v-1393b388]:not(:disabled):hover{transform:translateY(-1px)}@media (prefers-reduced-motion: reduce){.chat-messages .message[data-v-1393b388],.chat-panel[data-v-1393b388],.citation-chips[data-v-1393b388],.reading-bars span[data-v-1393b388],.cm-typing-dot[data-v-1393b388],.message-streaming[data-v-1393b388]>:last-child:after,.status-indicator.online[data-v-1393b388]{animation:none!important}.message-streaming[data-v-1393b388]>:last-child:after{display:none}}.chat-container.theme-glass .chat-panel[data-v-1393b388]{box-shadow:0 30px 80px -20px #0000008c,0 0 50px #9d8cff1f;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}.chat-container.theme-glass .message-bubble[data-v-1393b388]{box-shadow:0 2px 12px #00000029}.chat-container.theme-terminal .chat-panel[data-v-1393b388],.chat-container.theme-terminal .chat-messages[data-v-1393b388],.chat-container.theme-terminal .message-bubble[data-v-1393b388],.chat-container.theme-terminal .chat-input[data-v-1393b388],.chat-container.theme-terminal .cm-quick-action[data-v-1393b388],.chat-container.theme-terminal .powered-by[data-v-1393b388],.chat-container.theme-terminal .header-info h3[data-v-1393b388],.chat-container.theme-terminal .reading-label[data-v-1393b388],.chat-container.theme-terminal .citation-chip[data-v-1393b388]{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.chat-container.theme-terminal .citation-chip[data-v-1393b388]{border-radius:4px}.chat-container.theme-terminal .cm-quick-action[data-v-1393b388]{border-radius:6px}.chat-container.theme-calm .chat-panel[data-v-1393b388]{box-shadow:0 24px 60px -24px #00000080}.chat-container.theme-sunrise .chat-panel[data-v-1393b388]{box-shadow:0 30px 80px -28px #00000040,0 0 60px var(--cm-glow, rgba(0, 0, 0, .06));border:1px solid var(--cm-border, rgba(0, 0, 0, .08))}.chat-container.aurora .initializing-overlay[data-v-1393b388],.chat-container.theme-glass .initializing-overlay[data-v-1393b388],.chat-container.theme-terminal .initializing-overlay[data-v-1393b388],.chat-container.theme-calm .initializing-overlay[data-v-1393b388],.chat-container.aurora .loading-history[data-v-1393b388],.chat-container.theme-glass .loading-history[data-v-1393b388],.chat-container.theme-terminal .loading-history[data-v-1393b388],.chat-container.theme-calm .loading-history[data-v-1393b388],.chat-container.aurora .connection-status[data-v-1393b388],.chat-container.theme-glass .connection-status[data-v-1393b388],.chat-container.theme-terminal .connection-status[data-v-1393b388],.chat-container.theme-calm .connection-status[data-v-1393b388]{background:#0d0e14eb!important;color:#f2f3f8;border-bottom-color:#ffffff14}.chat-container.aurora .attach-button[data-v-1393b388],.chat-container.theme-glass .attach-button[data-v-1393b388],.chat-container.theme-terminal .attach-button[data-v-1393b388],.chat-container.theme-calm .attach-button[data-v-1393b388]{background:#ffffff14!important;color:#f2f3f8!important}.connection-status[data-v-1393b388]{position:absolute;top:0;left:0;right:0;padding:12px;text-align:center;z-index:100;background:#fffffff2;border-bottom:1px solid var(--border-color)}.connecting-message[data-v-1393b388]{color:var(--text-color);display:flex;align-items:center;justify-content:center;gap:8px}.failed-message[data-v-1393b388]{color:var(--error-color)}.reconnect-button[data-v-1393b388]{background:none;border:none;color:var(--primary-color);text-decoration:underline;cursor:pointer;padding:4px 8px;margin-left:8px}.reconnect-button[data-v-1393b388]:hover{color:var(--primary-dark)}.loading-dots[data-v-1393b388]{display:flex;gap:4px;margin-left:4px}.loading-dots .dot[data-v-1393b388]{width:6px;height:6px;background:currentColor;border-radius:50%;animation:bounce-1393b388 1.4s infinite ease-in-out}.loading-dots .dot[data-v-1393b388]:nth-child(1){animation-delay:-.32s}.loading-dots .dot[data-v-1393b388]:nth-child(2){animation-delay:-.16s}@keyframes bounce-1393b388{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.message-input input.disabled[data-v-1393b388],.email-input input.disabled[data-v-1393b388]{background-color:#0000000d!important;cursor:not-allowed;color:var(--text-muted)}.message-input input.disabled[data-v-1393b388]::placeholder,.email-input input.disabled[data-v-1393b388]::placeholder{color:var(--text-muted)}.message.agent-message[data-v-1393b388]{margin-right:auto;justify-content:flex-start}.agent-name[data-v-1393b388]{font-size:12px;color:#9ca3af;margin-top:4px;margin-left:8px}.message-info[data-v-1393b388]{display:flex;flex-direction:column;align-items:flex-start;gap:2px;margin-top:4px}.message.system-message[data-v-1393b388]{align-self:center;max-width:100%;margin:var(--space-sm) 0}.system-message .message-bubble[data-v-1393b388]{background:#0000000d;color:var(--text-muted);font-size:.85em;padding:var(--space-xs) var(--space-md);border-radius:var(--radius-lg);text-align:center}.initializing-overlay[data-v-1393b388]{position:absolute;top:0;left:0;right:0;bottom:0;background:#fffffff2;display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:1000;border-radius:var(--radius-lg)}.loading-text[data-v-1393b388]{margin-top:var(--space-md);color:var(--text-color);font-size:var(--text-md)}.loading-spinner[data-v-1393b388]{display:flex;gap:6px}.loading-spinner .dot[data-v-1393b388]{width:10px;height:10px;background:var(--accent-solid);border-radius:50%;animation:bounce-1393b388 1.4s infinite ease-in-out}.loading-spinner .dot[data-v-1393b388]:nth-child(1){animation-delay:-.32s}.loading-spinner .dot[data-v-1393b388]:nth-child(2){animation-delay:-.16s}.rating-dialog[data-v-1393b388]{position:absolute;top:0;left:0;right:0;bottom:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000}.rating-content[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;gap:1rem;padding:.5rem;width:100%}.star-rating[data-v-1393b388]{display:flex;gap:.75rem;justify-content:center;margin:0 0 24px}.star-button[data-v-1393b388]{background:none;border:none;cursor:pointer;font-size:28px;color:#d1d5db;transition:all var(--transition-fast);padding:0 4px;transform-origin:center;line-height:1}.star-button[data-v-1393b388]:hover{transform:scale(1.1)}.star-button.selected[data-v-1393b388]{transform:scale(1.05)}.star-button.warning[data-v-1393b388]{color:var(--error-color);text-shadow:0 0 5px rgba(239,68,68,.3)}.star-button.success[data-v-1393b388]{color:var(--success-color);text-shadow:0 0 5px rgba(16,185,129,.3)}.star-button[data-v-1393b388]:disabled{cursor:not-allowed;opacity:.7}.submit-button[data-v-1393b388]{padding:12px 20px;border:none;border-radius:var(--radius-md);background-color:var(--accent-solid);color:#fff;cursor:pointer;font-size:var(--text-base);font-weight:600;transition:all var(--transition-fast);width:100%;text-align:center;display:block;margin-top:16px}.submit-button[data-v-1393b388]:hover:not(:disabled){opacity:.9}.submit-button[data-v-1393b388]:disabled{opacity:.7;cursor:not-allowed}.rating-prompt[data-v-1393b388]{font-size:var(--text-sm);color:var(--text-primary);margin-bottom:24px;text-align:center;font-weight:500}.rating-message[data-v-1393b388]{align-self:center;width:100%;max-width:500px;margin:var(--space-sm) 0}.rating-message .message-bubble[data-v-1393b388]{background-color:#fff;padding:var(--space-md) var(--space-md);border-radius:12px;box-shadow:0 4px 8px #00000014;border:1px solid var(--border-color);transition:all var(--transition-normal)}.rating-content[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;width:100%;padding:0}.rating-prompt[data-v-1393b388]{font-size:var(--text-sm);color:var(--text-primary);margin-bottom:20px;text-align:center;font-weight:500}.star-rating[data-v-1393b388]{display:flex;gap:.75rem;justify-content:center;margin:0 0 20px}.feedback-wrapper[data-v-1393b388]{width:100%}.feedback-section[data-v-1393b388]{display:flex;flex-direction:column;gap:2px;width:100%;padding:0;margin-bottom:4px}.feedback-input[data-v-1393b388]{padding:10px 14px;border:1px solid var(--border-color);border-radius:8px;font-size:var(--text-sm);transition:border-color var(--transition-fast);background-color:#fff;box-shadow:inset 0 1px 2px #0000000d}.feedback-input[data-v-1393b388]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 2px #f346111a}.feedback-counter[data-v-1393b388]{font-size:var(--text-xs);color:var(--text-muted);text-align:right;margin-right:4px;padding:0 4px}.submit-rating-button[data-v-1393b388]{padding:10px 20px;border:none;border-radius:8px;background-color:var(--accent-solid);color:#fff;cursor:pointer;font-size:var(--text-base);font-weight:600;transition:all var(--transition-fast);width:100%;text-align:center;display:block;margin:12px 0 0;box-shadow:0 2px 4px #0000001a}.submit-rating-button[data-v-1393b388]:hover:not(:disabled){opacity:.95;box-shadow:0 4px 6px #0000001a;transform:translateY(-1px)}.submit-rating-button[data-v-1393b388]:active:not(:disabled){transform:translateY(0);box-shadow:0 1px 2px #0000001a}.submit-rating-button[data-v-1393b388]:disabled{opacity:.7;cursor:not-allowed}.submitted .star-button[data-v-1393b388]{pointer-events:none;opacity:.7}.submitted-feedback-wrapper[data-v-1393b388]{width:100%;margin-top:16px}.submitted-feedback[data-v-1393b388]{padding:12px 16px;border:1px solid var(--border-color);border-radius:8px;background-color:#f9f9f9;margin-bottom:8px}.submitted-feedback-text[data-v-1393b388]{font-size:var(--text-sm);color:var(--text-secondary);margin:0;line-height:1.5;word-break:break-word}.submitted-message[data-v-1393b388]{font-size:var(--text-sm);color:var(--success-color);text-align:center;font-weight:500;margin-top:8px}.message.product-message .message-bubble[data-v-1393b388]{padding:0;background:none;border:none;box-shadow:none;width:100%;max-width:none}.product-card-compact[data-v-1393b388]{display:flex;flex-direction:row;align-items:flex-start;border:1px solid var(--border-color);border-radius:var(--radius-lg);overflow:hidden;background-color:var(--background-base);box-shadow:var(--shadow-sm);padding:var(--space-md);gap:var(--space-md);width:100%;transition:box-shadow var(--transition-fast)}.product-card-compact[data-v-1393b388]:hover{box-shadow:var(--shadow-md)}.product-card-compact.carousel-item[data-v-1393b388]{flex-direction:column;align-items:stretch;width:160px;flex-shrink:0;padding:0;gap:0;height:auto;border-radius:var(--radius-md);box-shadow:var(--shadow-sm)}.product-card-compact.carousel-item[data-v-1393b388]:hover{box-shadow:var(--shadow-md)}.product-card-compact.single-product[data-v-1393b388]{max-width:280px;align-self:flex-start;padding:var(--space-sm);gap:var(--space-sm);display:flex;flex-direction:row;align-items:flex-start}.product-card-compact.single-product .product-image-compact[data-v-1393b388]{width:50px;height:50px;border-radius:var(--radius-xs);flex-shrink:0}.product-card-compact.single-product .product-info-compact[data-v-1393b388]{display:flex;flex-direction:column;justify-content:space-between;flex:1;min-height:50px;gap:var(--space-xxs)}.product-card-compact.single-product .product-text-info[data-v-1393b388]{display:flex;flex-direction:column;gap:1px}.product-card-compact.single-product .product-title-compact[data-v-1393b388]{font-size:var(--text-xs);font-weight:500;line-height:1.3;white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;text-overflow:ellipsis;margin:0}.product-card-compact.single-product .product-variant-compact[data-v-1393b388]{font-size:10px;color:var(--text-muted);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.product-card-compact.single-product .product-price-compact[data-v-1393b388]{font-size:var(--text-xs);font-weight:600;margin-top:2px}.product-card-compact.single-product .product-actions-compact[data-v-1393b388]{margin-top:auto;width:100%}.product-card-compact.single-product .view-details-button-compact[data-v-1393b388]{width:100%;padding:5px 8px;font-size:11px;justify-content:center}.product-image-compact[data-v-1393b388]{position:relative;width:60px;height:60px;aspect-ratio:1 / 1;background-color:var(--background-soft);overflow:hidden;border:none;border-radius:var(--radius-sm);flex-shrink:0}.product-card-compact.carousel-item .product-image-compact[data-v-1393b388]{width:100%;height:auto;aspect-ratio:1 / 1;border-radius:0;border-bottom:1px solid var(--border-color)}.product-thumbnail[data-v-1393b388]{display:block;width:100%;height:100%;object-fit:cover;transition:transform var(--transition-fast)}.product-thumbnail[data-v-1393b388]:hover{transform:scale(1.05)}.product-info-compact[data-v-1393b388]{display:flex;flex-direction:column;gap:2px;flex:1;justify-content:center;min-width:0}.product-card-compact.carousel-item .product-info-compact[data-v-1393b388]{padding:var(--space-sm);gap:var(--space-xs);justify-content:flex-start}.product-title-compact[data-v-1393b388]{margin:0;font-size:var(--text-sm);font-weight:500;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.product-variant-compact[data-v-1393b388]{font-size:var(--text-xs);color:var(--text-muted);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.product-price-compact[data-v-1393b388]{font-size:var(--text-sm);font-weight:600;color:var(--text-primary);white-space:nowrap;margin-top:2px}.product-actions-compact[data-v-1393b388]{display:flex;gap:var(--space-xs);margin-top:var(--space-sm)}.product-actions-compact.single[data-v-1393b388]{justify-content:flex-start}.product-card-compact.carousel-item .product-actions-compact[data-v-1393b388]{margin-top:auto;padding-top:var(--space-xs)}.add-to-cart-button-compact[data-v-1393b388],.view-details-button-compact[data-v-1393b388]{flex:none;padding:6px 10px;border:1px solid var(--border-color);border-radius:var(--radius-sm);font-weight:500;font-size:var(--text-xs);cursor:pointer;transition:all var(--transition-fast);text-align:center;white-space:nowrap;background-color:var(--background-base);color:var(--text-secondary);line-height:1}.add-to-cart-button-compact[data-v-1393b388]{background-color:var(--accent-solid);color:#fff;border-color:transparent;box-shadow:var(--shadow-xs);flex:1}.view-details-button-compact[data-v-1393b388]{display:inline-flex;align-items:center;gap:4px}.external-link-icon[data-v-1393b388]{font-size:1em;line-height:1;display:inline-block}.add-to-cart-button-compact[data-v-1393b388]:hover:not(:disabled){opacity:.9;box-shadow:var(--shadow-sm)}.add-to-cart-button-compact[data-v-1393b388]:disabled{background-color:#ccc;cursor:not-allowed;opacity:.7;box-shadow:none}.view-details-button-compact[data-v-1393b388]:hover{background-color:var(--background-soft);border-color:var(--border-color-hover);color:var(--text-primary)}.product-message-container[data-v-1393b388]{width:100%;overflow:hidden}.products-carousel[data-v-1393b388]{margin:var(--space-xs) 0;width:100%;padding:var(--space-xs);background:#00000005;border-radius:20px}.carousel-items[data-v-1393b388]{display:flex;flex-direction:row;gap:var(--space-sm);margin-top:var(--space-xs);overflow-x:auto;padding:var(--space-xs);padding-bottom:var(--space-md);scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.3) rgba(0,0,0,.1)}.carousel-items[data-v-1393b388]::-webkit-scrollbar{display:block;height:8px}.carousel-items[data-v-1393b388]::-webkit-scrollbar-track{background:#0000000d;border-radius:4px}.carousel-items[data-v-1393b388]::-webkit-scrollbar-thumb{background-color:#0003;border-radius:4px;transition:background-color .2s}.carousel-items[data-v-1393b388]::-webkit-scrollbar-thumb:hover{background-color:#0000004d}.product-card-compact[data-v-1393b388]{background-color:#fff;border:1px solid rgba(0,0,0,.08);border-radius:20px;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a;overflow:hidden;width:180px;flex-shrink:0;transition:all .2s ease}.product-card-compact[data-v-1393b388]:hover{transform:translateY(-2px);box-shadow:0 4px 12px #00000014,0 2px 4px #0000000f}.product-card-compact .product-info-compact[data-v-1393b388]{display:flex;flex-direction:column;padding:var(--space-sm) var(--space-sm);gap:var(--space-xs);background-color:#fff}.product-card-compact .product-title-compact[data-v-1393b388]{font-size:var(--text-sm);font-weight:500;line-height:1.4;color:var(--text-primary);overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;margin:0;min-height:2.8em}.product-card-compact .product-price-compact[data-v-1393b388]{font-size:var(--text-sm);font-weight:600;color:var(--text-primary);margin-top:2px}.product-card-compact .view-details-button-compact[data-v-1393b388]{width:100%;padding:8px 12px;background-color:#fff;color:var(--text-primary);border:1px solid var(--border-color);border-radius:16px;font-size:var(--text-xs);font-weight:500;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .2s;box-shadow:0 1px 2px #0000000a;margin-top:var(--space-xs)}.product-card-compact .view-details-button-compact[data-v-1393b388]:hover{background-color:var(--background-soft);border-color:var(--border-color-hover);box-shadow:0 2px 4px #00000014}.product-message-container[data-v-1393b388]{width:100%;margin:var(--space-sm) 0;padding:0 var(--space-xs)}.carousel-title[data-v-1393b388]{font-size:var(--text-base);font-weight:600;margin-bottom:var(--space-sm);color:var(--text-primary);padding:0 var(--space-xs)}.no-products-message[data-v-1393b388]{padding:var(--space-md);color:var(--text-muted);text-align:center;font-style:italic;font-size:var(--text-sm)}.message.form-message[data-v-1393b388]{align-self:center;width:100%;max-width:min(520px,100%);margin:var(--space-md) 0}.form-message .message-bubble[data-v-1393b388]{background:linear-gradient(145deg,#fff,#f8f9fa);padding:var(--space-lg);border-radius:24px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #ffffff0d;border:1px solid rgba(0,0,0,.06);width:100%;max-width:none;box-sizing:border-box;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);position:relative;overflow:hidden}.form-message .message-bubble[data-v-1393b388]:before{content:"";position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--accent-solid),#ff6b6b,#4ecdc4,var(--accent-solid));background-size:200% 100%;animation:gradientShift-1393b388 3s ease infinite}@keyframes gradientShift-1393b388{0%,to{background-position:0% 50%}50%{background-position:100% 50%}}.form-content[data-v-1393b388]{width:100%;position:relative}.form-header[data-v-1393b388]{margin-bottom:var(--space-xl);text-align:center;position:relative}.form-title[data-v-1393b388]{font-size:28px;font-weight:700;background:#000;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin:0 0 var(--space-sm) 0;letter-spacing:-.02em}.form-message .form-header[data-v-1393b388]{margin-bottom:var(--space-md);text-align:left}.form-message .form-title[data-v-1393b388]{font-size:var(--text-lg);margin-bottom:var(--space-xs)}.form-message .form-description[data-v-1393b388]{font-size:var(--text-sm)}.form-message .form-fields[data-v-1393b388]{gap:var(--space-md)}.form-message .form-field[data-v-1393b388]{gap:var(--space-xs)}.form-message .field-label[data-v-1393b388]{margin-bottom:0}.form-message .form-input[data-v-1393b388],.form-message .form-textarea[data-v-1393b388],.form-message .form-select[data-v-1393b388]{padding:var(--space-sm) var(--space-md);border-radius:var(--radius-lg)}.form-message .form-textarea[data-v-1393b388]{min-height:80px}.form-message .form-actions[data-v-1393b388]{margin-top:var(--space-md)}.form-message .form-submit-button[data-v-1393b388]{width:100%;padding:var(--space-sm) var(--space-lg)}.form-description[data-v-1393b388]{font-size:var(--text-base);color:var(--text-secondary);margin:0;line-height:1.6;opacity:.8}.chat-container.aurora .form-message .message-bubble[data-v-1393b388],.chat-container.theme-glass .form-message .message-bubble[data-v-1393b388],.chat-container.theme-terminal .form-message .message-bubble[data-v-1393b388],.chat-container.theme-calm .form-message .message-bubble[data-v-1393b388]{background:#ffffff0a!important;border:1px solid rgba(255,255,255,.1)!important;box-shadow:0 20px 40px -12px #00000080!important}.chat-container.aurora .form-title[data-v-1393b388],.chat-container.theme-glass .form-title[data-v-1393b388],.chat-container.theme-terminal .form-title[data-v-1393b388],.chat-container.theme-calm .form-title[data-v-1393b388]{background:none!important;-webkit-text-fill-color:#F2F3F8!important;color:#f2f3f8!important}.chat-container.aurora .form-description[data-v-1393b388],.chat-container.theme-glass .form-description[data-v-1393b388],.chat-container.theme-terminal .form-description[data-v-1393b388],.chat-container.theme-calm .form-description[data-v-1393b388],.chat-container.aurora .field-label[data-v-1393b388],.chat-container.theme-glass .field-label[data-v-1393b388],.chat-container.theme-terminal .field-label[data-v-1393b388],.chat-container.theme-calm .field-label[data-v-1393b388]{color:#c7ccd6!important}.chat-container.aurora .form-input[data-v-1393b388],.chat-container.theme-glass .form-input[data-v-1393b388],.chat-container.theme-terminal .form-input[data-v-1393b388],.chat-container.theme-calm .form-input[data-v-1393b388],.chat-container.aurora .form-textarea[data-v-1393b388],.chat-container.theme-glass .form-textarea[data-v-1393b388],.chat-container.theme-terminal .form-textarea[data-v-1393b388],.chat-container.theme-calm .form-textarea[data-v-1393b388]{background:#ffffff0f!important;color:#f2f3f8!important;border-color:#ffffff29!important;box-shadow:none!important}.chat-container.aurora .form-input[data-v-1393b388]::placeholder,.chat-container.theme-glass .form-input[data-v-1393b388]::placeholder,.chat-container.theme-terminal .form-input[data-v-1393b388]::placeholder,.chat-container.theme-calm .form-input[data-v-1393b388]::placeholder,.chat-container.aurora .form-textarea[data-v-1393b388]::placeholder,.chat-container.theme-glass .form-textarea[data-v-1393b388]::placeholder,.chat-container.theme-terminal .form-textarea[data-v-1393b388]::placeholder,.chat-container.theme-calm .form-textarea[data-v-1393b388]::placeholder{color:#f2f3f880!important}@media (max-width: 768px){.message.form-message[data-v-1393b388]{max-width:100%;margin:var(--space-sm) 0}.form-message .message-bubble[data-v-1393b388]{padding:var(--space-lg);border-radius:20px;margin:0 var(--space-xs)}.form-title[data-v-1393b388]{font-size:24px;letter-spacing:-.01em}.form-description[data-v-1393b388]{font-size:var(--text-sm)}}.user-input-content[data-v-1393b388]{width:100%;display:flex;flex-direction:column;gap:var(--space-md)}.user-input-prompt[data-v-1393b388]{font-size:var(--text-base);color:var(--text-primary);line-height:1.5}.user-input-form[data-v-1393b388]{display:flex;flex-direction:column;gap:var(--space-sm);padding:var(--space-md);background:var(--background-soft);border-radius:var(--radius-md);border:1px solid var(--border-color)}.user-input-textarea[data-v-1393b388]{width:100%;padding:var(--space-sm) var(--space-md);border:1px solid var(--border-color);border-radius:var(--radius-md);background:var(--background-base);color:var(--text-primary);font-size:var(--text-base);font-family:inherit;resize:vertical;min-height:80px;transition:border-color .2s ease,box-shadow .2s ease}.user-input-textarea[data-v-1393b388]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 2px #f346111a}.user-input-textarea[data-v-1393b388]::placeholder{color:var(--text-muted)}.user-input-actions[data-v-1393b388]{display:flex;justify-content:flex-end}.user-input-submit-button[data-v-1393b388]{padding:var(--space-sm) var(--space-lg);background:var(--accent-solid);color:#fff;border:none;border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:600;cursor:pointer;transition:all .2s ease;min-width:100px}.user-input-submit-button[data-v-1393b388]:hover:not(:disabled){background:var(--primary-dark);transform:translateY(-1px)}.user-input-submit-button[data-v-1393b388]:disabled{opacity:.6;cursor:not-allowed;transform:none}.user-input-submitted[data-v-1393b388]{padding:var(--space-md);background:var(--background-soft);border-radius:var(--radius-md);border:1px solid var(--border-color)}.submitted-input[data-v-1393b388]{font-size:var(--text-sm);color:var(--text-secondary);line-height:1.4}.submitted-input strong[data-v-1393b388]{color:var(--text-primary);font-weight:600}@media (max-width: 768px){.user-input-form[data-v-1393b388]{padding:var(--space-sm);gap:var(--space-xs)}.user-input-textarea[data-v-1393b388]{min-height:60px;padding:var(--space-xs) var(--space-sm)}.user-input-submit-button[data-v-1393b388]{padding:var(--space-xs) var(--space-md);font-size:var(--text-xs);min-width:80px}.user-input-submitted[data-v-1393b388]{padding:var(--space-sm)}}.chat-container.ask-anything-style[data-v-1393b388]{max-width:400px;min-width:400px;width:400px;margin:0 auto;box-shadow:0 25px 50px #00000026;border-radius:24px;background:#fff}@media (max-width: 1024px) and (min-width: 769px){.chat-container.ask-anything-style[data-v-1393b388]{max-width:700px;min-width:500px;margin:10px auto;height:650px}}.chat-container.ask-anything-style .chat-messages[data-v-1393b388]{flex:1!important;overflow-y:auto!important;padding:var(--space-xl)!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;justify-content:flex-start!important;gap:var(--space-md)!important;-webkit-overflow-scrolling:touch!important;scroll-behavior:smooth!important;margin-top:0!important;max-width:600px!important;margin-left:auto!important;margin-right:auto!important;width:100%!important;box-sizing:border-box!important}.chat-container.ask-anything-style .chat-messages .message[data-v-1393b388]{display:flex!important;gap:var(--space-sm)!important;max-width:85%!important;align-items:flex-start!important;margin-bottom:var(--space-md)!important;margin-left:0!important;margin-right:0!important;width:auto!important;align-self:unset!important;justify-content:unset!important;text-align:unset!important}.chat-container.ask-anything-style .chat-messages .message.user-message[data-v-1393b388],.chat-container.ask-anything-style .message.user-message[data-v-1393b388],.chat-container.ask-anything-style div.message.user-message[data-v-1393b388]{align-self:flex-end!important;margin-left:auto!important;margin-right:0!important;flex-direction:row-reverse!important;text-align:right!important;justify-content:flex-start!important;width:auto!important;max-width:85%!important}.chat-container.ask-anything-style .chat-messages .message.agent-message[data-v-1393b388],.chat-container.ask-anything-style .chat-messages .message.bot[data-v-1393b388],.chat-container.ask-anything-style .chat-messages .message.agent[data-v-1393b388],.chat-container.ask-anything-style .message.agent-message[data-v-1393b388],.chat-container.ask-anything-style .message.bot[data-v-1393b388],.chat-container.ask-anything-style .message.agent[data-v-1393b388],.chat-container.ask-anything-style div.message.agent-message[data-v-1393b388],.chat-container.ask-anything-style div.message.bot[data-v-1393b388],.chat-container.ask-anything-style div.message.agent[data-v-1393b388]{align-self:flex-start!important;margin-left:0!important;margin-right:auto!important;flex-direction:row!important;text-align:left!important;justify-content:flex-start!important;width:auto!important;max-width:85%!important}.chat-container.ask-anything-style .typing-indicator[data-v-1393b388]{display:flex!important;gap:4px!important;padding:12px 16px!important;margin-top:var(--space-md)!important;align-self:flex-start!important;margin-left:0!important;margin-right:auto!important;width:auto!important;max-width:85%!important;justify-content:flex-start!important}.chat-container.ask-anything-style .chat-messages .message.system-message[data-v-1393b388],.chat-container.ask-anything-style .message.system-message[data-v-1393b388]{align-self:center!important;margin:var(--space-sm) auto!important;text-align:center!important;max-width:100%!important;justify-content:center!important}.chat-container.ask-anything-style .message-bubble[data-v-1393b388]{padding:var(--space-md) var(--space-lg)!important;border-radius:20px!important;line-height:1.4!important;box-shadow:0 2px 8px #00000014!important;max-width:100%!important;width:auto!important;display:inline-block!important}.chat-container.ask-anything-style .user-message .message-bubble[data-v-1393b388]{border-bottom-right-radius:6px!important}.chat-container.ask-anything-style .agent-message .message-bubble[data-v-1393b388],.chat-container.ask-anything-style .bot .message-bubble[data-v-1393b388],.chat-container.ask-anything-style .agent .message-bubble[data-v-1393b388]{border-bottom-left-radius:6px!important}.chat-panel.ask-anything-chat[data-v-1393b388]{max-width:700px;margin:0 auto;background:var(--background-base);border-radius:var(--radius-lg);box-shadow:0 8px 32px #0000001f}.chat-input.ask-anything-input[data-v-1393b388]{padding:var(--space-xl)!important;background:var(--background-base)!important;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)!important;border-radius:0 0 var(--radius-lg) var(--radius-lg)!important;display:flex!important;flex-direction:column!important;align-items:center!important}.chat-input.ask-anything-input .message-input[data-v-1393b388]{max-width:600px!important;margin:0 auto!important;gap:var(--space-md)!important;display:flex!important;align-items:center!important;width:100%!important}.chat-input.ask-anything-input .email-input[data-v-1393b388]{max-width:600px!important;margin:0 auto var(--space-md) auto!important;width:100%!important}.chat-input.ask-anything-input .email-input input[data-v-1393b388]{padding:18px 24px;border:2px solid var(--border-color);border-radius:16px;font-size:1rem;font-weight:500;background:var(--background-base);color:var(--text-primary);transition:all .3s ease;box-shadow:0 4px 6px #0000000d}.chat-input.ask-anything-input .email-input input[data-v-1393b388]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 4px #f346111a,0 8px 16px #0000001a;transform:translateY(-1px)}.ask-anything-field[data-v-1393b388]{padding:18px 24px!important;border:2px solid var(--border-color)!important;border-radius:16px!important;font-size:1rem!important;font-weight:500!important;background:var(--background-base)!important;color:var(--text-primary)!important;transition:all .3s ease!important;box-shadow:0 4px 6px #0000000d!important}.ask-anything-field[data-v-1393b388]:focus{outline:none!important;border-color:var(--primary-color)!important;box-shadow:0 0 0 4px #f346111a,0 8px 16px #0000001a!important;transform:translateY(-1px)!important}.ask-anything-field[data-v-1393b388]::placeholder{color:var(--text-muted)!important;font-weight:400!important}.send-button.ask-anything-send[data-v-1393b388]{padding:18px!important;min-width:56px!important;height:56px!important;border-radius:16px!important;transition:all .3s ease!important;box-shadow:0 4px 14px #f346114d!important}.send-button.ask-anything-send[data-v-1393b388]:hover:not(:disabled){transform:translateY(-2px)!important;box-shadow:0 8px 25px #f3461166!important}.send-button.ask-anything-send[data-v-1393b388]:active:not(:disabled){transform:translateY(0)!important;box-shadow:0 4px 14px #f346114d!important}.welcome-message-section[data-v-1393b388]{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:space-between;background:var(--background-base);border-radius:var(--radius-lg);position:relative;overflow:hidden;padding:var(--space-xl);box-sizing:border-box}.welcome-content[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;max-width:600px;text-align:center;flex:1}.welcome-header[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;gap:var(--space-lg)}.welcome-avatar[data-v-1393b388]{width:80px;height:80px;border-radius:50%;object-fit:cover;box-shadow:0 8px 32px #0000001f;border:4px solid white}.welcome-title[data-v-1393b388]{font-size:2.5rem;font-weight:700;color:#1f2937;margin:0;line-height:1.2;letter-spacing:-.02em}.welcome-subtitle[data-v-1393b388]{font-size:1.125rem;color:var(--text-secondary);margin:0;line-height:1.6;max-width:500px;font-weight:400}.welcome-input-section[data-v-1393b388]{width:100%;height:100%;display:flex;flex-direction:column;background:var(--background-base);border-radius:var(--radius-lg);position:relative}.welcome-input-container[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-lg);max-width:600px;margin:0 auto;width:100%;padding:0}.welcome-input-container .email-input[data-v-1393b388]{width:100%;margin-bottom:var(--space-md)}.welcome-email-input[data-v-1393b388]{width:100%;padding:18px 24px;border:2px solid var(--border-color);border-radius:16px;font-size:1rem;font-weight:500;background:var(--background-base);color:var(--text-primary);transition:all .3s ease;box-shadow:0 4px 6px #0000000d}.welcome-email-input[data-v-1393b388]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 4px #f346111a,0 8px 16px #0000001a;transform:translateY(-1px)}.welcome-email-input.invalid[data-v-1393b388]{border-color:var(--error-color);box-shadow:0 0 0 4px #ef44441a}.welcome-email-input.disabled[data-v-1393b388]{background-color:#0000000d;cursor:not-allowed;opacity:.7}.welcome-message-input[data-v-1393b388]{display:flex;gap:var(--space-md);width:100%;align-items:center}.welcome-message-field[data-v-1393b388]{flex:1;padding:18px 24px;border:2px solid var(--border-color);border-radius:16px;font-size:1rem;font-weight:500;background:var(--background-base);color:var(--text-primary);transition:all .3s ease;box-shadow:0 4px 6px #0000000d}.welcome-message-field[data-v-1393b388]:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 4px #f346111a,0 8px 16px #0000001a;transform:translateY(-1px)}.welcome-message-field.disabled[data-v-1393b388]{background-color:#0000000d;cursor:not-allowed;opacity:.7}.welcome-message-field[data-v-1393b388]::placeholder{color:var(--text-muted);font-weight:400}.welcome-send-button[data-v-1393b388]{display:flex;align-items:center;justify-content:center;padding:18px;min-width:56px;height:56px;border:none;border-radius:16px;cursor:pointer;color:#fff;transition:all .3s ease;box-shadow:0 4px 14px #f346114d}.welcome-send-button[data-v-1393b388]:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 8px 25px #f3461166}.welcome-send-button[data-v-1393b388]:active:not(:disabled){transform:translateY(0);box-shadow:0 4px 14px #f346114d}.welcome-send-button[data-v-1393b388]:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:0 2px 4px #0000001a}.chat-container.aurora .chat-input.ask-anything-input[data-v-1393b388]{background:transparent!important}.chat-container.aurora .ask-anything-field[data-v-1393b388],.chat-container.aurora .chat-input.ask-anything-input .email-input input[data-v-1393b388]{background:#ffffff0f!important;color:#f2f3f8!important;border-color:#ffffff29!important;border-color:color-mix(in srgb,var(--cm-accent, #9D8CFF) 35%,transparent)!important;box-shadow:none!important}.chat-container.aurora .ask-anything-field[data-v-1393b388]::placeholder,.chat-container.aurora .chat-input.ask-anything-input .email-input input[data-v-1393b388]::placeholder{color:#f2f3f880!important}.chat-container.aurora .powered-by[data-v-1393b388]{color:#f2f3f899;border-top-color:#ffffff14}.welcome-orb[data-v-1393b388]{width:120px;height:120px;border-radius:50%;flex-shrink:0}.welcome-message-section.aurora .welcome-avatar[data-v-1393b388]{width:120px;height:120px;border:none;box-shadow:0 8px 40px #9d8cff59}.welcome-message-section.aurora .welcome-title[data-v-1393b388]{color:#fff}.welcome-message-section.aurora .welcome-subtitle[data-v-1393b388]{color:#fff9}.welcome-message-section.aurora .welcome-message-input[data-v-1393b388]{background:#ffffff0d;border:1px solid rgba(255,255,255,.1);border-radius:999px;padding:6px 6px 6px 8px}.welcome-message-section.aurora .welcome-message-field[data-v-1393b388]{background:transparent;border:none;box-shadow:none;color:#fff;border-radius:999px}.welcome-message-section.aurora .welcome-message-field[data-v-1393b388]:focus{border:none;box-shadow:none;transform:none}.welcome-message-section.aurora .welcome-message-field[data-v-1393b388]::placeholder{color:#ffffff73}.welcome-message-section.aurora .welcome-send-button.aurora-send[data-v-1393b388]{border-radius:50%;min-width:48px;width:48px;height:48px;padding:0}.powered-by-welcome[data-v-1393b388]{text-align:center;font-size:.75rem;color:var(--text-muted);padding:var(--space-md);background:transparent;border:none;display:flex;align-items:center;justify-content:center;gap:6px;margin-top:auto}@media (max-width: 768px){.chat-container.ask-anything-style[data-v-1393b388]{min-width:100vw!important;max-width:100vw!important;width:100vw!important;height:100vh!important;height:100dvh!important;margin:0!important;border-radius:0!important;box-shadow:none!important;position:fixed!important;top:0!important;left:0!important;right:0!important;bottom:0!important}.chat-panel.ask-anything-chat[data-v-1393b388]{max-width:100%!important;width:100vw!important;height:100vh!important;height:100dvh!important;margin:0!important;border-radius:0!important}.chat-container.ask-anything-style .chat-messages[data-v-1393b388]{padding:var(--space-lg)!important;max-width:100%!important;padding-top:calc(var(--space-lg) + 60px)!important;height:calc(100vh - 180px)!important;height:calc(100dvh - 180px)!important}.chat-input.ask-anything-input[data-v-1393b388]{padding:var(--space-lg)!important;border-radius:0!important;bottom:0!important;left:0!important;right:0!important;width:100vw!important;box-sizing:border-box!important}.chat-input.ask-anything-input .message-input[data-v-1393b388],.chat-input.ask-anything-input .email-input[data-v-1393b388]{max-width:100%}.ask-anything-field[data-v-1393b388]{padding:16px 20px!important;font-size:.9rem!important;border-radius:12px!important}.send-button.ask-anything-send[data-v-1393b388]{padding:16px!important;min-width:52px!important;height:52px!important;border-radius:12px!important}.welcome-title[data-v-1393b388]{font-size:2rem}.welcome-subtitle[data-v-1393b388]{font-size:1rem}.welcome-input-container[data-v-1393b388]{padding:var(--space-lg);gap:var(--space-md)}.welcome-email-input[data-v-1393b388],.welcome-message-field[data-v-1393b388]{padding:16px 20px;font-size:.9rem;border-radius:12px}.welcome-send-button[data-v-1393b388]{padding:16px;min-width:52px;height:52px;border-radius:12px}.welcome-avatar[data-v-1393b388]{width:64px;height:64px}.welcome-message-input[data-v-1393b388]{gap:var(--space-sm)}}@media (max-width: 480px){.welcome-title[data-v-1393b388]{font-size:1.75rem}.welcome-subtitle[data-v-1393b388]{font-size:.9rem}.welcome-input-container[data-v-1393b388]{padding:var(--space-md)}.welcome-email-input[data-v-1393b388],.welcome-message-field[data-v-1393b388]{padding:14px 18px;font-size:.85rem}.welcome-send-button[data-v-1393b388]{padding:14px;min-width:48px;height:48px}.chat-input.ask-anything-input[data-v-1393b388]{padding:var(--space-md)}.ask-anything-field[data-v-1393b388]{padding:14px 18px!important;font-size:.85rem!important}.send-button.ask-anything-send[data-v-1393b388]{padding:14px!important;min-width:48px!important;height:48px!important}}.ask-anything-top[data-v-1393b388]{padding:var(--space-md);display:flex;align-items:center;border-bottom:1px solid var(--border-color)}.ask-anything-header[data-v-1393b388]{display:flex;align-items:center;gap:var(--space-sm)}.ask-anything-subtitle[data-v-1393b388]{margin:0;font-size:var(--text-sm);color:var(--text-secondary)}.message-attachments[data-v-1393b388]{margin-top:8px;display:flex;flex-direction:column;gap:6px}.attachment-item[data-v-1393b388]{display:flex;align-items:center}.attachment-link[data-v-1393b388]{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#0000000d;border:1px solid rgba(0,0,0,.1);border-radius:8px;color:inherit;text-decoration:none;font-size:13px;transition:all .2s;max-width:100%;overflow:hidden}.attachment-link[data-v-1393b388]:hover{background:#0000001a;border-color:#0003}.attachment-link svg[data-v-1393b388]{flex-shrink:0;opacity:.7}.attachment-size[data-v-1393b388]{opacity:.7;font-size:11px;margin-left:4px}.user-message .attachment-link[data-v-1393b388]{background:#fff3;border-color:#ffffff4d;color:inherit}.user-message .attachment-link[data-v-1393b388]:hover{background:#ffffff4d;border-color:#ffffff80}.attachment-image-container[data-v-1393b388]{display:flex;flex-direction:column;gap:6px;max-width:300px;margin-top:8px}.attachment-image[data-v-1393b388]{width:100%;max-height:300px;border-radius:8px;object-fit:contain;border:1px solid rgba(0,0,0,.1);cursor:pointer;transition:all .2s;background:#00000005}.attachment-image[data-v-1393b388]:hover{border-color:#0003;box-shadow:0 2px 8px #0000001a}.user-message .attachment-image[data-v-1393b388]{border-color:#ffffff4d}.user-message .attachment-image[data-v-1393b388]:hover{border-color:#fff9;box-shadow:0 2px 8px #fff3}.attachment-image-info[data-v-1393b388]{display:flex;align-items:center;font-size:12px}.attachment-image-info .attachment-link[data-v-1393b388]{margin:0;padding:4px 8px;font-size:12px}.file-previews-widget[data-v-1393b388]{display:flex;flex-wrap:wrap;gap:10px;padding:12px;background:linear-gradient(135deg,#f3461108,#f3461103);border-radius:12px;margin-bottom:10px;border:1px dashed rgba(243,70,17,.2);animation:fadeIn-1393b388 .3s ease-in-out}@keyframes fadeIn-1393b388{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.file-preview-widget[data-v-1393b388]{display:flex;align-items:center;gap:12px;padding:12px 14px;background:linear-gradient(135deg,#fff,#fafbfc);border:2px solid #e5e7eb;border-radius:14px;font-size:13px;position:relative;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 2px 4px #0000000a,0 1px 2px #00000005;max-width:100%;overflow:hidden}.file-preview-widget[data-v-1393b388]:before{content:"";position:absolute;top:0;left:0;width:4px;height:100%;background:linear-gradient(180deg,#818cf8,#a78bfa,#c084fc);opacity:0;transition:opacity .3s ease}.file-preview-widget[data-v-1393b388]:hover{box-shadow:0 8px 16px #7c3aed1f,0 4px 8px #7c3aed14;transform:translateY(-2px);border-color:#c4b5fd}.file-preview-widget[data-v-1393b388]:hover:before{opacity:1}.file-preview-content-widget[data-v-1393b388]{display:flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:12px;background:linear-gradient(135deg,#f0f4ff,#e9d5ff);flex-shrink:0;overflow:hidden;position:relative;box-shadow:0 2px 8px #7c3aed1a,inset 0 1px 2px #ffffff80}.file-preview-content-widget[data-v-1393b388]:after{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(255,255,255,.4) 0%,transparent 100%);pointer-events:none}.file-preview-image-widget[data-v-1393b388]{width:48px;height:48px;object-fit:cover;border-radius:10px;position:relative;z-index:1;transition:transform .3s ease}.file-preview-widget:hover .file-preview-image-widget[data-v-1393b388]{transform:scale(1.05)}.file-preview-icon-widget[data-v-1393b388]{display:flex;align-items:center;justify-content:center;color:#7c3aed;position:relative;z-index:1;transition:transform .3s ease}.file-preview-widget:hover .file-preview-icon-widget[data-v-1393b388]{transform:scale(1.1) rotate(-5deg)}.file-preview-info-widget[data-v-1393b388]{display:flex;flex-direction:column;gap:4px;min-width:0;flex:1}.file-preview-name-widget[data-v-1393b388]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600;color:#1f2937;font-size:13px;line-height:1.4;letter-spacing:-.01em}.file-preview-size-widget[data-v-1393b388]{font-size:11px;color:#9ca3af;font-weight:500;letter-spacing:.01em}.file-preview-remove-widget[data-v-1393b388]{display:flex;align-items:center;justify-content:center;width:28px;height:28px;min-width:28px;background:linear-gradient(135deg,#fee2e2,#fecaca);border:1.5px solid #fca5a5;border-radius:8px;color:#dc2626;cursor:pointer;font-size:20px;font-weight:700;padding:0;flex-shrink:0;transition:all .2s cubic-bezier(.4,0,.2,1);line-height:1;box-shadow:0 2px 4px #dc26261a}.file-preview-remove-widget[data-v-1393b388]:hover{background:linear-gradient(135deg,#fca5a5,#f87171);border-color:#ef4444;color:#fff;transform:scale(1.1) rotate(90deg);box-shadow:0 4px 8px #dc262640}.file-preview-remove-widget[data-v-1393b388]:active{transform:scale(1) rotate(90deg);box-shadow:0 2px 4px #dc262633}.attach-button[data-v-1393b388]{display:flex;align-items:center;justify-content:center;width:44px;height:44px;min-width:44px;background:linear-gradient(135deg,#fff,#f8f9fa);border:2px solid #e5e7eb;color:#6b7280;cursor:pointer;border-radius:50%;transition:all .3s cubic-bezier(.4,0,.2,1);flex-shrink:0;position:relative;overflow:visible;box-shadow:0 2px 4px #0000000f,0 1px 2px #0000000a}.attach-button[data-v-1393b388]:before{content:"";position:absolute;top:-2px;left:-2px;right:-2px;bottom:-2px;background:linear-gradient(135deg,#f3461166,#d93a0c66,#ef444466);border-radius:50%;opacity:0;transition:opacity .3s ease;z-index:-1;filter:blur(8px)}.attach-button-glow[data-v-1393b388]{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:100%;height:100%;border-radius:50%;background:radial-gradient(circle,rgba(243,70,17,.2) 0%,transparent 70%);opacity:0;transition:all .4s ease;pointer-events:none;z-index:-1}.attach-button[data-v-1393b388]:hover:not(:disabled){background:linear-gradient(135deg,#fff,#fafbfc);border-color:var(--primary-color);color:var(--primary-color);transform:translateY(-3px) scale(1.05);box-shadow:0 8px 16px #f3461126,0 4px 8px #f346111a,0 0 0 4px #f346111a}.attach-button[data-v-1393b388]:hover:not(:disabled):before{opacity:1;animation:pulseGlow-1393b388 2s ease-in-out infinite}.attach-button:hover:not(:disabled) .attach-button-glow[data-v-1393b388]{opacity:1;width:150%;height:150%}.attach-button[data-v-1393b388]:active:not(:disabled){transform:translateY(-1px) scale(1);box-shadow:0 4px 8px #f3461133,0 0 0 3px #f3461126;transition:all .1s ease}.attach-button[data-v-1393b388]:disabled{opacity:.4;cursor:not-allowed;background:#f3f4f6;transform:none;box-shadow:none}.attach-button svg[data-v-1393b388]{position:relative;z-index:2;transition:all .3s cubic-bezier(.4,0,.2,1);filter:drop-shadow(0 1px 2px rgba(0,0,0,.1))}.attach-button:hover:not(:disabled) svg[data-v-1393b388]{transform:rotate(-15deg) scale(1.1);filter:drop-shadow(0 2px 4px rgba(243,70,17,.3))}.attach-button:active:not(:disabled) svg[data-v-1393b388]{transform:rotate(-15deg) scale(1.05)}@keyframes pulseGlow-1393b388{0%,to{opacity:.6;transform:scale(1)}50%{opacity:1;transform:scale(1.1)}}.message-input[data-v-1393b388]{display:flex;align-items:center;gap:8px;padding:8px 12px}.message-input input[data-v-1393b388]{flex:1;padding:var(--space-sm) var(--space-md);border:1.5px solid rgba(127,127,127,.3);border:1.5px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent);border-radius:var(--radius-lg);font-size:14px;transition:all .2s ease}.message-input input[data-v-1393b388]:focus{outline:none;border-color:var(--cm-accent, #C9F24E);box-shadow:0 0 0 3px color-mix(in srgb,var(--cm-accent, #C9F24E) 22%,transparent)}.message-input input[data-v-1393b388]:disabled{background-color:#0000000d;cursor:not-allowed}.send-button[data-v-1393b388]{display:flex;align-items:center;justify-content:center;padding:var(--space-sm);min-width:40px;height:40px;border:none;border-radius:var(--radius-lg);cursor:pointer;color:#fff;transition:all .2s ease;position:relative;overflow:hidden}.send-button[data-v-1393b388]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:linear-gradient(135deg,rgba(255,255,255,.2) 0%,transparent 100%);opacity:0;transition:opacity .2s ease}.send-button[data-v-1393b388]:hover:not(:disabled):before{opacity:1}.send-button[data-v-1393b388]:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 4px 12px #0003}.send-button[data-v-1393b388]:active:not(:disabled){transform:translateY(0);box-shadow:0 2px 6px #00000026}.send-button[data-v-1393b388]:disabled{opacity:.5;cursor:not-allowed}.send-button svg[data-v-1393b388]{position:relative;z-index:1}.upload-progress-widget[data-v-1393b388]{display:flex;align-items:center;gap:12px;padding:12px 16px;background:linear-gradient(135deg,#7c3aed14,#a855f70d);border-radius:12px;margin-bottom:10px;border:2px solid rgba(124,58,237,.2);animation:uploadPulse-1393b388 2s ease-in-out infinite;box-shadow:0 2px 8px #7c3aed1a;position:relative;overflow:hidden}.upload-progress-widget[data-v-1393b388]:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.3) 50%,transparent 100%);animation:shimmer-1393b388 2s infinite}@keyframes uploadPulse-1393b388{0%,to{opacity:1;transform:scale(1)}50%{opacity:.85;transform:scale(.995)}}@keyframes shimmer-1393b388{0%{left:-100%}to{left:100%}}.upload-spinner-widget[data-v-1393b388]{width:20px;height:20px;border:2.5px solid rgba(124,58,237,.2);border-top:2.5px solid #7c3aed;border-radius:50%;animation:modernSpin-1393b388 .8s linear infinite;box-shadow:0 0 8px #7c3aed4d}@keyframes modernSpin-1393b388{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.upload-text-widget[data-v-1393b388]{font-size:13px;color:#7c3aed;font-weight:600;letter-spacing:.01em;text-shadow:0 1px 2px rgba(124,58,237,.1)}.attachment-restriction-message[data-v-1393b388]{display:flex;align-items:center;justify-content:center;padding:10px 14px;background:linear-gradient(135deg,#3b82f614,#60a5fa0d);border-radius:10px;margin-bottom:10px;border:1px solid rgba(59,130,246,.2);box-shadow:0 2px 6px #3b82f614}.restriction-text[data-v-1393b388]{font-size:12px;color:#3b82f6;font-weight:500;text-align:center;letter-spacing:.01em;line-height:1.4}.preview-modal-overlay[data-v-1393b388]{position:fixed;top:0;left:0;right:0;bottom:0;background:#000c;display:flex;align-items:center;justify-content:center;z-index:9999;animation:fadeIn-1393b388 .2s ease-in}.preview-modal-content[data-v-1393b388]{position:relative;max-width:90%;max-height:90vh;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 20px 60px #0000004d;animation:slideUp-1393b388 .3s ease-out}.preview-modal-image-container[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:20px;max-width:100%;max-height:100%}.preview-modal-image[data-v-1393b388]{max-width:100%;max-height:70vh;object-fit:contain;border-radius:8px}.preview-modal-filename[data-v-1393b388]{font-size:14px;color:#666;text-align:center;max-width:100%;word-break:break-word;padding:0 12px}.preview-modal-close[data-v-1393b388]{position:absolute;top:12px;right:12px;background:#0009;border:none;color:#fff;width:36px;height:36px;border-radius:50%;font-size:24px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;z-index:10000}.preview-modal-close[data-v-1393b388]:hover{background:#000c;transform:scale(1.1)}@keyframes fadeIn-1393b388{0%{opacity:0}to{opacity:1}}@keyframes slideUp-1393b388{0%{transform:translateY(20px);opacity:0}to{transform:translateY(0);opacity:1}}.widget-unavailable-overlay[data-v-1393b388]{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:linear-gradient(135deg,#f8fafc,#f1f5f9);border-radius:var(--radius-lg);overflow:hidden;position:relative}.cm-error-close[data-v-1393b388]{position:fixed;top:12px;right:12px;z-index:10;width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:none;border-radius:50%;background:#78788c1f;color:#4b5563;font-size:22px;line-height:1;cursor:pointer;transition:background .15s ease}.cm-error-close[data-v-1393b388]:hover{background:#78788c38}.widget-unavailable-overlay[data-v-1393b388]:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background:radial-gradient(circle at 20% 30%,rgba(99,102,241,.08) 0%,transparent 50%),radial-gradient(circle at 80% 70%,rgba(168,85,247,.06) 0%,transparent 50%);pointer-events:none}.widget-unavailable-card[data-v-1393b388]{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:32px 24px;text-align:center;position:relative;z-index:1;max-width:90%}.widget-unavailable-icon-wrapper[data-v-1393b388]{display:flex;align-items:center;justify-content:center;width:72px;height:72px;background:linear-gradient(135deg,#e0e7ff,#c7d2fe);border-radius:20px;margin-bottom:20px;box-shadow:0 8px 24px #6366f126}.widget-unavailable-icon[data-v-1393b388]{width:36px;height:36px;color:#6366f1}.widget-unavailable-title[data-v-1393b388]{font-size:20px;font-weight:600;color:#1e293b;margin:0 0 12px;letter-spacing:-.01em}.widget-unavailable-message[data-v-1393b388]{font-size:14px;line-height:1.6;color:#64748b;margin:0 0 24px;max-width:280px}.widget-unavailable-footer[data-v-1393b388]{display:flex;align-items:center;justify-content:center;gap:6px;font-size:12px;color:#94a3b8;opacity:.8}.chattermate-logo-small[data-v-1393b388]{opacity:.6}@media (max-width: 768px){.widget-unavailable-card[data-v-1393b388]{padding:24px 20px}.widget-unavailable-icon-wrapper[data-v-1393b388]{width:64px;height:64px;border-radius:16px}.widget-unavailable-icon[data-v-1393b388]{width:32px;height:32px}.widget-unavailable-title[data-v-1393b388]{font-size:18px}.widget-unavailable-message[data-v-1393b388]{font-size:13px;max-width:260px}}:root{--background-base: #ffffff;--background-soft: #f8f9fa;--border-color: #e5e7eb;--text-color: #111827;--text-muted: #6b7280;--error-color: #ef4444;--success-color: #22c55e;--space-xs: 8px;--space-sm: 12px;--space-md: 16px;--space-lg: 24px;--space-xl: 32px;--text-xs: 12px;--text-sm: 14px;--text-md: 16px;--text-lg: 18px;--text-base: 16px;--radius-sm: 6px;--radius-lg: 12px;--radius-full: 9999px;--text-primary: #111827;--text-secondary: #6b7280;--primary-color: #f34611;--primary-dark: #d93d0f}.form-fields{display:flex;flex-direction:column;gap:var(--space-lg)}.form-field{display:flex;flex-direction:column;gap:var(--space-sm);position:relative}.field-label{font-size:var(--text-sm);font-weight:600;color:var(--text-primary);display:flex;align-items:center;gap:var(--space-xs);margin-bottom:var(--space-xs);letter-spacing:.02em}.required-indicator{color:#ff4757;font-weight:700;font-size:16px}.form-input,.form-textarea,.form-select{box-sizing:border-box;padding:16px 20px;border:2px solid transparent;background:#f8f9facc;border-radius:16px;font-size:var(--text-base);font-weight:500;transition:all .3s cubic-bezier(.4,0,.2,1);font-family:inherit;position:relative;box-shadow:0 1px 3px #0000001a,inset 0 1px 2px #0000001a}.form-input::placeholder,.form-textarea::placeholder{color:#9ca3af;font-weight:400;transition:all .3s ease}.form-input:hover,.form-textarea:hover,.form-select:hover{background:#f8f9fa;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f;transform:translateY(-1px)}.form-input:focus,.form-textarea:focus,.form-select:focus{outline:none;border-color:var(--primary-color);background:#fff;box-shadow:0 0 0 4px #f346111a,0 8px 16px -4px #0000001a;transform:translateY(-2px)}.form-input:focus::placeholder,.form-textarea:focus::placeholder{color:transparent;transform:translateY(-2px)}.form-input.error,.form-textarea.error,.form-select.error{border-color:#ff4757;background:#ff47570d;animation:shake .5s ease-in-out}@keyframes shake{0%,to{transform:translate(0)}25%{transform:translate(-5px)}75%{transform:translate(5px)}}.form-input.error:focus,.form-textarea.error:focus,.form-select.error:focus{box-shadow:0 0 0 4px #ff475733,0 8px 16px -4px #ff47571a}.form-input:disabled,.form-textarea:disabled,.form-select:disabled{background-color:#0000000d;cursor:not-allowed;opacity:.6;transform:none}.form-textarea{resize:vertical;min-height:100px;line-height:1.6}.checkbox-field,.radio-field{display:flex;flex-direction:column;gap:var(--space-sm);padding:var(--space-sm);background:#f8f9fa80;border-radius:12px;border:1px solid rgba(0,0,0,.08)}.checkbox-field{flex-direction:row;align-items:center;padding:var(--space-md)}.radio-option{display:flex;align-items:center;gap:var(--space-sm);padding:var(--space-sm) var(--space-md);border-radius:8px;transition:all .2s ease;cursor:pointer}.radio-option:hover{background:#f346110d}.form-checkbox,.form-radio{width:20px;height:20px;accent-color:var(--primary-color);cursor:pointer;transition:all .2s ease}.form-checkbox:checked,.form-radio:checked{transform:scale(1.1)}.checkbox-label,.radio-label{font-size:var(--text-base);color:var(--text-primary);cursor:pointer;-webkit-user-select:none;user-select:none;font-weight:500;transition:color .2s ease}.radio-option:hover .radio-label{color:var(--primary-color)}.field-error{font-size:var(--text-sm);color:#ff4757;margin-top:var(--space-xs);display:flex;align-items:flex-start;gap:var(--space-sm);padding:var(--space-sm);background:#ff47571a;border-radius:8px;border-left:3px solid #ff4757;font-weight:500;line-height:1.45}.field-error:before{content:"⚠";font-size:15px;line-height:1.45;flex-shrink:0;animation:pulse 2s infinite}@keyframes pulse{0%,to{opacity:1}50%{opacity:.5}}.form-actions{margin-top:var(--space-xl);display:flex;justify-content:center;gap:var(--space-md)}.form-submit-button{padding:16px 32px;background:linear-gradient(135deg,var(--accent-solid) 0%,#ff6b47 100%);color:#fff;border:none;border-radius:16px;font-size:var(--text-base);font-weight:700;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);min-width:140px;box-shadow:0 4px 14px #f346114d,0 2px 4px #0000001a;position:relative;overflow:hidden;letter-spacing:.02em}.form-submit-button:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.2),transparent);transition:left .5s ease}.form-submit-button:hover:not(:disabled){transform:translateY(-3px);box-shadow:0 8px 25px #f3461166,0 4px 8px #00000026;background:linear-gradient(135deg,#ff6b47 0%,var(--accent-solid) 100%)}.form-submit-button:hover:not(:disabled):before{left:100%}.form-submit-button:active:not(:disabled){transform:translateY(-1px);box-shadow:0 4px 14px #f346114d,0 2px 4px #0000001a}.form-submit-button:disabled{opacity:.6;cursor:not-allowed;transform:none;background:#9ca3af;box-shadow:none}.form-submit-button:disabled:before{display:none}.landing-page-fullscreen{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#fff;border-radius:var(--radius-lg);position:relative;overflow:hidden}.landing-page-fullscreen .landing-page-content{display:flex;flex-direction:column;justify-content:space-between;height:100%;padding:48px;box-sizing:border-box}.landing-page-fullscreen .landing-page-header{display:flex;flex-direction:column;gap:24px;text-align:left;align-items:flex-start}.landing-page-fullscreen .landing-page-heading{font-size:30px;font-weight:700;margin:0;line-height:1.2;color:#1f2937;letter-spacing:-.02em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.landing-page-fullscreen .landing-page-text{font-size:16px;line-height:1.6;margin:0;color:#6b7280;font-weight:400;max-width:400px}.landing-page-fullscreen .landing-page-actions{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-md)}.landing-page-fullscreen .landing-page-button{background:none;border:none;color:#dc2626;font-size:16px;font-weight:600;cursor:pointer;padding:0;text-decoration:none;display:inline-flex;align-items:center;gap:var(--space-xs);transition:all .2s ease}.landing-page-fullscreen .landing-page-button:hover{color:#b91c1c;text-decoration:underline}.landing-page-fullscreen .landing-page-button:after{content:"→";font-size:16px;transition:transform .2s ease}.landing-page-fullscreen .landing-page-button:hover:after{transform:translate(2px)}.powered-by-landing{position:absolute;bottom:0;left:0;right:0;text-align:center;font-size:.75rem;opacity:.6;color:#9ca3af;padding:12px 16px;background:transparent;border:none;-webkit-backdrop-filter:none;backdrop-filter:none;display:flex;align-items:center;justify-content:center;gap:6px}.chattermate-logo{flex-shrink:0;opacity:.8}.form-fullscreen{width:100%;height:100%;background:#fff;border-radius:var(--radius-lg);position:relative;overflow:hidden;display:flex;align-items:flex-start;justify-content:center;overflow-y:auto}.form-fullscreen-content{width:100%;max-width:600px;padding:48px 32px 80px;display:flex;flex-direction:column;gap:32px;min-height:100%;box-sizing:border-box}.form-fullscreen .form-header{text-align:left;margin-bottom:24px}.form-fullscreen .form-title{font-size:28px;font-weight:700;color:var(--text-primary);margin:0 0 16px;line-height:1.3}.form-fullscreen .form-description{font-size:16px;color:var(--text-secondary);margin:0;line-height:1.5}.form-fullscreen .form-fields{display:flex;flex-direction:column;gap:24px}.form-fullscreen .form-field{display:flex;flex-direction:column;gap:8px}.form-fullscreen .field-label{font-size:14px;font-weight:600;color:var(--text-primary);margin:0}.form-fullscreen .required-indicator{color:#dc2626;margin-left:4px}.form-fullscreen .form-input,.form-fullscreen .form-textarea,.form-fullscreen .form-select{padding:12px 16px;border:2px solid var(--border-color);border-radius:8px;font-size:16px;color:var(--text-primary);background:var(--background-base);transition:all .2s ease}.form-fullscreen .form-input:focus,.form-fullscreen .form-textarea:focus,.form-fullscreen .form-select:focus{outline:none;border-color:var(--primary-color);box-shadow:0 0 0 3px #f346111a}.form-fullscreen .form-input.error,.form-fullscreen .form-textarea.error,.form-fullscreen .form-select.error{border-color:#dc2626;box-shadow:0 0 0 3px #dc26261a}.form-fullscreen .form-textarea{resize:vertical;min-height:100px}.form-fullscreen .checkbox-field,.form-fullscreen .radio-field{display:flex;align-items:center;gap:12px;cursor:pointer}.form-fullscreen .form-checkbox,.form-fullscreen .form-radio{width:20px;height:20px;cursor:pointer}.form-fullscreen .checkbox-label,.form-fullscreen .radio-label{font-size:16px;color:var(--text-primary);cursor:pointer}.form-fullscreen .radio-group{display:flex;flex-direction:column;gap:12px}.form-fullscreen .field-error{color:#dc2626;font-size:14px;margin-top:4px}.form-fullscreen .form-actions{display:flex;justify-content:center;margin-top:24px}.form-fullscreen .submit-form-button{padding:16px 32px;background:var(--accent-solid);color:#fff;border:none;border-radius:8px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s ease;min-width:160px;display:flex;align-items:center;justify-content:center;gap:8px}.form-fullscreen .submit-form-button:hover:not(:disabled){background:var(--primary-dark);transform:translateY(-1px)}.form-fullscreen .submit-form-button:disabled{opacity:.7;cursor:not-allowed;transform:none}.form-fullscreen .loading-spinner-inline{display:flex;gap:4px}.form-fullscreen .loading-spinner-inline .dot{width:6px;height:6px;border-radius:50%;background:currentColor;animation:pulse 1.5s ease-in-out infinite}.form-fullscreen .loading-spinner-inline .dot:nth-child(2){animation-delay:.3s}.form-fullscreen .loading-spinner-inline .dot:nth-child(3){animation-delay:.6s}@media (max-width: 768px){.form-fields{gap:var(--space-md)}.form-input,.form-textarea,.form-select{padding:14px 16px;border-radius:12px;font-size:var(--text-sm)}.form-submit-button{padding:14px 28px;font-size:var(--text-sm);min-width:120px;border-radius:12px}.checkbox-field,.radio-field{padding:var(--space-sm)}.radio-option{padding:var(--space-xs) var(--space-sm)}.form-checkbox,.form-radio{width:18px;height:18px}.powered-by-landing{font-size:.7rem;padding:8px 12px}.form-fullscreen-content{padding:24px 20px;gap:24px}.form-fullscreen .form-title{font-size:24px}.form-fullscreen .form-description{font-size:15px}.form-fullscreen .form-fields{gap:20px}.form-fullscreen .submit-form-button{width:100%;padding:14px 24px}}.widget-container{position:fixed;bottom:90px;right:20px;z-index:999998;width:400px;height:580px;max-width:90vw;max-height:80vh;background:#fff;border-radius:16px;border:none;box-shadow:none;position:relative;overflow:hidden;font-family:Inter,-apple-system,BlinkMacSystemFont,sans-serif}.widget-container:after{content:"";position:absolute;top:0;left:0;right:0;bottom:0;pointer-events:none;border-radius:16px;border:1px solid #e5e7eb}.widget-container .widget-root{width:100%!important;height:100%!important;border-radius:16px!important;box-shadow:none!important}.widget-container .widget-builder{width:100%!important;height:100%!important;font-family:Inter,-apple-system,BlinkMacSystemFont,sans-serif!important;background:#fff!important;border-radius:16px!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}.widget-container .chat-widget{height:100%!important;display:flex!important;flex-direction:column!important}.widget-container .chat-container{width:100%!important;height:580px!important;display:flex!important;flex-direction:column!important;background:transparent!important;overflow:hidden!important;position:relative!important;border-radius:var(--radius-lg)!important;font-family:Inter,-apple-system,BlinkMacSystemFont,sans-serif!important}.widget-container .chat-panel{background:var(--background-base)!important;display:flex!important;flex-direction:column!important;height:100%!important;transition:all .3s ease!important;border-radius:0!important}.widget-container .chat-input{padding:var(--space-md)!important;border-top:1px solid color-mix(in srgb,currentColor 12%,transparent)!important;display:flex!important;flex-direction:column!important;gap:var(--space-sm)!important}.widget-container .message-input{display:flex!important;gap:var(--space-sm)!important}.widget-container .message-input input{flex:1!important;padding:var(--space-sm) var(--space-md)!important;border:1.5px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent)!important;border-radius:var(--radius-lg)!important}.widget-container .send-button{display:flex!important;align-items:center!important;justify-content:center!important;padding:var(--space-sm)!important;min-width:40px!important;height:40px!important;border:none!important;border-radius:var(--radius-lg)!important;cursor:pointer!important;color:#fff!important}.widget-container .chat-messages{flex:1!important;overflow-y:auto!important;padding:var(--space-md)!important;display:flex!important;flex-direction:column!important;gap:var(--space-md)!important;-webkit-overflow-scrolling:touch!important;scroll-behavior:smooth!important;margin-top:var(--space-sm)!important}.widget-container .message{display:flex!important;gap:var(--space-sm)!important;max-width:95%!important;align-items:flex-start!important;margin-bottom:var(--space-md)!important}.widget-container .message-bubble{padding:12px 25px!important;border-radius:20px!important;line-height:1.4!important;box-shadow:0 2px 4px #0000000d!important;max-width:95%!important}.widget-container .user-message{align-self:flex-end!important;flex-direction:row-reverse!important}.widget-container .user-message .message-bubble{border-bottom-right-radius:4px!important}.widget-container .agent-message .message-bubble,.widget-container .bot .message-bubble{border-bottom-left-radius:4px!important;background-color:#f5f5f5!important}.widget-container .message.system-message{align-self:center!important;max-width:100%!important;margin:var(--space-sm) 0!important}.widget-container .system-message .message-bubble{background:#0000000d!important;color:var(--text-muted)!important;font-size:.85em!important;padding:var(--space-xs) var(--space-md)!important;border-radius:var(--radius-lg)!important;text-align:center!important}.widget-container .message.form-message{align-self:center!important;width:100%!important;max-width:520px!important;margin:var(--space-md) 0!important}.widget-container .form-message .message-bubble{background:linear-gradient(145deg,#fff,#f8f9fa)!important;padding:var(--space-xl)!important;border-radius:24px!important;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a,0 0 0 1px #ffffff0d!important;border:1px solid rgba(0,0,0,.06)!important;width:100%!important;max-width:none!important}.widget-container .welcome-message-section{width:100%!important;height:100%!important;display:flex!important;flex-direction:column!important;align-items:center!important;justify-content:space-between!important;background:var(--background-base)!important;border-radius:var(--radius-lg)!important;position:relative!important;overflow:hidden!important;padding:var(--space-xl)!important;box-sizing:border-box!important}.widget-container .welcome-content{display:flex!important;flex-direction:column!important;align-items:center!important;justify-content:center!important;width:100%!important;max-width:600px!important;text-align:center!important;flex:1!important}.widget-container .welcome-header{display:flex!important;flex-direction:column!important;align-items:center!important;gap:var(--space-lg)!important}.widget-container .welcome-title{font-size:1.5rem!important;font-weight:700!important;color:#1f2937!important;margin:0!important;line-height:1.2!important;letter-spacing:-.02em!important}.widget-container .welcome-subtitle{font-size:1rem!important;color:var(--text-secondary)!important;margin:0!important;line-height:1.6!important;max-width:500px!important;font-weight:400!important}.widget-container .welcome-input-container{display:flex!important;flex-direction:column!important;align-items:center!important;justify-content:center!important;gap:var(--space-lg)!important;max-width:600px!important;margin:0 auto!important;width:100%!important;padding:0!important}.widget-container .welcome-message-input{display:flex!important;gap:var(--space-md)!important;width:100%!important;align-items:center!important}.widget-container .welcome-message-field{flex:1!important;padding:16px 20px!important;border:2px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent)!important;border-radius:16px!important;font-size:1rem!important;font-weight:500!important;background:var(--background-base)!important;color:var(--text-primary)!important;transition:all .3s ease!important;box-shadow:0 4px 6px #0000000d!important}.widget-container .welcome-send-button{display:flex!important;align-items:center!important;justify-content:center!important;padding:16px!important;min-width:52px!important;height:52px!important;border:none!important;border-radius:16px!important;cursor:pointer!important;color:#fff!important;transition:all .3s ease!important;box-shadow:0 4px 14px #f346114d!important}.widget-container .welcome-orb{width:120px!important;height:120px!important;border-radius:50%!important;flex-shrink:0!important}.widget-container .welcome-message-section.aurora .welcome-avatar{width:120px!important;height:120px!important;border:none!important;box-shadow:0 8px 40px #9d8cff59!important}.widget-container .welcome-message-section.aurora .welcome-title{color:#fff!important}.widget-container .welcome-message-section.aurora .welcome-subtitle{color:#fff9!important}.widget-container .welcome-message-section.aurora .welcome-message-input{background:#ffffff0d!important;border:1px solid rgba(255,255,255,.1)!important;border-radius:999px!important;padding:6px 6px 6px 8px!important}.widget-container .welcome-message-section.aurora .welcome-message-field{background:transparent!important;border:none!important;box-shadow:none!important;color:#fff!important;border-radius:999px!important}.widget-container .welcome-message-section.aurora .welcome-message-field::placeholder{color:#ffffff73!important}.widget-container .welcome-message-section.aurora .welcome-send-button.aurora-send{border-radius:50%!important;min-width:48px!important;width:48px!important;height:48px!important;padding:0!important}.widget-container .message.product-message .message-bubble{padding:0!important;background:none!important;border:none!important;box-shadow:none!important;width:100%!important;max-width:none!important}.widget-container .product-message-container{width:100%!important;margin:var(--space-sm) 0!important;padding:0 var(--space-xs)!important}.widget-container .products-carousel{margin:var(--space-xs) 0!important;width:100%!important;padding:var(--space-xs)!important;background:#00000005!important;border-radius:20px!important}.widget-container .carousel-title{font-size:var(--text-base)!important;font-weight:600!important;margin-bottom:var(--space-sm)!important;color:var(--text-primary)!important;padding:0 var(--space-xs)!important}.widget-container .carousel-items{display:flex!important;flex-direction:row!important;gap:var(--space-sm)!important;margin-top:var(--space-xs)!important;overflow-x:auto!important;padding:var(--space-xs)!important;padding-bottom:var(--space-md)!important;scroll-snap-type:x mandatory!important;-webkit-overflow-scrolling:touch!important}.widget-container .product-card-compact{background-color:#fff!important;border:1px solid rgba(0,0,0,.08)!important;border-radius:20px!important;box-shadow:0 2px 8px #0000000f,0 1px 2px #0000000a!important;overflow:hidden!important;width:180px!important;flex-shrink:0!important;transition:all .2s ease!important}.widget-container .product-card-compact:hover{transform:translateY(-2px)!important;box-shadow:0 4px 12px #00000014,0 2px 4px #0000000f!important}.widget-container .message.rating-message{align-self:center!important;width:100%!important;max-width:500px!important;margin:var(--space-sm) 0!important}.widget-container .rating-message .message-bubble{background-color:#fff!important;padding:var(--space-md) var(--space-md)!important;border-radius:12px!important;box-shadow:0 4px 8px #00000014!important;border:1px solid var(--border-color)!important}.widget-container .rating-content{display:flex!important;flex-direction:column!important;align-items:center!important;width:100%!important;padding:0!important}.widget-container .star-rating{display:flex!important;gap:.75rem!important;justify-content:center!important;margin:0 0 20px!important}.widget-container .star-button{background:none!important;border:none!important;cursor:pointer!important;font-size:28px!important;color:#d1d5db!important;transition:all .2s ease!important;padding:0 4px!important;line-height:1!important}.widget-container .star-button.selected.warning{color:var(--error-color)!important}.widget-container .star-button.selected.success{color:var(--success-color)!important}.widget-container .user-input-content{width:100%!important;display:flex!important;flex-direction:column!important;gap:var(--space-md)!important}.widget-container .user-input-form{display:flex!important;flex-direction:column!important;gap:var(--space-sm)!important;padding:var(--space-md)!important;background:var(--background-soft)!important;border-radius:var(--radius-md)!important;border:1px solid var(--border-color)!important}.widget-container .user-input-textarea{width:100%!important;padding:var(--space-sm) var(--space-md)!important;border:1px solid var(--border-color)!important;border-radius:var(--radius-md)!important;background:var(--background-base)!important;color:var(--text-primary)!important;font-size:var(--text-base)!important;font-family:inherit!important;resize:vertical!important;min-height:80px!important}.widget-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#6b7280;gap:1rem}.widget-loading i{font-size:2rem;color:#6366f1}.widget-loading p{margin:0;font-size:.875rem}.widget-container .chat-container.ask-anything-style{max-width:800px!important;min-width:400px!important;margin:0 auto!important}.widget-container .chat-container.ask-anything-style .chat-messages{max-width:600px!important;margin-left:auto!important;margin-right:auto!important;padding:var(--space-xl)!important}.widget-container .chat-container.ask-anything-style .chat-messages .message{max-width:85%!important}.widget-container .chat-container.ask-anything-style .message.user-message{align-self:flex-end!important;margin-left:auto!important;margin-right:0!important;flex-direction:row-reverse!important}.widget-container .chat-container.ask-anything-style .message.agent-message{align-self:flex-start!important;margin-left:0!important;margin-right:auto!important;flex-direction:row!important}.widget-container .chat-input.ask-anything-input{padding:var(--space-xl)!important;display:flex!important;flex-direction:column!important;align-items:center!important}.widget-container .chat-input.ask-anything-input .message-input{max-width:600px!important;margin:0 auto!important;gap:var(--space-md)!important;width:100%!important}.widget-container .ask-anything-field{padding:18px 24px!important;border:2px solid color-mix(in srgb,var(--cm-accent, #C9F24E) 35%,transparent)!important;border-radius:16px!important;font-size:1rem!important;font-weight:500!important;background:var(--background-base)!important;color:var(--text-primary)!important;transition:all .3s ease!important;box-shadow:0 4px 6px #0000000d!important}.widget-container .send-button.ask-anything-send{padding:18px!important;min-width:56px!important;height:56px!important;border-radius:16px!important;box-shadow:0 4px 14px #f346114d!important}.widget-container .typing-indicator{display:flex!important;gap:4px!important;padding:12px 16px!important;margin-top:var(--space-md)!important;align-self:flex-start!important}.widget-container .dot{width:8px!important;height:8px!important;background:currentColor!important;border-radius:50%!important;opacity:.6!important;animation:bounce 1.4s infinite ease-in-out!important}.widget-container .chat-header{padding:var(--space-md)!important;display:flex!important;justify-content:flex-start!important;align-items:center!important}.widget-container .header-content{display:flex!important;align-items:center!important;gap:var(--space-sm)!important}.widget-container .header-avatar{width:32px!important;height:32px!important;border-radius:50%!important;object-fit:cover!important}.widget-container .header-info h3{margin:0!important;font-size:var(--text-md)!important}.widget-container .status{display:flex!important;align-items:center!important;gap:var(--space-xs)!important;font-size:var(--text-sm)!important}.widget-container .status-indicator{width:8px!important;height:8px!important;border-radius:50%!important;background:var(--success-color)!important}.widget-container .powered-by,.widget-container .powered-by-welcome,.widget-container .powered-by-landing{text-align:center!important;font-size:.75rem!important;opacity:.6!important;color:var(--text-muted)!important;padding:var(--space-md)!important;display:flex!important;align-items:center!important;justify-content:center!important;gap:6px!important}@media (max-width: 768px){.widget-container{width:100vw!important;height:100vh!important;height:100dvh!important;max-width:100vw!important;max-height:100vh!important;max-height:100dvh!important;top:0!important;left:0!important;bottom:0!important;right:0!important;border-radius:0!important;position:fixed!important}.widget-container .chat-container{height:calc(100vh - 60px)!important;height:calc(100dvh - 60px)!important;width:100vw!important;border-radius:0!important}.widget-container .widget-root,.widget-container .widget-builder{border-radius:0!important}}@media (max-width: 480px){.widget-container .chat-container.ask-anything-style{min-width:100%!important;max-width:100%!important}.widget-container .chat-container.ask-anything-style .chat-messages{padding:var(--space-lg)!important;max-width:100%!important}.widget-container .welcome-title{font-size:1.25rem!important}.widget-container .welcome-subtitle{font-size:.9rem!important}} diff --git a/backend/assets/widget.js b/backend/assets/widget.js new file mode 100644 index 0000000..da1378b --- /dev/null +++ b/backend/assets/widget.js @@ -0,0 +1,12778 @@ +var lf = Object.defineProperty; +var cf = (e, t, n) => t in e ? lf(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; +var et = (e, t, n) => cf(e, typeof t != "symbol" ? t + "" : t, n); +/** +* @vue/shared v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/ +/*! #__NO_SIDE_EFFECTS__ */ +// @__NO_SIDE_EFFECTS__ +function Jo(e) { + const t = /* @__PURE__ */ Object.create(null); + for (const n of e.split(",")) t[n] = 1; + return (n) => n in t; +} +const tt = {}, Ms = [], _n = () => { +}, uf = () => !1, Ni = (e) => e.charCodeAt(0) === 111 && e.charCodeAt(1) === 110 && // uppercase letter +(e.charCodeAt(2) > 122 || e.charCodeAt(2) < 97), Qo = (e) => e.startsWith("onUpdate:"), Pt = Object.assign, ea = (e, t) => { + const n = e.indexOf(t); + n > -1 && e.splice(n, 1); +}, ff = Object.prototype.hasOwnProperty, je = (e, t) => ff.call(e, t), be = Array.isArray, Fs = (e) => Pi(e) === "[object Map]", fc = (e) => Pi(e) === "[object Set]", ke = (e) => typeof e == "function", gt = (e) => typeof e == "string", ns = (e) => typeof e == "symbol", ut = (e) => e !== null && typeof e == "object", hc = (e) => (ut(e) || ke(e)) && ke(e.then) && ke(e.catch), dc = Object.prototype.toString, Pi = (e) => dc.call(e), hf = (e) => Pi(e).slice(8, -1), pc = (e) => Pi(e) === "[object Object]", ta = (e) => gt(e) && e !== "NaN" && e[0] !== "-" && "" + parseInt(e, 10) === e, kr = /* @__PURE__ */ Jo( + // the leading comma is intentional so empty string "" is also included + ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" +), Mi = (e) => { + const t = /* @__PURE__ */ Object.create(null); + return (n) => t[n] || (t[n] = e(n)); +}, df = /-(\w)/g, Qn = Mi( + (e) => e.replace(df, (t, n) => n ? n.toUpperCase() : "") +), pf = /\B([A-Z])/g, ss = Mi( + (e) => e.replace(pf, "-$1").toLowerCase() +), mc = Mi((e) => e.charAt(0).toUpperCase() + e.slice(1)), io = Mi( + (e) => e ? `on${mc(e)}` : "" +), Zn = (e, t) => !Object.is(e, t), ri = (e, ...t) => { + for (let n = 0; n < e.length; n++) + e[n](...t); +}, Io = (e, t, n, s = !1) => { + Object.defineProperty(e, t, { + configurable: !0, + enumerable: !1, + writable: s, + value: n + }); +}, Lo = (e) => { + const t = parseFloat(e); + return isNaN(t) ? e : t; +}; +let Ja; +const Fi = () => Ja || (Ja = typeof globalThis < "u" ? globalThis : typeof self < "u" ? self : typeof window < "u" ? window : typeof global < "u" ? global : {}); +function Se(e) { + if (be(e)) { + const t = {}; + for (let n = 0; n < e.length; n++) { + const s = e[n], r = gt(s) ? yf(s) : Se(s); + if (r) + for (const i in r) + t[i] = r[i]; + } + return t; + } else if (gt(e) || ut(e)) + return e; +} +const mf = /;(?![^(]*\))/g, gf = /:([^]+)/, _f = /\/\*[^]*?\*\//g; +function yf(e) { + const t = {}; + return e.replace(_f, "").split(mf).forEach((n) => { + if (n) { + const s = n.split(gf); + s.length > 1 && (t[s[0].trim()] = s[1].trim()); + } + }), t; +} +function Ue(e) { + let t = ""; + if (gt(e)) + t = e; + else if (be(e)) + for (let n = 0; n < e.length; n++) { + const s = Ue(e[n]); + s && (t += s + " "); + } + else if (ut(e)) + for (const n in e) + e[n] && (t += n + " "); + return t.trim(); +} +const bf = "itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly", vf = /* @__PURE__ */ Jo(bf); +function gc(e) { + return !!e || e === ""; +} +const _c = (e) => !!(e && e.__v_isRef === !0), se = (e) => gt(e) ? e : e == null ? "" : be(e) || ut(e) && (e.toString === dc || !ke(e.toString)) ? _c(e) ? se(e.value) : JSON.stringify(e, yc, 2) : String(e), yc = (e, t) => _c(t) ? yc(e, t.value) : Fs(t) ? { + [`Map(${t.size})`]: [...t.entries()].reduce( + (n, [s, r], i) => (n[oo(s, i) + " =>"] = r, n), + {} + ) +} : fc(t) ? { + [`Set(${t.size})`]: [...t.values()].map((n) => oo(n)) +} : ns(t) ? oo(t) : ut(t) && !be(t) && !pc(t) ? String(t) : t, oo = (e, t = "") => { + var n; + return ( + // Symbol.description in es2019+ so we need to cast here to pass + // the lib: es2016 check + ns(e) ? `Symbol(${(n = e.description) != null ? n : t})` : e + ); +}; +/** +* @vue/reactivity v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/ +let zt; +class wf { + constructor(t = !1) { + this.detached = t, this._active = !0, this._on = 0, this.effects = [], this.cleanups = [], this._isPaused = !1, this.parent = zt, !t && zt && (this.index = (zt.scopes || (zt.scopes = [])).push( + this + ) - 1); + } + get active() { + return this._active; + } + pause() { + if (this._active) { + this._isPaused = !0; + let t, n; + if (this.scopes) + for (t = 0, n = this.scopes.length; t < n; t++) + this.scopes[t].pause(); + for (t = 0, n = this.effects.length; t < n; t++) + this.effects[t].pause(); + } + } + /** + * Resumes the effect scope, including all child scopes and effects. + */ + resume() { + if (this._active && this._isPaused) { + this._isPaused = !1; + let t, n; + if (this.scopes) + for (t = 0, n = this.scopes.length; t < n; t++) + this.scopes[t].resume(); + for (t = 0, n = this.effects.length; t < n; t++) + this.effects[t].resume(); + } + } + run(t) { + if (this._active) { + const n = zt; + try { + return zt = this, t(); + } finally { + zt = n; + } + } + } + /** + * This should only be called on non-detached scopes + * @internal + */ + on() { + ++this._on === 1 && (this.prevScope = zt, zt = this); + } + /** + * This should only be called on non-detached scopes + * @internal + */ + off() { + this._on > 0 && --this._on === 0 && (zt = this.prevScope, this.prevScope = void 0); + } + stop(t) { + if (this._active) { + this._active = !1; + let n, s; + for (n = 0, s = this.effects.length; n < s; n++) + this.effects[n].stop(); + for (this.effects.length = 0, n = 0, s = this.cleanups.length; n < s; n++) + this.cleanups[n](); + if (this.cleanups.length = 0, this.scopes) { + for (n = 0, s = this.scopes.length; n < s; n++) + this.scopes[n].stop(!0); + this.scopes.length = 0; + } + if (!this.detached && this.parent && !t) { + const r = this.parent.scopes.pop(); + r && r !== this && (this.parent.scopes[this.index] = r, r.index = this.index); + } + this.parent = void 0; + } + } +} +function kf() { + return zt; +} +let ot; +const ao = /* @__PURE__ */ new WeakSet(); +class bc { + constructor(t) { + this.fn = t, this.deps = void 0, this.depsTail = void 0, this.flags = 5, this.next = void 0, this.cleanup = void 0, this.scheduler = void 0, zt && zt.active && zt.effects.push(this); + } + pause() { + this.flags |= 64; + } + resume() { + this.flags & 64 && (this.flags &= -65, ao.has(this) && (ao.delete(this), this.trigger())); + } + /** + * @internal + */ + notify() { + this.flags & 2 && !(this.flags & 32) || this.flags & 8 || wc(this); + } + run() { + if (!(this.flags & 1)) + return this.fn(); + this.flags |= 2, Qa(this), kc(this); + const t = ot, n = ln; + ot = this, ln = !0; + try { + return this.fn(); + } finally { + Tc(this), ot = t, ln = n, this.flags &= -3; + } + } + stop() { + if (this.flags & 1) { + for (let t = this.deps; t; t = t.nextDep) + ra(t); + this.deps = this.depsTail = void 0, Qa(this), this.onStop && this.onStop(), this.flags &= -2; + } + } + trigger() { + this.flags & 64 ? ao.add(this) : this.scheduler ? this.scheduler() : this.runIfDirty(); + } + /** + * @internal + */ + runIfDirty() { + Oo(this) && this.run(); + } + get dirty() { + return Oo(this); + } +} +let vc = 0, Tr, xr; +function wc(e, t = !1) { + if (e.flags |= 8, t) { + e.next = xr, xr = e; + return; + } + e.next = Tr, Tr = e; +} +function na() { + vc++; +} +function sa() { + if (--vc > 0) + return; + if (xr) { + let t = xr; + for (xr = void 0; t; ) { + const n = t.next; + t.next = void 0, t.flags &= -9, t = n; + } + } + let e; + for (; Tr; ) { + let t = Tr; + for (Tr = void 0; t; ) { + const n = t.next; + if (t.next = void 0, t.flags &= -9, t.flags & 1) + try { + t.trigger(); + } catch (s) { + e || (e = s); + } + t = n; + } + } + if (e) throw e; +} +function kc(e) { + for (let t = e.deps; t; t = t.nextDep) + t.version = -1, t.prevActiveLink = t.dep.activeLink, t.dep.activeLink = t; +} +function Tc(e) { + let t, n = e.depsTail, s = n; + for (; s; ) { + const r = s.prevDep; + s.version === -1 ? (s === n && (n = r), ra(s), Tf(s)) : t = s, s.dep.activeLink = s.prevActiveLink, s.prevActiveLink = void 0, s = r; + } + e.deps = t, e.depsTail = n; +} +function Oo(e) { + for (let t = e.deps; t; t = t.nextDep) + if (t.dep.version !== t.version || t.dep.computed && (xc(t.dep.computed) || t.dep.version !== t.version)) + return !0; + return !!e._dirty; +} +function xc(e) { + if (e.flags & 4 && !(e.flags & 16) || (e.flags &= -17, e.globalVersion === Ir) || (e.globalVersion = Ir, !e.isSSR && e.flags & 128 && (!e.deps && !e._dirty || !Oo(e)))) + return; + e.flags |= 2; + const t = e.dep, n = ot, s = ln; + ot = e, ln = !0; + try { + kc(e); + const r = e.fn(e._value); + (t.version === 0 || Zn(r, e._value)) && (e.flags |= 128, e._value = r, t.version++); + } catch (r) { + throw t.version++, r; + } finally { + ot = n, ln = s, Tc(e), e.flags &= -3; + } +} +function ra(e, t = !1) { + const { dep: n, prevSub: s, nextSub: r } = e; + if (s && (s.nextSub = r, e.prevSub = void 0), r && (r.prevSub = s, e.nextSub = void 0), n.subs === e && (n.subs = s, !s && n.computed)) { + n.computed.flags &= -5; + for (let i = n.computed.deps; i; i = i.nextDep) + ra(i, !0); + } + !t && !--n.sc && n.map && n.map.delete(n.key); +} +function Tf(e) { + const { prevDep: t, nextDep: n } = e; + t && (t.nextDep = n, e.prevDep = void 0), n && (n.prevDep = t, e.nextDep = void 0); +} +let ln = !0; +const Ac = []; +function Dn() { + Ac.push(ln), ln = !1; +} +function $n() { + const e = Ac.pop(); + ln = e === void 0 ? !0 : e; +} +function Qa(e) { + const { cleanup: t } = e; + if (e.cleanup = void 0, t) { + const n = ot; + ot = void 0; + try { + t(); + } finally { + ot = n; + } + } +} +let Ir = 0; +class xf { + constructor(t, n) { + this.sub = t, this.dep = n, this.version = n.version, this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0; + } +} +class ia { + // TODO isolatedDeclarations "__v_skip" + constructor(t) { + this.computed = t, this.version = 0, this.activeLink = void 0, this.subs = void 0, this.map = void 0, this.key = void 0, this.sc = 0, this.__v_skip = !0; + } + track(t) { + if (!ot || !ln || ot === this.computed) + return; + let n = this.activeLink; + if (n === void 0 || n.sub !== ot) + n = this.activeLink = new xf(ot, this), ot.deps ? (n.prevDep = ot.depsTail, ot.depsTail.nextDep = n, ot.depsTail = n) : ot.deps = ot.depsTail = n, Sc(n); + else if (n.version === -1 && (n.version = this.version, n.nextDep)) { + const s = n.nextDep; + s.prevDep = n.prevDep, n.prevDep && (n.prevDep.nextDep = s), n.prevDep = ot.depsTail, n.nextDep = void 0, ot.depsTail.nextDep = n, ot.depsTail = n, ot.deps === n && (ot.deps = s); + } + return n; + } + trigger(t) { + this.version++, Ir++, this.notify(t); + } + notify(t) { + na(); + try { + for (let n = this.subs; n; n = n.prevSub) + n.sub.notify() && n.sub.dep.notify(); + } finally { + sa(); + } + } +} +function Sc(e) { + if (e.dep.sc++, e.sub.flags & 4) { + const t = e.dep.computed; + if (t && !e.dep.subs) { + t.flags |= 20; + for (let s = t.deps; s; s = s.nextDep) + Sc(s); + } + const n = e.dep.subs; + n !== e && (e.prevSub = n, n && (n.nextSub = e)), e.dep.subs = e; + } +} +const No = /* @__PURE__ */ new WeakMap(), ys = Symbol( + "" +), Po = Symbol( + "" +), Lr = Symbol( + "" +); +function Ot(e, t, n) { + if (ln && ot) { + let s = No.get(e); + s || No.set(e, s = /* @__PURE__ */ new Map()); + let r = s.get(n); + r || (s.set(n, r = new ia()), r.map = s, r.key = n), r.track(); + } +} +function Nn(e, t, n, s, r, i) { + const o = No.get(e); + if (!o) { + Ir++; + return; + } + const a = (l) => { + l && l.trigger(); + }; + if (na(), t === "clear") + o.forEach(a); + else { + const l = be(e), d = l && ta(n); + if (l && n === "length") { + const c = Number(s); + o.forEach((v, w) => { + (w === "length" || w === Lr || !ns(w) && w >= c) && a(v); + }); + } else + switch ((n !== void 0 || o.has(void 0)) && a(o.get(n)), d && a(o.get(Lr)), t) { + case "add": + l ? d && a(o.get("length")) : (a(o.get(ys)), Fs(e) && a(o.get(Po))); + break; + case "delete": + l || (a(o.get(ys)), Fs(e) && a(o.get(Po))); + break; + case "set": + Fs(e) && a(o.get(ys)); + break; + } + } + sa(); +} +function Ls(e) { + const t = We(e); + return t === e ? t : (Ot(t, "iterate", Lr), sn(e) ? t : t.map(Et)); +} +function Di(e) { + return Ot(e = We(e), "iterate", Lr), e; +} +const Af = { + __proto__: null, + [Symbol.iterator]() { + return lo(this, Symbol.iterator, Et); + }, + concat(...e) { + return Ls(this).concat( + ...e.map((t) => be(t) ? Ls(t) : t) + ); + }, + entries() { + return lo(this, "entries", (e) => (e[1] = Et(e[1]), e)); + }, + every(e, t) { + return In(this, "every", e, t, void 0, arguments); + }, + filter(e, t) { + return In(this, "filter", e, t, (n) => n.map(Et), arguments); + }, + find(e, t) { + return In(this, "find", e, t, Et, arguments); + }, + findIndex(e, t) { + return In(this, "findIndex", e, t, void 0, arguments); + }, + findLast(e, t) { + return In(this, "findLast", e, t, Et, arguments); + }, + findLastIndex(e, t) { + return In(this, "findLastIndex", e, t, void 0, arguments); + }, + // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement + forEach(e, t) { + return In(this, "forEach", e, t, void 0, arguments); + }, + includes(...e) { + return co(this, "includes", e); + }, + indexOf(...e) { + return co(this, "indexOf", e); + }, + join(e) { + return Ls(this).join(e); + }, + // keys() iterator only reads `length`, no optimisation required + lastIndexOf(...e) { + return co(this, "lastIndexOf", e); + }, + map(e, t) { + return In(this, "map", e, t, void 0, arguments); + }, + pop() { + return lr(this, "pop"); + }, + push(...e) { + return lr(this, "push", e); + }, + reduce(e, ...t) { + return el(this, "reduce", e, t); + }, + reduceRight(e, ...t) { + return el(this, "reduceRight", e, t); + }, + shift() { + return lr(this, "shift"); + }, + // slice could use ARRAY_ITERATE but also seems to beg for range tracking + some(e, t) { + return In(this, "some", e, t, void 0, arguments); + }, + splice(...e) { + return lr(this, "splice", e); + }, + toReversed() { + return Ls(this).toReversed(); + }, + toSorted(e) { + return Ls(this).toSorted(e); + }, + toSpliced(...e) { + return Ls(this).toSpliced(...e); + }, + unshift(...e) { + return lr(this, "unshift", e); + }, + values() { + return lo(this, "values", Et); + } +}; +function lo(e, t, n) { + const s = Di(e), r = s[t](); + return s !== e && !sn(e) && (r._next = r.next, r.next = () => { + const i = r._next(); + return i.value && (i.value = n(i.value)), i; + }), r; +} +const Sf = Array.prototype; +function In(e, t, n, s, r, i) { + const o = Di(e), a = o !== e && !sn(e), l = o[t]; + if (l !== Sf[t]) { + const v = l.apply(e, i); + return a ? Et(v) : v; + } + let d = n; + o !== e && (a ? d = function(v, w) { + return n.call(this, Et(v), w, e); + } : n.length > 2 && (d = function(v, w) { + return n.call(this, v, w, e); + })); + const c = l.call(o, d, s); + return a && r ? r(c) : c; +} +function el(e, t, n, s) { + const r = Di(e); + let i = n; + return r !== e && (sn(e) ? n.length > 3 && (i = function(o, a, l) { + return n.call(this, o, a, l, e); + }) : i = function(o, a, l) { + return n.call(this, o, Et(a), l, e); + }), r[t](i, ...s); +} +function co(e, t, n) { + const s = We(e); + Ot(s, "iterate", Lr); + const r = s[t](...n); + return (r === -1 || r === !1) && la(n[0]) ? (n[0] = We(n[0]), s[t](...n)) : r; +} +function lr(e, t, n = []) { + Dn(), na(); + const s = We(e)[t].apply(e, n); + return sa(), $n(), s; +} +const Ef = /* @__PURE__ */ Jo("__proto__,__v_isRef,__isVue"), Ec = new Set( + /* @__PURE__ */ Object.getOwnPropertyNames(Symbol).filter((e) => e !== "arguments" && e !== "caller").map((e) => Symbol[e]).filter(ns) +); +function Cf(e) { + ns(e) || (e = String(e)); + const t = We(this); + return Ot(t, "has", e), t.hasOwnProperty(e); +} +class Cc { + constructor(t = !1, n = !1) { + this._isReadonly = t, this._isShallow = n; + } + get(t, n, s) { + if (n === "__v_skip") return t.__v_skip; + const r = this._isReadonly, i = this._isShallow; + if (n === "__v_isReactive") + return !r; + if (n === "__v_isReadonly") + return r; + if (n === "__v_isShallow") + return i; + if (n === "__v_raw") + return s === (r ? i ? $f : Oc : i ? Lc : Ic).get(t) || // receiver is not the reactive proxy, but has the same prototype + // this means the receiver is a user proxy of the reactive proxy + Object.getPrototypeOf(t) === Object.getPrototypeOf(s) ? t : void 0; + const o = be(t); + if (!r) { + let l; + if (o && (l = Af[n])) + return l; + if (n === "hasOwnProperty") + return Cf; + } + const a = Reflect.get( + t, + n, + // if this is a proxy wrapping a ref, return methods using the raw ref + // as receiver so that we don't have to call `toRaw` on the ref in all + // its class methods + Nt(t) ? t : s + ); + return (ns(n) ? Ec.has(n) : Ef(n)) || (r || Ot(t, "get", n), i) ? a : Nt(a) ? o && ta(n) ? a : a.value : ut(a) ? r ? Nc(a) : $i(a) : a; + } +} +class Rc extends Cc { + constructor(t = !1) { + super(!1, t); + } + set(t, n, s, r) { + let i = t[n]; + if (!this._isShallow) { + const l = es(i); + if (!sn(s) && !es(s) && (i = We(i), s = We(s)), !be(t) && Nt(i) && !Nt(s)) + return l ? !1 : (i.value = s, !0); + } + const o = be(t) && ta(n) ? Number(n) < t.length : je(t, n), a = Reflect.set( + t, + n, + s, + Nt(t) ? t : r + ); + return t === We(r) && (o ? Zn(s, i) && Nn(t, "set", n, s) : Nn(t, "add", n, s)), a; + } + deleteProperty(t, n) { + const s = je(t, n); + t[n]; + const r = Reflect.deleteProperty(t, n); + return r && s && Nn(t, "delete", n, void 0), r; + } + has(t, n) { + const s = Reflect.has(t, n); + return (!ns(n) || !Ec.has(n)) && Ot(t, "has", n), s; + } + ownKeys(t) { + return Ot( + t, + "iterate", + be(t) ? "length" : ys + ), Reflect.ownKeys(t); + } +} +class Rf extends Cc { + constructor(t = !1) { + super(!0, t); + } + set(t, n) { + return !0; + } + deleteProperty(t, n) { + return !0; + } +} +const If = /* @__PURE__ */ new Rc(), Lf = /* @__PURE__ */ new Rf(), Of = /* @__PURE__ */ new Rc(!0); +const Mo = (e) => e, Zr = (e) => Reflect.getPrototypeOf(e); +function Nf(e, t, n) { + return function(...s) { + const r = this.__v_raw, i = We(r), o = Fs(i), a = e === "entries" || e === Symbol.iterator && o, l = e === "keys" && o, d = r[e](...s), c = n ? Mo : t ? vi : Et; + return !t && Ot( + i, + "iterate", + l ? Po : ys + ), { + // iterator protocol + next() { + const { value: v, done: w } = d.next(); + return w ? { value: v, done: w } : { + value: a ? [c(v[0]), c(v[1])] : c(v), + done: w + }; + }, + // iterable protocol + [Symbol.iterator]() { + return this; + } + }; + }; +} +function Jr(e) { + return function(...t) { + return e === "delete" ? !1 : e === "clear" ? void 0 : this; + }; +} +function Pf(e, t) { + const n = { + get(r) { + const i = this.__v_raw, o = We(i), a = We(r); + e || (Zn(r, a) && Ot(o, "get", r), Ot(o, "get", a)); + const { has: l } = Zr(o), d = t ? Mo : e ? vi : Et; + if (l.call(o, r)) + return d(i.get(r)); + if (l.call(o, a)) + return d(i.get(a)); + i !== o && i.get(r); + }, + get size() { + const r = this.__v_raw; + return !e && Ot(We(r), "iterate", ys), Reflect.get(r, "size", r); + }, + has(r) { + const i = this.__v_raw, o = We(i), a = We(r); + return e || (Zn(r, a) && Ot(o, "has", r), Ot(o, "has", a)), r === a ? i.has(r) : i.has(r) || i.has(a); + }, + forEach(r, i) { + const o = this, a = o.__v_raw, l = We(a), d = t ? Mo : e ? vi : Et; + return !e && Ot(l, "iterate", ys), a.forEach((c, v) => r.call(i, d(c), d(v), o)); + } + }; + return Pt( + n, + e ? { + add: Jr("add"), + set: Jr("set"), + delete: Jr("delete"), + clear: Jr("clear") + } : { + add(r) { + !t && !sn(r) && !es(r) && (r = We(r)); + const i = We(this); + return Zr(i).has.call(i, r) || (i.add(r), Nn(i, "add", r, r)), this; + }, + set(r, i) { + !t && !sn(i) && !es(i) && (i = We(i)); + const o = We(this), { has: a, get: l } = Zr(o); + let d = a.call(o, r); + d || (r = We(r), d = a.call(o, r)); + const c = l.call(o, r); + return o.set(r, i), d ? Zn(i, c) && Nn(o, "set", r, i) : Nn(o, "add", r, i), this; + }, + delete(r) { + const i = We(this), { has: o, get: a } = Zr(i); + let l = o.call(i, r); + l || (r = We(r), l = o.call(i, r)), a && a.call(i, r); + const d = i.delete(r); + return l && Nn(i, "delete", r, void 0), d; + }, + clear() { + const r = We(this), i = r.size !== 0, o = r.clear(); + return i && Nn( + r, + "clear", + void 0, + void 0 + ), o; + } + } + ), [ + "keys", + "values", + "entries", + Symbol.iterator + ].forEach((r) => { + n[r] = Nf(r, e, t); + }), n; +} +function oa(e, t) { + const n = Pf(e, t); + return (s, r, i) => r === "__v_isReactive" ? !e : r === "__v_isReadonly" ? e : r === "__v_raw" ? s : Reflect.get( + je(n, r) && r in s ? n : s, + r, + i + ); +} +const Mf = { + get: /* @__PURE__ */ oa(!1, !1) +}, Ff = { + get: /* @__PURE__ */ oa(!1, !0) +}, Df = { + get: /* @__PURE__ */ oa(!0, !1) +}; +const Ic = /* @__PURE__ */ new WeakMap(), Lc = /* @__PURE__ */ new WeakMap(), Oc = /* @__PURE__ */ new WeakMap(), $f = /* @__PURE__ */ new WeakMap(); +function Bf(e) { + switch (e) { + case "Object": + case "Array": + return 1; + case "Map": + case "Set": + case "WeakMap": + case "WeakSet": + return 2; + default: + return 0; + } +} +function Uf(e) { + return e.__v_skip || !Object.isExtensible(e) ? 0 : Bf(hf(e)); +} +function $i(e) { + return es(e) ? e : aa( + e, + !1, + If, + Mf, + Ic + ); +} +function zf(e) { + return aa( + e, + !1, + Of, + Ff, + Lc + ); +} +function Nc(e) { + return aa( + e, + !0, + Lf, + Df, + Oc + ); +} +function aa(e, t, n, s, r) { + if (!ut(e) || e.__v_raw && !(t && e.__v_isReactive)) + return e; + const i = Uf(e); + if (i === 0) + return e; + const o = r.get(e); + if (o) + return o; + const a = new Proxy( + e, + i === 2 ? s : n + ); + return r.set(e, a), a; +} +function Ds(e) { + return es(e) ? Ds(e.__v_raw) : !!(e && e.__v_isReactive); +} +function es(e) { + return !!(e && e.__v_isReadonly); +} +function sn(e) { + return !!(e && e.__v_isShallow); +} +function la(e) { + return e ? !!e.__v_raw : !1; +} +function We(e) { + const t = e && e.__v_raw; + return t ? We(t) : e; +} +function Hf(e) { + return !je(e, "__v_skip") && Object.isExtensible(e) && Io(e, "__v_skip", !0), e; +} +const Et = (e) => ut(e) ? $i(e) : e, vi = (e) => ut(e) ? Nc(e) : e; +function Nt(e) { + return e ? e.__v_isRef === !0 : !1; +} +function ce(e) { + return qf(e, !1); +} +function qf(e, t) { + return Nt(e) ? e : new Wf(e, t); +} +class Wf { + constructor(t, n) { + this.dep = new ia(), this.__v_isRef = !0, this.__v_isShallow = !1, this._rawValue = n ? t : We(t), this._value = n ? t : Et(t), this.__v_isShallow = n; + } + get value() { + return this.dep.track(), this._value; + } + set value(t) { + const n = this._rawValue, s = this.__v_isShallow || sn(t) || es(t); + t = s ? t : We(t), Zn(t, n) && (this._rawValue = t, this._value = s ? t : Et(t), this.dep.trigger()); + } +} +function E(e) { + return Nt(e) ? e.value : e; +} +const jf = { + get: (e, t, n) => t === "__v_raw" ? e : E(Reflect.get(e, t, n)), + set: (e, t, n, s) => { + const r = e[t]; + return Nt(r) && !Nt(n) ? (r.value = n, !0) : Reflect.set(e, t, n, s); + } +}; +function Pc(e) { + return Ds(e) ? e : new Proxy(e, jf); +} +class Vf { + constructor(t, n, s) { + this.fn = t, this.setter = n, this._value = void 0, this.dep = new ia(this), this.__v_isRef = !0, this.deps = void 0, this.depsTail = void 0, this.flags = 16, this.globalVersion = Ir - 1, this.next = void 0, this.effect = this, this.__v_isReadonly = !n, this.isSSR = s; + } + /** + * @internal + */ + notify() { + if (this.flags |= 16, !(this.flags & 8) && // avoid infinite self recursion + ot !== this) + return wc(this, !0), !0; + } + get value() { + const t = this.dep.track(); + return xc(this), t && (t.version = this.dep.version), this._value; + } + set value(t) { + this.setter && this.setter(t); + } +} +function Kf(e, t, n = !1) { + let s, r; + return ke(e) ? s = e : (s = e.get, r = e.set), new Vf(s, r, n); +} +const Qr = {}, wi = /* @__PURE__ */ new WeakMap(); +let ms; +function Gf(e, t = !1, n = ms) { + if (n) { + let s = wi.get(n); + s || wi.set(n, s = []), s.push(e); + } +} +function Yf(e, t, n = tt) { + const { immediate: s, deep: r, once: i, scheduler: o, augmentJob: a, call: l } = n, d = (R) => r ? R : sn(R) || r === !1 || r === 0 ? Pn(R, 1) : Pn(R); + let c, v, w, z, I = !1, K = !1; + if (Nt(e) ? (v = () => e.value, I = sn(e)) : Ds(e) ? (v = () => d(e), I = !0) : be(e) ? (K = !0, I = e.some((R) => Ds(R) || sn(R)), v = () => e.map((R) => { + if (Nt(R)) + return R.value; + if (Ds(R)) + return d(R); + if (ke(R)) + return l ? l(R, 2) : R(); + })) : ke(e) ? t ? v = l ? () => l(e, 2) : e : v = () => { + if (w) { + Dn(); + try { + w(); + } finally { + $n(); + } + } + const R = ms; + ms = c; + try { + return l ? l(e, 3, [z]) : e(z); + } finally { + ms = R; + } + } : v = _n, t && r) { + const R = v, N = r === !0 ? 1 / 0 : r; + v = () => Pn(R(), N); + } + const F = kf(), oe = () => { + c.stop(), F && F.active && ea(F.effects, c); + }; + if (i && t) { + const R = t; + t = (...N) => { + R(...N), oe(); + }; + } + let fe = K ? new Array(e.length).fill(Qr) : Qr; + const pe = (R) => { + if (!(!(c.flags & 1) || !c.dirty && !R)) + if (t) { + const N = c.run(); + if (r || I || (K ? N.some((Y, Z) => Zn(Y, fe[Z])) : Zn(N, fe))) { + w && w(); + const Y = ms; + ms = c; + try { + const Z = [ + N, + // pass undefined as the old value when it's changed for the first time + fe === Qr ? void 0 : K && fe[0] === Qr ? [] : fe, + z + ]; + fe = N, l ? l(t, 3, Z) : ( + // @ts-expect-error + t(...Z) + ); + } finally { + ms = Y; + } + } + } else + c.run(); + }; + return a && a(pe), c = new bc(v), c.scheduler = o ? () => o(pe, !1) : pe, z = (R) => Gf(R, !1, c), w = c.onStop = () => { + const R = wi.get(c); + if (R) { + if (l) + l(R, 4); + else + for (const N of R) N(); + wi.delete(c); + } + }, t ? s ? pe(!0) : fe = c.run() : o ? o(pe.bind(null, !0), !0) : c.run(), oe.pause = c.pause.bind(c), oe.resume = c.resume.bind(c), oe.stop = oe, oe; +} +function Pn(e, t = 1 / 0, n) { + if (t <= 0 || !ut(e) || e.__v_skip || (n = n || /* @__PURE__ */ new Set(), n.has(e))) + return e; + if (n.add(e), t--, Nt(e)) + Pn(e.value, t, n); + else if (be(e)) + for (let s = 0; s < e.length; s++) + Pn(e[s], t, n); + else if (fc(e) || Fs(e)) + e.forEach((s) => { + Pn(s, t, n); + }); + else if (pc(e)) { + for (const s in e) + Pn(e[s], t, n); + for (const s of Object.getOwnPropertySymbols(e)) + Object.prototype.propertyIsEnumerable.call(e, s) && Pn(e[s], t, n); + } + return e; +} +/** +* @vue/runtime-core v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/ +function Mr(e, t, n, s) { + try { + return s ? e(...s) : e(); + } catch (r) { + Bi(r, t, n); + } +} +function vn(e, t, n, s) { + if (ke(e)) { + const r = Mr(e, t, n, s); + return r && hc(r) && r.catch((i) => { + Bi(i, t, n); + }), r; + } + if (be(e)) { + const r = []; + for (let i = 0; i < e.length; i++) + r.push(vn(e[i], t, n, s)); + return r; + } +} +function Bi(e, t, n, s = !0) { + const r = t ? t.vnode : null, { errorHandler: i, throwUnhandledErrorInProduction: o } = t && t.appContext.config || tt; + if (t) { + let a = t.parent; + const l = t.proxy, d = `https://vuejs.org/error-reference/#runtime-${n}`; + for (; a; ) { + const c = a.ec; + if (c) { + for (let v = 0; v < c.length; v++) + if (c[v](e, l, d) === !1) + return; + } + a = a.parent; + } + if (i) { + Dn(), Mr(i, null, 10, [ + e, + l, + d + ]), $n(); + return; + } + } + Xf(e, n, r, s, o); +} +function Xf(e, t, n, s = !0, r = !1) { + if (r) + throw e; + console.error(e); +} +const $t = []; +let mn = -1; +const $s = []; +let Gn = null, Os = 0; +const Mc = /* @__PURE__ */ Promise.resolve(); +let ki = null; +function bs(e) { + const t = ki || Mc; + return e ? t.then(this ? e.bind(this) : e) : t; +} +function Zf(e) { + let t = mn + 1, n = $t.length; + for (; t < n; ) { + const s = t + n >>> 1, r = $t[s], i = Or(r); + i < e || i === e && r.flags & 2 ? t = s + 1 : n = s; + } + return t; +} +function ca(e) { + if (!(e.flags & 1)) { + const t = Or(e), n = $t[$t.length - 1]; + !n || // fast path when the job id is larger than the tail + !(e.flags & 2) && t >= Or(n) ? $t.push(e) : $t.splice(Zf(t), 0, e), e.flags |= 1, Fc(); + } +} +function Fc() { + ki || (ki = Mc.then($c)); +} +function Jf(e) { + be(e) ? $s.push(...e) : Gn && e.id === -1 ? Gn.splice(Os + 1, 0, e) : e.flags & 1 || ($s.push(e), e.flags |= 1), Fc(); +} +function tl(e, t, n = mn + 1) { + for (; n < $t.length; n++) { + const s = $t[n]; + if (s && s.flags & 2) { + if (e && s.id !== e.uid) + continue; + $t.splice(n, 1), n--, s.flags & 4 && (s.flags &= -2), s(), s.flags & 4 || (s.flags &= -2); + } + } +} +function Dc(e) { + if ($s.length) { + const t = [...new Set($s)].sort( + (n, s) => Or(n) - Or(s) + ); + if ($s.length = 0, Gn) { + Gn.push(...t); + return; + } + for (Gn = t, Os = 0; Os < Gn.length; Os++) { + const n = Gn[Os]; + n.flags & 4 && (n.flags &= -2), n.flags & 8 || n(), n.flags &= -2; + } + Gn = null, Os = 0; + } +} +const Or = (e) => e.id == null ? e.flags & 2 ? -1 : 1 / 0 : e.id; +function $c(e) { + try { + for (mn = 0; mn < $t.length; mn++) { + const t = $t[mn]; + t && !(t.flags & 8) && (t.flags & 4 && (t.flags &= -2), Mr( + t, + t.i, + t.i ? 15 : 14 + ), t.flags & 4 || (t.flags &= -2)); + } + } finally { + for (; mn < $t.length; mn++) { + const t = $t[mn]; + t && (t.flags &= -2); + } + mn = -1, $t.length = 0, Dc(), ki = null, ($t.length || $s.length) && $c(); + } +} +let nn = null, Bc = null; +function Ti(e) { + const t = nn; + return nn = e, Bc = e && e.type.__scopeId || null, t; +} +function Qf(e, t = nn, n) { + if (!t || e._n) + return e; + const s = (...r) => { + s._d && ul(-1); + const i = Ti(t); + let o; + try { + o = e(...r); + } finally { + Ti(i), s._d && ul(1); + } + return o; + }; + return s._n = !0, s._c = !0, s._d = !0, s; +} +function Vn(e, t) { + if (nn === null) + return e; + const n = Wi(nn), s = e.dirs || (e.dirs = []); + for (let r = 0; r < t.length; r++) { + let [i, o, a, l = tt] = t[r]; + i && (ke(i) && (i = { + mounted: i, + updated: i + }), i.deep && Pn(o), s.push({ + dir: i, + instance: n, + value: o, + oldValue: void 0, + arg: a, + modifiers: l + })); + } + return e; +} +function us(e, t, n, s) { + const r = e.dirs, i = t && t.dirs; + for (let o = 0; o < r.length; o++) { + const a = r[o]; + i && (a.oldValue = i[o].value); + let l = a.dir[s]; + l && (Dn(), vn(l, n, 8, [ + e.el, + a, + e, + t + ]), $n()); + } +} +const eh = Symbol("_vte"), th = (e) => e.__isTeleport; +function ua(e, t) { + e.shapeFlag & 6 && e.component ? (e.transition = t, ua(e.component.subTree, t)) : e.shapeFlag & 128 ? (e.ssContent.transition = t.clone(e.ssContent), e.ssFallback.transition = t.clone(e.ssFallback)) : e.transition = t; +} +/*! #__NO_SIDE_EFFECTS__ */ +// @__NO_SIDE_EFFECTS__ +function fa(e, t) { + return ke(e) ? ( + // #8236: extend call and options.name access are considered side-effects + // by Rollup, so we have to wrap it in a pure-annotated IIFE. + Pt({ name: e.name }, t, { setup: e }) + ) : e; +} +function Uc(e) { + e.ids = [e.ids[0] + e.ids[2]++ + "-", 0, 0]; +} +function Ar(e, t, n, s, r = !1) { + if (be(e)) { + e.forEach( + (I, K) => Ar( + I, + t && (be(t) ? t[K] : t), + n, + s, + r + ) + ); + return; + } + if (Sr(s) && !r) { + s.shapeFlag & 512 && s.type.__asyncResolved && s.component.subTree.component && Ar(e, t, n, s.component.subTree); + return; + } + const i = s.shapeFlag & 4 ? Wi(s.component) : s.el, o = r ? null : i, { i: a, r: l } = e, d = t && t.r, c = a.refs === tt ? a.refs = {} : a.refs, v = a.setupState, w = We(v), z = v === tt ? () => !1 : (I) => je(w, I); + if (d != null && d !== l && (gt(d) ? (c[d] = null, z(d) && (v[d] = null)) : Nt(d) && (d.value = null)), ke(l)) + Mr(l, a, 12, [o, c]); + else { + const I = gt(l), K = Nt(l); + if (I || K) { + const F = () => { + if (e.f) { + const oe = I ? z(l) ? v[l] : c[l] : l.value; + r ? be(oe) && ea(oe, i) : be(oe) ? oe.includes(i) || oe.push(i) : I ? (c[l] = [i], z(l) && (v[l] = c[l])) : (l.value = [i], e.k && (c[e.k] = l.value)); + } else I ? (c[l] = o, z(l) && (v[l] = o)) : K && (l.value = o, e.k && (c[e.k] = o)); + }; + o ? (F.id = -1, jt(F, n)) : F(); + } + } +} +Fi().requestIdleCallback; +Fi().cancelIdleCallback; +const Sr = (e) => !!e.type.__asyncLoader, zc = (e) => e.type.__isKeepAlive; +function nh(e, t) { + Hc(e, "a", t); +} +function sh(e, t) { + Hc(e, "da", t); +} +function Hc(e, t, n = Bt) { + const s = e.__wdc || (e.__wdc = () => { + let r = n; + for (; r; ) { + if (r.isDeactivated) + return; + r = r.parent; + } + return e(); + }); + if (Ui(t, s, n), n) { + let r = n.parent; + for (; r && r.parent; ) + zc(r.parent.vnode) && rh(s, t, n, r), r = r.parent; + } +} +function rh(e, t, n, s) { + const r = Ui( + t, + e, + s, + !0 + /* prepend */ + ); + Fr(() => { + ea(s[t], r); + }, n); +} +function Ui(e, t, n = Bt, s = !1) { + if (n) { + const r = n[e] || (n[e] = []), i = t.__weh || (t.__weh = (...o) => { + Dn(); + const a = Dr(n), l = vn(t, n, e, o); + return a(), $n(), l; + }); + return s ? r.unshift(i) : r.push(i), i; + } +} +const Bn = (e) => (t, n = Bt) => { + (!Pr || e === "sp") && Ui(e, (...s) => t(...s), n); +}, ih = Bn("bm"), zi = Bn("m"), oh = Bn( + "bu" +), ah = Bn("u"), qc = Bn( + "bum" +), Fr = Bn("um"), lh = Bn( + "sp" +), ch = Bn("rtg"), uh = Bn("rtc"); +function fh(e, t = Bt) { + Ui("ec", e, t); +} +const hh = Symbol.for("v-ndc"); +function St(e, t, n, s) { + let r; + const i = n, o = be(e); + if (o || gt(e)) { + const a = o && Ds(e); + let l = !1, d = !1; + a && (l = !sn(e), d = es(e), e = Di(e)), r = new Array(e.length); + for (let c = 0, v = e.length; c < v; c++) + r[c] = t( + l ? d ? vi(Et(e[c])) : Et(e[c]) : e[c], + c, + void 0, + i + ); + } else if (typeof e == "number") { + r = new Array(e); + for (let a = 0; a < e; a++) + r[a] = t(a + 1, a, void 0, i); + } else if (ut(e)) + if (e[Symbol.iterator]) + r = Array.from( + e, + (a, l) => t(a, l, void 0, i) + ); + else { + const a = Object.keys(e); + r = new Array(a.length); + for (let l = 0, d = a.length; l < d; l++) { + const c = a[l]; + r[l] = t(e[c], c, l, i); + } + } + else + r = []; + return r; +} +const Fo = (e) => e ? uu(e) ? Wi(e) : Fo(e.parent) : null, Er = ( + // Move PURE marker to new line to workaround compiler discarding it + // due to type annotation + /* @__PURE__ */ Pt(/* @__PURE__ */ Object.create(null), { + $: (e) => e, + $el: (e) => e.vnode.el, + $data: (e) => e.data, + $props: (e) => e.props, + $attrs: (e) => e.attrs, + $slots: (e) => e.slots, + $refs: (e) => e.refs, + $parent: (e) => Fo(e.parent), + $root: (e) => Fo(e.root), + $host: (e) => e.ce, + $emit: (e) => e.emit, + $options: (e) => jc(e), + $forceUpdate: (e) => e.f || (e.f = () => { + ca(e.update); + }), + $nextTick: (e) => e.n || (e.n = bs.bind(e.proxy)), + $watch: (e) => Ph.bind(e) + }) +), uo = (e, t) => e !== tt && !e.__isScriptSetup && je(e, t), dh = { + get({ _: e }, t) { + if (t === "__v_skip") + return !0; + const { ctx: n, setupState: s, data: r, props: i, accessCache: o, type: a, appContext: l } = e; + let d; + if (t[0] !== "$") { + const z = o[t]; + if (z !== void 0) + switch (z) { + case 1: + return s[t]; + case 2: + return r[t]; + case 4: + return n[t]; + case 3: + return i[t]; + } + else { + if (uo(s, t)) + return o[t] = 1, s[t]; + if (r !== tt && je(r, t)) + return o[t] = 2, r[t]; + if ( + // only cache other properties when instance has declared (thus stable) + // props + (d = e.propsOptions[0]) && je(d, t) + ) + return o[t] = 3, i[t]; + if (n !== tt && je(n, t)) + return o[t] = 4, n[t]; + Do && (o[t] = 0); + } + } + const c = Er[t]; + let v, w; + if (c) + return t === "$attrs" && Ot(e.attrs, "get", ""), c(e); + if ( + // css module (injected by vue-loader) + (v = a.__cssModules) && (v = v[t]) + ) + return v; + if (n !== tt && je(n, t)) + return o[t] = 4, n[t]; + if ( + // global properties + w = l.config.globalProperties, je(w, t) + ) + return w[t]; + }, + set({ _: e }, t, n) { + const { data: s, setupState: r, ctx: i } = e; + return uo(r, t) ? (r[t] = n, !0) : s !== tt && je(s, t) ? (s[t] = n, !0) : je(e.props, t) || t[0] === "$" && t.slice(1) in e ? !1 : (i[t] = n, !0); + }, + has({ + _: { data: e, setupState: t, accessCache: n, ctx: s, appContext: r, propsOptions: i } + }, o) { + let a; + return !!n[o] || e !== tt && je(e, o) || uo(t, o) || (a = i[0]) && je(a, o) || je(s, o) || je(Er, o) || je(r.config.globalProperties, o); + }, + defineProperty(e, t, n) { + return n.get != null ? e._.accessCache[t] = 0 : je(n, "value") && this.set(e, t, n.value, null), Reflect.defineProperty(e, t, n); + } +}; +function nl(e) { + return be(e) ? e.reduce( + (t, n) => (t[n] = null, t), + {} + ) : e; +} +let Do = !0; +function ph(e) { + const t = jc(e), n = e.proxy, s = e.ctx; + Do = !1, t.beforeCreate && sl(t.beforeCreate, e, "bc"); + const { + // state + data: r, + computed: i, + methods: o, + watch: a, + provide: l, + inject: d, + // lifecycle + created: c, + beforeMount: v, + mounted: w, + beforeUpdate: z, + updated: I, + activated: K, + deactivated: F, + beforeDestroy: oe, + beforeUnmount: fe, + destroyed: pe, + unmounted: R, + render: N, + renderTracked: Y, + renderTriggered: Z, + errorCaptured: Te, + serverPrefetch: Oe, + // public API + expose: _e, + inheritAttrs: Fe, + // assets + components: ve, + directives: Ke, + filters: Ge + } = t; + if (d && mh(d, s, null), o) + for (const ye in o) { + const ge = o[ye]; + ke(ge) && (s[ye] = ge.bind(n)); + } + if (r) { + const ye = r.call(n, n); + ut(ye) && (e.data = $i(ye)); + } + if (Do = !0, i) + for (const ye in i) { + const ge = i[ye], nt = ke(ge) ? ge.bind(n, n) : ke(ge.get) ? ge.get.bind(n, n) : _n, Xe = !ke(ge) && ke(ge.set) ? ge.set.bind(n) : _n, xe = de({ + get: nt, + set: Xe + }); + Object.defineProperty(s, ye, { + enumerable: !0, + configurable: !0, + get: () => xe.value, + set: (Je) => xe.value = Je + }); + } + if (a) + for (const ye in a) + Wc(a[ye], s, n, ye); + if (l) { + const ye = ke(l) ? l.call(n) : l; + Reflect.ownKeys(ye).forEach((ge) => { + wh(ge, ye[ge]); + }); + } + c && sl(c, e, "c"); + function ue(ye, ge) { + be(ge) ? ge.forEach((nt) => ye(nt.bind(n))) : ge && ye(ge.bind(n)); + } + if (ue(ih, v), ue(zi, w), ue(oh, z), ue(ah, I), ue(nh, K), ue(sh, F), ue(fh, Te), ue(uh, Y), ue(ch, Z), ue(qc, fe), ue(Fr, R), ue(lh, Oe), be(_e)) + if (_e.length) { + const ye = e.exposed || (e.exposed = {}); + _e.forEach((ge) => { + Object.defineProperty(ye, ge, { + get: () => n[ge], + set: (nt) => n[ge] = nt, + enumerable: !0 + }); + }); + } else e.exposed || (e.exposed = {}); + N && e.render === _n && (e.render = N), Fe != null && (e.inheritAttrs = Fe), ve && (e.components = ve), Ke && (e.directives = Ke), Oe && Uc(e); +} +function mh(e, t, n = _n) { + be(e) && (e = $o(e)); + for (const s in e) { + const r = e[s]; + let i; + ut(r) ? "default" in r ? i = ii( + r.from || s, + r.default, + !0 + ) : i = ii(r.from || s) : i = ii(r), Nt(i) ? Object.defineProperty(t, s, { + enumerable: !0, + configurable: !0, + get: () => i.value, + set: (o) => i.value = o + }) : t[s] = i; + } +} +function sl(e, t, n) { + vn( + be(e) ? e.map((s) => s.bind(t.proxy)) : e.bind(t.proxy), + t, + n + ); +} +function Wc(e, t, n, s) { + let r = s.includes(".") ? ru(n, s) : () => n[s]; + if (gt(e)) { + const i = t[e]; + ke(i) && Dt(r, i); + } else if (ke(e)) + Dt(r, e.bind(n)); + else if (ut(e)) + if (be(e)) + e.forEach((i) => Wc(i, t, n, s)); + else { + const i = ke(e.handler) ? e.handler.bind(n) : t[e.handler]; + ke(i) && Dt(r, i, e); + } +} +function jc(e) { + const t = e.type, { mixins: n, extends: s } = t, { + mixins: r, + optionsCache: i, + config: { optionMergeStrategies: o } + } = e.appContext, a = i.get(t); + let l; + return a ? l = a : !r.length && !n && !s ? l = t : (l = {}, r.length && r.forEach( + (d) => xi(l, d, o, !0) + ), xi(l, t, o)), ut(t) && i.set(t, l), l; +} +function xi(e, t, n, s = !1) { + const { mixins: r, extends: i } = t; + i && xi(e, i, n, !0), r && r.forEach( + (o) => xi(e, o, n, !0) + ); + for (const o in t) + if (!(s && o === "expose")) { + const a = gh[o] || n && n[o]; + e[o] = a ? a(e[o], t[o]) : t[o]; + } + return e; +} +const gh = { + data: rl, + props: il, + emits: il, + // objects + methods: yr, + computed: yr, + // lifecycle + beforeCreate: Ft, + created: Ft, + beforeMount: Ft, + mounted: Ft, + beforeUpdate: Ft, + updated: Ft, + beforeDestroy: Ft, + beforeUnmount: Ft, + destroyed: Ft, + unmounted: Ft, + activated: Ft, + deactivated: Ft, + errorCaptured: Ft, + serverPrefetch: Ft, + // assets + components: yr, + directives: yr, + // watch + watch: yh, + // provide / inject + provide: rl, + inject: _h +}; +function rl(e, t) { + return t ? e ? function() { + return Pt( + ke(e) ? e.call(this, this) : e, + ke(t) ? t.call(this, this) : t + ); + } : t : e; +} +function _h(e, t) { + return yr($o(e), $o(t)); +} +function $o(e) { + if (be(e)) { + const t = {}; + for (let n = 0; n < e.length; n++) + t[e[n]] = e[n]; + return t; + } + return e; +} +function Ft(e, t) { + return e ? [...new Set([].concat(e, t))] : t; +} +function yr(e, t) { + return e ? Pt(/* @__PURE__ */ Object.create(null), e, t) : t; +} +function il(e, t) { + return e ? be(e) && be(t) ? [.../* @__PURE__ */ new Set([...e, ...t])] : Pt( + /* @__PURE__ */ Object.create(null), + nl(e), + nl(t ?? {}) + ) : t; +} +function yh(e, t) { + if (!e) return t; + if (!t) return e; + const n = Pt(/* @__PURE__ */ Object.create(null), e); + for (const s in t) + n[s] = Ft(e[s], t[s]); + return n; +} +function Vc() { + return { + app: null, + config: { + isNativeTag: uf, + performance: !1, + globalProperties: {}, + optionMergeStrategies: {}, + errorHandler: void 0, + warnHandler: void 0, + compilerOptions: {} + }, + mixins: [], + components: {}, + directives: {}, + provides: /* @__PURE__ */ Object.create(null), + optionsCache: /* @__PURE__ */ new WeakMap(), + propsCache: /* @__PURE__ */ new WeakMap(), + emitsCache: /* @__PURE__ */ new WeakMap() + }; +} +let bh = 0; +function vh(e, t) { + return function(s, r = null) { + ke(s) || (s = Pt({}, s)), r != null && !ut(r) && (r = null); + const i = Vc(), o = /* @__PURE__ */ new WeakSet(), a = []; + let l = !1; + const d = i.app = { + _uid: bh++, + _component: s, + _props: r, + _container: null, + _context: i, + _instance: null, + version: td, + get config() { + return i.config; + }, + set config(c) { + }, + use(c, ...v) { + return o.has(c) || (c && ke(c.install) ? (o.add(c), c.install(d, ...v)) : ke(c) && (o.add(c), c(d, ...v))), d; + }, + mixin(c) { + return i.mixins.includes(c) || i.mixins.push(c), d; + }, + component(c, v) { + return v ? (i.components[c] = v, d) : i.components[c]; + }, + directive(c, v) { + return v ? (i.directives[c] = v, d) : i.directives[c]; + }, + mount(c, v, w) { + if (!l) { + const z = d._ceVNode || yn(s, r); + return z.appContext = i, w === !0 ? w = "svg" : w === !1 && (w = void 0), e(z, c, w), l = !0, d._container = c, c.__vue_app__ = d, Wi(z.component); + } + }, + onUnmount(c) { + a.push(c); + }, + unmount() { + l && (vn( + a, + d._instance, + 16 + ), e(null, d._container), delete d._container.__vue_app__); + }, + provide(c, v) { + return i.provides[c] = v, d; + }, + runWithContext(c) { + const v = Bs; + Bs = d; + try { + return c(); + } finally { + Bs = v; + } + } + }; + return d; + }; +} +let Bs = null; +function wh(e, t) { + if (Bt) { + let n = Bt.provides; + const s = Bt.parent && Bt.parent.provides; + s === n && (n = Bt.provides = Object.create(s)), n[e] = t; + } +} +function ii(e, t, n = !1) { + const s = Yh(); + if (s || Bs) { + let r = Bs ? Bs._context.provides : s ? s.parent == null || s.ce ? s.vnode.appContext && s.vnode.appContext.provides : s.parent.provides : void 0; + if (r && e in r) + return r[e]; + if (arguments.length > 1) + return n && ke(t) ? t.call(s && s.proxy) : t; + } +} +const Kc = {}, Gc = () => Object.create(Kc), Yc = (e) => Object.getPrototypeOf(e) === Kc; +function kh(e, t, n, s = !1) { + const r = {}, i = Gc(); + e.propsDefaults = /* @__PURE__ */ Object.create(null), Xc(e, t, r, i); + for (const o in e.propsOptions[0]) + o in r || (r[o] = void 0); + n ? e.props = s ? r : zf(r) : e.type.props ? e.props = r : e.props = i, e.attrs = i; +} +function Th(e, t, n, s) { + const { + props: r, + attrs: i, + vnode: { patchFlag: o } + } = e, a = We(r), [l] = e.propsOptions; + let d = !1; + if ( + // always force full diff in dev + // - #1942 if hmr is enabled with sfc component + // - vite#872 non-sfc component used by sfc component + (s || o > 0) && !(o & 16) + ) { + if (o & 8) { + const c = e.vnode.dynamicProps; + for (let v = 0; v < c.length; v++) { + let w = c[v]; + if (Hi(e.emitsOptions, w)) + continue; + const z = t[w]; + if (l) + if (je(i, w)) + z !== i[w] && (i[w] = z, d = !0); + else { + const I = Qn(w); + r[I] = Bo( + l, + a, + I, + z, + e, + !1 + ); + } + else + z !== i[w] && (i[w] = z, d = !0); + } + } + } else { + Xc(e, t, r, i) && (d = !0); + let c; + for (const v in a) + (!t || // for camelCase + !je(t, v) && // it's possible the original props was passed in as kebab-case + // and converted to camelCase (#955) + ((c = ss(v)) === v || !je(t, c))) && (l ? n && // for camelCase + (n[v] !== void 0 || // for kebab-case + n[c] !== void 0) && (r[v] = Bo( + l, + a, + v, + void 0, + e, + !0 + )) : delete r[v]); + if (i !== a) + for (const v in i) + (!t || !je(t, v)) && (delete i[v], d = !0); + } + d && Nn(e.attrs, "set", ""); +} +function Xc(e, t, n, s) { + const [r, i] = e.propsOptions; + let o = !1, a; + if (t) + for (let l in t) { + if (kr(l)) + continue; + const d = t[l]; + let c; + r && je(r, c = Qn(l)) ? !i || !i.includes(c) ? n[c] = d : (a || (a = {}))[c] = d : Hi(e.emitsOptions, l) || (!(l in s) || d !== s[l]) && (s[l] = d, o = !0); + } + if (i) { + const l = We(n), d = a || tt; + for (let c = 0; c < i.length; c++) { + const v = i[c]; + n[v] = Bo( + r, + l, + v, + d[v], + e, + !je(d, v) + ); + } + } + return o; +} +function Bo(e, t, n, s, r, i) { + const o = e[n]; + if (o != null) { + const a = je(o, "default"); + if (a && s === void 0) { + const l = o.default; + if (o.type !== Function && !o.skipFactory && ke(l)) { + const { propsDefaults: d } = r; + if (n in d) + s = d[n]; + else { + const c = Dr(r); + s = d[n] = l.call( + null, + t + ), c(); + } + } else + s = l; + r.ce && r.ce._setProp(n, s); + } + o[ + 0 + /* shouldCast */ + ] && (i && !a ? s = !1 : o[ + 1 + /* shouldCastTrue */ + ] && (s === "" || s === ss(n)) && (s = !0)); + } + return s; +} +const xh = /* @__PURE__ */ new WeakMap(); +function Zc(e, t, n = !1) { + const s = n ? xh : t.propsCache, r = s.get(e); + if (r) + return r; + const i = e.props, o = {}, a = []; + let l = !1; + if (!ke(e)) { + const c = (v) => { + l = !0; + const [w, z] = Zc(v, t, !0); + Pt(o, w), z && a.push(...z); + }; + !n && t.mixins.length && t.mixins.forEach(c), e.extends && c(e.extends), e.mixins && e.mixins.forEach(c); + } + if (!i && !l) + return ut(e) && s.set(e, Ms), Ms; + if (be(i)) + for (let c = 0; c < i.length; c++) { + const v = Qn(i[c]); + ol(v) && (o[v] = tt); + } + else if (i) + for (const c in i) { + const v = Qn(c); + if (ol(v)) { + const w = i[c], z = o[v] = be(w) || ke(w) ? { type: w } : Pt({}, w), I = z.type; + let K = !1, F = !0; + if (be(I)) + for (let oe = 0; oe < I.length; ++oe) { + const fe = I[oe], pe = ke(fe) && fe.name; + if (pe === "Boolean") { + K = !0; + break; + } else pe === "String" && (F = !1); + } + else + K = ke(I) && I.name === "Boolean"; + z[ + 0 + /* shouldCast */ + ] = K, z[ + 1 + /* shouldCastTrue */ + ] = F, (K || je(z, "default")) && a.push(v); + } + } + const d = [o, a]; + return ut(e) && s.set(e, d), d; +} +function ol(e) { + return e[0] !== "$" && !kr(e); +} +const ha = (e) => e === "_" || e === "__" || e === "_ctx" || e === "$stable", da = (e) => be(e) ? e.map(gn) : [gn(e)], Ah = (e, t, n) => { + if (t._n) + return t; + const s = Qf((...r) => da(t(...r)), n); + return s._c = !1, s; +}, Jc = (e, t, n) => { + const s = e._ctx; + for (const r in e) { + if (ha(r)) continue; + const i = e[r]; + if (ke(i)) + t[r] = Ah(r, i, s); + else if (i != null) { + const o = da(i); + t[r] = () => o; + } + } +}, Qc = (e, t) => { + const n = da(t); + e.slots.default = () => n; +}, eu = (e, t, n) => { + for (const s in t) + (n || !ha(s)) && (e[s] = t[s]); +}, Sh = (e, t, n) => { + const s = e.slots = Gc(); + if (e.vnode.shapeFlag & 32) { + const r = t.__; + r && Io(s, "__", r, !0); + const i = t._; + i ? (eu(s, t, n), n && Io(s, "_", i, !0)) : Jc(t, s); + } else t && Qc(e, t); +}, Eh = (e, t, n) => { + const { vnode: s, slots: r } = e; + let i = !0, o = tt; + if (s.shapeFlag & 32) { + const a = t._; + a ? n && a === 1 ? i = !1 : eu(r, t, n) : (i = !t.$stable, Jc(t, r)), o = t; + } else t && (Qc(e, t), o = { default: 1 }); + if (i) + for (const a in r) + !ha(a) && o[a] == null && delete r[a]; +}, jt = zh; +function Ch(e) { + return Rh(e); +} +function Rh(e, t) { + const n = Fi(); + n.__VUE__ = !0; + const { + insert: s, + remove: r, + patchProp: i, + createElement: o, + createText: a, + createComment: l, + setText: d, + setElementText: c, + parentNode: v, + nextSibling: w, + setScopeId: z = _n, + insertStaticContent: I + } = e, K = (g, y, C, P = null, $ = null, L = null, G = void 0, q = null, W = !!y.dynamicChildren) => { + if (g === y) + return; + g && !cr(g, y) && (P = pt(g), Je(g, $, L, !0), g = null), y.patchFlag === -2 && (W = !1, y.dynamicChildren = null); + const { type: U, ref: te, shapeFlag: X } = y; + switch (U) { + case qi: + F(g, y, C, P); + break; + case ts: + oe(g, y, C, P); + break; + case oi: + g == null && fe(y, C, P, G); + break; + case ze: + ve( + g, + y, + C, + P, + $, + L, + G, + q, + W + ); + break; + default: + X & 1 ? N( + g, + y, + C, + P, + $, + L, + G, + q, + W + ) : X & 6 ? Ke( + g, + y, + C, + P, + $, + L, + G, + q, + W + ) : (X & 64 || X & 128) && U.process( + g, + y, + C, + P, + $, + L, + G, + q, + W, + Mt + ); + } + te != null && $ ? Ar(te, g && g.ref, L, y || g, !y) : te == null && g && g.ref != null && Ar(g.ref, null, L, g, !0); + }, F = (g, y, C, P) => { + if (g == null) + s( + y.el = a(y.children), + C, + P + ); + else { + const $ = y.el = g.el; + y.children !== g.children && d($, y.children); + } + }, oe = (g, y, C, P) => { + g == null ? s( + y.el = l(y.children || ""), + C, + P + ) : y.el = g.el; + }, fe = (g, y, C, P) => { + [g.el, g.anchor] = I( + g.children, + y, + C, + P, + g.el, + g.anchor + ); + }, pe = ({ el: g, anchor: y }, C, P) => { + let $; + for (; g && g !== y; ) + $ = w(g), s(g, C, P), g = $; + s(y, C, P); + }, R = ({ el: g, anchor: y }) => { + let C; + for (; g && g !== y; ) + C = w(g), r(g), g = C; + r(y); + }, N = (g, y, C, P, $, L, G, q, W) => { + y.type === "svg" ? G = "svg" : y.type === "math" && (G = "mathml"), g == null ? Y( + y, + C, + P, + $, + L, + G, + q, + W + ) : Oe( + g, + y, + $, + L, + G, + q, + W + ); + }, Y = (g, y, C, P, $, L, G, q) => { + let W, U; + const { props: te, shapeFlag: X, transition: S, dirs: O } = g; + if (W = g.el = o( + g.type, + L, + te && te.is, + te + ), X & 8 ? c(W, g.children) : X & 16 && Te( + g.children, + W, + null, + P, + $, + fo(g, L), + G, + q + ), O && us(g, null, P, "created"), Z(W, g, g.scopeId, G, P), te) { + for (const J in te) + J !== "value" && !kr(J) && i(W, J, null, te[J], L, P); + "value" in te && i(W, "value", null, te.value, L), (U = te.onVnodeBeforeMount) && hn(U, P, g); + } + O && us(g, null, P, "beforeMount"); + const V = Ih($, S); + V && S.beforeEnter(W), s(W, y, C), ((U = te && te.onVnodeMounted) || V || O) && jt(() => { + U && hn(U, P, g), V && S.enter(W), O && us(g, null, P, "mounted"); + }, $); + }, Z = (g, y, C, P, $) => { + if (C && z(g, C), P) + for (let L = 0; L < P.length; L++) + z(g, P[L]); + if ($) { + let L = $.subTree; + if (y === L || ou(L.type) && (L.ssContent === y || L.ssFallback === y)) { + const G = $.vnode; + Z( + g, + G, + G.scopeId, + G.slotScopeIds, + $.parent + ); + } + } + }, Te = (g, y, C, P, $, L, G, q, W = 0) => { + for (let U = W; U < g.length; U++) { + const te = g[U] = q ? Yn(g[U]) : gn(g[U]); + K( + null, + te, + y, + C, + P, + $, + L, + G, + q + ); + } + }, Oe = (g, y, C, P, $, L, G) => { + const q = y.el = g.el; + let { patchFlag: W, dynamicChildren: U, dirs: te } = y; + W |= g.patchFlag & 16; + const X = g.props || tt, S = y.props || tt; + let O; + if (C && fs(C, !1), (O = S.onVnodeBeforeUpdate) && hn(O, C, y, g), te && us(y, g, C, "beforeUpdate"), C && fs(C, !0), (X.innerHTML && S.innerHTML == null || X.textContent && S.textContent == null) && c(q, ""), U ? _e( + g.dynamicChildren, + U, + q, + C, + P, + fo(y, $), + L + ) : G || ge( + g, + y, + q, + null, + C, + P, + fo(y, $), + L, + !1 + ), W > 0) { + if (W & 16) + Fe(q, X, S, C, $); + else if (W & 2 && X.class !== S.class && i(q, "class", null, S.class, $), W & 4 && i(q, "style", X.style, S.style, $), W & 8) { + const V = y.dynamicProps; + for (let J = 0; J < V.length; J++) { + const ae = V[J], we = X[ae], Ee = S[ae]; + (Ee !== we || ae === "value") && i(q, ae, we, Ee, $, C); + } + } + W & 1 && g.children !== y.children && c(q, y.children); + } else !G && U == null && Fe(q, X, S, C, $); + ((O = S.onVnodeUpdated) || te) && jt(() => { + O && hn(O, C, y, g), te && us(y, g, C, "updated"); + }, P); + }, _e = (g, y, C, P, $, L, G) => { + for (let q = 0; q < y.length; q++) { + const W = g[q], U = y[q], te = ( + // oldVNode may be an errored async setup() component inside Suspense + // which will not have a mounted element + W.el && // - In the case of a Fragment, we need to provide the actual parent + // of the Fragment itself so it can move its children. + (W.type === ze || // - In the case of different nodes, there is going to be a replacement + // which also requires the correct parent container + !cr(W, U) || // - In the case of a component, it could contain anything. + W.shapeFlag & 198) ? v(W.el) : ( + // In other cases, the parent container is not actually used so we + // just pass the block element here to avoid a DOM parentNode call. + C + ) + ); + K( + W, + U, + te, + null, + P, + $, + L, + G, + !0 + ); + } + }, Fe = (g, y, C, P, $) => { + if (y !== C) { + if (y !== tt) + for (const L in y) + !kr(L) && !(L in C) && i( + g, + L, + y[L], + null, + $, + P + ); + for (const L in C) { + if (kr(L)) continue; + const G = C[L], q = y[L]; + G !== q && L !== "value" && i(g, L, q, G, $, P); + } + "value" in C && i(g, "value", y.value, C.value, $); + } + }, ve = (g, y, C, P, $, L, G, q, W) => { + const U = y.el = g ? g.el : a(""), te = y.anchor = g ? g.anchor : a(""); + let { patchFlag: X, dynamicChildren: S, slotScopeIds: O } = y; + O && (q = q ? q.concat(O) : O), g == null ? (s(U, C, P), s(te, C, P), Te( + // #10007 + // such fragment like `<>` will be compiled into + // a fragment which doesn't have a children. + // In this case fallback to an empty array + y.children || [], + C, + te, + $, + L, + G, + q, + W + )) : X > 0 && X & 64 && S && // #2715 the previous fragment could've been a BAILed one as a result + // of renderSlot() with no valid children + g.dynamicChildren ? (_e( + g.dynamicChildren, + S, + C, + $, + L, + G, + q + ), // #2080 if the stable fragment has a key, it's a