/* observatorio/public/css/observatorio.css — a tela do Observatório Catarina.

   Nasceu da folha da InuIA (site-admin/public/css/inuia.css) e mantém a mesma forma:
   tela cheia, arte em caracteres atrás de tudo, fala revelada linha a linha, acervo
   abaixo da dobra. Os nomes de classe mudaram de `inuia-` para `obs-` porque esta é
   outra casa — não um tema da mesma página — e duas casas que compartilham nome de
   classe acabam compartilhando bug.

   O que foi acrescentado aqui, e não existia lá, está no fim do arquivo: a BUSCA NO
   ACERVO (a parte do portal que não custa nada e nunca fecha) e a MESA DOS AGENTES,
   onde as seis figuras aparecem acesas ou apagadas. */
.corpo-obs {
    /* A arte é desenhada em caracteres e recalculada pelo tamanho do viewport; se a página
       rolasse, a grade brigaria com a barra de rolagem a cada quadro. */
    /* Rola: o acervo mora abaixo da dobra. A arte é `fixed`, então ela NÃO rola junto —
       a cena fica parada e o conteúdo desliza por cima dela, que é o que dá a sensação de
       descer dentro do mesmo lugar em vez de trocar de página. */
    min-height: 100dvh; position: relative;
    /* touch-action é a camada que o iOS de fato respeita pra travar a pinça (o Safari
       ignora user-scalable=no do meta). Aqui isso não é restrição: a pinça é o gesto que
       abre a Lupa, que tem zoom próprio e melhor. Ver a trava completa em inuia.js. */
    touch-action: manipulation;
    /* Nada é selecionável por padrão nesta tela — ver o bloco logo abaixo. */
    -webkit-user-select: none; user-select: none;
}

/* === o que se pode selecionar ====================================================== */
/* A tela inteira nasce NÃO selecionável e o texto de verdade pede permissão de volta. É o
   contrário do padrão do navegador, e de propósito: o fundo aqui são dezenas de milhares
   de caracteres que formam a cena. Um toque longo no telefone pegava justamente eles, e
   copiar devolvia uma parede de glifos no lugar da frase que a pessoa queria.
   O fundo é FORMA, não conteúdo. Só se pode levar embora o que é texto: o que ela disse e
   o que ficou no acervo.
   -webkit-touch-callout também, senão o iOS ainda abre o menu de copiar/compartilhar por
   cima do que não deveria nem ser alcançável. */
.obs-frase, .obs-legenda,
.obs-memoria .pergunta, .obs-memoria .resposta, .obs-memoria .quando,
.obs-ponderacao, .obs-acervo h2, .obs-lateral-sub,
.obs-manchete .leitura, input {
    -webkit-user-select: text; user-select: text;
    -webkit-touch-callout: default;
    cursor: text;
}
/* E o que continua fora de alcance mesmo dentro de uma área selecionável: o sigilo de cada
   memória (é a marca dela, não texto) e a grade da manchete (é desenho — o fato em texto
   corrido está no .leitura, invisível, que é justamente o que o leitor de tela e a seleção
   devem alcançar). */
.obs-memoria .sigilo, .obs-manchete .grade {
    -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}

/* === bruma — o desfoque das bordas ================================================== */
/* No topo e na base o que rola por trás se dissolve em vez de terminar num corte seco na
   borda. Fica ABAIXO da fala (9), do cabeçalho e do rodapé (10) e ACIMA do acervo (6):
   quem embaça é o conteúdo passando, nunca o que se está lendo.
   A máscara é o que faz o desfoque MORRER na direção do centro: sem ela seria uma faixa
   borrada de altura fixa, com um corte visível no lugar do que ela veio esconder. */
.obs-bruma {
    position: fixed; left: 0; right: 0; z-index: 7; height: 96px;
    pointer-events: none;
    backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
}
.obs-bruma.topo {
    top: 0;
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 34%, transparent 100%);
}
/* A base é 4× mais alta que o topo: é dela que o texto sobe, e uma dissolução longa dá
   tempo de a linha ir aparecendo em vez de surgir de um corte. No topo o gesto é o
   contrário — a linha sai de cena — e um véu curto basta.
   A máscara continua com a mesma proporção (34% opaco, depois some), então o desfoque só
   fica mais LONGO, não mais forte. */
