/* Nexus AI (v2) — SÓ o invólucro. Os balões, cartões, faixas e o composer do Atlas 2 vêm da folha da v1,
   carregada antes desta; aqui não se redesenha nada disso — corrige-se a POSIÇÃO (a doca colada à base) e
   acrescenta-se o que não existe lá: a home, o menu e o orb.

   O ORB é roxo, pequeno e ancorado na base (curador 2026-09-10). Técnica: gaussiana SVG como o BP OS,
   e SÓ transformação e opacidade animadas — o `stdDeviation` do filtro é fixo, porque animar desfoque
   quadro a quadro é o que mata um telemóvel de mil reais.

   `--nivel` é escrita pelo JS a partir do som REAL (0..1). Sem som fica 0 e o orb não se mexe. */

:root {
  /* PALETA (curador 2026-09-10): «preferia que a paleta fosse mais próxima do azul que eles usam, … tons mais
     escuros … sem violeta e mais azul». Os nomes `--ai-roxo-*` ficam (são referenciados em todo o lado); os VALORES
     são a família azul-petróleo da operadora, escurecida. */
  --ai-roxo-claro: #5ec5dd;
  --ai-roxo: #22a3c4;
  --ai-roxo-fundo: #0e7490;
  --ai-orb-home: 76px;         /* pequeno, à la Siri — e ainda maior que o da conversa */
  --ai-orb-chat: 46px;         /* na conversa é o próprio círculo do composer */
  --ai-doca-pad: 12px;
}

/* ══ A PÁGINA NUNCA ROLA — as áreas INTERNAS é que rolam ═════════════════════════════════════════════
   Curador (2026-09-10): «o app está com problema de scroll, está gigante tendo que descer». Persegui os
   últimos 14 px por três caminhos — `dvh` contra `svh`, um absoluto sem `top` no fim do documento, e o
   `display: contents` a deixar o espaço em branco do markup formar uma linha anónima. Os três eram defeitos
   reais e ficaram corrigidos; nenhum era ESTE resíduo.
   Então em vez de continuar a caçar pixels, declara-se a regra que uma aplicação tem de cumprir de
   qualquer forma: o invólucro possui o ecrã e não rola. O que precisa de rolar — a resposta, a home num
   telemóvel pequeno — rola DENTRO de si, com o seu próprio contentor. Assim nenhum resíduo futuro de
   layout volta a aparecer como barra de rolagem na página inteira. */
html, body { height: 100%; overflow: hidden; overscroll-behavior: none; }

/* `svh` e não `dvh` em todo o lado onde o objectivo é «nunca ficar maior do que se vê». Medido em
   emulação de 390×844: `100dvh` valia 858 — a viewport GRANDE, a que existe com as barras do browser
   recolhidas — e a página ficava 14 px mais alta do que a janela, ou seja com barra de rolagem para nada.
   `svh` é a viewport PEQUENA, com as barras à vista: é o pior caso, e é o único que garante que não rola. */

/* ══ A APLICAÇÃO É DE TELEMÓVEL ═══════════════════════════════════════════════════════════════════════
   Curador 2026-09-10: «é aspect ratio de celular, não desktop». Nada aqui tinha largura máxima, logo num
   ecrã largo o menu esticava de ponta a ponta e os balões atravessavam a página inteira — foi o que ele
   viu. A aplicação vive numa COLUNA de largura de telemóvel, centrada; no telemóvel a coluna é o ecrã
   todo e isto não faz diferença nenhuma, no computador passa a parecer o que é. */
