/** * Single CSS string injected into the chat widget's Shadow DOM. * * Theme variables (`--vtilt-primary`, `--vtilt-font`, `--vtilt-radius`) are * set on `:host` from the ChatStore's theme signal, so component classes can * stay theme-agnostic. The widget lives inside a Shadow DOM so styles never * leak out to (or in from) the host page. * * Keep list / code-block / markdown rules in sync with * app/src/shared/styles/chat-message-content.css — the dashboard reuses the * same `.vtilt-md` / `.vtilt-user-md` class names for inbox previews. */ export declare const SHADOW_STYLES = "\n/* Load Inter so the widget looks like vTilt on every host page. The @import\n sits at the very top of the stylesheet (required by the spec) and only\n fetches the variable-axis subset we need. If the host's CSP blocks Google\n Fonts the font-family fallback chain below keeps the widget legible. */\n@import url(\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap\");\n\n:host {\n --vtilt-primary: #7B68EE;\n --vtilt-primary-shadow: rgba(123, 104, 238, 0.4);\n --vtilt-font: \"Inter var\", \"Inter\", -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n --vtilt-radius: 16px;\n --vtilt-text: #000000;\n --vtilt-text-muted: #666666;\n --vtilt-text-subtle: #888888;\n --vtilt-bg: #ffffff;\n --vtilt-bg-list: #FAFAFA;\n --vtilt-border: #E5E5E5;\n --vtilt-border-light: #EEEEEE;\n --vtilt-skeleton: #E8E8EA;\n --vtilt-online: #16A34A;\n\n all: initial;\n font-family: var(--vtilt-font);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n color: var(--vtilt-text);\n}\n\n*, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nbutton {\n font-family: inherit;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n touch-action: manipulation;\n}\n\n/* =========================================================================\n * Container + bubble + panel\n * ========================================================================= */\n\n/* The host element gets 'all: initial' inline (set in ChatRoot.tsx) so the\n host page can't position or style it. Inline styles win over ':host' rules\n in the cascade, so the host's effective font-family / color end up at the\n UA default \u2014 and shadow children inherit from there. We re-establish the\n widget typography on .vt-container (which lives *inside* the shadow tree)\n so every descendant inherits Inter + the themed text color, regardless of\n what the host page or the host element's inline reset look like. */\n.vt-container {\n position: fixed;\n bottom: var(--vt-bubble-offset-bottom, 20px);\n /* Pin the container to the bubble's intrinsic size so it keeps the\n * same bounding box even after the bubble itself goes display:none on\n * open. Without this the container collapses to 0x0 and the absolutely\n * positioned panel anchors at that zero-size point \u2014 which means the\n * opened panel no longer overlaps the bubble's last visible footprint\n * for any non-default anchor (e.g. a bubble dragged to the bottom-left\n * with data-position=\"bottom-right\" would see the panel jump 60px away\n * from where the bubble had been). With a fixed container size, the\n * panel's [data-panel-anchor] corner always coincides with the matching\n * corner of the bubble, regardless of drag offset. */\n width: 60px;\n height: 60px;\n z-index: 999999;\n font-family: var(--vtilt-font);\n font-size: 14px;\n line-height: 1.4;\n color: var(--vtilt-text);\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n.vt-container[data-position=\"bottom-right\"] {\n right: var(--vt-bubble-offset-right, 20px);\n}\n.vt-container[data-position=\"bottom-left\"] {\n left: var(--vt-bubble-offset-left, 20px);\n}\n.vt-container[data-hidden=\"true\"] { display: none; }\n\n.vt-bubble {\n width: 60px;\n height: 60px;\n border-radius: 50%;\n background: var(--vtilt-primary);\n cursor: pointer;\n /* Host is pointer-events:none; interactive islands must opt back in. */\n pointer-events: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 16px var(--vtilt-primary-shadow);\n transition: transform 0.15s ease, box-shadow 0.15s ease;\n position: relative;\n border: none;\n}\n.vt-bubble:hover { transform: scale(1.05); box-shadow: 0 6px 20px var(--vtilt-primary-shadow); }\n.vt-bubble:active { transform: scale(0.95); }\n.vt-container[data-open=\"true\"] .vt-bubble { display: none; }\n.vt-container[data-draggable=\"true\"] .vt-bubble:hover,\n.vt-container[data-draggable=\"true\"] .vt-bubble:active { transform: none; }\n\n.vt-bubble-badge {\n display: none;\n position: absolute;\n top: -4px;\n right: -4px;\n background: #E53935;\n color: white;\n font-size: 11px;\n font-weight: 700;\n min-width: 20px;\n height: 20px;\n border-radius: 10px;\n align-items: center;\n justify-content: center;\n padding: 0 6px;\n border: 2px solid white;\n}\n.vt-bubble-badge[data-visible=\"true\"] { display: flex; }\n\n/* Panel position is driven by [data-panel-anchor] on the container.\n * The chat widget recomputes the anchor from the bubble's actual viewport\n * position (see usePanelAnchor in ui/ChatRoot.tsx) so the panel always\n * extends into whichever side has more room \u2014 even after the bubble has\n * been dragged across the viewport. The default anchor (no attribute) is\n * \"bottom-right\" so the very first paint, before JS has computed the\n * anchor, matches the most common default position.\n *\n * Per-anchor max-width / max-height are exposed as the CSS variables\n * --vt-panel-max-w and --vt-panel-max-h. JS sets them to the available\n * room from the chosen anchor (capped by the panel's intrinsic 380x600\n * design size), so on small or oddly-shaped viewports the panel never\n * overruns the opposite edge. The min(...) wrapper falls back to the\n * original \"viewport minus 40px\" formula when the variables aren't set. */\n.vt-panel {\n display: none;\n flex-direction: column;\n position: absolute;\n bottom: 0;\n right: 0;\n width: 380px;\n height: 600px;\n pointer-events: auto;\n /* Effective size = min(intrinsic, room-from-anchor, viewport-minus-margin).\n * The viewport term is a backstop: while the chat is open we deliberately\n * freeze the per-anchor variable, so a viewport resize during open could\n * otherwise leave a stale value that overruns the opposite edge. */\n max-width: min(380px, var(--vt-panel-max-w, calc(100vw - 40px)), calc(100vw - 40px));\n max-height: min(600px, var(--vt-panel-max-h, calc(100vh - 40px)), calc(100vh - 40px));\n max-height: min(600px, var(--vt-panel-max-h, calc(100dvh - 40px)), calc(100dvh - 40px));\n background: var(--vtilt-bg);\n border-radius: var(--vtilt-radius);\n box-shadow: 0 5px 40px rgba(0, 0, 0, 0.16);\n overflow: hidden;\n}\n.vt-container[data-panel-anchor=\"bottom-left\"] .vt-panel { right: auto; left: 0; }\n.vt-container[data-panel-anchor=\"top-right\"] .vt-panel { bottom: auto; top: 0; }\n.vt-container[data-panel-anchor=\"top-left\"] .vt-panel { bottom: auto; top: 0; right: auto; left: 0; }\n.vt-container[data-open=\"true\"] .vt-panel { display: flex; }\n.vt-container[data-open=\"true\"] .vt-panel.vt-opening {\n animation: vt-open 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n /* The animation scales from 0.4 to 1; transform-origin decides which\n * corner of the panel that scale grows from. JS sets --vt-panel-origin\n * to the same corner usePanelAnchor picked for [data-panel-anchor], so\n * the panel always appears to \"pop\" out of the bubble. The fallback\n * matches the default bottom-right anchor used before JS computes a\n * direction (or in environments where the variable isn't set). */\n transform-origin: var(--vt-panel-origin, bottom right);\n}\n\n@keyframes vt-open {\n from { opacity: 0; transform: scale(0.4); }\n to { opacity: 1; transform: scale(1); }\n}\n\n/* =========================================================================\n * Header\n * ========================================================================= */\n\n.vt-header {\n background: var(--vtilt-bg);\n border-bottom: 1px solid var(--vtilt-border);\n padding: 18px 16px;\n padding-top: max(18px, env(safe-area-inset-top, 18px));\n display: flex;\n align-items: center;\n min-height: 60px;\n flex-shrink: 0;\n gap: 12px;\n}\n.vt-header[data-view=\"list\"] {\n justify-content: space-between;\n}\n.vt-header[data-view=\"conversation\"] {\n padding: 12px 16px;\n padding-top: max(12px, env(safe-area-inset-top, 12px));\n}\n\n.vt-header-title { font-weight: 600; font-size: 17px; color: var(--vtilt-text); }\n.vt-header-info { flex: 1; min-width: 0; }\n.vt-header-name { font-weight: 600; font-size: 16px; color: var(--vtilt-text); }\n.vt-header-status {\n font-size: 13px;\n color: var(--vtilt-online);\n display: flex;\n align-items: center;\n gap: 5px;\n margin-top: 1px;\n}\n.vt-header-status::before {\n content: \"\";\n width: 7px;\n height: 7px;\n background: var(--vtilt-online);\n border-radius: 50%;\n}\n\n.vt-iconbtn {\n background: transparent;\n border: none;\n color: var(--vtilt-text-muted);\n padding: 6px;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n}\n.vt-iconbtn:hover { color: var(--vtilt-text); background: #F0F0F0; }\n.vt-header-back { margin-left: -6px; }\n.vt-header-close { margin-right: -6px; }\n\n.vt-header-avatar {\n width: 44px;\n height: 44px;\n border-radius: 50%;\n background: var(--vtilt-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: white;\n}\n.vt-header-avatar[data-mode=\"support\"] { background: #DEDEDE; color: #666; }\n\n/* =========================================================================\n * Content area (channel list, conversation)\n * ========================================================================= */\n\n.vt-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n min-height: 0;\n background: var(--vtilt-bg);\n}\n\n/* Channel list ----------------------------------------------------------- */\n\n.vt-list-root {\n flex: 1;\n /* Hidden until data-overflowing \u2014 avoids iOS rubber-band on a short list. */\n overflow-y: hidden;\n overscroll-behavior: none;\n -webkit-overflow-scrolling: touch;\n min-height: 0;\n}\n.vt-list-root[data-overflowing=\"true\"] {\n overflow-y: auto;\n overscroll-behavior: contain;\n}\n\n.vt-list-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n text-align: center;\n}\n.vt-list-empty-icon {\n width: 72px;\n height: 72px;\n margin-bottom: 24px;\n background: var(--vtilt-primary);\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n.vt-list-empty-title {\n font-size: 18px;\n font-weight: 600;\n color: var(--vtilt-text);\n margin-bottom: 12px;\n line-height: 1.4;\n max-width: 280px;\n}\n.vt-list-empty-subtitle {\n font-size: 14px;\n color: var(--vtilt-text-muted);\n margin-bottom: 28px;\n}\n\n.vt-channel-item {\n padding: 14px 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n background: var(--vtilt-bg);\n border: none;\n width: 100%;\n text-align: left;\n border-bottom: 1px solid var(--vtilt-border-light);\n transition: background 0.1s ease;\n font: inherit;\n color: inherit;\n}\n.vt-channel-item:hover { background: #F5F5F5; }\n.vt-channel-item:active { background: #EBEBEB; }\n\n.vt-channel-avatar {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: var(--vtilt-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: white;\n}\n.vt-channel-avatar[data-mode=\"support\"] { background: #DEDEDE; color: #666; }\n\n.vt-channel-body { flex: 1; min-width: 0; }\n.vt-channel-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n}\n.vt-channel-name {\n font-weight: 500;\n font-size: 15px;\n color: var(--vtilt-text);\n line-height: 1.2;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.vt-channel-name[data-unread=\"true\"] { font-weight: 600; }\n.vt-channel-time {\n font-size: 13px;\n color: var(--vtilt-text-subtle);\n white-space: nowrap;\n flex-shrink: 0;\n}\n.vt-channel-preview {\n font-size: 14px;\n color: var(--vtilt-text-subtle);\n font-weight: 400;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n flex: 1;\n min-width: 0;\n line-height: 1.4;\n}\n.vt-channel-preview[data-unread=\"true\"] { color: #333; }\n.vt-channel-unread {\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: var(--vtilt-primary);\n border-radius: 10px;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 600;\n color: #FFFFFF;\n line-height: 1;\n}\n\n.vt-list-footer {\n padding: 16px;\n padding-bottom: max(16px, env(safe-area-inset-bottom, 16px));\n border-top: 1px solid var(--vtilt-border);\n}\n\n.vt-new-channel-btn {\n width: 100%;\n background: var(--vtilt-primary);\n color: white;\n border: none;\n border-radius: 100px;\n padding: 14px 24px;\n font-weight: 500;\n font-size: 15px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 10px;\n box-shadow: 0 2px 8px var(--vtilt-primary-shadow);\n transition: opacity 0.1s ease;\n}\n.vt-new-channel-btn:hover { opacity: 0.9; }\n.vt-new-channel-btn:active { opacity: 0.8; }\n\n/* Conversation ----------------------------------------------------------- */\n\n.vt-messages-wrapper {\n position: relative;\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.vt-messages {\n flex: 1;\n /* Hidden until data-overflowing so few-message chats are not a scroll\n * surface \u2014 iOS rubber-band on an empty list pans the visual viewport\n * and drags the composer off the keyboard. */\n overflow-y: hidden;\n overscroll-behavior: none;\n -webkit-overflow-scrolling: touch;\n padding: 20px 16px 24px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-height: 0;\n background: var(--vtilt-bg-list);\n}\n.vt-messages[data-overflowing=\"true\"] {\n overflow-y: auto;\n /* Contain overscroll so dragging past the top/bottom of the list does not\n * chain to (and scroll) the host page behind the open mobile panel. */\n overscroll-behavior: contain;\n}\n\n.vt-message {\n display: flex;\n flex-direction: column;\n animation: vt-message-in 0.22s ease-out both;\n}\n.vt-message[data-direction=\"in\"] { align-items: flex-start; }\n.vt-message[data-direction=\"out\"] { align-items: flex-end; }\n\n.vt-message-row {\n display: flex;\n gap: 10px;\n align-items: flex-end;\n max-width: 85%;\n}\n.vt-message[data-direction=\"out\"] .vt-message-row {\n flex-direction: row-reverse;\n}\n\n.vt-message-avatar {\n width: 32px;\n height: 32px;\n border-radius: 50%;\n background: var(--vtilt-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n flex-shrink: 0;\n color: white;\n}\n.vt-message-avatar[data-mode=\"support\"] { background: #DEDEDE; color: #666; }\n\n.vt-message-bubble {\n padding: 10px 14px;\n background: var(--vtilt-bg);\n border-radius: 20px 20px 20px 4px;\n font-size: 14px;\n line-height: 1.4;\n color: var(--vtilt-text);\n word-wrap: break-word;\n overflow-wrap: anywhere;\n min-width: 0;\n}\n.vt-message[data-direction=\"out\"] .vt-message-bubble {\n background: var(--vtilt-primary);\n color: white;\n border-radius: 20px 20px 4px 20px;\n}\n\n.vt-message-meta {\n font-size: 12px;\n color: var(--vtilt-text-subtle);\n margin-top: 6px;\n}\n.vt-message[data-direction=\"in\"] .vt-message-meta { margin-left: 46px; }\n.vt-message[data-direction=\"out\"] .vt-message-meta { margin-right: 4px; }\n\n.vt-message-retry {\n font: inherit;\n font-size: inherit;\n color: var(--vtilt-error, #dc2626);\n background: none;\n border: none;\n padding: 0;\n cursor: pointer;\n text-decoration: underline;\n}\n\n@keyframes vt-message-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Markdown content rules (parity with chat-message-content.css \u2014 Slack density) */\n.vt-md {\n max-width: 100%;\n overflow-wrap: anywhere;\n word-break: break-word;\n font-size: inherit;\n line-height: 1.4;\n}\n.vt-md p { margin: 0 0 0.35em 0; }\n.vt-md p:last-child { margin-bottom: 0; }\n.vt-md :is(b, strong) { font-weight: 600; }\n.vt-md :is(i, em) { font-style: italic; }\n.vt-md u { text-decoration: underline; }\n.vt-md :is(h1, h2, h3, h4, h5, h6) {\n font-size: 1em;\n font-weight: 600;\n margin: 0.45em 0 0.25em 0;\n line-height: 1.3;\n}\n.vt-md :is(h1, h2, h3):first-child { margin-top: 0; }\n.vt-md :is(ul, ol) {\n list-style: none;\n margin: 0.35em 0;\n padding: 0;\n}\n/* Hanging indent: marker in the left gutter; text in the padded box. */\n.vt-md li {\n position: relative;\n margin: 0.15em 0;\n padding: 0 0 0 1.5em;\n line-height: 1.4;\n}\n.vt-md ul > li::before {\n content: \"\u2022\";\n position: absolute;\n left: 0.4em;\n top: 0;\n line-height: 1.4;\n}\n.vt-md ul ul > li::before { content: \"\u25E6\"; }\n.vt-md ol { counter-reset: vt-ol; }\n.vt-md ol > li { counter-increment: vt-ol; }\n.vt-md ol > li::before {\n content: counter(vt-ol) \".\";\n position: absolute;\n left: 0;\n top: 0;\n width: 1.25em;\n text-align: right;\n line-height: 1.4;\n}\n.vt-md ol ol { counter-reset: vt-ol; }\n.vt-md li > :is(ul, ol) {\n margin: 0.15em 0 0 0;\n}\n.vt-md code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 0.85em;\n background: rgba(0, 0, 0, 0.06);\n padding: 0.1em 0.3em;\n border-radius: 4px;\n}\n.vt-md pre {\n margin: 0.4em 0;\n padding: 0.4em 0.5em;\n border-radius: 6px;\n background: rgba(0, 0, 0, 0.06);\n overflow-x: auto;\n white-space: pre-wrap;\n tab-size: 2;\n}\n.vt-md pre:first-child { margin-top: 0; }\n.vt-md pre:last-child { margin-bottom: 0; }\n.vt-md pre code {\n background: none;\n padding: 0;\n font-size: 0.8125em;\n line-height: 1.45;\n}\n.vt-md li > pre {\n margin-left: 0;\n margin-right: 0;\n}\n.vt-md blockquote {\n margin: 0.35em 0;\n padding-left: 0.65em;\n border-left: 3px solid rgba(0, 0, 0, 0.15);\n}\n.vt-md a {\n color: inherit;\n text-decoration: underline;\n cursor: pointer;\n pointer-events: auto;\n}\n\n/* Brand-colored \"out\" bubble inverts code/pre/quote backdrop. */\n.vt-user-md code { background: rgba(255, 255, 255, 0.2); }\n.vt-user-md pre { background: rgba(255, 255, 255, 0.15); }\n.vt-user-md pre code { background: none; }\n.vt-user-md blockquote { border-left-color: rgba(255, 255, 255, 0.35); }\n\n/* Typing bubble + new-messages pill -------------------------------------\n *\n * The typing indicator is no longer a separate strip above the composer\n * - it is an inline .vt-message rendered as the last child of the\n * message list, reusing the avatar + bubble shape so the dots morph\n * into the real message when it arrives. The only typing-specific\n * style is the [data-typing] modifier that switches the bubble layout\n * to inline-flex so the three dots sit horizontally instead of\n * stacking. The dot animation rules are reused by TypingBubble. */\n\n.vt-message-bubble[data-typing=\"true\"] {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n /* Critical: do NOT override the base .vt-message-bubble padding here.\n * The row uses align-items: flex-end, so a bubble shorter than a\n * single-line text bubble would bottom-align with the avatar and\n * visually sit lower than where the real message will appear. We\n * inherit 10px 14px from the base, then enforce a min content\n * height of one text line (14px font-size * 1.4 line-height) so the\n * total bubble height matches a single-line text bubble exactly.\n * When the dots morph into text the bubble stays put \u2014 no jolt. */\n min-height: 1.4em;\n}\n.vt-typing-dot {\n width: 6px;\n height: 6px;\n background: #B5B5B5;\n border-radius: 50%;\n animation: vt-typing-bounce 1.2s infinite;\n}\n.vt-typing-dot:nth-child(2) { animation-delay: 0.2s; }\n.vt-typing-dot:nth-child(3) { animation-delay: 0.4s; }\n@keyframes vt-typing-bounce {\n 0%, 60%, 100% { opacity: 0.35; transform: translateY(0); }\n 30% { opacity: 1; transform: translateY(-2px); }\n}\n\n.vt-new-pill {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n bottom: 12px;\n background: var(--vtilt-primary);\n color: white;\n padding: 6px 14px;\n border-radius: 999px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n border: none;\n box-shadow: 0 2px 8px rgba(0,0,0,0.15);\n display: none;\n align-items: center;\n gap: 6px;\n z-index: 5;\n transition: opacity 0.15s ease;\n}\n.vt-new-pill[data-visible=\"true\"] { display: flex; }\n.vt-new-pill:hover { opacity: 0.92; }\n\n/* Closed banner + connection banner ------------------------------------- */\n\n.vt-closed-banner {\n padding: 12px 16px;\n padding-bottom: max(12px, env(safe-area-inset-bottom, 12px));\n border-top: 1px solid var(--vtilt-border);\n text-align: center;\n background: var(--vtilt-bg-list);\n flex-shrink: 0;\n font-size: 14px;\n color: var(--vtilt-text-subtle);\n}\n\n/* Connection banner.\n *\n * Two visual modes:\n * - data-kind=\"reconnecting\": slim pill. Pulsing dot + \"Reconnecting\"\n * label. Slate background \u2014 informational, not alarming.\n * - data-kind=\"failed\": actionable card. Warning icon + \"Connection\n * lost\" + Retry button. Red because the user must act.\n */\n.vt-conn-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 12px;\n border-top: 1px solid var(--vtilt-border);\n font-size: 12px;\n font-weight: 500;\n flex-shrink: 0;\n}\n.vt-conn-banner[data-kind=\"reconnecting\"] {\n background: #F8FAFC;\n color: #475569;\n justify-content: center;\n}\n.vt-conn-banner[data-kind=\"failed\"] {\n background: #FEF2F2;\n color: #991B1B;\n}\n.vt-conn-banner-icon {\n display: inline-flex;\n flex-shrink: 0;\n color: #DC2626;\n}\n.vt-conn-banner-text {\n flex: 1;\n}\n.vt-conn-banner[data-kind=\"reconnecting\"] .vt-conn-banner-text {\n flex: 0 0 auto;\n}\n.vt-conn-banner-dot {\n display: inline-block;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: #F59E0B;\n flex-shrink: 0;\n animation: vt-conn-pulse 1.4s ease-in-out infinite;\n}\n@keyframes vt-conn-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.4; transform: scale(0.85); }\n}\n.vt-conn-banner-action {\n appearance: none;\n border: 1px solid #DC2626;\n background: transparent;\n color: #DC2626;\n font: inherit;\n font-weight: 600;\n font-size: 11px;\n padding: 3px 10px;\n border-radius: 999px;\n cursor: pointer;\n flex-shrink: 0;\n transition: background 0.12s ease, color 0.12s ease;\n}\n.vt-conn-banner-action:hover,\n.vt-conn-banner-action:focus-visible {\n background: #DC2626;\n color: #FFFFFF;\n outline: none;\n}\n@media (prefers-reduced-motion: reduce) {\n .vt-conn-banner-dot {\n animation: none;\n }\n}\n\n/* Input ----------------------------------------------------------------- */\n\n.vt-input-container {\n padding: 8px 12px;\n padding-bottom: max(8px, env(safe-area-inset-bottom, 8px));\n border-top: 1px solid var(--vtilt-border);\n flex-shrink: 0;\n background: var(--vtilt-bg);\n}\n.vt-input-box {\n position: relative;\n border: 1px solid #DDD;\n border-radius: 12px;\n background: var(--vtilt-bg);\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n.vt-input-box:focus-within {\n border-color: var(--vtilt-primary);\n box-shadow: 0 0 0 2px rgba(123, 104, 238, 0.12);\n}\n.vt-input-textarea {\n width: 100%;\n box-sizing: border-box;\n border: none;\n border-radius: 0;\n padding: 12px 48px 12px 12px;\n font-size: 16px;\n line-height: 1.4;\n outline: none;\n background: transparent;\n color: var(--vtilt-text);\n resize: none;\n overflow-y: auto;\n overflow-x: hidden;\n overscroll-behavior: contain;\n min-height: 60px;\n max-height: 120px;\n font-family: inherit;\n}\n.vt-input-textarea::placeholder { color: #999; }\n.vt-input-textarea:disabled { color: #BBB; cursor: not-allowed; }\n\n.vt-input-send {\n position: absolute;\n right: 4px;\n bottom: 4px;\n background: #E5E5E5;\n color: #999;\n border: none;\n border-radius: 50%;\n padding: 0;\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background-color 0.15s ease, color 0.15s ease, opacity 0.15s ease;\n opacity: 0.6;\n}\n.vt-input-send[data-active=\"true\"] {\n background: var(--vtilt-primary);\n color: white;\n cursor: pointer;\n opacity: 1;\n}\n.vt-input-send[data-active=\"true\"]:hover { opacity: 0.9; }\n.vt-input-send[data-active=\"true\"]:active { opacity: 0.8; transform: scale(0.95); }\n.vt-input-send:disabled { cursor: not-allowed; }\n\n.vt-input-container[data-pending=\"true\"] .vt-input-box {\n background: var(--vtilt-bg-list);\n border-color: var(--vtilt-border-light);\n}\n\n/* Skeletons ------------------------------------------------------------- */\n\n@keyframes vt-skeleton-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.55; }\n}\n.vt-skeleton {\n background: var(--vtilt-skeleton);\n border-radius: 6px;\n animation: vt-skeleton-pulse 2.2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n.vt-skeleton-circle {\n background: var(--vtilt-skeleton);\n border-radius: 50%;\n animation: vt-skeleton-pulse 2.2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n/* Built-in widgets ------------------------------------------------------ */\n\n.vt-widget { margin-top: 10px; }\n.vt-widget p { margin: 0 0 10px 0; font-size: 14px; color: #333; }\n.vt-widget-input {\n font-family: inherit;\n font-size: 14px;\n padding: 8px 10px;\n border: 1px solid #DDD;\n border-radius: 8px;\n width: 100%;\n margin-bottom: 8px;\n}\n.vt-widget-input:focus { outline: none; border-color: var(--vtilt-primary); }\n.vt-widget-btn {\n padding: 10px 16px;\n background: var(--vtilt-primary);\n color: white;\n border: none;\n border-radius: 12px;\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n}\n.vt-widget-btn[data-style=\"outline\"] {\n background: transparent;\n color: var(--vtilt-primary);\n border: 2px solid var(--vtilt-primary);\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.vt-widget-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n.vt-widget-thanks { font-size: 13px; color: var(--vtilt-online); margin: 0; }\n\n/* Mobile fullscreen ----------------------------------------------------\n *\n * On viewports <=480px the open panel covers the whole device, regardless\n * of where the bubble was dragged. The rules live in a media query (not\n * a JS-driven data attribute) so they remain correct on viewport resize\n * \u2014 rotation, browser-window resize, devtools toggling \u2014 without needing\n * a re-render to update an attribute.\n *\n * \"transform: none !important\" is required because bubble-drag.ts sets\n * an inline \"transform: translate3d(...)\" on the container. Per the CSS\n * cascade, author !important declarations win over inline normal styles,\n * so the open panel snaps back to (0, 0) and fills the viewport even\n * after the user has dragged the bubble away from its anchor corner.\n */\n\n@media (max-width: 480px) {\n .vt-bubble { width: 56px; height: 56px; }\n .vt-container { width: 56px; height: 56px; }\n\n /*\n * Industry iOS keyboard shell (Zulip / NeoKai):\n * top: vv.offsetTop\n * bottom: --keyboard-height (= clientHeight - vv.height - offsetTop)\n * That box IS the visual viewport. Panel fills it; composer is a flex footer\n * so only .vt-messages scrolls. JS rewrites the CSS variables on every\n * visualViewport resize *and* scroll so the composer stays glued to the\n * keyboard when iOS focus-pans offsetTop.\n */\n .vt-container[data-open=\"true\"] {\n top: var(--vt-vv-top, 0px);\n left: var(--vt-vv-left, 0px);\n right: auto;\n bottom: var(--keyboard-height, 0px);\n width: var(--vt-vv-width, 100vw);\n height: auto;\n max-height: none;\n overflow: hidden;\n overscroll-behavior: none;\n background: var(--vtilt-bg);\n transform: none !important;\n }\n .vt-container[data-open=\"true\"] .vt-panel {\n position: absolute;\n inset: 0;\n width: 100%;\n max-width: none;\n height: 100%;\n max-height: 100%;\n border-radius: 0;\n box-shadow: none;\n overflow: hidden;\n overscroll-behavior: none;\n }\n .vt-container[data-open=\"true\"] .vt-content {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n overscroll-behavior: none;\n }\n .vt-container[data-open=\"true\"] .vt-messages-wrapper {\n flex: 1;\n min-height: 0;\n }\n .vt-container[data-open=\"true\"] .vt-messages {\n /* No pan until content overflows \u2014 pan-y on an empty list lets iOS\n * scroll the visual viewport and drag the composer while the keyboard\n * is up. */\n touch-action: none;\n overscroll-behavior: none;\n }\n .vt-container[data-open=\"true\"] .vt-messages[data-overflowing=\"true\"] {\n touch-action: pan-y;\n overscroll-behavior: contain;\n }\n .vt-container[data-open=\"true\"] .vt-input-container {\n position: relative;\n z-index: 1;\n flex-shrink: 0;\n padding-bottom: max(8px, env(safe-area-inset-bottom, 8px));\n }\n /* Keyboard replaces the home indicator \u2014 drop bottom safe-area inset. */\n .vt-container[data-open=\"true\"][data-keyboard=\"true\"] .vt-input-container {\n padding-bottom: 8px;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }\n}\n";