/* Vanna chat theme — ClubPetro * Paleta espelhada do widget de referência (test.html): * - Brand orange: #F46A1F (primário / interativo) * - Brand orange dark: #DB5510 (badges, hover, accent forte) * - Purple deep: #4A1A56 (texto / estrutural) * - Purple medium: #7B2D8E (info / divisores secundários) * - Cream bg: #F8EFE2 (header, surfaces warm) * - Cream border: #EADFCB (borders sobre cream) * - Meta/muted: #8B7B6E * - Font: Open Sans * * CSS custom properties pierce the Shadow DOM, so overriding them on the * host element retemas tudo dentro do componente. * * Reference (todos os tokens disponíveis): * vanna/frontends/webcomponent/src/styles/vanna-design-tokens.ts */ /* Estratégia dual: * - vanna-chat: matchea o host element no documento (cascade externo). * - :host: matchea o host de qualquer shadow root onde este sheet * for adotado via adoptedStyleSheets (override em todo nested * custom element como vanna-message, plotly-chart, etc., já que * eles re-declaram os tokens via :host). * * Google Fonts é carregado via no HTML — @import não funciona * em CSSStyleSheet construída via replaceSync(). */ :host, vanna-chat { /* === Brand accent (orange) === */ --vanna-accent-primary-default: #f46a1f; --vanna-accent-primary-stronger: #db5510; --vanna-accent-primary-strongest: #b8460e; --vanna-accent-primary-subtle: rgba(244, 106, 31, 0.12); --vanna-accent-primary-hover: #e55c13; /* "Positive" também ancora no laranja pra manter coerência da marca */ --vanna-accent-positive-default: #f46a1f; --vanna-accent-positive-stronger: #db5510; --vanna-accent-positive-subtle: rgba(244, 106, 31, 0.12); /* Negativo continua vermelho (semântica de erro deve diferir da marca) */ --vanna-accent-negative-default: #dc2626; --vanna-accent-negative-stronger: #b91c1c; --vanna-accent-negative-subtle: rgba(220, 38, 38, 0.1); /* Warning em amber pra distinguir do laranja-primário */ --vanna-accent-warning-default: #d97706; --vanna-accent-warning-stronger: #b45309; --vanna-accent-warning-subtle: rgba(217, 119, 6, 0.1); /* === Foreground (texto) — roxo deep do widget de referência === */ --vanna-foreground-default: #4a1a56; --vanna-foreground-dimmer: #6b4673; --vanna-foreground-dimmest: #8b7b6e; /* === Backgrounds (cream warm pra ecoar fundo da logo + página) === * root/default ficam branco puro (legibilidade nas bolhas/tabelas); * higher/subtle/lower puxam #F8EFE2 (cream do test.html) pra dar * identidade em surfaces secundárias (sidebar, áreas de input, * separadores). */ --vanna-background-root: #ffffff; --vanna-background-default: #ffffff; --vanna-background-higher: #f8efe2; --vanna-background-highest: #f0e2c8; --vanna-background-subtle: #fbf5ea; --vanna-background-lower: #f8efe2; /* === Outlines / borders — cream-tinted pro look warm + brand orange * pro estado de hover/focus. */ --vanna-outline-default: rgba(244, 106, 31, 0.25); --vanna-outline-dimmer: #eadfcb; --vanna-outline-dimmest: #f3e8d2; --vanna-outline-hover: #f46a1f; /* === Typography === */ --vanna-font-family-default: "Open Sans", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* === Shape — bolhas um pouco menos arredondadas pra ar mais corporativo === */ --vanna-chat-bubble-radius: 14px; --vanna-chat-bubble-radius-sm: 8px; } /* ============================================================================ * Containment fixes — impedem mensagem/tabela de estourar a largura do chat. * * Estes seletores casam elementos *dentro* dos shadow roots onde este sheet * é adoptado (vanna-message, vanna-chat). Não dependem de :host. * ========================================================================== */ /* Bolha de mensagem: * 1) Texto puro mantém o limite original do upstream (~580px) — preserva * a estética de bolha de chat. Só adicionamos min-width:0 + overflow-wrap * pra texto longo quebrar dentro da bolha em vez de vazar. * 2) Quando a mensagem do assistente tem tabela/chart embutido, ampliamos * o max-width pra 100% — caso contrário a tabela empurraria a bolha * além do limite (display:flex sem min-width:0 nos children). */ .message { min-width: 0; box-sizing: border-box; } .message-content { min-width: 0; max-width: 100%; overflow-wrap: anywhere; word-break: break-word; box-sizing: border-box; } /* Headers de markdown dentro do balão (### etc., gerados pelo mdToHtml do embed-demo.html). Sem essas regras, o browser usa default que é grande demais e quebra o ritmo do balão. */ .message-content h1, .message-content h2, .message-content h3, .message-content h4, .message-content h5, .message-content h6 { font-weight: 600; line-height: 1.3; margin: 0.7em 0 0.3em 0; } .message-content h1 { font-size: 1.25em; } .message-content h2 { font-size: 1.15em; } .message-content h3 { font-size: 1.05em; } .message-content h4 { font-size: 1em; } .message-content h5 { font-size: 0.95em; } .message-content h6 { font-size: 0.9em; opacity: 0.85; } /* Primeiro header sem margem-top — evita gap visível na borda do balão. */ .message-content > h1:first-child, .message-content > h2:first-child, .message-content > h3:first-child, .message-content > h4:first-child, .message-content > h5:first-child, .message-content > h6:first-child { margin-top: 0; } /* Esconder o FAB nativo quando minimizado — substituímos por um CTA custom (.vanna-cta) renderizado fora do shadow DOM em embed-demo.html / no app React. A regra :host(.minimized) é a única que vence o posicionamento fixed do componente. * * overflow:hidden é cinto-de-segurança: o host fica 0x0 com overflow * clippando o .chat-layout interno (que mantemos com dimensões naturais * — vê regra abaixo), evitando o crash do ResizeObserver do Plotly. */ :host(.minimized) { background: transparent !important; box-shadow: none !important; width: 0 !important; height: 0 !important; overflow: hidden !important; pointer-events: none !important; } :host(.minimized) .minimized-icon { display: none !important; } /* Plotly ResizeObserver crash defense. Upstream (vanna-chat.ts:103-105) * faz `:host(.minimized) .chat-layout { display: none }` quando o chat * minimiza. Plotly tem um ResizeObserver no container do chart que dispara * `_t [as relayout]` em cada mutação de tamanho; quando o container vai * pra display:none, o `gd._fullLayout` interno fica undefined e o relayout * quebra com `TypeError: Cannot read properties of undefined (reading * 'width')` (kt at vanna-components.js:22888). * * Mantemos display:grid (cancela o display:none upstream) + dimensões * naturais — o host já é 0x0 com overflow:hidden, então o chat-layout * é clippado visualmente sem precisar mudar suas próprias dimensões. * Plotly não vê o container colapsar pra 0x0 e o ResizeObserver fica * quieto. pointer-events:none impede interação acidental. */ :host(.minimized) .chat-layout { display: grid !important; pointer-events: none !important; } /* CTA abre direto em maximized; estado intermediário "normal" não é exposto ao usuário. Esconde os botões de restore (que voltariam de maximized → normal) e maximize. Sobra só o .minimize, que dispara windowState='minimized' e o CTA volta a aparecer. */ .window-control-btn.restore, .window-control-btn.maximize { display: none !important; } .message.assistant:has(.rich-dataframe, .rich-component, .plotly-chart) { max-width: 100%; width: 100%; } /* Tabela de resultados (rich-dataframe): * - .dataframe-table-container já tem overflow:auto, mas falta limitar * o max-width do rich-component (wrapper) ao tamanho da bolha. * - Forçando 100% + box-sizing, a tabela com N colunas ganha scroll * horizontal interno em vez de empurrar a bolha. */ .rich-component, .rich-dataframe, .dataframe-table-container { max-width: 100%; width: 100%; box-sizing: border-box; } .dataframe-table-container { overflow-x: auto; overflow-y: auto; } /* Cabeçalhos longos quebram em duas linhas em vez de forçar coluna larga; * células de dado não quebram (preserva alinhamento numérico). */ .dataframe-table th { white-space: normal; overflow-wrap: anywhere; } .dataframe-table td { white-space: nowrap; } /* Avatar do header — substitui as iniciais "AV" pela logo ClubPetro. * Upstream renderiza
${initials}
num shadow * root; este sheet é adotado lá dentro via attachShadow patch (ver * embed-demo.html), então o seletor de classe vence o :host upstream em * specificity igual + ordem (adotado vai por último). font-size:0 esconde * as iniciais sem mudar o layout grid 44x44 do upstream. */ .chat-avatar { background-color: transparent; background-image: url(/clubpetro-logo.svg); background-repeat: no-repeat; background-size: 100%; background-position: center; backdrop-filter: none; border: none; overflow: visible; font-size: 0; color: transparent; } /* Bolha de mensagem do usuário — laranja brand sólido (sem gradient). * Cor única alinhada ao #F46A1F do widget de referência. Texto branco * mantém contraste suficiente em semibold (≈3.4:1 WCAG AA Large). * * align-self: flex-end + width: fit-content fazem a bolha encolher ao * tamanho do conteúdo (em vez de esticar até o max-width default do * upstream). O parent .chat-messages é flex-direction:column com * align-items:stretch implícito, então sem o override a bolha enche * a coluna inteira mesmo com texto curto. * * max-width replica o do upstream (vanna-message.ts:57). Necessário * porque `width: fit-content` sozinho pode empurrar a bolha além do * limite quando o conteúdo tem strings incompressíveis (URL longa, * palavra grudada) — overflow-wrap: anywhere já está em .message-content * mas o cap explícito é cinto + suspensório. min-width: 0 garante que * flex children obedeçam ao max-width. */ .message.user { background: #f46a1f; border-color: rgba(255, 255, 255, 0.18); align-self: flex-end; width: fit-content; max-width: min(80%, 500px); min-width: 0; box-sizing: border-box; } :host([theme="dark"]) .message.user { background: #f46a1f; } /* ============================================================================ * Header — cream bg, roxo deep no título, laranja no subtítulo. * * Upstream renderiza o header com gradient laranja cheio + overlay * radial branco e texto branco. Aqui invertemos: fundo cream #F8EFE2, * título em roxo #4A1A56 e subtítulo injetado via ::after no .header-text * (upstream tem property `subtitle` mas nunca renderiza no template — * pseudo-element é mais simples que rebuild). * ========================================================================== */ .chat-header { background: #f8efe2 !important; border-bottom: 1px solid #eadfcb !important; color: #4a1a56 !important; } .chat-header::before { display: none; } .chat-title { color: #4a1a56 !important; font-weight: 700; } .header-text { gap: 2px; } .header-text::after { content: "inteligência para seu posto"; color: #f46a1f; font-size: 12px; font-weight: 600; letter-spacing: 0.01em; line-height: 1.2; } /* Window control buttons (refresh / minimize / maximize / X) — repintar * com tinta roxa pra contrastar com o cream em vez do laranja original. * * .header-top-actions / .window-controls precisam de flex-shrink:0 porque * upstream (vanna-chat.ts:204-209) só seta `margin-left:auto` no actions, * sem proteção de shrink. Em larguras justas (chat ocupando metade da * viewport via :host(.maximized){left:50vw} + título/avatar consumindo o * .header-left flex:1) o squeeze do flexbox comprime os botões a 0 e o * minimize some. Travar shrink mantém os 32x32 sempre. * * .window-control-btn ganha `flex-shrink:0` pelo mesmo motivo + width * explícito no shadow (upstream põe width:32px sem flex-basis, então em * algumas combinações o shrink ainda toca). */ .header-top-actions, .window-controls { flex-shrink: 0 !important; } .window-control-btn { background: rgba(74, 26, 86, 0.05) !important; border-color: rgba(74, 26, 86, 0.1) !important; color: #4a1a56 !important; flex-shrink: 0 !important; } .window-control-btn:hover { background: rgba(74, 26, 86, 0.12) !important; } /* Maximizado ocupa só a metade direita da tela em vez da viewport inteira. * Upstream (vanna-chat.ts:62-75) usa top/left/right/bottom = space-6 (24px), * cobrindo full-screen com margem. Override de `left` pra 50vw mantém a * sensação de "side panel" — usuário ainda vê o app por trás à esquerda. * * overflow:hidden no host é cinto-de-segurança contra qualquer descendente * (bolha, chart, tabela) que rompa as constraints internas — nada visível * vaza além das bordas arredondadas. */ :host(.maximized) { left: 50vw !important; overflow: hidden !important; } /* Containment do .chat-main — é filho direto do grid .chat-layout * (1fr quando .compact). min-width:0 + overflow:hidden impedem que um * descendente largo (ex.: tabela com muitas colunas, chart Plotly que * recalculou layout estranho) empurre o próprio chat-main além da * coluna do grid, o que arrastaria o header e cortaria os botões. */ .chat-main { min-width: 0 !important; overflow: hidden !important; } /* Spinner do status bar — repintar de teal upstream pra roxo da marca. * Upstream (vanna-status-bar.ts:190-198) usa border teal #15A8A8 + glow * teal via @keyframes spinnerGlow. Como o sheet é adotado em todo shadow * root (incluindo o do ), tanto a regra de border * quanto o redefine do @keyframes vencem o upstream (mesma especificity, * adotado depois). */ .spinner { border-color: rgba(74, 26, 86, 0.18) !important; border-top-color: #4a1a56 !important; } @keyframes spinnerGlow { 0%, 100% { filter: drop-shadow(0 0 2px rgba(74, 26, 86, 0.45)); } 50% { filter: drop-shadow(0 0 6px rgba(74, 26, 86, 0.75)); } }