:root { --ai-col: 440px; }
.ai-home { max-width: var(--ai-col); margin-inline: auto; }
@media (min-width: 520px) {
  body { background: #e3f0f4; }
  .ai-home, .ai-resposta { background: #f5fbfd; box-shadow: 0 0 0 1px rgba(0,0,0,.06); }
  @media (prefers-color-scheme: dark) {
    body { background: #04141b; }
    .ai-home, .ai-resposta { background: #07202b; box-shadow: 0 0 0 1px rgba(255,255,255,.06); }
  }
}
body {
  margin: 0; --nivel: 0; --teclado: 0px;
  background: #f5fbfd; color: #14303a;
  font: 15px/1.45 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  overscroll-behavior-y: none;
}
@media (prefers-color-scheme: dark) { body { background: #07202b; color: #e3f0f4; } }

.salto { position: absolute; left: -9999px; }
.salto:focus { left: 8px; top: 8px; background: #fff; padding: 8px 12px; border-radius: 8px; z-index: 50; }

/* ══ HOME ═════════════════════════════════════════════════════════════════════════════════════════════
   Rola se o menu não couber; a doca nunca rola com ele. */
.ai-home {
  min-height: 100svh; max-height: 100svh; overflow-y: auto; box-sizing: border-box;
  padding: max(20px, env(safe-area-inset-top)) 18px calc(var(--ai-orb-home) + 84px + var(--teclado)) 18px;
  display: flex; flex-direction: column; gap: 6px;
}
.ai-marca { align-self: flex-start; height: 40px; width: auto; margin-bottom: 14px; }
.ai-saudacao { margin: 0; font-size: clamp(20px, 5.6vw, 25px); font-weight: 620; letter-spacing: -.02em; }
.ai-sub { margin: 0 0 18px; opacity: .62; font-size: 14px; }

.ai-menu { display: flex; flex-direction: column; gap: 9px; }
.ai-item {
  display: grid; grid-template-columns: 38px 1fr 18px; align-items: center; gap: 12px;
  width: 100%; box-sizing: border-box; padding: 14px 14px; min-height: 60px;   /* > 44px de alvo */
  border: 1px solid rgba(14,116,144, .16); border-radius: 16px;
  background: rgba(255, 255, 255, .78); color: inherit;
  font: inherit; font-weight: 520; text-align: left; cursor: pointer;
  transition: transform .16s cubic-bezier(.2,.8,.2,1), background .16s linear, border-color .16s linear;
}
@media (prefers-color-scheme: dark) {
  .ai-item { background: rgba(255,255,255,.045); border-color: rgba(94,197,221,.2); }
}
.ai-item:active { transform: scale(.985); background: rgba(34,163,196, .1); }
.ai-item:focus-visible { outline: 2px solid var(--ai-roxo); outline-offset: 2px; }
.ai-ic {
  display: grid; place-items: center; width: 38px; height: 38px; border-radius: 12px;
  background: linear-gradient(150deg, rgba(94,197,221,.28), rgba(14,116,144,.2)); color: var(--ai-roxo-fundo);
}
@media (prefers-color-scheme: dark) { .ai-ic { color: var(--ai-roxo-claro); } }
.ai-ic svg { width: 21px; height: 21px; }
.ai-seta { width: 8px; height: 8px; border-right: 2px solid currentColor; border-top: 2px solid currentColor;
           transform: rotate(45deg); opacity: .32; justify-self: end; }

/* ══ O PALCO — uma resposta de cada vez ══════════════════════════════════════════════════════════════
   Curador 2026-09-10: «cadê toda a experiência siri... fugindo de ser uma conversa no whatsapp». Aqui não
   há balões, não há horas, não há pilha. Há o que a assistente acabou de dizer, grande, e as opções por
   baixo. O `#conversa` da v1 continua no DOM porque é lá que o motor escreve — e está oculto. */
.ai-resposta {
  display: flex; flex-direction: column;
  min-height: 100svh; box-sizing: border-box;
  padding-bottom: calc(var(--ai-orb-home) + 44px + var(--teclado));
  max-width: var(--ai-col); margin-inline: auto;
}
.ai-oculto { position: absolute !important; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.ai-topo { display: flex; align-items: center; gap: 10px; padding: max(10px, env(safe-area-inset-top)) 14px 10px;
           background: linear-gradient(135deg, #0b3350, #0f5f88); color: #f2fbfd; }
.ai-topo .estado { color: #a6e1ee; font-size: .8rem; }
.ai-topo-marca { border-radius: 9px; background: #fff; padding: 3px; flex: 0 0 auto; }
body:not([data-ecra="entrada"]) .ai-topo-marca { display: none; }
.ai-voltar {
  width: 40px; height: 40px; flex: 0 0 40px; border: 0; border-radius: 12px; cursor: pointer;
  background: rgba(255,255,255,.14); position: relative;
}
.ai-voltar::before {
  content: ""; position: absolute; inset: 0; margin: auto; width: 9px; height: 9px;
  border-left: 2px solid #f2fbfd; border-bottom: 2px solid #f2fbfd; transform: translateX(2px) rotate(45deg);
}

.ai-palco { flex: 1 1 auto; overflow-y: auto; padding: 22px 18px 8px; display: flex; flex-direction: column; gap: 16px; }

/* A FALA — o texto da assistente. Grande, sem caixa, sem hora: é a resposta, não uma mensagem. */
.ai-fala { margin: 0; font-size: clamp(15px, 4.3vw, 17px); line-height: 1.45; font-weight: 480; }
.ai-fala p { margin: 0 0 10px; }
.ai-fala strong { font-weight: 700; }
.ai-fala:empty { display: none; }

.ai-ouvir {
  display: inline-grid; place-items: center; width: 38px; height: 38px; margin-top: 6px;
  border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(14,116,144,.12); color: var(--ai-roxo-fundo);
}
.ai-ouvir::before { content: "▶"; font-size: 13px; margin-left: 2px; }
.ai-ouvir[data-fase="parado"]::before { content: "▶"; }
.ai-ouvir[data-fase="carregando"]::before { content: ""; width: 15px; height: 15px; margin: 0;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%;
  animation: ai-roda .8s linear infinite; }
.ai-ouvir[data-fase="falando"]::before { content: "❚❚"; font-size: 11px; margin: 0; }
@keyframes ai-roda { to { transform: rotate(360deg); } }
@media (prefers-color-scheme: dark) { .ai-ouvir { background: rgba(94,197,221,.16); color: #d2f0f7; } }

/* CARTÕES — o que se copia, o que se baixa, o que se vê. */
.ai-cartoes { display: flex; flex-direction: column; gap: 10px; }
.ai-cartoes:empty { display: none; }
.ai-cartao {
  border: 1px solid rgba(14,116,144,.18); border-radius: 16px; padding: 14px;
  background: rgba(255,255,255,.8); display: flex; flex-direction: column; gap: 10px;
}
@media (prefers-color-scheme: dark) { .ai-cartao { background: rgba(255,255,255,.05); border-color: rgba(94,197,221,.22); } }
/* `.ai-copiar-txt` deixou de ser usado: o código não vai ao ecrã, vai ao botão. A regra fica por
   compatibilidade com qualquer nó antigo em cache, com a quebra garantida — nunca a arrastar o ecrã. */
.ai-copiar-txt {
  margin: 0; font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all; overflow-wrap: anywhere; opacity: .72; max-height: 4.5em; overflow: hidden;
}
/* ⚠ SÓ SCROLL VERTICAL (curador). Um `<pre>` não quebra por omissão, e basta um bloco de código
   curto — abaixo do limiar de extração — para pôr a página a arrastar de lado num telemóvel de
   320 px. A quebra é declarada onde o markdown do motor pode aterrar, nas três superfícies. */
.ai-native-text pre, .ai-native-text code,
.ai-fala pre, .ai-fala code,
.ai-cartao pre, .ai-cartao code {
  white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; max-width: 100%;
}
.ai-acao {
  align-self: flex-start; min-height: 44px; padding: 10px 18px; border: 0; border-radius: 22px;
  background: var(--ai-roxo-fundo); color: #fff; font: inherit; font-weight: 600; cursor: pointer;
  text-decoration: none; display: inline-grid; place-items: center;
}
.ai-img { width: 100%; border-radius: 12px; display: block; cursor: zoom-in; }

/* OPÇÕES — é por aqui que se anda sem teclado. Alvo grande, uma por linha. */
.ai-opcoes { display: flex; flex-direction: column; gap: 9px; }
.ai-opcoes:empty { display: none; }
.ai-opcao {
  width: 100%; min-height: 54px; padding: 14px 18px; text-align: left;
  border: 1.5px solid var(--ai-roxo); border-radius: 16px; background: transparent;
  color: var(--ai-roxo-fundo); font: inherit; font-weight: 600; cursor: pointer;
  transition: transform .12s cubic-bezier(.2,.8,.2,1), background .15s linear;
}
@media (prefers-color-scheme: dark) { .ai-opcao { color: #d2f0f7; } }
.ai-opcao:active { transform: scale(.985); }
.ai-opcao.escolhida { background: var(--ai-roxo); color: #fff; }
.ai-opcao:disabled { opacity: .45; }

/* A PENSAR — três pontos, e só enquanto se espera de verdade. */
.ai-pensando { display: flex; gap: 6px; margin: 4px 0 0; }
.ai-pensando span { width: 8px; height: 8px; border-radius: 50%; background: var(--ai-roxo); opacity: .5;
  animation: ai-pulso 1.1s ease-in-out infinite; }
.ai-pensando span:nth-child(2) { animation-delay: .16s; }
.ai-pensando span:nth-child(3) { animation-delay: .32s; }
@keyframes ai-pulso { 0%,100% { transform: translateY(0); opacity: .38; } 50% { transform: translateY(-5px); opacity: 1; } }

/* As faixas dispensáveis (digital, lembretes) ficam discretas no fundo do palco, e nunca na identificação. */
.ai-resposta .faixa {
  /* Medido a 2026-09-10: a faixa ocupava 721→808 e a doca 761→872 — cruzavam-se, e os botões dela ficavam
     por baixo do orb, ilegíveis. Ela é dispensável; o que não é dispensável é ser LEGÍVEL quando aparece. */
  margin: 8px 18px 12px; padding: 12px 14px; border-radius: 14px; border: 1px solid rgba(14,116,144,.16);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; row-gap: 6px; font-size: .86rem;
}
.ai-resposta .faixa span { flex: 1 1 100%; line-height: 1.35; }
.ai-resposta .faixa-sim { background: var(--ai-roxo-fundo); color: #fff; }
body[data-ecra="entrada"] .faixa { display: none !important; }

/* ══ DOCA — colada à base, nos dois ecrãs ═════════════════════════════════════════════════════════════ */
.ai-doca {
  position: fixed; left: 0; right: 0; bottom: var(--teclado); z-index: 20;
  padding: var(--ai-doca-pad) 14px calc(var(--ai-doca-pad) + env(safe-area-inset-bottom));
  background: linear-gradient(to top, rgba(245,251,253,1) 46%, rgba(245,251,253,.92) 72%, rgba(245,251,253,0));
  backdrop-filter: blur(10px);
}
@media (prefers-color-scheme: dark) {
  .ai-doca { background: linear-gradient(to top, rgba(7,32,43,1) 46%, rgba(7,32,43,.92) 72%, rgba(7,32,43,0)); }
}
.ai-doca .m-warow { display: flex; align-items: flex-end; gap: 10px; }

/* ══ O TECLADO SÓ EXISTE NA ENTRADA ═══════════════════════════════════════════════════════════════════
   Curador 2026-09-10: «quero como a Siri, só interagir por voz e cliques, sem precisar digitar (tirando o
   cpf, no início...)». A caixa de texto aparece EXCLUSIVAMENTE no ecrã de identificação. Depois disso a
   doca é o orb — e mais nada. O clipe de anexo sobrevive na conversa, porque enviar a solicitação médica
   é toque, não escrita. */
body:not([data-ecra="entrada"]) .ai-doca .m-wain { display: none; }
body:not([data-ecra="entrada"]) .ai-doca .m-wapill {
  background: none; border: 0; box-shadow: none; padding: 0; flex: 0 0 auto;
}
body[data-ecra="home"] .ai-doca .m-wapill { display: none; }        /* na home nem o clipe */
body[data-ecra="home"] .ai-doca .m-warow { justify-content: center; }
body[data-ecra="conversa"] .ai-doca .m-warow { justify-content: center; gap: 18px; }
body[data-ecra="home"] .att-chips, body[data-ecra="home"] .m-vnote { display: none; }

/* Na entrada o orb encolhe: ali quem manda é o teclado, e o orb está trancado de qualquer forma. */
body[data-ecra="entrada"] .ai-orbe { width: var(--ai-orb-chat); height: var(--ai-orb-chat); }

/* ══ O ORB ════════════════════════════════════════════════════════════════════════════════════════════
   O <button> da v1 fica POR CIMA, transparente, na mesma área: é ele que recebe o toque e carrega o
   rótulo. O SVG é pintura. */
.ai-orbe { position: relative; flex: 0 0 auto; width: var(--ai-orb-chat); height: var(--ai-orb-chat); }
body[data-ecra="home"] .ai-orbe, body[data-ecra="conversa"] .ai-orbe {
  width: var(--ai-orb-home); height: var(--ai-orb-home);
}

.ai-orb { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; display: block; }
.ai-orbe .m-wacircle {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: none !important; border: 0; box-shadow: none; color: #fff;
  display: grid; place-items: center; cursor: pointer; border-radius: 50%;
}
.ai-orbe .m-wacircle svg { position: relative; z-index: 1; filter: drop-shadow(0 1px 2px rgba(0,0,0,.35)); }
.ai-orbe .m-wacircle:focus-visible { outline: 2px solid var(--ai-roxo-claro); outline-offset: 3px; }

/* A escala segue o SOM (`--nivel`). Repouso = respiração lenta e muito pequena; o resto é o sinal. */
.ai-orb { transform: scale(calc(1 + var(--nivel) * .1)); transition: transform .06s linear; }
.ai-orb .ai-f1, .ai-orb .ai-f2 { transform-origin: 60px 60px; will-change: transform, opacity; }

@keyframes ai-gira1 { to { transform: rotate(360deg); } }
@keyframes ai-gira2 { to { transform: rotate(-360deg); } }
@keyframes ai-respira { 0%,100% { transform: scale(1); opacity: .9; } 50% { transform: scale(1.05); opacity: 1; } }

.ai-orb .ai-f1 { animation: ai-gira1 14s linear infinite; }
.ai-orb .ai-f2 { animation: ai-gira2 19s linear infinite; }
.ai-orb .ai-anel { transform-origin: 60px 60px; }

/* Repouso: respira devagar. */
.ai-orb[data-estado="repouso"] .ai-anel { animation: ai-respira 4.5s ease-in-out infinite; }

/* A ouvir: as fitas aceleram e o anel acende — mas o TAMANHO continua a vir do som, não da classe. */
.ai-orb[data-estado="ouvindo"] .ai-f1 { animation-duration: 4s; }
.ai-orb[data-estado="ouvindo"] .ai-f2 { animation-duration: 5.5s; }
.ai-orb[data-estado="ouvindo"] .ai-anel { opacity: .95; stroke: #7dd3e8; }

/* A falar: idem, com o sinal do altifalante. */
.ai-orb[data-estado="falando"] .ai-f1 { animation-duration: 6s; }
.ai-orb[data-estado="falando"] .ai-f2 { animation-duration: 8s; }
.ai-orb[data-estado="falando"] .ai-anel { opacity: .9; stroke: var(--ai-roxo-claro); }

/* Pronto a enviar texto: o orb serena e o ícone da v1 (seta) manda no significado. */
.ai-orb[data-estado="enviar"] .ai-anel { opacity: .8; stroke: #a6e1ee; }

/* Bloqueado: apaga-se. A voz está trancada pelo servidor e o orb tem de PARECER trancado. */
.ai-orb[data-estado="bloqueado"] { filter: grayscale(.85); opacity: .42; }
.ai-orb[data-estado="bloqueado"] .ai-f1,
.ai-orb[data-estado="bloqueado"] .ai-f2,
.ai-orb[data-estado="bloqueado"] .ai-anel { animation: none; }

/* Quem pediu menos movimento recebe menos movimento. */
@media (prefers-reduced-motion: reduce) {
  .ai-orb .ai-f1, .ai-orb .ai-f2, .ai-orb .ai-anel { animation: none !important; }
  .ai-orb { transition: none; transform: none; }
  .ai-item { transition: none; }
}

/* ══ ACERTOS MEDIDOS NO CO-BROWSER, em emulação de telemóvel 390×844 (2026-09-10) ════════════════════
   Quatro defeitos que só a captura revelou — e que eu não teria visto sem abrir o browser. */

/* 1. A conversa abria no TOPO, a mostrar a identificação já passada, em vez da última mensagem. A folha
      da v1 dá `overflow-y:auto` ao `.conversa`; aqui garante-se que o fundo é o que se vê ao entrar.
      (A rolagem em si é do JS; isto impede o salto visual enquanto ele corre.) */
/*  ↑ `auto` é DELIBERADO e foi medido. Com `smooth` a rolagem programática NUNCA aterrava: no co-browser,
    `cv.scrollTop = cv.scrollHeight` deixava o valor em 0 mesmo passado 1,2 s, enquanto forçar `auto` no
    mesmo elemento saltava para 2133 no mesmo instante. Numa conversa quem manda é a última mensagem, e
    uma animação que pode ser engolida não pode ficar entre a pessoa e o que acabou de chegar. */

/* 2. A faixa da passkey ficava CORTADA a meio («Guardar este aparelho com a sua digital?» sem a última
      linha), porque nasceu desenhada para um rodapé que aqui é a doca. Passa a envolver o texto e a ter
      altura própria, e afasta-se da doca. */
  flex-wrap: wrap; align-items: center; row-gap: 6px;
  padding: 10px 14px calc(10px + var(--ai-orb-home) / 2);
  border-radius: 14px 14px 0 0;
}

/* 3. O cabeçalho e os botões vinham no verde da v1 e brigavam com o roxo do orb. A v2 é violeta — o
      «mais próximo de uma Siri» que o curador pediu — logo re-tinge-se AQUI, sem tocar na v1: a folha
      dela continua a servir /nexus-chat exactamente como está. */
@media (prefers-color-scheme: dark) {
}

/* 4. O clipe de anexo ficava solto e desalinhado do orb depois de a caixa de texto sair. Alinha-se com
      ele e ganha alvo de toque próprio. */
body[data-ecra="conversa"] .ai-doca .m-waico {
  width: 46px; height: 46px; border-radius: 50%;
  background: rgba(14,116,144, .1); color: var(--ai-roxo-fundo);
}
@media (prefers-color-scheme: dark) {
  body[data-ecra="conversa"] .ai-doca .m-waico { background: rgba(94,197,221,.14); color: #d2f0f7; }
}

/* A marca no cabeçalho SÓ na identificação: ali não há botão de voltar e a barra ficava só com texto —
   e é o primeiro ecrã que a pessoa vê, logo é onde a marca conta. Depois disso quem ocupa o canto é o
   caminho de volta, que é mais útil que um logótipo repetido. */
.ai-topo-marca { border-radius: 9px; background: #fff; padding: 3px; flex: 0 0 auto; }
body:not([data-ecra="entrada"]) .ai-topo-marca { display: none; }

/* A faixa da digital NÃO aparece durante a identificação. Medido na captura de primeira visita: nascia
   junto com a primeira pergunta do motor, e pedir para guardar o aparelho a quem ainda não disse quem é
   não faz sentido nenhum — é ruído em cima do único passo que a pessoa tem de dar. */
body[data-ecra="entrada"] .faixa { display: none !important; }

/* ══ CORRECÇÕES DO TICK 1 (medidas no co-browser, emulação 390×844) ═══════════════════════════════════ */

/* O fundo bege do chat da v1 vazava: `#chat` é um selector de ID e vence a minha classe. Medido:
   respostaBg = rgb(236,229,221). A v2 não é papel de parede de conversa. */
/* A folha da v1 tem `#chat { height: 100svh; background: var(--fundo) }` — selector de ID, que vence
   qualquer classe minha. Foi ele que trouxe o bege do chat E que empurrou o campo do CPF para fora do
   ecrã: a secção reclamava a altura toda e o `#composer`, que é IRMÃO dela, ficava a 848px numa janela de
   844. Duas vezes o mesmo laço, e das duas vezes só a captura o mostrou. Qualifica-se pelo ID. */
#chat.ai-resposta { background: transparent; }
body[data-ecra="entrada"] #chat.ai-resposta { height: auto; }

/* ══ A IDENTIFICAÇÃO É UM LOGIN ══════════════════════════════════════════════════════════════════════
   Curador: «a parte de cpf que é antes, como se fosse um login». Um login não tem microfone ao fundo do
   ecrã nem caixa de conversa: tem a pergunta, um campo e um botão. A doca inteira sai daqui — o orb só
   existe depois, quando a voz existe. */
body[data-ecra="entrada"] .ai-doca { display: none; }
/* O `#composer` é IRMÃO do `#chat`, não filho: com `.ai-resposta` a ocupar `min-height:100svh`, o campo do
   CPF ficava empurrado para BAIXO DA DOBRA — invisível, e o ecrã de login sem sítio para escrever. Medido
   na captura de 390×844 (2026-09-10): a pergunta aparecia e o campo não existia em lado nenhum.
   Na entrada o palco deixa de reclamar o ecrã inteiro e o corpo passa a ser a coluna que empilha os dois. */
body[data-ecra="entrada"] { min-height: 100svh; display: flex; flex-direction: column; }
body[data-ecra="entrada"] .ai-resposta {
  min-height: 0; flex: 0 0 auto; padding-bottom: 4px;
}
/* Tecto DURO no ecrã de identificação. Mesmo com o turno certo à vista, uma pergunta longa não pode
   empurrar o campo para fora da janela nem fazer a página crescer — foi assim que ela chegou a 2720 px
   numa janela de 900. Se não couber, rola AQUI DENTRO, e o campo fica sempre onde a pessoa o vê. */
body[data-ecra="entrada"] .ai-palco { max-height: 58dvh; overflow-y: auto; }
body[data-ecra="entrada"] .ai-palco { justify-content: flex-start; padding-top: 30px; }

/* o campo, trazido para o palco e vestido de campo de formulário */
body[data-ecra="entrada"] .ai-campo { display: block; }
.ai-campo { display: none; }
body[data-ecra="entrada"] #composer {
  display: block; position: static; padding: 0 18px; background: none; backdrop-filter: none;
  /* A COLUNA mantém-se. `max-width: none` estava aqui para desfazer a doca fixa e desfez também a largura
     da aplicação: medido em 1280×900, o campo atravessava o ecrã inteiro enquanto o cartão da pergunta
     ficava nos 440 px — pareciam duas aplicações diferentes na mesma página. */
  /* `margin-inline: auto` dentro de um contentor FLEX desliga o esticar e encolhe o item ao conteúdo —
     medido: 292px em vez de 440, desalinhado do cartão. `align-self: center` + `width: 100%` centra sem
     matar a largura. */
  width: 100%; max-width: var(--ai-col); align-self: center; margin: 4px 0 0; box-sizing: border-box;
}

/* Num ecrã alto, a identificação vive no MEIO. Encostada ao topo com 500 px de vazio por baixo não parece
   um ecrã de entrada, parece uma página que não acabou de carregar. */
@media (min-height: 720px) {
  body[data-ecra="entrada"] { justify-content: center; padding-bottom: 8vh; }
}
body[data-ecra="entrada"] #composer .m-warow { display: block; }
body[data-ecra="entrada"] #composer .m-wapill {
  display: block; background: rgba(255,255,255,.9); border: 1.5px solid rgba(14,116,144,.28);
  border-radius: 14px; padding: 4px 12px; box-shadow: none;
}
@media (prefers-color-scheme: dark) {
  body[data-ecra="entrada"] #composer .m-wapill { background: rgba(255,255,255,.06); border-color: rgba(94,197,221,.3); }
}
body[data-ecra="entrada"] #composer .m-waico { display: none; }        /* anexo não pertence a um login */
body[data-ecra="entrada"] #composer .m-wain {
  display: block; width: 100%; min-height: 48px; font-size: 17px; background: none; border: 0;
}
body[data-ecra="entrada"] .ai-orbe { display: none; }

.ai-continuar { align-self: stretch; margin: 14px 18px 0; text-align: center; }
.ai-continuar[hidden] { display: none; }

/* a marca maior no login — é o primeiro ecrã e é onde a marca conta */
body[data-ecra="entrada"] .ai-topo { padding-bottom: 14px; }
body[data-ecra="entrada"] .ai-topo-marca { width: 44px; height: 44px; }

/* ══ MOVIMENTO (curador: «e que tenhamos mais animações na tela») ══════════════════════════════════════
   Regra dura, herdada da decisão dele de 09-09: NADA de WebGL, e **nunca** se anima o desfoque quadro a
   quadro. Anima-se `transform` e `opacity`, que o compositor resolve na GPU sem repintar — é o que faz
   isto correr num telemóvel de mil reais. Nenhuma animação aqui mede tempo de trabalho real: as que
   dizem «estou a fazer algo» (o orb, os três pontos) já vêm do sinal verdadeiro; estas são só a
   continuidade entre um ecrã e o seguinte, para a mudança não ser um corte seco. */

@keyframes ai-entra-baixo { from { opacity: 0; transform: translate3d(0, 14px, 0); } to { opacity: 1; transform: none; } }
@keyframes ai-entra-lado  { from { opacity: 0; transform: translate3d(16px, 0, 0); } to { opacity: 1; transform: none; } }
@keyframes ai-surge       { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: none; } }

/* A troca de ecrã. `.ai-home` e `.ai-resposta` alternam com `hidden`, logo a animação corre sempre que o
   elemento reaparece — sem estado extra em JS. */
.ai-home:not([hidden]), .ai-resposta:not([hidden]) { animation: ai-entra-baixo .28s cubic-bezier(.2,.8,.2,1) both; }

/* Os itens do menu entram em cascata curta. 40 ms entre cada um: perceptível como sequência, longe de
   parecer lento (o quinto item chega aos 160 ms). */
.ai-item { animation: ai-entra-baixo .3s cubic-bezier(.2,.8,.2,1) both; }
.ai-item:nth-child(1) { animation-delay: .02s; }
.ai-item:nth-child(2) { animation-delay: .06s; }
.ai-item:nth-child(3) { animation-delay: .10s; }
.ai-item:nth-child(4) { animation-delay: .14s; }
.ai-item:nth-child(5) { animation-delay: .18s; }

/* A resposta: o texto surge, os cartões sobem, as opções entram de lado. Três gestos distintos para três
   coisas distintas — se entrassem todas iguais, o ecrã leria-se como um bloco só. */
.ai-fala:not(:empty)  { animation: ai-surge .26s cubic-bezier(.2,.8,.2,1) both; }
.ai-cartao            { animation: ai-entra-baixo .32s cubic-bezier(.2,.8,.2,1) both; }
.ai-cartao:nth-child(2) { animation-delay: .05s; }
.ai-cartao:nth-child(3) { animation-delay: .10s; }
.ai-opcao             { animation: ai-entra-lado .26s cubic-bezier(.2,.8,.2,1) both; }
.ai-opcao:nth-child(1) { animation-delay: .04s; }
.ai-opcao:nth-child(2) { animation-delay: .09s; }
.ai-opcao:nth-child(3) { animation-delay: .14s; }
.ai-opcao:nth-child(4) { animation-delay: .19s; }
.ai-opcao:nth-child(5) { animation-delay: .24s; }
.ai-opcao:nth-child(6) { animation-delay: .29s; }

/* O orb cresce ao passar da conversa para a home e encolhe ao voltar. É a mesma peça a mudar de papel,
   e vê-se que é a mesma — não desaparece um e aparece outro. */
.ai-orbe { transition: width .3s cubic-bezier(.2,.8,.2,1), height .3s cubic-bezier(.2,.8,.2,1); }

/* Quem pediu menos movimento recebe MENOS MOVIMENTO, não «menos um pouco». */
@media (prefers-reduced-motion: reduce) {
  .ai-home:not([hidden]), .ai-resposta:not([hidden]),
  .ai-item, .ai-fala, .ai-cartao, .ai-opcao { animation: none !important; }
  .ai-orbe { transition: none; }
}

/* O contentor dos símbolos SVG. Era `style="position:absolute"` no HTML e a CSP (`style-src 'self'`) fá-lo
   ficar VERMELHO na consola a cada carga — inline é inline, venha de onde vier. O instrumento de erros
   apanhou-o à primeira; sem ele, era ruído invisível que ninguém ia caçar. */
/* PRESO À ORIGEM, e não «absoluto» e mais nada. Um elemento absoluto sem `top`/`left` fica na posição
   ESTÁTICA que teria — e este vive no fim do documento, logo empurrava a área de rolagem para 858 numa
   janela de 844. Todos os elementos mediam 844 e a página rolava na mesma: era este, com altura zero. */
.ai-simbolos { position: absolute; top: 0; left: 0; width: 0; height: 0; overflow: hidden; }

/* Cada parte da resposta é um bloco próprio: a lista, depois a nota do PDF, depois os botões. Espaçam-se
   como parágrafos de um mesmo texto — não como mensagens separadas, que era o que se queria evitar. */
.ai-parte + .ai-parte { margin-top: 12px; }
.ai-parte { animation: ai-surge .24s cubic-bezier(.2,.8,.2,1) both; }
@media (prefers-reduced-motion: reduce) { .ai-parte { animation: none !important; } }

/* O que a assistente OUVIU, para a pessoa confirmar. Entre aspas e em itálico: é a fala dela repetida de
   volta, não uma resposta — a diferença tem de se ver sem ler. */
.ai-ouvido { margin: 0; font-style: italic; opacity: .9; }
.ai-opcao.ai-secundaria { border-style: dashed; opacity: .85; }

/* Avisos do motor. Discretos mas VISÍVEIS — a alternativa que estava lá era não aparecerem de todo. */
.ai-aviso {
  margin: 0; padding: 12px 14px; border-radius: 14px; font-size: .92rem;
  background: rgba(234, 179, 8, .12); border: 1px solid rgba(180, 83, 9, .28); color: #7c2d12;
}
@media (prefers-color-scheme: dark) { .ai-aviso { color: #fde68a; border-color: rgba(253,230,138,.3); } }


/* ══ A MOLDURA DE TELEMÓVEL ═══════════════════════════════════════════════════════════════════════════
   Curador 2026-09-10: «no navegador ficar com uma imagem de celular e a tela dele. E no mobile sim, só o
   app sem a borda de celular».

   No telemóvel: `#ai-fone` é transparente e não faz nada — a aplicação ocupa o ecrã.
   No computador: vira um telemóvel desenhado em CSS (sem imagem a carregar), com a aplicação lá dentro.

   O `transform: translateZ(0)` é LOAD-BEARING e não decoração: um elemento com `transform` passa a ser o
   bloco de referência dos descendentes `position: fixed`. Sem ele, a doca do orb colava-se à JANELA e
   ficava fora do telemóvel desenhado. Com ele, cola-se à base do aparelho, como tem de ser. */
/* `display: block` e NÃO `contents`. Com `contents`, os filhos do aparelho passam a ser filhos do corpo —
   e passam a sê-lo TAMBÉM os nós de espaço em branco do markup entre as etiquetas, que formam uma linha de
   texto anónima. Medido em 390×844: o `#home` começava em y=14 e o documento ficava com 858. Não era
   altura a mais, era um DESLOCAMENTO no topo — e por isso nenhuma medição de altura o encontrava. */
#ai-fone { display: block; }

@media (min-width: 760px) and (min-height: 700px) {
  body { display: grid; place-items: center; min-height: 100svh; background: #0f2029; }
  @media (prefers-color-scheme: light) { body { background: #e1eef2; } }

  #ai-fone {
    display: block; position: relative; overflow: hidden;
    /* O APARELHO USA O ESPACO QUE EXISTE. Media a 2026-09-10 no fold aberto do curador (884x1104 CSS):
       390x844 fixos deixavam 260 px mortos por baixo e liam-se como «uma caixa apertada la em cima».
       A altura passa a ser a da janela (menos uma folga) e a largura sai da PROPORCAO de um telemovel
       em pe — 390:844, que e o que ele pediu: «peca a proporcao e espaco de um celular em pe». */
    height: min(calc(100dvh - 28px), 1040px);
    aspect-ratio: 390 / 844;
    width: auto; max-width: calc(100vw - 28px);
    flex: 0 0 auto;
    transform: translateZ(0);                       /* ← faz do aparelho o bloco de referência do fixed */
    border-radius: 46px;
    background: var(--ai-ecra, #f5fbfd);
    box-shadow: 0 0 0 11px #0d0b12, 0 0 0 13px #24343b, 0 26px 70px rgba(0,0,0,.55);
  }
  @media (prefers-color-scheme: dark) { #ai-fone { --ai-ecra: #07202b; } }

  /* a ilha do topo, como no aparelho real — é o que faz o desenho ler-se como telemóvel e não como caixa */
  #ai-fone::before {
    content: ""; position: absolute; z-index: 40; top: 10px; left: 50%; transform: translateX(-50%);
    width: 108px; height: 26px; border-radius: 14px; background: #0d0b12;
  }
  /* e a barra de gesto na base */
  #ai-fone::after {
    content: ""; position: absolute; z-index: 40; bottom: 7px; left: 50%; transform: translateX(-50%);
    width: 128px; height: 5px; border-radius: 3px; background: currentColor; opacity: .22;
  }

  /* Dentro do aparelho tudo mede-se PELO APARELHO, não pela janela. `100svh` continua a valer a JANELA
     mesmo dentro de um elemento com transform — medido: numa janela de 900 px, a secção ficava com 900 e
     transbordava 56 px para fora do telemóvel desenhado. É preciso `height` fixa, e não só `min-height`:
     o `#chat { height: 100svh }` da folha da v1 é selector de ID e ganha a qualquer classe minha. */
  #ai-fone .ai-home, #ai-fone .ai-resposta { max-width: none; }
  #ai-fone .ai-home, #ai-fone #chat.ai-resposta { height: 100%; min-height: 100%; max-height: 100%; }
  #ai-fone .ai-topo, #ai-fone .ai-home { padding-top: 44px; }      /* espaço para a ilha */
  /* `vw` mede a JANELA, nao o aparelho desenhado: num fold de 884 px o texto encostava sempre ao tecto.
     Dentro da moldura o tamanho e do aparelho. */
  #ai-fone .ai-fala { font-size: 16px; }
  #ai-fone .ai-saudacao { font-size: 23px; }
  #ai-fone .ai-doca, #ai-fone .ai-bolha { position: absolute; }
  #ai-fone .ai-doca { padding-bottom: 22px; }
  /* espaço reservado à doca DENTRO do aparelho: o orb (76) + a barra de gesto + folga para a faixa */
  #ai-fone .ai-resposta { padding-bottom: calc(var(--ai-orb-home) + 72px); }
  #ai-fone .ai-home { padding-bottom: calc(var(--ai-orb-home) + 84px); }
  body[data-ecra="entrada"] { display: grid; }                      /* a moldura centra, não o corpo */
  /* TERCEIRA vez o mesmo laço, e desta vez foi o DESKTOP que o mostrou: `#ai-fone #chat.ai-resposta`
     (dois IDs) vence `body[data-ecra="entrada"] #ai-fone .ai-resposta` (um ID), e por isso o `#chat`
     continuava com 844px FIXOS no ecrã de login. Medido em 1280×900: conteúdo 844+59=903 dentro de um
     aparelho de 844 — o campo do CPF ficava a y=817..876 e a moldura cortava-o ao meio. Quem qualifica
     pelo ID tem de continuar a qualificar pelo ID. */
  body[data-ecra="entrada"] #ai-fone #chat.ai-resposta { min-height: 0; max-height: none; height: auto; flex: 1 1 auto; }
  /* Centrar o APARELHO inteiro deixava uma faixa branca por cima da barra roxa: o cabeçalho da marca
     flutuava a 136px do topo e a aplicação lia-se como meia-carregada. O cabeçalho pertence ao topo do
     aparelho; quem se centra é a PERGUNTA com o campo, dentro do palco. */
  body[data-ecra="entrada"] #ai-fone { justify-content: flex-start; }
  body[data-ecra="entrada"] #ai-fone .ai-palco { max-height: none; justify-content: center; padding-top: 8px; }
  body[data-ecra="entrada"] #ai-fone #composer { margin-bottom: 26px; }
  body[data-ecra="entrada"] #ai-fone { display: flex; flex-direction: column; justify-content: center; }
}

/* ══ ÍCONES DE APLICAÇÃO ══════════════════════════════════════════════════════════════════════════════
   Curador: «podem ter alguns ícones na tela, como se fossem apps». Grelha de três, alvo grande, e o nome
   por baixo — o vocabulário que qualquer pessoa já sabe ler de um telemóvel. */
.ai-apps {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px 6px; margin-top: 26px;
  justify-items: center;                 /* o ÍCONE alinha, não o rótulo — «Agendamento» é largo e puxava
                                            a célula, deixando a grelha torta */
}
.ai-app {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 6px 2px; min-height: 92px; border: 0; background: none; color: inherit;
  font: inherit; font-size: .84rem; font-weight: 560; cursor: pointer; text-align: center;
  animation: ai-entra-baixo .3s cubic-bezier(.2,.8,.2,1) both;
}
.ai-app:nth-child(1){animation-delay:.02s} .ai-app:nth-child(2){animation-delay:.05s}
.ai-app:nth-child(3){animation-delay:.08s} .ai-app:nth-child(4){animation-delay:.11s}
.ai-app:nth-child(5){animation-delay:.14s} .ai-app:nth-child(6){animation-delay:.17s}
.ai-app:nth-child(7){animation-delay:.20s}
.ai-app .ai-ic {
  width: 60px; height: 60px; border-radius: 19px; display: grid; place-items: center;
  background: linear-gradient(150deg, #22a3c4, #0e7490); color: #fff;
  box-shadow: 0 6px 16px rgba(109, 40, 217, .28);
  transition: transform .16s cubic-bezier(.2,.8,.2,1);
}
.ai-app .ai-ic svg { width: 27px; height: 27px; }
.ai-app:active .ai-ic { transform: scale(.92); }
.ai-app:focus-visible { outline: 2px solid var(--ai-roxo); outline-offset: 3px; border-radius: 14px; }
.ai-app .ai-rot { line-height: 1.2; max-width: 96px; }
@media (prefers-reduced-motion: reduce) { .ai-app { animation: none !important; } }

/* ══ A BOLHA DO QUE SE OUVIU ══════════════════════════════════════════════════════════════════════════
   A imagem que o curador mandou: a bolha clara mesmo por cima do círculo. Mostra o que o STT entendeu —
   é a prova de que ele ouviu bem, antes de a pessoa mandar seguir. */
.ai-bolha {
  position: fixed; left: 0; right: 0; z-index: 21;
  bottom: calc(var(--ai-orb-home) + 46px + var(--teclado) + env(safe-area-inset-bottom));
  margin-inline: auto; max-width: min(var(--ai-col), 92%); width: fit-content;
  padding: 12px 16px; border-radius: 20px 20px 20px 6px;
  background: rgba(255,255,255,.97); color: #14303a; font-size: .96rem; line-height: 1.4;
  box-shadow: 0 6px 22px rgba(11,51,80, .18);
  display: flex; flex-direction: column; gap: 10px;
  animation: ai-entra-baixo .24s cubic-bezier(.2,.8,.2,1) both;
}
.ai-bolha[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .ai-bolha { animation: none !important; } }

/* Dica de lista longa. Discreta: é um atalho, não uma instrução obrigatória. */
.ai-dica { margin: 0 0 4px; font-size: .88rem; opacity: .7; }


.ai-bolha-txt { margin: 0; }
.ai-bolha-acoes { display: flex; gap: 8px; }
.ai-bolha-acoes:empty { display: none; }
.ai-bolha-btn {
  flex: 1 1 auto; min-height: 40px; padding: 8px 14px; border: 0; border-radius: 14px;
  background: var(--ai-roxo-fundo); color: #fff; font: inherit; font-weight: 600; cursor: pointer;
}
.ai-bolha-btn2 { background: rgba(14,116,144,.12); color: var(--ai-roxo-fundo); }

/* Enquanto se confirma o que foi ouvido, as faixas dispensáveis calam-se: a bolha tapava-as e ficava
   texto por baixo de texto. A decisão do momento é uma só. */
body[data-confirma="sim"] .faixa { display: none !important; }

/* ══ O ORB A TRABALHAR ════════════════════════════════════════════════════════════════════════════════
   Curador (2026-09-09): «o que demorar quase 1 minuto, tudo bem, e se for gerar o xtts, não falar
   sintetizando voz, preferir estender a fase de execução». Então enquanto se espera o orb TRABALHA — e
   nunca diz o que está a fazer por dentro. Mede trabalho real: começa no envio, acaba quando a resposta
   chega, sem duração mínima nem máxima inventada.

   O anel dá uma volta contínua; as fitas aceleram. Só transform e opacity — nada de desfoque animado. */
@keyframes ai-orbita { to { transform: rotate(360deg); } }
@keyframes ai-pulsa-anel { 0%,100% { opacity: .45; } 50% { opacity: 1; } }

.ai-orb[data-estado="executando"] .ai-f1 { animation-duration: 2.6s; }
.ai-orb[data-estado="executando"] .ai-f2 { animation-duration: 3.4s; }
.ai-orb[data-estado="executando"] .ai-anel {
  stroke: #7dd3e8; stroke-dasharray: 82 245; stroke-width: 3;
  animation: ai-orbita 1.15s linear infinite, ai-pulsa-anel 1.8s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .ai-orb[data-estado="executando"] .ai-anel { animation: none !important; stroke-dasharray: none; }
}

/* ══ VISOR DE IMAGEM ══════════════════════════════════════════════════════════════════════════════════
   Curador (2026-09-10): «clica na imagem, em vez dele só fazer a animação de aumentar a imagem, depois
   tem um botão de fazer download». Vive DENTRO do aparelho (o `#ai-fone` é bloco de referência por causa
   do `translateZ(0)`), por isso `absolute` chega e não escapa da moldura desenhada. */
.ai-visor {
  position: absolute; inset: 0; z-index: 60;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  padding: 22px; background: rgba(10, 6, 20, .93); backdrop-filter: blur(3px);
  animation: ai-surge .18s ease-out both;
}
.ai-visor[hidden] { display: none; }
.ai-visor-img {
  max-width: 100%; max-height: 68%; object-fit: contain;
  border-radius: 14px; box-shadow: 0 18px 50px rgba(0,0,0,.5);
  animation: ai-entra-baixo .24s cubic-bezier(.2,.8,.2,1) both;
}
.ai-visor-acoes { display: flex; gap: 10px; }
.ai-visor-fechar {
  position: absolute; top: 14px; right: 14px; width: 38px; height: 38px;
  border: 0; border-radius: 50%; cursor: pointer; font-size: 17px; line-height: 1;
  background: rgba(255,255,255,.14); color: #fff;
}
@media (prefers-reduced-motion: reduce) {
  .ai-visor, .ai-visor-img { animation: none !important; }
}

/* ══ THREAD ═══════════════════════════════════════════════════════════════════════════════════════════
   Curador (2026-09-10): «a cada interação está limpando a tela e sumindo tudo ao invés de ficar a thread».
   Os turnos passados ficam, mais discretos, acima do turno corrente. Quem rola é o palco — a PÁGINA
   continua a não crescer, que era a queixa anterior e continua a valer. */
.ai-thread { display: flex; flex-direction: column; gap: 18px; }
.ai-thread:empty { display: none; }
.ai-turno {
  display: flex; flex-direction: column; gap: 8px;
  padding-bottom: 16px; border-bottom: 1px solid rgba(14,116,144,.14);
  opacity: .62;                                   /* passado: legível, mas não disputa com o presente */
}
.ai-turno-rot {
  margin: 0; align-self: flex-end; max-width: 80%;
  padding: 6px 12px; border-radius: 14px 14px 4px 14px;
  background: var(--ai-roxo-fundo, #0e7490); color: #fff;
  font-size: .84rem; line-height: 1.3;            /* o que a PESSOA pediu, do lado dela */
}
.ai-turno .ai-parte { font-size: .92em; }
.ai-opcoes-passadas .ai-opcao { pointer-events: none; }
@media (prefers-color-scheme: dark) {
  .ai-turno { border-bottom-color: rgba(94,197,221,.16); }
}

/* A linha de espera: é a APLICAÇÃO a dizer o que está a fazer, nunca a assistente a falar. Por isso é
   discreta, em itálico, e sai de cena assim que a resposta verdadeira chega. Cronometrado a 2026-09-10:
   até 5,6 s entre o toque e a primeira parte — três pontinhos não dizem se o pedido sequer chegou. */
.ai-espera {
  margin: 0 0 2px; font-size: .9rem; font-style: italic; opacity: .66;
  animation: ai-surge .2s ease-out both;
}
@media (prefers-reduced-motion: reduce) { .ai-espera { animation: none !important; } }
/* cartão da leitura DIRECTA — chega antes do motor; discreto e factual */
.ai-directo { border-left: 3px solid var(--ai-roxo, #0e91b0); }
.ai-directo-titulo { margin: 0 0 4px; font-weight: 600; }
.ai-directo-item { margin: 0; font-size: .92em; opacity: .85; }

/* ══ O LOGIN É UM POPUP (curador 2026-09-10) ═════════════════════════════════════════════════════════ */
body[data-ecra="entrada"] #chat.ai-resposta { display: none !important; }   /* nada de conversa por trás */
body[data-ecra="entrada"] .ai-ouvir { display: none !important; }
.ai-login[hidden] { display: none; }
.ai-login {
  position: absolute; inset: 0; z-index: 30;
  display: flex; align-items: center; justify-content: center; padding: 22px;
  background: radial-gradient(120% 90% at 50% 0%, rgba(14,116,144,.22), transparent 60%), var(--ai-ecra, #f5fbfd);
}
body:not(.ai-tem-fone) .ai-login { position: fixed; }
.ai-login-cartao {
  width: 100%; max-width: 360px; padding: 26px 22px 18px; border-radius: 22px;
  background: rgba(255,255,255,.96); color: #14303a;
  box-shadow: 0 24px 60px rgba(11,51,80,.22), 0 0 0 1px rgba(14,116,144,.10);
  display: flex; flex-direction: column; align-items: stretch; gap: 10px;
  animation: ai-entra-baixo .28s cubic-bezier(.2,.8,.2,1) both;
}
@media (prefers-color-scheme: dark) { .ai-login-cartao { background: rgba(11,36,49,.97); color: #e6f4f8; } }
.ai-login-marca { align-self: center; width: 140px; height: auto; margin-bottom: 4px; }
.ai-login-titulo { margin: 0; text-align: center; font-size: 22px; font-weight: 640; letter-spacing: -.01em; }
.ai-login-sub { margin: 0 0 6px; text-align: center; font-size: 14px; opacity: .72; }
.ai-login-nota { margin: 8px 0 0; text-align: center; font-size: 12px; opacity: .55; }
/* a pergunta do motor, como RÓTULO de formulário — não como balão */
.ai-login-corpo .ai-fala { font-size: 15px; line-height: 1.4; font-weight: 520; margin: 0 0 4px; }
.ai-login-corpo .ai-fala:empty { display: none; }
/* o campo: um input de login */
.ai-login-corpo #composer { position: static; display: block; padding: 0; background: none; backdrop-filter: none; margin: 0; width: 100%; max-width: none; }
.ai-login-corpo #composer .m-warow { display: block; }
.ai-login-corpo #composer .m-wapill { display: block; background: rgba(14,116,144,.06); border: 1.5px solid rgba(14,116,144,.35); border-radius: 14px; padding: 4px 14px; box-shadow: none; }
.ai-login-corpo #composer .m-waico { display: none; }
.ai-login-corpo #composer .m-wain { display: block; width: 100%; min-height: 50px; font-size: 20px; letter-spacing: .04em; background: none; border: 0; }
#ai-login[data-tem-opcoes="sim"] #composer { display: none; }
/* as opções do step-up: linhas de escolha */
.ai-login-corpo .ai-opcoes { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.ai-login-corpo .ai-opcoes:empty { display: none; }
.ai-login-corpo .ai-opcao { width: 100%; text-align: left; padding: 13px 14px; border-radius: 12px; }
/* o botão */
.ai-login-corpo .ai-continuar { margin: 6px 0 0; align-self: stretch; padding: 13px; font-size: 16px; }
.ai-login-corpo .ai-continuar[disabled] { opacity: .45; cursor: default; }
.ai-login-corpo .ai-continuar[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .ai-login-cartao { animation: none !important; } }
/* polimento medido na captura: o campo e o botão ocupam a largura do cartão, como num login */
.ai-login-corpo #composer .m-wapill { width: 100%; max-width: none; box-sizing: border-box; }
.ai-login-corpo .ai-continuar { display: block; width: 100%; box-sizing: border-box; }
.ai-directo-img { display: block; max-width: 100%; border-radius: 12px; cursor: zoom-in; }
/* O CLIPE só no agendamento (curador 2026-09-10). Em todo o lado, o botão de anexar não existe. */
.ai-doca .m-waico { display: none !important; }
body[data-ecra="conversa"][data-fluxo="agendamento"] .ai-doca .m-waico { display: inline-flex !important; }
/* PAINEL DE APP (leituras directas): sem thread por trás — o facto é o ecrã. */
body[data-fluxo="app"] .ai-thread { display: none !important; }
body[data-fluxo="app"] .ai-directo { border-left: 0; padding: 16px; font-size: 1.02em; }
body[data-fluxo="app"] .ai-directo-titulo { font-size: 1.15em; margin-bottom: 10px; }
.ai-directo-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 0; border-top: 1px solid rgba(14,116,144,.12); }
.ai-directo-btn { flex: 0 0 auto; }

/* ══ O ALTIFALANTE — canto superior direito (curador 2026-09-10) ══════════════════════════════════════
   `fixed` de propósito: dentro do telemóvel desenhado o `transform` do #ai-fone torna-o o bloco de
   referência (cola-se ao canto DO APARELHO); num telemóvel real cola-se ao canto do ecrã. */
.ai-som {
  position: fixed; z-index: 45; top: max(10px, env(safe-area-inset-top)); right: 12px;
  width: 40px; height: 40px; padding: 0; border: 0; border-radius: 12px; cursor: pointer;
  display: grid; place-items: center;
  background: rgba(14,116,144,.12); color: var(--ai-roxo-fundo, #0e7490);
}
.ai-som svg { width: 22px; height: 22px; display: block; }
.ai-som .ai-som-off, .ai-som[data-ligado="nao"] .ai-som-on { display: none; }
.ai-som[data-ligado="nao"] .ai-som-off { display: block; }
.ai-som[data-ligado="nao"] { opacity: .72; }
body[data-ecra="entrada"] .ai-som { display: none; }
body[data-ecra="conversa"] .ai-som { background: rgba(255,255,255,.16); color: #f2fbfd; }
.ai-topo { padding-right: 62px; }                 /* o título não passa por baixo do botão */
body.ai-tem-fone .ai-som { top: 44px; }          /* SÓ na moldura desenhada: debaixo da ilha. Num telemóvel real o #ai-fone existe sem moldura, e 44 px punha o botão a sair do cabeçalho (medido a 390×844). */
@media (prefers-color-scheme: dark) {
  .ai-som { background: rgba(94,197,221,.16); color: #d2f0f7; }
}

/* ══ ECRÃS DE APLICAÇÃO (curador 2026-09-10: «app real, com front end») ══════════════════════════════
   O mesmo esqueleto do #chat: cabeçalho roxo, corpo que rola por dentro, altura do aparelho. Interage-se
   por toque, e o orb continua na doca: falar é a outra forma de fazer o mesmo. */
/* ── A ALTURA DO ECRÃ DE APLICAÇÃO, e porque é que ele não rolava no telemóvel ────────────────────
   Curador (2026-09-11, no aparelho dele, no ecrã da carteirinha): «não tem scroll … não consegui
   fazer scroll». A causa não estava no corpo, que sempre teve `overflow-y: auto`: estava na ALTURA.
   `#ai-fone` está SEMPRE no markup — no computador vira o telemóvel desenhado, no telemóvel é
   transparente — e a regra que dava altura ao ecrã era `#ai-fone .ai-ecra { height: 100% }`. No
   aparelho desenhado o pai tem altura e os 100% resolvem; no telemóvel real o pai tem altura
   AUTOMÁTICA, e `height: 100%` contra um pai automático resolve para `auto`. Sem altura, o corpo
   cresce em vez de rolar — e como o `body` é `overflow: hidden` por desenho (a PÁGINA não cresce,
   quem rola é a caixa de dentro), o que passava do ecrã ficava inalcançável. Não era um scroll
   partido: era um scroll que nunca chegou a existir, e só no telemóvel.
   O discriminante certo já existia neste ficheiro para os botões do topo — a classe `ai-tem-fone`,
   que só está no corpo quando a moldura é desenhada. As regras da moldura passam a usá-la, e o
   telemóvel passa a ter o tecto do ecrã. */
.ai-ecra {
  display: flex; flex-direction: column; box-sizing: border-box;
  min-height: 100svh; max-height: 100svh; max-width: var(--ai-col); margin-inline: auto;
}
.ai-ecra[hidden] { display: none; }
body.ai-tem-fone #ai-fone .ai-ecra { max-width: none; height: 100%; min-height: 100%; max-height: 100%; }
.ai-ecra-topo {
  display: flex; align-items: center; gap: 10px; padding: max(10px, env(safe-area-inset-top)) 62px 10px 14px;
  background: linear-gradient(135deg, #0b3350, #0f5f88); color: #f2fbfd;
}
body.ai-tem-fone #ai-fone .ai-ecra-topo { padding-top: 44px; }   /* a ilha SÓ existe no aparelho desenhado */
.ai-ecra-titulo { margin: 0; font-size: 1.05rem; font-weight: 700; flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ai-ecra-accao:empty { display: none; }
.ai-ecra-corpo { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 14px 28px; display: flex; flex-direction: column; gap: 10px; }
/* O ORB FICA EM TODOS OS ECRÃS (curador 2026-09-10, segunda correcção): «o orb pode funcionar para qualquer
   função, é um app de verdade, que você pode usar e navegar, e o orb é uma forma de interagir agentic
   native». Por isso o corpo reserva o espaço da doca, exactamente como a conversa. */
.ai-ecra { padding-bottom: calc(var(--ai-orb-home) + 44px + var(--teclado)); }
body.ai-tem-fone #ai-fone .ai-ecra { padding-bottom: calc(var(--ai-orb-home) + 72px); }

/* cartões de lista */
.ai-card {
  background: rgba(255,255,255,.9); border: 1px solid rgba(14,116,144,.16); border-radius: 16px;
  padding: 12px 14px; display: flex; flex-direction: column; gap: 6px;
}
.ai-card[data-toque] { cursor: pointer; }
.ai-card[data-toque]:active { transform: scale(.985); }
.ai-card-linha { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.ai-card-titulo { margin: 0; font-weight: 700; font-size: 1rem; }
.ai-card-sub { margin: 0; font-size: .9rem; opacity: .78; }
.ai-card-grande { margin: 2px 0; font-size: 1.45rem; font-weight: 800; letter-spacing: -.01em; }
.ai-card-meta { margin: 0; font-size: .82rem; opacity: .66; }
/* O MOTIVO DE UMA NEGATIVA não é meta: é a frase que a pessoa abriu o ecrã para ler. Tem contraste
   próprio e uma barra à esquerda, para se distinguir de um código ou de uma data. */
.ai-card-alerta { margin: 6px 0 0; padding-left: 10px; border-left: 3px solid #e0a33c;
  font-size: .86rem; line-height: 1.4; color: #f0c98a; }
.ai-secao { margin: 8px 2px 0; font-size: .8rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; opacity: .6; }
.ai-chip {
  display: inline-block; padding: 3px 9px; border-radius: 999px; font-size: .74rem; font-weight: 700;
  background: rgba(14,116,144,.12); color: #0b5c73; white-space: nowrap;
}
.ai-chip.-ok { background: rgba(22,163,74,.14); color: #166534; }
.ai-chip.-aviso { background: rgba(234,179,8,.18); color: #854d0e; }
.ai-chip.-erro { background: rgba(220,38,38,.12); color: #991b1b; }
.ai-chip.-neutro { background: rgba(100,116,139,.14); color: #334155; }
.ai-acoes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; }
.ai-btn, .ai-btn2 {
  display: inline-flex; align-items: center; gap: 6px; min-height: 40px; padding: 8px 14px;
  border-radius: 12px; border: 0; cursor: pointer; font: inherit; font-weight: 700; font-size: .9rem;
}
.ai-btn { background: var(--ai-roxo-fundo, #0e7490); color: #fff; }
.ai-btn2 { background: rgba(14,116,144,.1); color: var(--ai-roxo-fundo, #0e7490); }
.ai-btn:disabled, .ai-btn2:disabled { opacity: .55; cursor: default; }
.ai-btn svg, .ai-btn2 svg { width: 18px; height: 18px; }
.ai-btn.-feito { background: #166534; }
a.ai-btn, a.ai-btn2 { text-decoration: none; }
.ai-vazio { text-align: center; padding: 28px 12px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.ai-vazio .ai-ic { width: 56px; height: 56px; }
.ai-vazio p { margin: 0; }
.ai-vazio .ai-card-sub { max-width: 28ch; }
.ai-esqueleto { height: 74px; border-radius: 16px; background: linear-gradient(90deg, rgba(14,116,144,.08), rgba(14,116,144,.16), rgba(14,116,144,.08)); background-size: 200% 100%; animation: ai-brilho 1.1s linear infinite; }
@keyframes ai-brilho { to { background-position: -200% 0; } }
.ai-nota { margin: 0; font-size: .82rem; opacity: .7; text-align: center; }
.ai-cart-img { display: block; width: 100%; border-radius: 14px; box-shadow: 0 10px 30px rgba(11,51,80,.25); cursor: zoom-in; }
/* filtros da rede: selects nativos — toque, nunca teclado */
.ai-filtros { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.ai-filtros select {
  width: 100%; min-height: 42px; padding: 8px 10px; border-radius: 12px; font: inherit; font-size: .9rem;
  border: 1.5px solid rgba(14,116,144,.28); background: rgba(255,255,255,.92); color: inherit;
}
.ai-filtros select:first-child { grid-column: 1 / -1; }
.ai-contagem { margin: 0; font-size: .82rem; opacity: .66; }
@media (prefers-color-scheme: dark) {
  .ai-card { background: rgba(255,255,255,.06); border-color: rgba(94,197,221,.2); }
  .ai-chip { background: rgba(94,197,221,.18); color: #d2f0f7; }
  .ai-btn2 { background: rgba(94,197,221,.16); color: #d2f0f7; }
  .ai-filtros select { background: rgba(255,255,255,.06); }
}
@media (prefers-reduced-motion: reduce) { .ai-esqueleto { animation: none; } }

/* nos ecrãs de app a doca é a da HOME: só o orb, centrado, tamanho grande. Medido a 2026-09-10: sem isto o
   orb ficava encostado à esquerda (a pílula do composer escondida deixava a fila alinhada ao início). */
body[data-ecra="app"] .ai-doca .m-wapill { display: none; }
body[data-ecra="app"] .ai-doca .m-warow { justify-content: center; }
body[data-ecra="app"] .att-chips, body[data-ecra="app"] .m-vnote { display: none; }
body[data-ecra="app"] .ai-orbe { width: var(--ai-orb-home); height: var(--ai-orb-home); }
/* nada rola na horizontal: nomes compridos quebram, chips encurtam, a linha do cartão embrulha */
.ai-ecra-corpo { overflow-x: hidden; }
.ai-card { min-width: 0; }
.ai-card-linha { flex-wrap: wrap; }
.ai-card-linha > p { min-width: 0; flex: 1 1 60%; overflow-wrap: anywhere; }
.ai-chip { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }

/* ══ O ORB SÃO AS CURVAS, SEMPRE a mexer (curador 2026-09-10; sem estrela — curador 2026-09-16) ════════
   O glifo da v1 (microfone / avião de enviar — a «pipa») some. Nada fica por cima das curvas: a estrela de
   IA saiu. Trancado deixa de ser cinzento e parado: fica mais suave, mas vivo.
   AS CURVAS RESPONDEM AO SOM: o grupo `.ai-fitas` escala com `--nivel` (0..1, medido do sinal real pelo
   JS). Sem som, escala 1 — as fitas só giram. É a mesma variável que já move o orb e a aura. */
.ai-orbe .m-wacircle svg { display: none !important; }
.ai-orb .ai-fitas { transform-origin: 60px 60px; transform: scale(calc(1 + var(--nivel, 0) * .22)); transition: transform .06s linear; }
.ai-orb[data-estado="bloqueado"] { filter: saturate(.55); opacity: .7; }
.ai-orb[data-estado="bloqueado"] .ai-f1 { animation: ai-gira1 22s linear infinite; }
.ai-orb[data-estado="bloqueado"] .ai-f2 { animation: ai-gira2 30s linear infinite; }
.ai-orb[data-estado="bloqueado"] .ai-anel { animation: ai-respira 6s ease-in-out infinite; opacity: .35; }
/* MENU ADAPTATIVO: até o perfil chegar só ficam os atalhos que não dependem dele (rede, ligar) */
.ai-apps.ai-perfil-pendente .ai-app[data-precisa] { display: none; }
.ai-app[hidden] { display: none !important; }

/* ══ SAIR — ao lado do altifalante (curador 2026-09-10) ═══════════════════════════════════════════════ */
.ai-sair {
  position: fixed; z-index: 45; top: max(10px, env(safe-area-inset-top)); right: 60px;
  width: 40px; height: 40px; padding: 0; border: 0; border-radius: 12px; cursor: pointer;
  display: grid; place-items: center; background: rgba(14,116,144,.12); color: var(--ai-roxo-fundo, #0e7490);
}
.ai-sair svg { width: 22px; height: 22px; display: block; }
body[data-ecra="entrada"] .ai-sair { display: none; }
body[data-ecra="conversa"] .ai-sair, body[data-ecra="app"] .ai-sair { background: rgba(255,255,255,.16); color: #f2fbfd; }
body.ai-tem-fone .ai-sair { top: 44px; }
.ai-topo, .ai-ecra-topo { padding-right: 110px; }          /* dois botões no canto */
@media (prefers-color-scheme: dark) { .ai-sair { background: rgba(94,197,221,.16); color: #d2f0f7; } }

/* SELECTS legíveis em modo escuro (curador 2026-09-10: «todas as especialidades … está branco e não escuro»).
   O <select> herdava `color` claro do tema e o popup nativo pintava texto claro sobre fundo claro. */
.ai-filtros select, .ai-filtros option { color: #10232c; background: #fff; }
@media (prefers-color-scheme: dark) {
  .ai-filtros select, .ai-filtros option { color: #f2fbfd; background: #0b2431; }
}

/* O LEMBRETE DE CONSULTA não é popup de boas-vindas (curador 2026-09-10: «essa pergunta deveria vir só quando
   finalizar um agendamento»). A faixa da v1 só se mostra dentro do fluxo de agendamento. */
body:not([data-fluxo="agendamento"]) #faixa-push { display: none !important; }

/* a linha «fora de lugar» em cima do campo do CPF era o border-top do #composer da v1 (medido: 1px rgb(217,222,220)) */
.ai-login-corpo #composer { border-top: 0 !important; box-shadow: none !important; }

/* ══ A ESPERA DA IDENTIFICAÇÃO ═══════════════════════════════════════════════════════════════════════ */
.ai-login-espera { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 6px 0 0; font-size: .95rem; font-weight: 600; color: var(--ai-roxo-fundo, #0e7490); }
.ai-login-espera[hidden] { display: none; }
.ai-roda { width: 18px; height: 18px; border: 2.5px solid currentColor; border-top-color: transparent; border-radius: 50%; animation: ai-roda .8s linear infinite; flex: 0 0 auto; }
.ai-login.-a-confirmar .ai-login-corpo { opacity: .55; pointer-events: none; }
@media (prefers-color-scheme: dark) { .ai-login-espera { color: #d2f0f7; } }

/* ══ O CAMPO DE ESCOLHA e a FOLHA (o dropdown da casa) ═══════════════════════════════════════════════ */
.ai-filtros .ai-escolha {
  width: 100%; min-height: 44px; padding: 8px 12px; border-radius: 14px; font: inherit; font-size: .92rem; font-weight: 600;
  border: 1.5px solid rgba(14,116,144,.3); background: rgba(255,255,255,.92); color: inherit; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 8px; text-align: left;
}
.ai-filtros .ai-escolha:first-child { grid-column: 1 / -1; }
.ai-filtros .ai-escolha span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-filtros .ai-escolha svg { width: 18px; height: 18px; flex: 0 0 auto; opacity: .7; }
.ai-filtros .ai-escolha.-vazio { font-weight: 500; opacity: .8; }
.ai-folha { position: fixed; inset: 0; z-index: 70; display: flex; align-items: flex-end; justify-content: center; background: rgba(10,6,20,.55); backdrop-filter: blur(2px); animation: ai-surge .16s ease-out both; }
.ai-folha[hidden] { display: none; }
body.ai-tem-fone #ai-fone .ai-folha { position: absolute; }
.ai-folha-cartao { width: 100%; max-height: 72%; display: flex; flex-direction: column; background: var(--ai-ecra, #f5fbfd); border-radius: 22px 22px 0 0; box-shadow: 0 -12px 40px rgba(0,0,0,.35); animation: ai-entra-baixo .22s cubic-bezier(.2,.8,.2,1) both; }
.ai-folha-topo { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px 8px; }
.ai-folha-topo .ai-visor-fechar { position: static; background: rgba(14,116,144,.12); color: var(--ai-roxo-fundo, #0e7490); }
.ai-folha-titulo { margin: 0; font-size: 1rem; font-weight: 800; }
.ai-folha-lista { overflow-y: auto; padding: 4px 12px 18px; display: flex; flex-direction: column; gap: 6px; }
.ai-folha-item { min-height: 46px; padding: 10px 14px; border: 0; border-radius: 12px; background: rgba(14,116,144,.06); color: inherit; font: inherit; font-size: .95rem; text-align: left; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ai-folha-item.-actual { background: var(--ai-roxo-fundo, #0e7490); color: #fff; font-weight: 700; }
.ai-folha-item:active { transform: scale(.985); }
@media (prefers-color-scheme: dark) {
  .ai-filtros .ai-escolha { background: rgba(255,255,255,.06); border-color: rgba(94,197,221,.3); }
  .ai-folha-cartao { background: #0b2431; }
  .ai-folha-item { background: rgba(255,255,255,.06); }
}
@media (prefers-reduced-motion: reduce) { .ai-folha, .ai-folha-cartao { animation: none !important; } }
/* o visto do item actual da folha tem o tamanho de um ícone, não da folha inteira (medido: SVG sem tamanho) */
.ai-folha-item svg { width: 20px; height: 20px; flex: 0 0 auto; }
.ai-folha-item span { min-width: 0; overflow-wrap: anywhere; }

/* ══ A CAPTAR: o orb RESPIRA e um halo pulsa (curador 2026-09-10) ════════════════════════════════════
   «deixar claro no orb quando ele está ou não captando, com light pulsing, ou light breathing». O estado
   vive no wrapper (`.ai-orbe[data-estado]`, escrito pelo mesmo `pintar()` que pinta o SVG) para o halo
   poder ser um pseudo-elemento por trás do orb, sem depender de `:has()`. */
.ai-orbe::after {
  content: ""; position: absolute; inset: -6px; border-radius: 50%; pointer-events: none;
  border: 2px solid var(--ai-roxo-claro, #5ec5dd); opacity: 0; transform: scale(.92);
}
.ai-orbe[data-estado="ouvindo"]::after { animation: ai-pulso 1.6s ease-out infinite; }
@keyframes ai-pulso {
  0%   { opacity: .55; transform: scale(.92); }
  70%  { opacity: 0;   transform: scale(1.35); }
  100% { opacity: 0;   transform: scale(1.35); }
}
/* e o próprio orb ganha um brilho que cresce e decresce — a «respiração» */
.ai-orbe[data-estado="ouvindo"] .ai-orb { animation: ai-brilha 1.6s ease-in-out infinite; }
@keyframes ai-brilha {
  0%,100% { filter: drop-shadow(0 0 2px rgba(94,197,221,.35)); }
  50%     { filter: drop-shadow(0 0 14px rgba(94,197,221,.85)); }
}
/* a executar: o halo fica contínuo e lento, para não competir com o de captar */
.ai-orbe[data-estado="executando"]::after { animation: ai-pulso 2.6s ease-out infinite; opacity: .3; }
/* ══ TRÊS AURAS, TRÊS ESTADOS (curador 2026-09-16: «aura azul/fala e verde/captura, amplitude real») ═════
   · a FALAR (áudio remoto da Bella a tocar): aura AZUL-CLARA, opacidade e raio a seguir `--nivel` do sinal;
   · a CAPTAR (o microfone a apanhar fala da pessoa): aura VERDE, raio a seguir `--nivel` do microfone;
   · a OUVIR (voz aberta, silêncio): o pulso azul lento de sempre — não é captura, e não se pinta de verde;
   · MUDO / DESLIGADO: risco vermelho (ou cinzento) a cortar o orb, aura parada.
   Nenhuma destas cores nasce de um temporizador: `--nivel` é escrita pelo `medir()` a partir do AnalyserNode. */
.ai-orbe[data-estado="falando"] .ai-orb { transform: scale(calc(1 + var(--nivel, 0) * .18)); }
.ai-orbe[data-estado="falando"]::after {
  border-color: #7dd3e8; opacity: calc(.25 + var(--nivel, 0) * .6);
  transform: scale(calc(1 + var(--nivel, 0) * .25));
  transition: transform .06s linear, opacity .06s linear;
}
.ai-orbe[data-estado="falando"] .ai-orb .ai-anel { stroke: #7dd3e8; }
.ai-orbe[data-estado="captando"]::after {
  border-color: #4ade80; opacity: .9;
  transform: scale(calc(1.06 + var(--nivel, 0) * .22));
  transition: transform .06s linear;
}
.ai-orbe[data-estado="captando"] .ai-orb { transform: scale(calc(1 + var(--nivel, 0) * .12)); }
.ai-orb[data-estado="captando"] .ai-anel { stroke: #4ade80; opacity: 1; stroke-width: 3; }
.ai-orb[data-estado="mudo"] { filter: grayscale(1); }
.ai-orbe[data-estado="mudo"]::after { border-color: #ef4444; opacity: 1; animation: none; }
.ai-orbe[data-estado="mudo"]::before { content: ''; position: absolute; z-index: 4;
  width: 72%; height: 4px; left: 50%; top: 50%; background: #ef4444; border-radius: 2px;
  transform: translate(-50%, -50%) rotate(-45deg); transform-origin: center; pointer-events: none; }
body[data-live="connecting"] .ai-orbe::after,
.ai-orbe[data-estado="executando"]::after { border-color: #fbbf24; }
@media (prefers-reduced-motion: reduce) {
  .ai-orbe::after, .ai-orbe[data-estado="ouvindo"] .ai-orb { animation: none !important; }
  .ai-orbe[data-estado="ouvindo"]::after { opacity: .5; }
  .ai-orbe[data-estado="falando"] .ai-orb,
  .ai-orbe[data-estado="falando"]::after { transform: none; transition: none; }
  .ai-orbe[data-estado="captando"] .ai-orb,
  .ai-orbe[data-estado="captando"]::after { transform: none; transition: none; }
}

/* ══ TECLADO À MÃO ═══════════════════════════════════════════════════════════════════════════════════
   Fechado: o orb ao centro e o ícone de teclado no canto direito. Aberto: o campo corre para fora do
   ícone, empurra o orb para a esquerda, e o ícone vira microfone (voltar à voz). */
.ai-teclado {
  width: 44px; height: 44px; flex: 0 0 auto; padding: 0; border: 0; border-radius: 14px; cursor: pointer;
  display: grid; place-items: center; order: 3;
  background: rgba(14,116,144,.1); color: var(--ai-roxo-fundo, #0e7490);
}
.ai-teclado svg { width: 22px; height: 22px; }
.ai-teclado .ai-tec-mic { display: none; }
body[data-teclado="sim"] .ai-teclado .ai-tec-mic { display: block; }
body[data-teclado="sim"] .ai-teclado .ai-tec-kb { display: none; }
body[data-ecra="entrada"] .ai-teclado { display: none; }        /* na entrada o campo já é o protagonista */
@media (prefers-color-scheme: dark) { .ai-teclado { background: rgba(94,197,221,.16); color: #d2f0f7; } }

/* ── ENVIAR (curador 2026-09-11) ───────────────────────────────────────────────────────────────────
   Só existe quando o teclado está aberto E há alguma coisa escrita. Fora disso não ocupa espaço: um
   botão de enviar permanentemente à vista e inerte ensina à pessoa que o botão não funciona. É cheio,
   não fantasma — é a acção primária da barra enquanto se escreve, e tem de se distinguir do clipe e
   do microfone, que são cinzentos. */
.ai-enviar {
  display: none; width: 44px; height: 44px; flex: 0 0 auto; padding: 0; border: 0; border-radius: 14px;
  cursor: pointer; place-items: center; order: 4; background: #0e7490; color: #fff;
}
.ai-enviar svg { width: 22px; height: 22px; }
.ai-enviar:active { transform: scale(.94); }
body[data-teclado="sim"][data-escrito="sim"] .ai-enviar { display: grid; }
@media (prefers-color-scheme: dark) { .ai-enviar { background: #22a3c4; color: #04222c; } }

/* fechado: a pílula não ocupa espaço; aberta: corre para fora */
body:not([data-ecra="entrada"]) .ai-doca .m-wapill {
  max-width: 0; opacity: 0; overflow: hidden; padding: 0; margin: 0;
  transition: max-width .26s cubic-bezier(.2,.8,.2,1), opacity .18s linear;
}
body[data-teclado="sim"] .ai-doca .m-warow { justify-content: flex-start !important; gap: 10px; }
body[data-teclado="sim"] .ai-doca .m-wapill {
  display: flex !important; align-items: center; gap: 8px; flex: 1 1 auto; max-width: 100%; opacity: 1;
  padding: 4px 12px; background: rgba(255,255,255,.92); border: 1.5px solid rgba(14,116,144,.3);
  border-radius: 16px; order: 2; min-width: 0;
}
body[data-teclado="sim"] .ai-doca .m-wain {
  display: block !important; flex: 1 1 auto; min-width: 0; width: 100%; min-height: 42px; max-height: 96px;
  background: none; border: 0; font-size: 16px; resize: none;
}
body[data-teclado="sim"] .ai-orbe { order: 1; width: var(--ai-orb-chat); height: var(--ai-orb-chat); }
body[data-teclado="sim"] .ai-doca .m-waico { display: block; }   /* com teclado aberto, o clipe faz sentido */
body[data-teclado="sim"][data-fluxo="outro"] .ai-doca .m-waico,
body[data-teclado="sim"][data-ecra="home"] .ai-doca .m-waico { display: none; }
@media (prefers-color-scheme: dark) {
  body[data-teclado="sim"] .ai-doca .m-wapill { background: rgba(255,255,255,.07); border-color: rgba(94,197,221,.3); }
}
@media (prefers-reduced-motion: reduce) {
  body:not([data-ecra="entrada"]) .ai-doca .m-wapill { transition: none; }
}

/* a bolha do que se OUVIU deixa de ser confirmação: é recibo, e sai sozinha */
.ai-bolha[data-recibo="sim"] { opacity: .96; }

/* O TEXTO DO LOGIN É MENOR QUE O DA CONVERSA (curador 2026-09-11: «o início que pede o CPF, diminui o
   tamanho do texto também»). No login a fala do motor é uma INSTRUÇÃO curta, não uma resposta para ler
   com calma — e no corpo da conversa ela ocupava metade do cartão antes de a pessoa chegar ao campo.
   Vive só dentro do cartão de login: a mesma fala, fora dali, mantém o tamanho de leitura. */
.ai-login .ai-fala { font-size: .95rem; line-height: 1.45; }
.ai-login .ai-fala p { margin: 0 0 .4em; }

/* A falha de envio é um aviso COM ACÇÃO — sem botão, dizer «não foi enviada» é deixar a pessoa
   sem saída, que é o mesmo beco de não dizer nada. */
.ai-aviso.-falha { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ai-aviso-acao { border: 0; border-radius: 12px; padding: 7px 14px; font: inherit; font-weight: 600;
  cursor: pointer; background: var(--ai-roxo-fundo, #0e7490); color: #fff; }

/* O Guia Médico não é um prestador: é a FONTE. Fica com o peso de um atalho, não de um cartão. */
.ai-guia { display: flex; align-items: center; gap: 10px; margin: 2px 0 4px; padding: 11px 14px;
  border-radius: 14px; text-decoration: none; font-size: .9rem; font-weight: 600;
  background: rgba(94,197,221,.12); color: var(--ai-roxo-claro, #5ec5dd);
  border: 1px solid rgba(94,197,221,.25); }
.ai-guia svg { width: 18px; height: 18px; flex: 0 0 auto; }

/* Um aviso que a pessoa TEM de ler antes de pagar não pode ter o peso de uma nota de rodapé. */
.ai-nota.-aviso { padding: 10px 12px; border-radius: 12px; opacity: 1;
  background: rgba(224,163,60,.10); border: 1px solid rgba(224,163,60,.28); color: #f0c98a; }

/* Um número para ditar ao telefone lê-se melhor em monoespaçado, e com espaço entre os dígitos. */
.ai-card-grande.-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .06em; }

/* O cartão do calendário: um ícone, uma frase e um botão. O link nunca se mostra — o que a pessoa
   precisa de saber é o que acontece ao tocar, não o endereço. */
.ai-cal .ai-card-linha { align-items: center; gap: 10px; }
.ai-cal .ai-card-linha svg { width: 20px; height: 20px; flex: 0 0 auto; }

/* O convite de instalação vive na casa, abaixo dos atalhos, e sai quando a pessoa disser que não. */
.ai-instalar { margin-top: 14px; }
.ai-instalar .ai-card-meta svg { width: 17px; height: 17px; vertical-align: -3px; margin: 0 2px; }

/* A FAIXA DE RESUMO das autorizações: a resposta a «foi liberado?» num relance, antes de a pessoa
   ter de contar cartões. Números grandes, rótulo pequeno, cor só onde ela informa. */
.ai-resumo { display: flex; gap: 8px; margin: 10px 0 2px; flex-wrap: wrap; }
.ai-resumo-bloco { flex: 1 1 0; min-width: 84px; padding: 8px 10px; border-radius: 12px;
  display: flex; flex-direction: column; gap: 1px; background: rgba(255,255,255,.05);
  border: 1px solid rgba(255,255,255,.08); }
.ai-resumo-n { font-size: 1.35rem; font-weight: 700; line-height: 1.1; }
.ai-resumo-rot { font-size: .72rem; opacity: .7; }
.ai-resumo-bloco.-ok .ai-resumo-n { color: #6ee7b7; }
.ai-resumo-bloco.-erro .ai-resumo-n { color: #fca5a5; }
.ai-resumo-bloco.-aviso .ai-resumo-n { color: #f0c98a; }
/* O motivo da negativa com rótulo próprio: sem ele a frase do TASY lia-se como descrição. */
.ai-card-alerta .ai-rotulo { display: block; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .07em; opacity: .75; margin-bottom: 2px; }
.ai-resumo-bloco.-neutro .ai-resumo-n { opacity: .72; }

/* Anexar sem abrir o teclado. */
body[data-ecra="conversa"][data-fluxo="agendamento"] .ai-doca #btn-anexo {
  display: block; flex: 0 0 44px; width: 44px; height: 44px; order: 0;
}

.ai-envio { white-space: pre-wrap; overflow-wrap: anywhere; }

.ai-native-options { display: grid; gap: 10px; margin: 14px 0; }
.ai-native-option { text-align: left; white-space: normal; }
.ai-native-image { display: block; max-width: 100%; height: auto; border-radius: 12px; }
.ai-native-text { overflow-wrap: anywhere; margin: 12px 0; }
.ai-native-reply { display: flex; gap: 8px; margin-top: 16px; }
.ai-native-reply input { min-width: 0; flex: 1; padding: 12px; border: 1px solid var(--borda, #666); border-radius: 12px; background: transparent; color: inherit; }
.ai-native-error { margin: 16px 0; }

/* Rede de segurança do demo (Kernel 16/09): nenhum parágrafo da conversa nem do módulo nativo arrasta o
   ecrã — se um token longo escapar às duas formas copiáveis, quebra dentro da largura. Medido: 546 px
   de `#ai-fala` numa janela de 320 antes desta regra. */
.ai-fala, .ai-fala p, .ai-fala li, .ai-parte, .ai-parte p, .ai-native-text, .ai-native-text p { overflow-wrap: anywhere; word-break: break-word; max-width: 100%; }
.ai-copiavel-marca { opacity: .72; font-style: italic; }

.ai-link-btn { display: inline-block; margin: 6px 0; text-decoration: none; }