.obs-bruma.base {
    bottom: 0; height: 384px;
    -webkit-mask-image: linear-gradient(to top, #000 0%, #000 34%, transparent 100%);
            mask-image: linear-gradient(to top, #000 0%, #000 34%, transparent 100%);
}
@media (max-width: 720px) {
    .obs-bruma { height: 76px; }
    .obs-bruma.base { height: 304px; }
}

/* === camada 1: a arte ============================================================== */
.obs-tela {
    position: fixed; inset: 0; margin: 0; z-index: 0;
    font-family: var(--font-mono); font-size: 12px; line-height: 1.02;
    letter-spacing: 0; white-space: pre; overflow: hidden;
    user-select: none; pointer-events: none;
    /* As cores "do estilo" que mudam: um gradiente com as variáveis do tema recortado
       pelo próprio texto. Gira devagar, então a cena inteira respira de cor sem que seja
       preciso pintar caractere por caractere (que seria um <span> por célula — dezenas de
       milhares de nós por quadro). */
    background: linear-gradient(var(--angulo, 120deg),
        var(--sec) 0%, var(--green) 28%, var(--blue) 52%, var(--text) 74%, var(--orange) 100%);
    background-size: 220% 220%;
    -webkit-background-clip: text; background-clip: text;
    color: transparent; -webkit-text-fill-color: transparent;
    opacity: .5;
    transition: opacity 1.2s ease;
}
.corpo-obs.pensando .obs-tela { opacity: .78; }

/* Véu radial: escurece as bordas e abre o centro, pra frase ficar legível por cima da
   arte sem precisar de caixa opaca — o texto flutua na cena em vez de tapá-la. */
.obs-veu {
    position: fixed; inset: 0; z-index: 1; pointer-events: none;
    background: radial-gradient(ellipse at center, rgba(0,0,0,.72) 0%, rgba(0,0,0,.42) 42%, rgba(0,0,0,.86) 100%);
}

/* === camada 2: ecos (perguntas antigas à deriva) =================================== */
.obs-ecos { position: fixed; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
.obs-eco {
    position: absolute; white-space: nowrap; font-size: .62rem; letter-spacing: .1em;
    text-transform: lowercase; color: var(--sec); opacity: 0;
    animation: eco-deriva var(--dur, 38s) linear infinite;
}
@keyframes eco-deriva {
    0%   { opacity: 0;   transform: translateX(0); }
    12%  { opacity: .30; }
    88%  { opacity: .30; }
    100% { opacity: 0;   transform: translateX(-46vw); }
}

/* === topo ========================================================================== */
.obs-topo {
    position: fixed; top: 0; left: 0; right: 0; z-index: 10;
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    /* safe-area no topo também: com viewport-fit=cover a página passa a desenhar sob o
       entalhe do iPhone, e sem isto a marca ficaria atrás dele.
       42px à direita abrem espaço pro sinal de sistema no ar, que mora fora do cabeçalho
       (fixo por conta própria) pra não sumir junto com ele no acervo. */
    padding: calc(14px + env(safe-area-inset-top, 0px)) 42px 14px 22px;
}

/* === sinal de sistema no ar ========================================================= */
/* Mesma leitura visual do site e do app: verde ligado, laranja pulsando quando a conexão
   cai. No canto DIREITO — à esquerda ele disputaria com a marca INUIA.
   Fica FORA do cabeçalho de propósito: ele é estado do sistema, não navegação — e sobrevive
   a qualquer mudança de arranjo do cabeçalho. */
.net-status {
    position: fixed; top: calc(16px + env(safe-area-inset-top, 0px)); right: 16px;
    width: 8px; height: 8px; border-radius: 50%; z-index: 30;
    background: var(--sec); box-shadow: 0 0 6px currentColor;
    transition: background .3s, box-shadow .3s;
}
.net-status.online { background: var(--green); box-shadow: 0 0 6px var(--green); color: var(--green); }
/* Pensando, a COR vem do JS (atualizarLed): interpola --green → --orange → --red conforme
   a profundidade do pedido e o tempo que ele já leva. Estas duas regras são só o ponto de
   partida e a rede de segurança se o estilo em linha faltar.
   O laranja aqui não briga com o de "offline" porque os dois se distinguem pelo
   COMPORTAMENTO, não pela cor: offline pisca por inteiro (netPulse), pensamento apenas
   oscila e nunca apaga. */
.net-status.pensa { background: var(--orange); color: var(--orange); }
.net-status.longe { background: var(--red); color: var(--red); }
.net-status.offline {
    background: var(--orange); box-shadow: 0 0 6px var(--orange);
    animation: netPulse 1s infinite alternate;
}
@keyframes netPulse { from { opacity: .5; } to { opacity: 1; } }
.obs-marca { font-family: var(--font-title); font-size: 1.5rem; letter-spacing: .1em; text-decoration: none; }
.obs-marca b { color: var(--green); }
.obs-marca span { color: var(--text); opacity: .85; }
.obs-estado {
    font-size: .62rem; letter-spacing: .22em; text-transform: uppercase; color: var(--sec);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.corpo-obs.pensando .obs-estado { color: var(--green); }
.obs-nav a { background: rgba(0,0,0,.35); backdrop-filter: blur(6px); }
.obs-nav a.ativa { color: var(--text); border-color: var(--green); }

/* === centro: a fala ================================================================ */
/* A fala se centra NO QUE SOBRA, e não na tela inteira: a altura continua 100dvh (senão o
   acervo subiria pra dentro da primeira dobra), mas o rodapé, a manchete e o teclado são
   descontados por padding. Com box-sizing: border-box isso reserva por dentro, e o
   align-items: center passa a centrar na área livre. É daqui que sai o "nada por cima do
   texto": as três coisas dividem a tela em vez de se empilharem.
   As três medidas são escritas por inuia.js. */
/* A FALA NUNCA SAI DA TELA. Ela é o que a pessoa veio ler, e descer pro acervo não devia
   custar perder a frase no meio. O centro é fixo e a altura vem de --fala-topo, calculada
   em inuia.js (posicionarFala) porque depende da altura real da caixa, que muda a cada
   frase — uma de duas linhas e outra de cinco não se centram no mesmo lugar.
   Uma propriedade só, animável: a transição faz a fala SUBIR até o alto quando o acervo
   entra, em vez de saltar.
   pointer-events: none no contêiner pra que o acervo continue clicável por trás; só a
   caixa da fala recebe toque. */
.obs-centro {
    position: fixed; inset: 0; z-index: 9;
    display: flex; align-items: flex-start; justify-content: center;
    padding: var(--fala-topo, 34vh) 6vw 0;
    transition: padding-top .55s cubic-bezier(.22,.7,.25,1);
    pointer-events: none;
}
/* O acervo passa POR TRÁS da fala; sem esta margem ele começaria no topo do documento,
   já que o centro deixou de ocupar lugar no fluxo. */
.obs-acervo { margin-top: 100dvh; }

/* SEM OBJETO ATRÁS DO TEXTO. Antes havia um halo elíptico aqui: a página se fechando na cor
   do tema atrás da frase, com o desfoque mascarado pela mesma elipse. Resolvia contraste e
   ainda assim era uma COISA — uma forma que aparecia junto com a fala e sumia com ela, e a
   tela ficou melhor sem. Ficou só o que já era do próprio texto: a sombra da letra
   (.obs-frase), que não desenha borda nem contorno em lugar nenhum.
   Se um dia a legibilidade voltar a pedir socorro sobre a cena, o caminho NÃO é o retângulo
   (já rejeitado) nem o halo (removido aqui): é adensar a sombra do texto. */
.obs-fala {
    position: relative; isolation: isolate;
    max-width: 62ch; text-align: center; pointer-events: auto;
    padding: 20px 26px;
    transition: opacity .45s ease;
}
/* Com uma memória acesa, a fala do centro SOME — quem acendeu está lendo o que ficou, e a
   onça falando por cima disso é ruído. O campo de perguntar (no rodapé) continua onde
   sempre esteve: acender não tira a palavra de ninguém. */
body.memorias-acesas .obs-fala { opacity: 0; pointer-events: none; }
.obs-legenda {
    margin: 0 0 12px; min-height: 1em;
    font-size: .6rem; letter-spacing: .26em; text-transform: uppercase; color: var(--sec);
}
.obs-frase {
    margin: 0; min-height: 2.6em;
    font-family: var(--font-mono); font-weight: 500;
    font-size: clamp(1rem, 2.5vw, 1.7rem); line-height: 1.55; letter-spacing: .02em;
    color: var(--text); text-shadow: 0 0 26px rgba(0,0,0,.95), 0 0 8px rgba(0,0,0,.9);
}
/* Caractere ainda embaralhado: apagado, pra leitura "montar" da esquerda pra direita. */
.obs-frase .cru { color: var(--sec); opacity: .55; }

/* === rodapé: a relação ============================================================= */
/* O rodapé é o rodapé, inclusive no telefone — campo, nota e atalhos no pé da tela.
   env(safe-area-inset-bottom) reserva a barra de gestos do iPhone (só funciona porque o
   meta viewport ganhou viewport-fit=cover); --teclado é o quanto o teclado virtual está
   cobrindo, medido pelo visualViewport em inuia.js, porque no iOS o Safari NÃO encolhe o
   layout quando o teclado abre — ele só reduz a viewport visual, e um `bottom: 0` fixo
   ficaria escondido atrás do teclado. */
.obs-rodape {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 10;
    padding: 0 6vw calc(22px + env(safe-area-inset-bottom, 0px));
    transform: translateY(calc(-1 * var(--teclado, 0px)));
    transition: transform .2s ease;
}
.obs-forma {
    display: flex; align-items: center; gap: 10px; max-width: 72ch; margin: 0 auto;
    border: 1px solid var(--panel-border); border-radius: 10px;
    background: rgba(0,0,0,.55); backdrop-filter: blur(10px);
    padding: 6px 6px 6px 14px;
    transition: border-color .25s, box-shadow .25s;
}
.obs-forma:focus-within { border-color: var(--green); box-shadow: 0 0 26px var(--glow); }
.obs-prompt { color: var(--green); font-weight: 700; }
.obs-forma input {
    flex: 1; border: none; background: none; padding: 10px 0; font-size: .92rem; color: var(--text);
}
.obs-forma input:focus { outline: none; }
.obs-forma input::placeholder { color: var(--sec); opacity: .7; }
.obs-forma button {
    border: 1px solid var(--panel-border); border-radius: 7px; background: none; color: var(--sec);
    padding: 9px 18px; font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
}
.obs-forma button:hover:not(:disabled) { color: #000; background: var(--green); border-color: var(--green); }
.obs-forma button:disabled { opacity: .4; cursor: progress; }
.obs-nota {
    max-width: 72ch; margin: 10px auto 0; text-align: center;
    font-size: .62rem; line-height: 1.5; letter-spacing: .04em; color: var(--sec); opacity: .75;
}
.obs-nota.erro { color: var(--red); opacity: 1; }

/* === o convite: uma linha, e o toque copia ========================================== */
/* A nota é de UMA LINHA — é a regra que existe pra ela não crescer e subir por cima da
   fala. Escrever um link inteiro aqui quebrava em três, e é o defeito que isto vem
   consertar: nowrap, e o que não couber vira reticência. A linha não pode crescer nunca,
   aconteça o que acontecer com o conteúdo.
   Verde e monoespaçada porque é código, não frase: a diferença de forma já diz que aquilo
   ali se leva, não se lê. É o único estado em que a nota vira alvo de toque. */
.obs-nota.convite {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    cursor: pointer; opacity: 1;
    color: var(--green); font-family: var(--font-mono); letter-spacing: .06em;
}
.obs-nota.convite:hover { opacity: .75; }
/* Último recurso, quando o navegador não tem área de transferência: o link inteiro fica à
   vista e selecionável — e continua em UMA linha, rolando na horizontal. Uma linha que
   rola é um incômodo; uma linha que vira três empurra o rodapé pra cima da fala, e essa é
   a que não pode acontecer. */
.obs-nota.convite.inteiro {
    overflow-x: auto; text-overflow: clip; cursor: text;
    -webkit-user-select: text; user-select: text;
    -webkit-touch-callout: default;
}

/* === presença, atalhos e memórias ================================================== */
.obs-presenca {
    font-size: .62rem; letter-spacing: .12em; color: var(--sec);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 34vw;
}
.obs-digitando { color: var(--green); }
/* As reticências pulsam sozinhas — é a única animação da barra, e diz "agora". */
.obs-digitando b { animation: pisca 1.1s steps(1) infinite; }
@keyframes pisca { 0%, 49% { opacity: 1; } 50%, 100% { opacity: .25; } }

.obs-atalhos { display: flex; gap: 10px; justify-content: center; margin-top: 10px; }
.obs-atalhos button {
    background: none; border: none; color: var(--sec); opacity: .7;
    font-size: .62rem; letter-spacing: .14em; text-transform: uppercase;
    border-bottom: 1px solid transparent; padding: 2px 0;
}
.obs-atalhos button:hover { color: var(--text); opacity: 1; border-bottom-color: var(--green); }

/* === o acervo — abaixo da dobra =================================================== */
.obs-acervo {
    position: relative; z-index: 6;
    min-height: 100dvh; padding: 8vh 6vw 22vh;
    background: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,.82) 12%, rgba(0,0,0,.9) 100%);
}
.obs-acervo-topo { max-width: 78ch; margin: 0 auto 26px; }
.obs-acervo h2 {
    font-family: var(--font-title); font-size: 2rem; letter-spacing: .08em; margin: 0;
}
.obs-acervo #inuiaListaMemorias { max-width: 78ch; margin: 0 auto; }
.obs-lateral-sub { font-size: .72rem; color: var(--sec); line-height: 1.55; margin: 8px 0 0; }

/* Cada memória entra em duas colunas: o sigilo (a marca que só ela tem) e o texto. O
   sigilo é pequeno de propósito — serve pra reconhecer de relance, não pra competir. */
/* A COR DE UMA MEMÓRIA sai da assinatura dela (--tom, escrito no cartão por inuia.js), que
   é sempre uma cor da paleta do tema em uso — nunca uma paleta paralela. Apagada, ela fica
   ENTRE O FUNDO E O MEIO do caminho até o texto pleno: mais brilhante que o fundo, longe do
   branco. Acesa, sobe quase até o fim.
   A mistura é com --bg (color-mix) e NÃO opacity: opacity apagaria o cartão inteiro atrás
   de um vidro fosco, sigilo e botões junto. Misturando com o fundo, a hierarquia interna
   continua de pé (pergunta acima das linhas, data abaixo de tudo) e o que muda é só o
   quanto aquilo está aceso.
   Três níveis, e não um: é o que mantém a memória legível como texto em vez de virar um
   bloco de uma cor só. */
.obs-memoria {
    border-top: 1px solid var(--panel-border); padding: 18px 0; font-size: .78rem;
    display: grid; grid-template-columns: 5ch 1fr; gap: 16px;
    --brasa: var(--tom, var(--sec));
    --forte: 52%; --medio: 37%; --fraco: 29%;
    cursor: pointer;
    transition: border-color .45s ease;
}
/* Tocar acende, tocar de novo apaga (inuia.js). Com qualquer uma acesa a fala do centro
   some — quem acendeu está lendo, e o resto se cala. */
.obs-memoria.acesa {
    --forte: 100%; --medio: 80%; --fraco: 60%;
    border-top-color: color-mix(in srgb, var(--brasa) 40%, var(--bg));
}
/* Onde color-mix não existe, vale a linha de cima de cada par: a cor do tema, chapada. */
.obs-memoria .pergunta, .obs-memoria .autor { color: var(--sec); }
.obs-memoria .pergunta, .obs-memoria .autor {
    color: color-mix(in srgb, var(--brasa) var(--forte), var(--bg));
}
.obs-memoria .pergunta, .obs-memoria .resposta .linha,
.obs-memoria .quando, .obs-memoria .sigilo, .obs-memoria .autor {
    transition: color .45s ease, text-shadow .45s ease;
}
/* O brilho de estar acesa: um halo curto na própria cor dela, só na pergunta. É o mesmo
   princípio da tela inteira — sem borda, sem caixa, só a luz subindo onde o olho vai. */
.obs-memoria.acesa .pergunta {
    text-shadow: 0 0 24px color-mix(in srgb, var(--brasa) 28%, transparent);
}
/* O cartão inteiro não pisca mais: quem se forma são os blocos DENTRO dele, de cima pra
   baixo (ver .passo abaixo). Uma memória chegando inteira de uma vez era um despejo; ela
   se montando na ordem em que foi vivida é uma leitura. */

/* display:grid na linha de cima vence o [hidden] da folha do navegador — regra de autor
   ganha da do agente, independentemente de especificidade. Sem esta linha o filtro do
   acervo esconde e o cartão continua na tela, que foi exatamente o que aconteceu. */
.obs-memoria[hidden] { display: none; }

/* O marco dos 200px: invisível, sem toque, só um ponto pro observador saber onde a rolagem
   passou. Absoluto no documento, não fixo na tela — ele precisa ficar parado enquanto a
   página anda. */
.obs-marco { position: absolute; top: 200px; left: 0; width: 1px; height: 1px; pointer-events: none; }

/* O achado do enter pisca uma vez: entre doze resultados, dizer QUAL é este. */
.obs-memoria.achada { animation: memoria-achada 2.2s ease both; }
@keyframes memoria-achada {
    0%, 100% { box-shadow: none; }
    12%, 60% { box-shadow: -14px 0 0 -12px var(--green); }
}
@media (prefers-reduced-motion: reduce) { .obs-memoria.achada { animation: none; } }

/* A formação: cada bloco entra com um atraso proporcional à sua posição, então a memória
   se escreve descendo — sigilo, data, pergunta, e as linhas da resposta uma após a outra.
   É atraso de transição, não quadro a quadro: custa zero e a GPU resolve sozinha. */
.obs-memoria .passo {
    opacity: 0; transform: translateY(8px);
    transition: opacity .5s ease, transform .5s ease;
}
.obs-memoria.a-vista .passo {
    opacity: 1; transform: none;
    transition-delay: calc(var(--i, 0) * 55ms);
}
@media (prefers-reduced-motion: reduce) {
    .obs-memoria .passo { opacity: 1; transform: none; transition: none; }
}
.obs-memoria .sigilo {
    font-size: .62rem; line-height: 1.1; white-space: pre; color: var(--tom, var(--sec));
    color: color-mix(in srgb, var(--brasa) var(--fraco), var(--bg));
    align-self: start; padding-top: 2px;
}
.obs-memoria .conteudo { min-width: 0; }
.obs-memoria .quando {
    font-size: .6rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sec);
    color: color-mix(in srgb, var(--brasa) var(--fraco), var(--bg));
}
.obs-memoria .pergunta { margin: 6px 0 0; line-height: 1.5; }
/* A sombra sob a pergunta: as linhas da resposta EMERGEM dela. Elas sobem 8px ao entrar
   (o .passo acima), e esta faixa escura no alto da resposta é o lugar de onde saem — sem
   ela o texto só aparecia; com ela, ele vem de algum lugar. */
.obs-memoria .resposta { position: relative; margin: 12px 0 0; padding-top: 12px; }
.obs-memoria .resposta::before {
    content: ''; position: absolute; top: -8px; left: -6px; right: -6px; height: 30px;
    background: linear-gradient(to bottom,
        rgba(0,0,0,.66) 0%, rgba(0,0,0,.34) 42%, rgba(0,0,0,0) 100%);
    pointer-events: none; z-index: 2;
}
/* Cada divisão da resposta é um bloco próprio — é assim que ela foi escrita (uma frase por
   linha) e agora é assim que fica guardada. Antes virava um parágrafo só, cortado em 260
   caracteres: perdia o texto e perdia a forma. */
.obs-memoria .resposta .linha {
    margin: 0 0 7px; color: var(--sec); font-size: .72rem; line-height: 1.6;
    color: color-mix(in srgb, var(--brasa) var(--medio), var(--bg));
}
/* A primeira linha é onde a onça responde de fato — puxa o olho e as outras seguem. */
.obs-memoria .resposta .linha:first-child { color: var(--text); }
.obs-memoria .resposta .linha:first-child {
    color: color-mix(in srgb, var(--brasa) var(--forte), var(--bg));
}
.obs-memoria .resposta .linha:last-child { margin-bottom: 0; }
.obs-memoria .acoes { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.obs-memoria button {
    background: none; border: 1px solid var(--panel-border); border-radius: 6px; color: var(--sec);
    font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; padding: 5px 11px;
}
.obs-memoria button.apagar:hover { color: #000; background: var(--red); border-color: var(--red); }
.obs-memoria button.guardar:hover { color: #000; background: var(--green); border-color: var(--green); }
/* A fala dela quando alguém pede pra esquecer: aparece ANTES de qualquer botão de
   confirmação, porque a segunda opção só existe se for lida antes da decisão. */
.obs-ponderacao { margin: 10px 0 0; padding: 10px 12px; border-left: 2px solid var(--green);
    font-size: .72rem; line-height: 1.55; color: var(--text); background: rgba(255,255,255,.03); }

/* === o acervo no computador: um mosaico ============================================ */
/* No telefone é uma coluna e pronto. Aqui há largura sobrando, e uma resposta de vinte
   linhas espremida na mesma coluna estreita de uma de duas era desperdício dos dois lados.
   O PORTE vem do tamanho do que foi dito (porteDaMemoria, em inuia.js) — quem falou mais
   ocupa mais. A forma segue o conteúdo, que é a mesma regra da assinatura: o rosto de uma
   memória sai do próprio texto, nunca de sorteio.
   align-items: start pra cada cartão ter a altura que precisa, sem esticar pra alcançar o
   vizinho — é o que faz o mosaico ter formas diferentes de verdade. */
@media (min-width: 900px) {
    .obs-acervo #inuiaListaMemorias {
        max-width: 118ch;
        display: grid; grid-template-columns: repeat(6, 1fr);
        gap: 30px 34px; align-items: start;
    }
    .obs-acervo-topo { max-width: 118ch; }

    .obs-memoria.porte-largo    { grid-column: span 6; }
    .obs-memoria.porte-meio     { grid-column: span 3; }
    .obs-memoria.porte-estreito { grid-column: span 2; }

    /* Maior não é só mais larga: a que tem mais a dizer também é lida maior. */
    .obs-memoria.porte-largo { grid-template-columns: 7ch 1fr; }
    .obs-memoria.porte-largo .sigilo { font-size: .78rem; }
    .obs-memoria.porte-largo .pergunta { font-size: 1.18rem; line-height: 1.45; }
    .obs-memoria.porte-largo .resposta .linha { font-size: .84rem; line-height: 1.65; }
    /* Sem dividir a resposta larga em colunas: multicol com filho transformado e um
       ::before absoluto dentro é terreno instável entre navegadores, e o ganho seria só
       estético. A largura maior com a linha maior já entrega a forma diferente. */

    .obs-memoria.porte-meio .pergunta { font-size: 1rem; }
    .obs-memoria.porte-estreito .pergunta { font-size: .86rem; }
    .obs-memoria.porte-estreito { grid-template-columns: 4ch 1fr; gap: 12px; }
}

/* === celular ======================================================================= */
/* O campo VOLTOU pro pé da tela, junto com a nota e os atalhos — no telefone o rodapé é
   rodapé, como no computador.
   Ele tinha subido pro alto porque, com ele embaixo, abrir o teclado empurrava a página e
   a cena escorregava junto. A causa real não era a posição: era o iOS, onde o Safari não
   encolhe o layout quando o teclado abre (ele ignora o interactive-widget do meta e só
   reduz a viewport VISUAL), então qualquer `bottom: 0` fixo some atrás do teclado. Isso
   agora é resolvido na origem, medindo o teclado pelo visualViewport e subindo o rodapé
   exatamente o que ele cobre — ver --teclado em inuia.js. Com a causa tratada, o campo
   pode ficar onde ele deve ficar, e o alto da tela fica todo pra fala.
   100dvh e não 100vh pelo mesmo espírito: dvh acompanha a barra do navegador, vh não. */
@media (max-width: 720px) {
    .obs-presenca { display: none; }
    .obs-nav { display: none; }          /* a marca já leva de volta ao Dashboard */
    .obs-tela { font-size: 10px; }

    .corpo-obs { min-height: 100dvh; }
    .obs-topo { padding: calc(10px + env(safe-area-inset-top, 0px)) 34px 10px 16px; }
    .obs-marca { font-size: 1.2rem; }

    .obs-rodape { padding: 0 4vw calc(14px + env(safe-area-inset-bottom, 0px)); }
    .obs-forma { padding: 4px 4px 4px 12px; }
    .obs-forma input { padding: 12px 0; font-size: 16px; }  /* 16px evita o zoom do iOS */
    .obs-forma button { padding: 9px 12px; font-size: .6rem; letter-spacing: .08em; }
    .obs-nota { margin-top: 6px; }
    .obs-atalhos { margin-top: 6px; }

    .obs-centro { padding-left: 5vw; padding-right: 5vw; }
    /* Margem fixa no alto do acervo pro filtro não encostar no primeiro texto. É número
       cravado, e não var(--rodape-alt), de propósito: a altura do rodapé muda quando ele
       vira barra, e amarrar o padding a ela faria a página mexer o scroll, que mexeria no
       observador, que mexeria na classe — um laço que nunca assenta. */
    .obs-acervo { padding-top: calc(84px + env(safe-area-inset-top, 0px)); }
    .obs-fala { width: 100%; }
    /* A fala é o que a pessoa veio ler, e no telefone ela era a menor coisa da tela. */
    .obs-frase { font-size: 1.18rem; line-height: 1.5; }
    .obs-legenda { font-size: .58rem; letter-spacing: .2em; }
}

/* === teclado aberto: o campo encosta na fala ======================================= */
/* Subir o rodapé o tanto que o teclado cobre resolveu o campo sumir, mas deixou a fala lá
   em cima e o campo lá embaixo, com um vão morto no meio. Aqui a fala DESCE até encostar
   nele: o que se lê e o que se escreve ficam na mesma altura do olho.
   A nota, os atalhos e a manchete saem de cena — quem está escrevendo não precisa deles, e
   cada linha a menos é uma linha da fala dela a mais. */
/* Com o teclado aberto quem desce a fala é posicionarFala(), em inuia.js: ela ancora no
   PÉ do espaço livre, encostada no campo. Um align-items aqui brigaria com o padding-top
   que posiciona a caixa e a jogaria pro fundo da tela. */
body.teclado-aberto .obs-nota,
body.teclado-aberto .obs-atalhos,
body.teclado-aberto .obs-vozes,
body.teclado-aberto .obs-manchete { display: none; }

/* === no acervo: SÓ a busca aparece ================================================= */
/* Nada mais se mexe. O campo de perguntar fica no rodapé onde sempre esteve, a fala dela
   fica onde está, o cabeçalho continua no lugar. A única coisa que a rolagem faz é revelar
   a busca no alto — o que se está lendo não deveria sair do lugar só porque a página andou.
   A manchete some, e é a única exceção: um fato do mundo atravessando a tela enquanto se
   relê o próprio acervo é interrupção, não companhia. */
body.no-acervo .obs-manchete { display: none; }

/* === procurar no que ficou ========================================================== */
/* Caixa PRÓPRIA, embaixo da de perguntar. As duas convivem no alto da tela porque são
   coisas diferentes: uma traz o que ainda não existe, a outra reencontra o que já foi
   dito. Antes era uma caixa só trocando de função, e atravessar o limiar da rolagem pra
   frente e pra trás a fazia piscar entre as duas.
   Mais discreta que a de perguntar de propósito — perguntar continua sendo o gesto
   principal desta tela. */
.obs-busca {
    /* Fixa no alto, logo abaixo do cabeçalho — e é a ÚNICA coisa que a rolagem revela.
       --topo-alt é a altura real do cabeçalho, medida em inuia.js: cravar um número aqui
       erraria no iPhone, onde a safe-area muda a altura conforme o aparelho. */
    position: fixed; z-index: 11;
    top: calc(var(--topo-alt, 56px) + 8px);
    left: 50%; transform: translateX(-50%);
    width: min(72ch, 92vw);
    display: flex; align-items: center; gap: 9px;
    padding: 3px 10px 3px 12px;
    border: 1px solid var(--panel-border); border-radius: 9px;
    background: rgba(0,0,0,.62); backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    transition: border-color .25s;
    animation: busca-surge .45s cubic-bezier(.22,.7,.25,1) both;
}
/* Surge descendo de leve, "de forma gentil" — não salta pronta na tela. */
@keyframes busca-surge {
    from { opacity: 0; transform: translate(-50%, -10px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
@media (prefers-reduced-motion: reduce) { .obs-busca { animation: none; } }
/* display:flex acima venceria o [hidden] da folha do navegador (regra de autor ganha da do
   agente). Sem esta linha a busca nasceria visível e nunca sumiria — foi exatamente o que
   já aconteceu com os cartões do acervo. */
.obs-busca[hidden] { display: none; }
.obs-busca:focus-within { border-color: var(--green); }
.obs-busca .sinal { color: var(--sec); font-size: .9rem; line-height: 1; opacity: .8; }
.obs-busca input {
    flex: 1; border: none; background: none; padding: 8px 0;
    font-size: .82rem; color: var(--text);
}
.obs-busca input:focus { outline: none; }
.obs-busca input::placeholder { color: var(--sec); opacity: .7; }
/* A contagem é o resultado da conta, e é só isso — não pede clique nem promete ação. */
.obs-busca .conta {
    font-size: .62rem; letter-spacing: .12em; color: var(--green);
    min-width: 2ch; text-align: right;
}
.obs-busca .limpar {
    background: none; border: none; color: var(--sec);
    font-size: 1rem; line-height: 1; padding: 2px 4px;
}
.obs-busca .limpar:hover { color: var(--red); }
@media (max-width: 720px) {
    .obs-busca input { font-size: 16px; }   /* 16px evita o zoom do iOS */
}

/* Quem pede menos movimento recebe a mesma cena, parada: a arte continua desenhada (é o
   ambiente da página), mas sem quadro a quadro, sem gradiente girando e sem deriva. */
@media (prefers-reduced-motion: reduce) {
    .obs-eco { animation: none; opacity: .22; }
    .obs-tela { transition: none; }
}

/* Sala aberta: há alguém de fora ouvindo, e a Observatório perde o acesso ao código — não por
   promessa no prompt, mas porque as ferramentas de leitura não vão junto na chamada. O
   selo fica visível o tempo todo pra que a mudança de assunto nunca pareça evasiva. */
.obs-selo-aberta { display: none; }
.sala-aberta .obs-selo-aberta {
    display: inline-block; white-space: nowrap;
    font-size: .58rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--orange); border: 1px solid var(--orange); border-radius: 999px;
    padding: 3px 10px; opacity: .9;
}

/* === vozes na sala ================================================================== */
.obs-vozes { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 0 auto 10px; max-width: 72ch; }
/* Mesmo caso do resto: display de autor vence o [hidden] do navegador. Aqui isso já era
   um defeito antigo — pra quem não é anfitrião a caixa das vozes ficava "escondida" e
   continuava ocupando os 10px de margem dela no rodapé. */
.obs-vozes[hidden] { display: none; }
.obs-voz {
    background: rgba(0,0,0,.55); backdrop-filter: blur(8px);
    border: 1px solid var(--panel-border); border-radius: 999px; color: var(--sec);
    font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; padding: 5px 13px;
}
.obs-voz:hover { color: var(--text); border-color: var(--green); }
/* Quem responde a próxima pergunta — a única que precisa saltar aos olhos. */
.obs-voz.ativa { color: #000; background: var(--green); border-color: var(--green); font-weight: 700; }
.obs-voz.convocar { border-style: dashed; }
.obs-voz .sair { margin-left: 7px; opacity: .6; }
.obs-voz .sair:hover { opacity: 1; color: var(--red); }

/* Memória que a pessoa não trouxe, mas ouviu: fica ao lado das suas, com a borda marcada
   pra distinguir de relance. Ouvir também é lembrar — mas apagar continua sendo de quem
   falou, então esta não tem botão. */
.obs-memoria.ouvida { border-left: 2px solid var(--panel-border); padding-left: 10px; }
.obs-memoria .ouvida-selo { color: var(--blue); }

/* Casa da onça (inuia.temporama.com.br): a marca cresce e a navegação do painel encolhe
   pra um caminho de volta discreto. Lá ela não é uma aba do admin — o admin é que é
   outro lugar, alcançável daqui. */
.casa-catarina .obs-marca { font-size: 1.9rem; }
.casa-catarina .obs-nav { opacity: .45; }
.casa-catarina .obs-nav:hover { opacity: 1; }

/* === manchete — o mundo se montando na sala ======================================== */
/* Não é caixa: é grade de caracteres. Sem borda desenhada — a moldura é a rarefação dos
   glifos nas bordas, feita no próprio texto. Assim ela pertence à cena em vez de flutuar
   por cima como aviso de navegador.

   ESTE BLOCO ESTAVA DUPLICADO, e é a causa do "retângulo que chama atenção demais": o
   arquivo trazia DOIS .obs-manchete de desenhos diferentes — este, e um anterior de
   caixa opaca com borda laranja entrando deslizando. O antigo vinha depois e ganhava a
   cascata, então o que ia pro ar era o desenho velho, contradizendo o comentário do
   próprio JS ("ela NÃO corre nem desliza: se MONTA") e mantendo .cerca/.corpo/.titulo
   como CSS morto, sem elemento correspondente no DOM. O antigo foi removido; junto foram
   as cópias repetidas de "vozes na sala", "memória ouvida" e "casa da onça", idênticas às
   de cima e portanto sem efeito nenhum.

   Ela mora ACIMA do rodapé e nunca no meio da tela: a fala da Observatório é o que a pessoa veio
   ler, e nada passa por cima dela. --rodape-alt e --teclado são medidos em inuia.js. */
.obs-manchete {
    position: fixed; left: 50%; z-index: 20;
    bottom: calc(var(--rodape-alt, 150px) + var(--teclado, 0px) + 10px);
    transform: translateX(-50%);
    width: max-content; max-width: 94vw;
    margin: 0; padding: 8px 12px;
    background: rgba(0,0,0,.5); backdrop-filter: blur(3px);
    text-shadow: 0 0 14px rgba(0,0,0,.95);
    cursor: pointer; user-select: none;
    transition: opacity .5s ease, bottom .25s ease;
}
.obs-manchete:hover { background: rgba(0,0,0,.66); }
.obs-manchete.saindo { opacity: 0; }

/* A proporção com a fala do centro é direta: lá é clamp(1rem, 2.5vw, 1.7rem), aqui é 0,42
   disso. A razão fixa faz as duas crescerem juntas quando a tela cresce. */
.obs-manchete .grade {
    margin: 0; font-family: var(--font-mono);
    font-size: clamp(.42rem, 1.05vw, .714rem);
    line-height: 1.25; white-space: pre; letter-spacing: 0;
    color: var(--text); overflow: hidden;
}

/* O fato em texto corrido, só para leitor de tela — a grade acima é forma, e ouvir uma
   manchete picada em colunas de caracteres não é ler notícia. */
.obs-manchete .leitura {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; margin: 0;
}

/* As saídas. Mesmo desenho dos atalhos do rodapé de propósito: é a mesma classe de gesto,
   e repetir a forma é o que faz a notícia parecer parte desta tela e não um anúncio. */
.obs-manchete .acoes { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-top: 8px; }
.obs-manchete .acoes button {
    background: none; border: none; color: var(--sec); opacity: .8;
    font-size: .6rem; letter-spacing: .14em; text-transform: uppercase;
    border-bottom: 1px solid transparent; padding: 2px 0;
}
.obs-manchete .acoes button:hover { color: var(--text); opacity: 1; border-bottom-color: var(--green); }
/* A travessia é a única coisa nesta tela que sai da casa e custa dinheiro, e é a única
   marcada em laranja. A cor não é enfeite: é o aviso de que esta porta dá pra fora. */
.obs-manchete .acoes button.horizonte { color: var(--orange); opacity: .9; }
.obs-manchete .acoes button.horizonte:hover { border-bottom-color: var(--orange); }
.obs-manchete .acoes button:disabled { opacity: .35; cursor: progress; }

@media (max-width: 720px) {
    .obs-manchete { max-width: 96vw; padding: 6px 8px; }
    /* No telefone a grade de 56 colunas encolhia até virar risco de lápis. O piso sobe e
       a largura continua cabendo: 56 colunas a .62rem dão ~333px num iPhone de 390. */
    .obs-manchete .grade { font-size: min(.62rem, 2.6vw); }
}
@media (prefers-reduced-motion: reduce) { .obs-manchete { transition: none; } }


/* === o acervo aberto — busca na Constituição, na Declaração e no que se aprendeu ===
   Esta é a parte do portal que responde sem chamar modelo nenhum: SQLite com índice
   de texto, do outro lado. Por isso ela não tem teto, não pede nome e não pisca a luz
   do esforço — procurar aqui custa o mesmo que rolar a página. */
.obs-achados { display: grid; gap: 10px; margin-top: 14px; }
.obs-achado {
    border: 1px solid var(--panel-border); border-radius: 10px; padding: 10px 12px;
    background: rgba(0,0,0,0.42); backdrop-filter: blur(6px); text-align: left;
    color: inherit; font-family: inherit; cursor: pointer; width: 100%;
    -webkit-user-select: text; user-select: text;
}
.obs-achado:hover, .obs-achado:focus-visible { border-color: var(--green); }
.obs-achado .selo {
    font-size: .58rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--sec); border: 1px solid var(--panel-border); border-radius: 999px;
    padding: 1px 8px; margin-right: 8px;
}
.obs-achado .selo.fundacao { color: var(--green); border-color: var(--green); }
.obs-achado .selo.licao { color: var(--blue); border-color: var(--blue); }
.obs-achado .selo.guarda { color: var(--orange); border-color: var(--orange); }
.obs-achado h3 { font-size: .82rem; margin: 6px 0 4px; font-weight: 700; }
.obs-achado p { font-size: .74rem; line-height: 1.5; color: var(--sec); margin: 0; }
.obs-achado .inteiro { display: none; font-size: .78rem; line-height: 1.65; margin-top: 8px; white-space: pre-wrap; }
.obs-achado.aberto .inteiro { display: block; }
.obs-achado.aberto p { display: none; }
.obs-achado .fonte { display: block; margin-top: 8px; font-size: .62rem; color: var(--sec); opacity: .75; }

/* === a mesa dos agentes — seis figuras, acesas ou apagadas ======================= */
.obs-mesa { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); margin-top: 12px; }
.obs-agente {
    border: 1px solid var(--panel-border); border-radius: 10px; padding: 9px 11px;
    background: rgba(0,0,0,0.42); backdrop-filter: blur(6px);
}
.obs-agente.aceso { border-color: var(--green); box-shadow: 0 0 12px -4px var(--glow); }
.obs-agente .nome { font-size: .8rem; font-weight: 700; letter-spacing: .04em; }
.obs-agente .papel { font-size: .6rem; letter-spacing: .16em; text-transform: uppercase; color: var(--sec); }
.obs-agente .diz { font-size: .7rem; line-height: 1.5; color: var(--sec); margin: 6px 0 0; }
.obs-agente .luz {
    display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 7px;
    background: var(--panel-border);
}
.obs-agente.aceso .luz { background: var(--green); box-shadow: 0 0 7px var(--green); }
.obs-agente .comandos { display: flex; gap: 6px; margin-top: 8px; }
.obs-agente .comandos button {
    background: transparent; border: 1px solid var(--panel-border); border-radius: 999px;
    color: inherit; font-size: .62rem; padding: 3px 10px;
}
.obs-agente .comandos button:hover { border-color: var(--green); color: var(--green); }

/* O rodapé do acervo: quantos registros a casa guarda. Números pequenos, sem alarde —
   é a única métrica que o portal mostra a quem chega. */
.obs-numeros { font-size: .64rem; color: var(--sec); letter-spacing: .06em; margin-top: 18px; }
.obs-numeros b { color: var(--text); font-weight: 700; }
