
  /* ═══════════════════════════════════════════════════════════════════════
     O CARD DE RELATÓRIO — fonte única servida em /relatorio.css.
     A MESMA folha veste as três superfícies: o chat (home), o preview de
     /relatorios e a página pública /c/<slug>. Portado de
     mockups/relatorio-chat/rc.css (gabarito a3-pronto, aprovado 06/08/2026).

     O relatório v2 é NATIVO do design system v5 (branco, Inter, número em IBM
     Plex Mono) — abandona a linguagem creme/serif do envelope legado.

     ⚠️ TODA cor vai com FALLBACK: os tokens do ds-v5 (--tinta, --linha, --campo,
     --verm, --ok, --sutil, --mut, --lateral, --hover, --atencao) existem na home
     e nas páginas do app, mas NÃO na página pública /c/, que tem paleta própria.
     Sem o fallback, o card renderiza sem cor exatamente na superfície que a gente
     manda para fora. (O /c/ ganhou uma ponte de tokens escopada em .cbody-rel —
     o fallback continua sendo a rede de segurança, não a solução.)
     ═══════════════════════════════════════════════════════════════════════ */

  /* ---- o card dentro da conversa ----------------------------------------
     Regra da casa "texto estreito, dado largo": em tela grande o relatório
     sangra para fora da coluna de 760px do chat. */
  .relatorio-turno{margin:16px 0 6px}
  @media(min-width:1240px){.relatorio-turno{margin-left:-100px;margin-right:-100px}}
  @media(min-width:1100px) and (max-width:1239px){.relatorio-turno{margin-left:-52px;margin-right:-52px}}
  .rel-selo-ajuste{font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.06em;
    text-transform:uppercase;color:var(--ok,#1f7a45);border:1px solid currentColor;border-radius:6px;
    padding:3px 9px;display:inline-block;margin-bottom:10px}

  .rel-card{border:1px solid var(--linha,#e8e6e2);border-radius:16px;background:var(--campo,#fff);
    overflow:hidden;margin:16px 0;box-shadow:0 2px 16px rgba(13,13,13,.055);
    font-family:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
    color:var(--tinta,#0d0d0d);line-height:1.55;text-align:left}
  [data-theme="escuro"] .rel-card{box-shadow:0 2px 16px rgba(0,0,0,.3)}
  .rel-card .mono{font-family:'IBM Plex Mono',monospace;font-variant-numeric:tabular-nums}

  /* ---- tokens de GRÁFICO (--g-*) ----------------------------------------
     ⚠️ NADA de crase neste comentário: ele vive dentro de um template literal
     e uma crase FECHA a string (regra 9 do CLAUDE.md, 5ª ocorrência no repo).

     Os SVGs são inline no documento, então enxergam variáveis CSS — e o
     render_svg.ts já pede os tokens do vocabulário do gabarito com fallback
     (var(--g-destaque, var(--verm,#e3120b))). Faltava alguém DECLARAR os
     tokens: o ds-v5 não os tem, então até aqui todo desenho vivia do fallback,
     e um fallback é uma cor fixa — no tema escuro ela não vira.

     Eles nascem no .rel-card (não no :root) de propósito: a paleta de gráfico
     é do relatório, e o card é a única superfície comum ao chat, ao preview de
     /relatorios e à página pública /c/ — declarar no :root não alcançaria o
     /c/, que tem paleta própria.

     Só os tokens que o desenho NOVO precisa entram agora. --g-linha, --g-barra,
     --g-grid e --g-eixo continuam de fora para que linha, barra e eixo
     renderizem exatamente como hoje: token novo em gráfico já validado é
     regressão visual sem pedido. */
  .rel-card{--g-destaque:#e3120b;--g-surf:#ffffff;
    --gc1:#2e2b27;--gc2:#6e6a63;--gc3:#9c9891;--gc4:#c7c3bc}
  [data-theme="escuro"] .rel-card{--g-destaque:#f25653;--g-surf:#1a1b20;
    --gc1:#d8d5d0;--gc2:#8b8880;--gc3:#605d58;--gc4:#42403c}

  /* ---- cabeçalho: kicker + estado + ações -------------------------------- */
  .rel-cab{display:flex;align-items:center;gap:10px;padding:10px 16px;
    border-bottom:1px solid var(--linha,#e8e6e2);background:var(--lateral,#f7f6f4);flex-wrap:wrap}
  .rel-cab .kick{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.13em;
    text-transform:uppercase;color:var(--sutil,#6e6e6e);display:flex;align-items:center;gap:7px}
  .rel-cab .kick::before{content:"";width:7px;height:7px;background:var(--verm,#e3120b);
    transform:rotate(45deg);display:inline-block;flex-shrink:0}
  .rel-cab .esp{flex:1}
  .rel-cab .badge{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.05em;
    text-transform:uppercase;padding:3px 9px;border-radius:6px;border:1px solid var(--linha,#e8e6e2);
    color:var(--sutil,#6e6e6e);white-space:nowrap;display:inline-block}
  .rel-acao{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--mut,#6b6b6b);
    border:1px solid transparent;border-radius:9px;padding:5px 10px;cursor:pointer;background:transparent;
    font-family:inherit;white-space:nowrap}
  .rel-acao:hover{background:var(--campo,#fff);border-color:var(--linha,#e8e6e2);color:var(--tinta,#0d0d0d)}
  .rel-acao.forte{background:var(--tinta,#0d0d0d);color:var(--fundo,#fff);border-color:var(--tinta,#0d0d0d);font-weight:600}
  .rel-acao.forte:hover{background:var(--verm,#e3120b);border-color:var(--verm,#e3120b);color:#fff}
  /* ação que ainda não pode agir NÃO some: fica visível, apagada e com o porquê no
     title — o usuário precisa saber que existe e o que destrava. */
  .rel-acao.desabilitado,.rel-acao[disabled]{opacity:.45;cursor:not-allowed}
  .rel-acao[disabled]:hover{background:transparent;border-color:transparent;color:var(--mut,#6b6b6b)}
  .rel-acao.forte[disabled]:hover{background:var(--tinta,#0d0d0d);border-color:var(--tinta,#0d0d0d);color:var(--fundo,#fff)}
  .rel-cab .desabilitado{display:inline-flex;gap:10px}
  /* Estados do badge. "prévia"/"rascunho"/"congelado" são neutros; "salvo" confirma e
     "não salvo" DENUNCIA. Erro de rede jamais pode deixar o card dizendo "salvo": um
     badge mentiroso é pior que nenhum, porque o usuário fecha a aba confiando nele. */
  .rel-cab .badge.ok{color:var(--ok,#1f7a45);border-color:currentColor}
  .rel-cab .badge.falha{color:var(--verm,#e3120b);border-color:currentColor}
  .rel-cab .badge.andando{opacity:.6}
  /* "dado parcial" (e2). É um badge de CONTEÚDO, não de persistência: fica ao LADO do
     estado (prévia/rascunho/salvo), nunca no lugar dele — o cliente troca o PRIMEIRO
     .rel-cab .badge por querySelector, então o de conteúdo tem de vir DEPOIS.
     Usa --atencao, o MESMO token da .rel-flag: âmbar, já validado nos dois temas. O
     vermelho fica reservado ao acento de marca e ao defeito nosso (.badge.falha) —
     "a fonte não declarou" é fato do documento, não erro nem alarme. */
  .rel-cab .badge.atencao{color:var(--atencao,#8a6a12);border-color:currentColor}

  /* ---- link público gerado pelo Compartilhar ------------------------------
     Nasce só depois que a rota responde com o slug; some no snapshot /c/ (lá não há
     o que compartilhar de novo) e na impressão. O <input readonly> existe para o link
     ser SELECIONÁVEL e copiável mesmo quando a Clipboard API não está disponível
     (http, permissão negada) — o botão "copiar" é o atalho, nunca o único caminho. */
  .rel-link{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:10px 16px;
    border-bottom:1px solid var(--linha,#e8e6e2);background:var(--lateral,#f7f6f4);font-size:12px}
  .rel-link .lb{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.06em;
    text-transform:uppercase;color:var(--sutil,#6e6e6e);white-space:nowrap}
  .rel-link input{flex:1;min-width:180px;font-family:'IBM Plex Mono',monospace;font-size:12px;
    padding:6px 9px;border:1px solid var(--linha,#e8e6e2);border-radius:8px;background:var(--campo,#fff);
    color:var(--tinta,#0d0d0d)}
  .rel-link a{color:var(--verm,#e3120b);text-decoration:none;white-space:nowrap}
  .rel-link a:hover{text-decoration:underline}

  /* ---- título + linha de meta ------------------------------------------- */
  .rel-tit{padding:16px 20px 0}
  .rel-tit h3{font-family:'Inter Tight','Inter',sans-serif;font-size:21px;font-weight:700;
    letter-spacing:-.02em;line-height:1.25;margin:0;color:var(--tinta,#0d0d0d)}
  .rel-tit .meta{font-size:12px;color:var(--sutil,#6e6e6e);margin-top:5px;display:flex;gap:9px;
    flex-wrap:wrap;align-items:center}
  .rel-tit .meta .mono{font-family:'IBM Plex Mono',monospace}
  .rel-tit .meta .sep{opacity:.45}
  /* CARIMBO DE PERÍODO: procedência, não pedido — por isso vive na linha de meta e
     não nos chips. Sobrevive ao /c/ público e ao PDF, que é onde ele mais importa
     (o leitor externo não tem a conversa para perguntar de que período se fala). */
  .rel-tit .meta .carimbo b{color:var(--mut,#6b6b6b);font-weight:600}
  .rel-tit .meta b{color:var(--tinta,#0d0d0d);font-weight:600}
  .rel-card .classe{font-family:'IBM Plex Mono',monospace;font-size:10px;color:var(--verm,#e3120b);
    border:1px solid currentColor;border-radius:6px;padding:2px 8px;text-transform:uppercase;flex-shrink:0}
  .rel-card .classe.fii{color:var(--ok,#1f7a45)}
  .rel-card .classe.cri{color:#3b5bd6}
  .rel-card .classe.fip{color:var(--classe-fip,#7a3fa8)}
  .rel-card .classe.c555{color:var(--mut,#6b6b6b)}

  /* ---- chips: o ajuste comum vira clique --------------------------------
     Cada chip carrega o PEDIDO EM PORTUGUÊS num data-rel-pedido; o chat escreve
     esse texto no composer e envia. Chip e fala são o mesmo caminho.
     Fora do chat (preview de /relatorios, snapshot /c/) não há para onde mandar
     o pedido — então os chips e as ações não aparecem, em vez de aparecerem
     mortos. É o mesmo motivo pelo qual o snapshot já sai sem eles do servidor. */
  .rel-chips{display:flex;gap:7px;flex-wrap:wrap;padding:14px 20px 15px;align-items:center}
  .chip-grupo{display:inline-flex;border:1px solid var(--linha,#e8e6e2);border-radius:999px;overflow:hidden}
  .chip-grupo .lb{font-size:11px;color:var(--sutil,#6e6e6e);padding:6px 10px 6px 13px;
    font-family:'IBM Plex Mono',monospace;text-transform:uppercase;letter-spacing:.06em;align-self:center}
  .chip-grupo button{border:none;background:transparent;font-size:12.5px;color:var(--mut,#6b6b6b);
    padding:6px 12px;cursor:pointer;font-family:inherit;border-left:1px solid var(--linha,#e8e6e2)}
  .chip-grupo button:hover{background:var(--hover,#efedea);color:var(--tinta,#0d0d0d)}
  .chip-grupo button.on{background:var(--tinta,#0d0d0d);color:var(--fundo,#fff);font-weight:600}
  .rel-chips .chip{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;color:var(--mut,#6b6b6b);
    border:1px solid var(--linha,#e8e6e2);border-radius:999px;padding:6px 13px;cursor:pointer;
    background:transparent;font-family:inherit}
  .rel-chips .chip:hover{background:var(--hover,#efedea);color:var(--tinta,#0d0d0d)}
  .rel-chips .chip.pontilhado{border-style:dashed}
  .rel-card .rel-chips{display:none}
  .rel-card .rel-cab .rel-acao{display:none}
  .relatorio-turno .rel-card .rel-chips,.rel-card.rel-interativo .rel-chips{display:flex}
  .relatorio-turno .rel-card .rel-acao,.rel-card.rel-interativo .rel-acao{display:inline-flex}

  /* ---- faixa de indicadores ---------------------------------------------
     Colunas vêm do servidor em data-cols (5 KPIs numa grade de 4 deixa um indicador
     sozinho numa linha inteira — o mockup nunca mostra isso).
     Os fios entre células são box-shadow, não border: com overflow:hidden no
     contêiner eles somem sozinhos na borda e na última linha, sem nth-child
     recontado a cada mudança no número de indicadores. */
  .rel-kpis{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
    border-top:1px solid var(--linha,#e8e6e2);border-bottom:1px solid var(--linha,#e8e6e2);overflow:hidden}
  .rel-kpis[data-cols="1"]{grid-template-columns:minmax(0,1fr)}
  .rel-kpis[data-cols="2"]{grid-template-columns:repeat(2,minmax(0,1fr))}
  .rel-kpis[data-cols="3"]{grid-template-columns:repeat(3,minmax(0,1fr))}
  .rel-kpi{padding:14px 18px 12px;min-width:0;position:relative;
    box-shadow:1px 0 0 0 var(--linha,#e8e6e2),0 1px 0 0 var(--linha,#e8e6e2)}
  .rel-kpi:last-child{box-shadow:0 1px 0 0 var(--linha,#e8e6e2)}
  .rel-kpi .l{font-size:10.5px;color:var(--sutil,#6e6e6e);text-transform:uppercase;letter-spacing:.05em;
    font-family:'IBM Plex Mono',monospace;line-height:1.3}
  .rel-kpi .v{font-family:'IBM Plex Mono',monospace;font-size:22px;font-weight:600;margin-top:4px;
    letter-spacing:-.02em;line-height:1.1;color:var(--tinta,#0d0d0d);overflow-wrap:break-word}
  /* subtítulo do indicador (D5): a copy que EXPLICA o número, entre ele e o
     carimbo de fonte. Escrita no template e validada pelo lint anti-número da
     SPEC — antes o render a jogava fora, e 5 dos 8 subtítulos do mensal_fii
     eram texto revisado que nunca chegou à tela. */
  .rel-kpi .sub{font-size:11.5px;color:var(--mut,#6b6b6b);margin-top:5px;line-height:1.45}
  .rel-kpi .c{font-size:11px;color:var(--sutil,#6e6e6e);margin-top:3px;line-height:1.4}
  .rel-kpi .nota{font-size:11px;color:var(--atencao,#8a6a12);margin-top:7px;line-height:1.4}
  /* SPARKLINE (gabarito a3): a FORMA da série de onde saiu o número — sem eixo, sem
     escala, sem rótulo. Largura limitada porque uma linha esticada por uma célula de
     420px vira gráfico, e gráfico sem eixo mente sobre magnitude; a série completa,
     com escala e competência, está no bloco logo abaixo. Fica por último na célula:
     nunca empurra o carimbo de fonte, que é o que sustenta o número. */
  .rel-kpi .rel-spark{display:block;width:100%;max-width:132px;height:auto;margin-top:9px;
    opacity:.85;overflow:visible}
  .rel-kpi:hover .rel-spark{opacity:1}
  /* No celular a faixa vira 1 coluna e a célula fica larga: o teto de 132px segura a
     proporção e a linha não se transforma num gráfico de largura cheia. */
  @media(max-width:460px){.rel-kpi .rel-spark{max-width:150px}}
  /* nota longa: recolhida por padrao. O texto continua INTEIRO ali — so nao
     sequestra a faixa de indicadores. Aberta na impressao (o PDF nao tem clique). */
  .rel-kpi .nota-longa{cursor:pointer}
  .rel-kpi .nota-longa summary{list-style:none;font-weight:500;text-decoration:underline;
    text-underline-offset:2px;outline:none}
  .rel-kpi .nota-longa summary::-webkit-details-marker{display:none}
  .rel-kpi .nota-longa[open]{padding-bottom:2px}
  @media print{ .rel-kpi .nota-longa{display:block} .rel-kpi .nota-longa summary{display:none} }
  /* valor ausente: itálico e cinza, NUNCA zero. */
  .rel-kpi.vazio .v{color:var(--sutil,#6e6e6e);font-size:15px;font-style:italic;font-family:inherit;font-weight:400}
  /* defeito NOSSO de leitura ≠ silêncio da fonte: a cor de risco marca a diferença. */
  .rel-kpi.vazio.falha .v{color:var(--verm-texto,#c8100a)}
  /* ÚLTIMA LINHA INCOMPLETA. colunasKpi() evita o indicador SOZINHO, mas não o buraco:
     5 numa grade de 3 dá 3+2 e sobra uma célula vazia, com fio e tudo, à direita do
     último indicador — o gabarito nunca mostra isso. O último estica e ocupa o que
     sobrou. Escopado em min-width:821px porque o seletor de atributo é mais específico
     que a regra do tablet e venceria dentro da media query de 2 colunas. */
  @media(min-width:821px){
    .rel-kpis[data-cols="3"]>.rel-kpi:last-child:nth-child(3n+2){grid-column:span 2}
    .rel-kpis[data-cols="3"]>.rel-kpi:last-child:nth-child(3n+1){grid-column:1/-1}
    .rel-kpis[data-cols="4"]>.rel-kpi:last-child:nth-child(4n+3){grid-column:span 2}
    .rel-kpis[data-cols="4"]>.rel-kpi:last-child:nth-child(4n+2){grid-column:span 3}
    .rel-kpis[data-cols="4"]>.rel-kpi:last-child:nth-child(4n+1){grid-column:1/-1}
  }
  /* No tablet a grade cai para 2 colunas SEM consultar colunasKpi() — e o cuidado do
     servidor com o órfão evapora: 5 indicadores viram 2+2+1 e 3 viram 2+1, deixando
     meia linha vazia (o buraco que o mockup nunca mostra). Com número ímpar, o último
     ocupa a linha inteira: some o buraco sem espremer os outros em 3 colunas de 240px. */
  @media(max-width:820px){.rel-kpis,.rel-kpis[data-cols="3"]{grid-template-columns:repeat(2,minmax(0,1fr))}
    .rel-kpis>.rel-kpi:last-child:nth-child(odd){grid-column:1/-1}}
  @media(max-width:460px){.rel-kpis,.rel-kpis[data-cols="2"],.rel-kpis[data-cols="3"]{grid-template-columns:minmax(0,1fr)}}

  /* ---- blocos ------------------------------------------------------------ */
  .rel-blocos{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))}
  .rel-bloco{padding:18px 20px 16px;border-bottom:1px solid var(--linha,#e8e6e2);
    grid-column:span 2;min-width:0;position:relative}
  .rel-bloco.meio{grid-column:span 1;border-right:1px solid var(--linha,#e8e6e2)}
  .rel-bloco.meio + .rel-bloco.meio{border-right:none}
  .rel-blocos > :last-child{border-bottom:none}
  @media(max-width:820px){.rel-bloco.meio{grid-column:span 2;border-right:none}}
  .rel-bloco-cab{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;margin-bottom:12px}
  .rel-bloco-tit{font-family:'IBM Plex Mono',monospace;font-size:10.5px;letter-spacing:.09em;
    text-transform:uppercase;color:var(--mut,#6b6b6b);font-weight:500;line-height:1.4}
  .rel-bloco-sub{font-size:12px;color:var(--sutil,#6e6e6e);margin-top:3px;font-family:inherit;
    letter-spacing:0;text-transform:none;line-height:1.45}
  /* ⚠️ NADA de crase neste comentário: ele vive dentro de um template literal e uma
     crase FECHA a string (regra 9 do CLAUDE.md — 6ª ocorrência, esta foi minha).

     A regra do rel-bloco-menu SAIU no mesmo commit que o botão (08/08/2026); o motivo,
     com os três números que decidiram, está no blocoEnvelope do renderer.ts. Código
     substituído é código REMOVIDO: regra de CSS sem emissor é a próxima sessão
     construindo em cima do que não existe. Não ficou regra de LEGADO porque a medição
     em prod deu ZERO — relatorio_share com 0 linhas e nenhuma resposta de
     pagina_compartilhada contendo a classe: não há byte congelado que dependa dela. */
  /* ---- O DIFF DO AJUSTE (gabarito a4) ------------------------------------
     Contrato com a frente que renderiza o card: EU pinto, ELA aplica a classe.
       .rel-bloco.novo   → bloco que ENTROU neste ajuste
       .rel-bloco.saindo → bloco que SAIU e continua visivel, para a pessoa VER o
                           que perdeu (hoje o bloco removido some e nada o anuncia)
     Rotulo padrao vem do CSS; a renderizadora pode trocar a palavra com o atributo
     data-diff-rotulo="…" sem tocar nesta folha.

     ⚠️ IDENTIDADE NUNCA SO POR COR (regra do gabarito): os dois estados carregam
     TEXTO, nao apenas fundo tingido. Fundo sozinho e' invisivel para quem nao
     distingue as duas cores — e "entrou" e "saiu" sao exatamente opostos.

     ⚠️ NADA de crase neste comentario: ele vive dentro de um template literal e uma
     crase FECHA a string (regra 9 do CLAUDE.md). */
  .rel-bloco.novo,.rel-bloco.saindo{position:relative}
  .rel-bloco.novo{background:color-mix(in srgb, var(--ok,#1f7a45) 7%, transparent);
    box-shadow:inset 3px 0 0 var(--ok,#1f7a45)}
  .rel-bloco.saindo{background:var(--lateral,#f7f6f4);box-shadow:inset 3px 0 0 var(--mut,#6b6b6b);opacity:.62}
  .rel-bloco.novo::before,.rel-bloco.saindo::before{
    position:absolute;top:8px;right:12px;z-index:1;
    font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:.09em;text-transform:uppercase;
    border-radius:999px;padding:2px 8px;line-height:1.5;white-space:nowrap}
  .rel-bloco.novo::before{content:"entrou neste ajuste";color:var(--ok,#1f7a45);border:1px solid var(--ok,#1f7a45)}
  .rel-bloco.saindo::before{content:"saiu deste ajuste";color:var(--mut,#6b6b6b);border:1px solid var(--mut,#6b6b6b)}
  .rel-bloco.novo[data-diff-rotulo]::before,.rel-bloco.saindo[data-diff-rotulo]::before{content:attr(data-diff-rotulo)}
  /* O selo nao pode cavalgar o titulo do bloco: o cabecalho abre espaco para ele. */
  .rel-bloco.novo>.rel-bloco-cab,.rel-bloco.saindo>.rel-bloco-cab{padding-right:132px}
  @media(max-width:460px){
    .rel-bloco.novo::before,.rel-bloco.saindo::before{position:static;display:inline-block;margin-bottom:8px}
    .rel-bloco.novo>.rel-bloco-cab,.rel-bloco.saindo>.rel-bloco-cab{padding-right:0}
  }
  /* IMPRESSAO: o PDF e' o documento FINAL, nao o rascunho. O bloco que saiu nao
     entra na folha, e o que entrou perde o realce (ele ja esta la — o realce so
     dizia "isto acabou de mudar", que e conversa de tela, nao de documento). */
  @media print{
    .rel-bloco.saindo{display:none!important}
    .rel-bloco.novo{background:none!important;box-shadow:none!important}
    .rel-bloco.novo::before,.rel-bloco.saindo::before{display:none!important}
    .rel-bloco.novo>.rel-bloco-cab{padding-right:0!important}
  }
  /* PAGINA PUBLICA (/c/): o snapshot e' congelado e definitivo — nao existe "ajuste"
     do lado de la, e um bloco tachado seria a pagina publica exibindo o rascunho de
     outra pessoa. A classe .cbody-rel so existe no paginaBase deste arquivo, entao
     esta regra e' cinto de seguranca contra HTML congelado que ja tenha nascido com
     a classe — nunca o caminho feliz. */
  .cbody-rel .rel-bloco.saindo{display:none}
  .cbody-rel .rel-bloco.novo{background:none;box-shadow:none}
  .cbody-rel .rel-bloco.novo::before{display:none}
  .cbody-rel .rel-bloco.novo>.rel-bloco-cab{padding-right:0}

  .rel-viz{min-height:20px}
  /* Barras horizontais ROLAM em vez de encolher o rótulo até ficar ilegível no
     celular (decisão do mockup): o rótulo da categoria é metade da informação. */
  .rel-bloco[data-tipo="barra"] .rel-viz{overflow-x:auto;-webkit-overflow-scrolling:touch}
  /* ...mas a EMPILHADA não: ela é uma faixa de 100% sem rótulo lateral, cabe em
     qualquer largura, e o min-width criaria rolagem horizontal onde não há o
     que rolar. A identidade das fatias mora na legenda abaixo, que quebra linha. */
  .rel-bloco[data-tipo="barra"]:not([data-marca="empilhada"]) .rel-viz svg{min-width:600px}
  .rel-bloco[data-marca="empilhada"] .rel-viz{overflow-x:visible}

  /* ---- legenda da composição (barra empilhada 100%) ----------------------
     "Identidade nunca só por cor" (README do gabarito): cada fatia aparece aqui
     com NOME, VALOR e PERCENTUAL — o percentual vindo da tool, nunca de uma
     divisão do render. Quebra em várias linhas em vez de encolher o texto. */
  .rel-legenda{display:flex;gap:8px 16px;flex-wrap:wrap;margin-top:11px}
  .rel-leg{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;
    color:var(--mut,#6b6b6b);min-width:0}
  .rel-leg i{width:9px;height:9px;border-radius:2.5px;display:inline-block;flex-shrink:0}
  /* O NÚMERO nunca quebra: no celular o flex espremia a célula do valor e
     "R$ 9,38 bi" saía em duas linhas ("R$ 9,38" / "bi"), que é a mesma doença da
     coluna de tabela esmagada. Quem quebra é o NOME da classe, que é prosa. */
  .rel-leg b{font-family:'IBM Plex Mono',monospace;color:var(--tinta,#0d0d0d);font-weight:500;
    white-space:nowrap}
  .rel-leg u{font-family:'IBM Plex Mono',monospace;text-decoration:none;
    color:var(--sutil,#6e6e6e);font-size:11px;white-space:nowrap}
  @media(max-width:460px){.rel-legenda{gap:6px 12px}.rel-leg{width:100%}}

  /* ---- estados de honestidade ------------------------------------------- */
  .rel-vazio{font-size:13.5px;color:var(--mut,#6b6b6b);padding:22px 14px 20px;text-align:center;
    border:1px dashed var(--linha,#e8e6e2);border-radius:10px;line-height:1.5}
  .rel-vazio b{display:block;color:var(--tinta,#0d0d0d);font-weight:500;margin-bottom:3px;font-size:14px}
  .rel-flag{font-size:12px;color:var(--atencao,#8a6a12);border-left:3px solid var(--atencao,#8a6a12);
    padding:7px 11px;margin-top:11px;border-radius:0 7px 7px 0;line-height:1.45;background:var(--lateral,#f7f6f4)}
  .rel-flag.risco{color:var(--verm-texto,#c8100a);border-left-color:var(--verm,#e3120b)}
  .rel-fonte{font-family:'IBM Plex Mono',monospace;font-size:9.5px;letter-spacing:.07em;
    color:var(--sutil,#6e6e6e);text-transform:uppercase;margin-top:12px;padding-top:9px;
    border-top:1px solid var(--linha,#e8e6e2);line-height:1.6}
  .rel-rod{padding:14px 20px 16px;background:var(--lateral,#f7f6f4);border-top:1px solid var(--linha,#e8e6e2);
    font-size:11.5px;color:var(--mut,#6b6b6b);line-height:1.6}
  .rel-rod b{color:var(--tinta,#0d0d0d);font-weight:500}

  /* ---- prévia que não fica guardada (B1) ---------------------------------
     Sem conta o relatório vive só nesta aba. O aviso nasce COM o card (estado
     conservador) e o cliente o remove quando o servidor afirma logado +
     relatorio_id. Some na impressão: um PDF não tem "recarregar a página". */
  .rel-previa-aviso{padding:11px 20px;border-top:1px solid var(--linha,#e8e6e2);
    background:var(--lateral,#f7f6f4);font-size:11.5px;line-height:1.55;
    color:var(--atencao,#8a6a12)}
  @media print{ .rel-previa-aviso{display:none!important} }

  /* ---- o lugar de um relatório que NÃO voltou (B1) ------------------------
     Ao reabrir uma conversa, o card volta do servidor (GET /api/relatorio/:id)
     — mas relatório montado sem conta nunca foi gravado e não tem de onde voltar.
     O turno NÃO pode ficar mudo com a prosa do Tomé dizendo "o relatório apareceu
     aqui": o lugar é ocupado por esta nota, que diz a verdade e oferece a saída.
     Tem a moldura tracejada do estado vazio, nunca a do card — não é relatório. */
  .rel-ausente{border:1px dashed var(--linha,#e8e6e2);border-radius:12px;
    padding:14px 16px;margin:16px 0 6px;font-size:12.5px;line-height:1.55;
    color:var(--mut,#6b6b6b);background:var(--lateral,#f7f6f4);
    font-family:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif}
  .rel-ausente b{display:block;color:var(--tinta,#0d0d0d);font-weight:600;
    font-size:13px;margin-bottom:3px}

  /* ---- tabela ------------------------------------------------------------ */
  .rel-tab-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:2px 0}
  table.rel-tab{border-collapse:collapse;width:100%;font-size:13px;min-width:520px;
    color:var(--tinta,#0d0d0d);overflow-wrap:normal;word-break:normal}
  table.rel-tab th{text-align:left;font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.05em;
    text-transform:uppercase;color:var(--sutil,#6e6e6e);background:var(--lateral,#f7f6f4);font-weight:600;
    border-bottom:1px solid var(--linha,#e8e6e2);padding:8px 12px;white-space:nowrap}
  table.rel-tab td{padding:8px 12px;border-bottom:1px solid var(--linha,#e8e6e2);white-space:nowrap}
  table.rel-tab tr:last-child td{border-bottom:none}
  table.rel-tab td.n,table.rel-tab th.n{text-align:right;font-family:'IBM Plex Mono',monospace}
  table.rel-tab td.n{font-size:12px}

  /* ---- impressão / PDF ---------------------------------------------------
     O botão "⬇ PDF" chama window.print(): sai o CARD, não a tela. Esconder por
     visibilidade (e não por display) preserva o layout interno do card. */
  @media print{
    @page{margin:14mm}
    html,body{height:auto!important;overflow:visible!important;background:#fff!important}
    body *{visibility:hidden!important}
    .rel-card,.rel-card *{visibility:visible!important}
    .rel-card{margin:0!important;border:none!important;border-radius:0!important;box-shadow:none!important;
      background:#fff!important}
    /* Uma conversa pode ter VÁRIOS relatórios. O clique em "PDF" marca o card pedido
       (e o beforeprint marca o último, quando a impressão veio do Ctrl+P) — só ele
       sai, ancorado no topo da folha. Sem a marca, nada é ancorado: melhor um PDF
       com espaço em branco do que dois cards empilhados um sobre o outro. */
    body.rel-imprimindo .rel-card{display:none!important}
    body.rel-imprimindo .rel-card.rel-imprimindo{display:block!important;position:absolute!important;
      left:0;top:0;width:100%}
    .rel-chips,.rel-cab .rel-acao,.rel-link{display:none!important}
    .rel-bloco,.rel-kpi,.rel-kpis,.rel-rod{break-inside:avoid;page-break-inside:avoid}
    .rel-tab-wrap{overflow:visible!important}
    table.rel-tab{min-width:0!important}
  }

  /* ---- LEGADO: snapshots congelados anteriores a 06/08/2026 --------------
     NENHUM caminho de código gera mais estas classes — o envelope antigo saiu
     no mesmo commit que o card novo entrou. O que sobrevive são os BYTES já
     gravados em pagina_compartilhada.resposta: um /c/<slug> publicado é
     imutável por contrato ("nunca muda embaixo do leitor"), e sem estas regras
     ele viraria texto cru. Some quando não houver mais share antigo em
     relatorio_share (uma query resolve; não apagar no escuro). */
  .rrel{max-width:900px;margin:0 auto}
  .rrel-titulo{font-family:var(--serif,Georgia,serif);font-size:clamp(20px,3vw,27px);line-height:1.2;margin:0 0 4px}
  .rrel-meta{font-size:12.5px;color:var(--mut,#5d574c);margin-bottom:14px}
  .rgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}
  .rbloco{border:1px solid var(--hair,#e3ddcc);border-radius:10px;background:var(--card,#fbf8f0);padding:14px 16px;overflow:hidden}
  .rbloco[data-tipo="serie_temporal"],.rbloco[data-tipo="barra"],.rbloco[data-tipo="tabela"]{grid-column:1 / -1}
  .rbloco-titulo{font-family:var(--cond,sans-serif);font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink2,#3f3a31);margin:0 0 10px}
  .rkpi-rot{display:none}
  .rkpi-val{font-family:var(--serif,Georgia,serif);font-size:clamp(22px,4vw,30px);line-height:1.05}
  .rkpi-comp{font-size:11.5px;color:var(--mut,#5d574c);margin-top:4px}
  .rvazio{font-family:var(--serif,Georgia,serif);font-style:italic;font-size:14px;color:var(--mut,#5d574c);padding:10px 0}
  .rflag{font-size:12px;color:var(--risco,#8f1d22);border-left:3px solid var(--risco,#8f1d22);padding:7px 10px;margin-top:10px;line-height:1.45}
  .rfonte{font-family:var(--cond,sans-serif);font-size:10px;letter-spacing:.08em;color:var(--mut,#5d574c);text-transform:uppercase;margin-top:10px;border-top:1px solid var(--hair,#e3ddcc);padding-top:8px}
  .rtab-wrap{overflow-x:auto}
  table.rtab{border-collapse:collapse;width:100%;font-size:13px}
  table.rtab th{text-align:left;font-family:var(--cond,sans-serif);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;color:var(--mut,#5d574c);border-bottom:1.5px solid var(--line,#c9c2b0);padding:6px 10px;white-space:nowrap}
  table.rtab td{padding:6px 10px;border-bottom:1px solid var(--hair,#e3ddcc);white-space:nowrap}
  @media(max-width:640px){.rgrid{grid-template-columns:1fr}}
