/* ===========================================================================
   hub.css, a folha única do hub da Zera (app.zeracompany.com/hub)
   ---------------------------------------------------------------------------
   POR QUE ESTE ARQUIVO EXISTE, E POR QUE ELE NÃO É O /estilo.css
   O hub carrega ESTA folha e NÃO carrega a folha do site. Não é preferência:
   dez regras de elemento daquela folha quebram a escala de painel, e três
   delas sozinhas já arrebentam a tela.

     estilo.css:202  body{font-size:17px; line-height:1.62}  → o hub é 14/20
     estilo.css:214  h1,h2,h3,h4{font-weight:750}            → o hub para em 600
     estilo.css:215  h1{font-size:clamp(...,3.6rem)}         → o hub não tem 4º degrau
     estilo.css:785  table{min-width:820px}                  → quebra em todo celular
     estilo.css:787  th,td{padding:13px 16px}                → o hub é 16px
     estilo.css:788  thead th{background; position:sticky}
     estilo.css:967  input[type=text]{border-radius:11px}    → o hub é 6px
     estilo.css:551  .card{border-radius:16px; box-shadow}   → o hub é 12px e SEM sombra
     estilo.css:459  .btn{padding:14px 22px; radius:12px}
     estilo.css:222  :focus-visible{outline:3px solid}       → o hub usa anel de 2px

   Os VALORES de marca vêm de lá (azul-aço #5B7A99, verde #25D366, as linhas,
   as superfícies claras, o bloco escuro das linhas 150-191). As REGRAS nascem
   aqui. É a mesma decisão que a folha do site tomou no comentário dela: um
   arquivo só, porque cópia divergente em uma semana é certeza, não risco.

   ONDE ELE MORA
   Na raiz de public/, fora de /assets/. O contrato de public/_headers é claro:
   só entra em /assets/ arquivo com hash no nome, porque lá o cache é de um ano
   e imutável. Este arquivo não tem hash. Convém acrescentar ao _headers, junto
   de /estilo.css:
       /hub.css
         Cache-Control: public, max-age=3600

   CSP
   O middleware manda style-src 'self' 'unsafe-inline'. Esta folha é servida do
   próprio domínio, então passa pelo 'self'. O JavaScript do hub NÃO pode ser
   inline: script-src é 'self' sem 'unsafe-inline' e sem hash. Todo script vive
   em /js/ e entra por <script src>. Nada aqui depende de JS para funcionar:
   tema, foco, rolagem de tabela e estados são CSS.

   NOMES DAS VARIÁVEIS
   O vocabulário é o do shadcn (--background, --foreground, --card, --primary,
   --muted, --accent, --destructive, --border, --input, --ring, --radius),
   porque é o vocabulário do material medido: quem for manter isto vai abrir a
   tela deles do lado e comparar nome por nome. Onde o hub precisa de um papel
   que o shadcn não tem (o azul quando ele é LETRA, o verde de ação, os cinco
   degraus de gráfico), o token nasce novo e o comentário diz por quê.
   No fim há um bloco de apelidos --hub-* espelhando a especificação escrita,
   para quem comparar folha e documento não precisar traduzir de cabeça.

   ORGANIZAÇÃO
   0. Fonte
   1. Tokens: claro, escuro e as duas guardas
   2. Apelidos --hub-*
   3. Base, tipografia, foco e movimento
   4. Faixa de aviso   5. Cabeçalho   6. Migalha dupla   7. Abas
   8. Cartão          8.1 Dobra e introdução de aba
   9. Tabela          10. Pastilha       11. Botões
   12. Campo          13. Vazio      14. Carregando     15. Erro e ausência
   16. Gráfico        17. Utilitários

   CONTRASTE
   Os 88 pares de cor desta folha foram calculados, claro e escuro, nos dois
   alvos (4,5:1 para texto, 3:1 para componente de UI). Zero reprovações.
     menor da folha:  3,18:1  borda do botão neutro contra o próprio
                              preenchimento, no claro (alvo 3:1)
     menor de TEXTO:  4,60:1  secundário sobre o accent, no escuro (alvo 4,5:1)
   Onde um valor medido reprovava, ele foi trocado e o comentário registra o
   número velho e o novo. São quatro: --muted-foreground (era #737373),
   --input (era #C3CEDB), --d-input (era #33455C) e o fundo do avatar.
   Isento por norma: controle desligado (1.4.3 e 1.4.11 dispensam componente
   inativo) e --border, que é decoração e não identifica controle nenhum.

   O QUE A MEDIDA NÃO PEGOU, E A BANCADA PEGOU
   Três defeitos só apareceram ao ABRIR o render em 1600, 1180, 900 e 375:
     1. no escuro o azulejo de ícone SUMIA dentro do cartão (1,02:1 de
        separação, contra 1,13:1 do tema claro). Ver --d-accent;
     2. em 900px o telefone se partia em três linhas em vez de a tabela rolar.
        Ver .hub-atomico;
     3. em 375px o nome da empresa encolhia até sobrar uma letra enquanto os
        botões ao lado ficavam inteiros. Ver a consulta de 900px do cabeçalho.
   Nenhum dos três dá erro, nenhum aparece em cálculo de contraste e nenhum
   seria visto por quem só lesse o código.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   0. Fonte
   Bloco copiado LITERALMENTE de estilo.css:50-57. Mesmo arquivo, mesmo
   unicode-range, mesmo font-display. É de propósito: o cache do navegador é o
   mesmo entre site e hub, então quem chega ao hub vindo de /comecar já tem a
   fonte em memória e o hub pinta sem FOUT. Trocar qualquer coisa aqui quebra
   essa reutilização.
   --------------------------------------------------------------------------- */
@font-face{
  font-family:"Inter";
  src:url("/fonts/inter-latin.woff2") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;
}

/* ---------------------------------------------------------------------------
   1. TOKENS
   --------------------------------------------------------------------------- */
:root{
  /* === SUPERFÍCIES ===
     [MEDIDO] no hub deles: fundo #ffffff, cartão branco em x=700 y=350,
     caixa da tabela #fbfbfb em y=440 e y=492, seção apagada #f5f5f5.
     Aqui as três superfícies apagadas usam os cinzas AZULADOS da casa
     (--bg-lift e --bg-soft de estilo.css:88-89) em vez do cinza neutro deles,
     porque o resto da marca é azul-aço e cinza neutro ao lado de azul-aço lê
     como sujeira, não como neutro. */
  --background:#FFFFFF;
  --foreground:#1f2a37;          /* 14,54:1 sobre o fundo */
  --card:#FFFFFF;                /* o cartão é branco; quem separa é a borda, não a cor */
  --card-foreground:#1f2a37;
  --muted:#F8FAFC;               /* caixa da tabela e preenchimento do botão neutro */
  --muted-foreground:#6B6B6B;    /* ver nota abaixo: NÃO é o #737373 medido */
  --secondary:#F4F7FA;           /* hover de superfície apagada, esqueleto */
  --secondary-foreground:#1f2a37;

  /* --muted-foreground: o valor medido deles é #737373, e ele REPROVA em duas
     das nossas superfícies: 4,41:1 sobre --secondary e 4,21:1 sobre --accent,
     contra os 4,5:1 exigidos. Escurecido para #6B6B6B, o pior caso vira
     4,73:1 (sobre o accent) e o melhor 5,33:1 (sobre o fundo). A diferença é
     invisível a olho e é a diferença entre passar e reprovar. */

  /* === AZUL-AÇO: quatro tokens, e o motivo está no número ===
     #5B7A99 como LETRA sobre branco dá 4,48:1 e reprova AA por dois
     centésimos. Então a cor de marca é SUPERFÍCIE (borda, anel, linha da aba,
     preenchimento), e existe uma versão escurecida para quando ela precisa ser
     letra. É o mesmo padrão que estilo.css:110-123 já aplica ao verde. */
  --primary:#5B7A99;             /* superfície e traço. 4,48:1 sobre branco: passa o 3:1 de UI */
  --primary-foreground:#FFFFFF;  /* letra SOBRE azul cheio, e só sobre --primary-strong. Ver o avatar */
  --primary-strong:#47617B;      /* quando o azul É a letra ou o ícone pequeno. 6,43:1 sobre branco */
  --primary-stronger:#35495E;    /* hover de texto e de borda. 9,27:1 sobre branco */
  --primary-edge:#DEE4EB;        /* [DERIVADO] #5B7A99 a 20% sobre branco: borda do bloco de destaque */

  /* --accent no shadcn é o tom de realce apagado (hover, azulejo). Cai certo
     no --hub-primaria-leve da especificação: mesma função, mesmo valor. */
  --accent:#EDF2F7;
  --accent-foreground:#47617B;   /* 5,71:1 sobre o accent */

  /* === VERDE: AÇÃO. Cheio é clicável, sempre. ===
     O shadcn não tem cadeira para isto, e o hub precisa: no site da casa o
     verde já significa canal e conversão (estilo.css:27-29). Se tudo é verde,
     nada é ação. */
  --action:#25D366;              /* o verde do WhatsApp, sem diluir: aqui a cor é reconhecimento */
  --action-hover:#1EBA57;
  --action-foreground:#04231A;   /* letra SOBRE o verde. 8,40:1. Branco dá 1,98:1 e está PROIBIDO */
  --action-text:#0A7C3E;         /* quando o verde É a letra sobre fundo claro. 5,30:1 */
  /* --action-edge existe por contraste, não por estética: o preenchimento
     #25D366 contra o branco da página dá 1,98:1, e a borda de um botão é
     componente de UI (3:1). Com esta borda a silhueta do botão fica em 3,62:1
     sobre o fundo, 3,46:1 sobre a caixa da tabela e 3,21:1 sobre o accent, que
     é o pior caso (o botão da faixa de destaque, que nasce sobre o degradê). */
  --action-edge:#149B45;

  /* === ESTADO: lavado e nunca clicável. NÃO usa cor de marca, de propósito. ===
     [MEDIDO] os fundos em channel.png: pronto x=833..903 y=481..500,
     esperando y=451, neutro y=585. O de erro nunca foi fotografado. */
  --ok:#F0FDF4;          --ok-foreground:#15532C;    /* 8,70:1 */
  --waiting:#FFFBEB;     --waiting-foreground:#78350F; /* 8,75:1 */
  --neutral:#F5F5F5;     --neutral-foreground:#525252; /* 7,17:1 */
  --danger-bg:#FEF2F2;   --danger-foreground:#991B1B;  /* 7,60:1 no fundo dele, 8,31:1 sobre branco */
  --destructive:#EF4444;                               /* traço destrutivo. 3,76:1 sobre branco */

  /* === LINHAS: DUAS, e elas têm alvos de contraste DIFERENTES ===
     --border é decoração: separa cartão do fundo e linha de linha. Não carrega
     informação nem identifica controle, então o 3:1 do WCAG 1.4.11 não se
     aplica a ela, e por isso ela pode ficar no tom medido (#E1E8F0 = 1,24:1).
     Quem "corrigir" isso transforma o painel numa grade pesada sem ganhar
     acessibilidade nenhuma.
     --input é OUTRA coisa: é o contorno de campo e de botão neutro, e ali o
     traço É o que identifica o controle: o preenchimento do campo é branco
     sobre página branca, então sem a borda não existe campo. O valor medido
     deles (#d1d5dc, e o nosso equivalente #C3CEDB) dá 1,59:1 e REPROVA o 3:1.
     Escurecido para #7A8FA6: 3,33:1 sobre o fundo e 3,18:1 sobre o
     preenchimento do botão neutro, mantendo a família azul-aço. */
  --border:#E1E8F0;
  --input:#7A8FA6;
  --ring:#5B7A99;                /* anel de foco. Pior caso 3,98:1 (sobre o accent) */
  /* [HARD] A TERCEIRA LINHA: --card-edge, a ARESTA DO CARTÃO NA JORNADA.
     Ela existe porque a jornada não desenha o fundo do hub. A página da jornada
     pinta --secondary, e o cartão pinta --card. No tema CLARO isso dá um cartão
     BRANCO sobre um cinza levemente mais escuro, e a aresta some por cima: o
     cartão avança. No tema ESCURO os papéis se invertem, e é aí que o cartão
     desaparece: --card #16202D é MAIS ESCURO que --secondary #1B2735, então o
     cartão recua, e a mancha dele contra a página mede 1,09:1.
     Por isso a aresta da jornada é um token separado de --border: no claro ela
     É --border (nada muda, e a folha continua com uma linha decorativa só); no
     escuro ela sobe, porque no escuro é a BORDA, e não a sombra, que separa.
     Ver --d-card-edge na paleta escura para o número e para a medição. */
  --card-edge:var(--border);

  /* === GRÁFICO ===
     Duas famílias, e a distinção decide se a tela de Insights vende.
     ORDENADA: mesma grandeza no tempo, ou faixas de um mesmo valor. Cinco
     degraus de UMA cor, do escuro ao claro. */
  --chart-1:#35495E; --chart-2:#47617B; --chart-3:#5B7A99;
  --chart-4:#8AA2B8; --chart-5:#B9C8D6;   /* o 5º é [DERIVADO] */
  /* CATEGÓRICA: coisas diferentes (SERVICE, UTILITY, MARKETING). Matizes
     diferentes, cada uma com a cor de LETRA dela para legenda e rótulo, porque
     cor de barra é superfície e não passa em contraste como letra. */
  --cat-1:#5B7A99; --cat-1-text:#47617B;   /* 6,43:1 */
  --cat-2:#1EBA57; --cat-2-text:#0A7C3E;   /* 5,30:1. Não é o #25D366: barra não pode parecer botão */
  --cat-3:#D08A2C; --cat-3-text:#9A560D;   /* 5,66:1. [DERIVADO] a casa não tem laranja */
  --cat-4:#94A3B8; --cat-4-text:#61748A;   /* 4,80:1 */

  /* === FORMA ===
     Todos [MEDIDO], menos onde está escrito. */
  --radius:6px;                  /* botão e campo. [MEDIDO] y=341→347 */
  --radius-card:12px;            /* cartão.        [MEDIDO] y=320→331 */
  --radius-tile:10px;            /* azulejo de ícone. [MEDIDO] y=344→354 */
  --radius-box:8px;              /* caixa da tabela.  [DERIVADO] */
  --radius-pill:9999px;

  /* === SOMBRA: UMA só, quase invisível. ===
     Num painel a hierarquia vem da BORDA e do ESPAÇO. Sombra forte em cartão
     de dado é o sinal mais rápido de painel amador. Esta existe para o menu
     suspenso e para o <dialog>, e NÃO para o cartão. */
  --shadow:0 1px 2px 0 rgba(0,0,0,.05);
  /* Véu do <dialog>. Fica em token porque cor literal solta no corpo da folha
     é justamente o que faz um tema novo nascer com um buraco claro no meio. */
  --overlay:rgba(4,8,12,.72);
  /* Sombra de rolagem da tabela. Ver .hub-tabela-caixa. Os 24% saíram da
     bancada: a 16% a sombra existia no código e não se via na tela clara. */
  --fade:rgba(31,42,55,.24);

  /* === MEDIDAS DE CASCA === */
  --hub-largura:1120px;          /* [MEDIDO] idêntico em viewport de 1484 e de 1590 */
  --hub-h-aviso:44px;            /* [MEDIDO] y=0..43, borda em 44 */
  --hub-h-cabecalho:64px;        /* [MEDIDO] y=45..108, borda em 109 */
  --hub-h-controle:38px;         /* botão padrão e campo. [MEDIDO] */
  --hub-h-controle-p:30px;       /* botão pequeno e de contorno. [MEDIDO] */

  --mono:ui-monospace,"SF Mono","Cascadia Mono","Segoe UI Mono",Menlo,Consolas,monospace;

  /* === PALETA ESCURA, DECLARADA UMA VEZ SÓ ===
     Por que os valores escuros moram aqui em vez de dentro de cada guarda:
     as duas guardas abaixo (preferência do sistema e classe .escuro) precisam
     da MESMA lista. Escrevendo os hexadecimais duas vezes, a primeira correção
     de contraste conserta um dos temas e esquece o outro, e é o mesmo motivo
     pelo qual esta folha é um arquivo só. Aqui o valor existe em um lugar e as
     guardas apenas o promovem.
     Não usei light-dark() de propósito: ela resolveria isto em uma linha, mas
     num navegador que não a conhece a propriedade fica SEM VALOR NENHUM, e uma
     tela com cartão de crédito no fluxo não pode ter como modo de falha texto
     invisível. Duas listas de atribuição são feias e sempre funcionam.
     Os valores são DERIVADOS do bloco .escuro de estilo.css:150-191. Nenhuma
     captura do hub deles no escuro entrou no material, então não há [MEDIDO]
     aqui: há cálculo de contraste, e ele está anotado. */
  --d-background:#0B1119;  --d-foreground:#EAF0F7;
  --d-card:#16202D;        --d-card-foreground:#EAF0F7;
  --d-muted:#111925;       --d-muted-foreground:#8195A9;   /* pior caso 4,90:1 */
  --d-secondary:#1B2735;   --d-secondary-foreground:#EAF0F7;
  --d-primary:#8AA2B8;     --d-primary-foreground:#0B1119; /* 7,16:1 */
  --d-primary-strong:#8AA2B8;   /* no escuro o azul claro JÁ é legível como letra: 6,21:1 no cartão */
  --d-primary-stronger:#A8BCCD;
  --d-primary-edge:#2E4257;
  /* --d-accent NÃO é o #18222F que sai direto do bloco escuro da casa, e o
     motivo apareceu na bancada, não na planilha: contra o cartão #16202D
     aquele valor dá 1,02:1 e o azulejo de ícone simplesmente SOME. E o
     azulejo é o que dá presença ao cartão (é ele, e não um título maior, que
     segura a hierarquia sem abrir um quarto degrau tipográfico). Sem ele o
     cartão escuro fica com um glifo solto no canto.
     No tema claro a mesma separação é 1,126:1 (#EDF2F7 contra o branco), e é
     essa a medida que este valor persegue: 1,157:1. Não mais que isso, senão
     o azulejo vira um botão. Todos os pares de texto continuam passando:
     letra 12,37:1, ícone 7,26:1, secundário sobre o degradê de destaque
     4,60:1 (com #22303F cairia para 4,36:1 e reprovaria). */
  --d-accent:#1F2C3A;      --d-accent-foreground:#A8BCCD;  /* 7,26:1 */
  --d-action:#25D366;      --d-action-hover:#3BE07A;
  --d-action-foreground:#04231A;  /* continua ESCURO sobre o verde: 8,40:1 e 9,62:1 */
  --d-action-text:#3BE07A;        /* 9,49:1 sobre o cartão */
  --d-action-edge:#25D366;        /* no escuro o próprio verde já dá 9,55:1 de silhueta */
  --d-ok:#102A1C;          --d-ok-foreground:#3BE07A;   /* 8,85:1 */
  --d-waiting:#241D10;     --d-waiting-foreground:#FBBF24; /* 10,00:1 */
  --d-neutral:#1B2735;     --d-neutral-foreground:#AFBECE; /* 7,98:1 */
  --d-danger-bg:#2A1416;   --d-danger-foreground:#FCA5A5;  /* 9,14:1 */
  --d-destructive:#EF4444;                                  /* 5,03:1 sobre o fundo */
  --d-border:#2B3A4C;
  /* [HARD] A ARESTA DO CARTÃO NA JORNADA, NO ESCURO. #415673.
     MEDIDO no navegador em 23/08/2026, nas cinco telas da jornada, com
     prefers-color-scheme: dark, contra o fundo que a página REALMENTE desenha
     (o body sai em #1B2735, lido do estilo computado, e não o #0B1119 do hub):

       antes  cartão #16202D sobre #1B2735 = 1,09:1   borda #2B3A4C = 1,31:1
       agora  cartão #16202D sobre #1B2735 = 1,09:1   borda #415673 = 2,02:1

     A mancha do cartão continua 1,09:1 de propósito: quem separa no escuro é a
     ARESTA. Sombra preta a 5% sobre fundo escuro não aparece (e a 40% só
     engorda o rodapé do cartão, ver .hub-portao__cartao), então a separação
     tem de vir de uma linha MAIS CLARA que a página.
     O valor é o degrau do meio de uma hierarquia de três, e o teto é o que
     impede a aresta de virar contorno de controle:
       --d-border    #2B3A4C  1,31:1   decoração (linha de tabela, divisória)
       --d-card-edge #415673  2,02:1   aresta de cartão sobre a página cinza
       --d-input     #5C7288  3,04:1   contorno de CONTROLE, o único que
                                       persegue o 3:1 do WCAG 1.4.11
     Subir a aresta até 3:1 faria o cartão brigar com o campo e o botão neutro,
     que são as coisas que a pessoa precisa achar primeiro nessas telas. */
  --d-card-edge:#415673;
  --d-input:#5C7288;       /* o #33455C da casa dava 1,94:1 e reprovava o 3:1. Este dá 3,80:1
                              sobre o fundo e 3,30:1 dentro do cartão, que é o pior caso */
  --d-ring:#8AA2B8;        /* pior caso 5,72:1 */
  --d-chart-1:#B9C8D6; --d-chart-2:#8AA2B8; --d-chart-3:#5B7A99;
  --d-chart-4:#47617B; --d-chart-5:#35495E;   /* a rampa INVERTE: no escuro o claro é que salta */
  --d-cat-1:#8AA2B8; --d-cat-1-text:#A8BCCD;
  --d-cat-2:#3BE07A; --d-cat-2-text:#3BE07A;
  --d-cat-3:#F0B45C; --d-cat-3-text:#F0B45C;  /* 8,91:1 sobre o cartão */
  --d-cat-4:#94A3B8; --d-cat-4-text:#AFBECE;  /* 8,67:1 sobre o cartão */
  --d-shadow:0 1px 2px 0 rgba(0,0,0,.4);
  --d-overlay:rgba(0,0,0,.78);   /* no escuro o véu precisa ser mais fechado: o mesmo
                                    rgba(4,8,12,.72) sobre um fundo já quase preto quase
                                    não separa a caixa do que ficou atrás */
  --d-fade:rgba(0,0,0,.55);      /* sombra sobre superfície escura precisa ser mais densa:
                                    preto a 16% sobre #111925 é literalmente invisível */

  color-scheme:light;
}

/* --- AS DUAS GUARDAS DO TEMA ESCURO ---
   A estrutura é a MESMA de estilo.css:150 e existe por dois caminhos:

   1. quem nunca escolheu tema cai na preferência do sistema. A guarda
      :root:not(.claro) deixa a escolha explícita "claro" vencer a preferência;
   2. .escuro é a classe da casa. No hub ela vai no <html>, gravada por
      /js/tema.js, e como todo componente lê de variável, nenhuma regra de
      componente precisa saber que o tema mudou, e foi por isso que a troca
      inteira coube em um bloco na folha do site.

   Atenção de quem for ligar isto: /js/tema.js NÃO EXISTE hoje no projeto, e
   estilo.css também não tem guarda de preferência de sistema (lá o .escuro é
   aplicado a seções: herói e rodapé). Enquanto o script não nascer, o hub
   segue a preferência do sistema e o botão de trocar tema não tem o que
   chamar. É buraco conhecido, não descuido.
   O .escuro também funciona em recorte (aplicado a uma seção e não ao <html>),
   igual ao site: ele redefine os tokens E pinta fundo e letra, senão a ilha
   escura fica com o fundo do pai. */
@media (prefers-color-scheme: dark){
  :root:not(.claro){
    --background:var(--d-background); --foreground:var(--d-foreground);
    --card:var(--d-card); --card-foreground:var(--d-card-foreground);
    --muted:var(--d-muted); --muted-foreground:var(--d-muted-foreground);
    --secondary:var(--d-secondary); --secondary-foreground:var(--d-secondary-foreground);
    --primary:var(--d-primary); --primary-foreground:var(--d-primary-foreground);
    --primary-strong:var(--d-primary-strong); --primary-stronger:var(--d-primary-stronger);
    --primary-edge:var(--d-primary-edge);
    --accent:var(--d-accent); --accent-foreground:var(--d-accent-foreground);
    --action:var(--d-action); --action-hover:var(--d-action-hover);
    --action-foreground:var(--d-action-foreground); --action-text:var(--d-action-text);
    --action-edge:var(--d-action-edge);
    --ok:var(--d-ok); --ok-foreground:var(--d-ok-foreground);
    --waiting:var(--d-waiting); --waiting-foreground:var(--d-waiting-foreground);
    --neutral:var(--d-neutral); --neutral-foreground:var(--d-neutral-foreground);
    --danger-bg:var(--d-danger-bg); --danger-foreground:var(--d-danger-foreground);
    --destructive:var(--d-destructive);
    --border:var(--d-border); --input:var(--d-input); --ring:var(--d-ring);
    --card-edge:var(--d-card-edge);
    --chart-1:var(--d-chart-1); --chart-2:var(--d-chart-2); --chart-3:var(--d-chart-3);
    --chart-4:var(--d-chart-4); --chart-5:var(--d-chart-5);
    --cat-1:var(--d-cat-1); --cat-1-text:var(--d-cat-1-text);
    --cat-2:var(--d-cat-2); --cat-2-text:var(--d-cat-2-text);
    --cat-3:var(--d-cat-3); --cat-3-text:var(--d-cat-3-text);
    --cat-4:var(--d-cat-4); --cat-4-text:var(--d-cat-4-text);
    --shadow:var(--d-shadow); --overlay:var(--d-overlay); --fade:var(--d-fade);
    color-scheme:dark;
  }
}
.escuro{
  --background:var(--d-background); --foreground:var(--d-foreground);
  --card:var(--d-card); --card-foreground:var(--d-card-foreground);
  --muted:var(--d-muted); --muted-foreground:var(--d-muted-foreground);
  --secondary:var(--d-secondary); --secondary-foreground:var(--d-secondary-foreground);
  --primary:var(--d-primary); --primary-foreground:var(--d-primary-foreground);
  --primary-strong:var(--d-primary-strong); --primary-stronger:var(--d-primary-stronger);
  --primary-edge:var(--d-primary-edge);
  --accent:var(--d-accent); --accent-foreground:var(--d-accent-foreground);
  --action:var(--d-action); --action-hover:var(--d-action-hover);
  --action-foreground:var(--d-action-foreground); --action-text:var(--d-action-text);
  --action-edge:var(--d-action-edge);
  --ok:var(--d-ok); --ok-foreground:var(--d-ok-foreground);
  --waiting:var(--d-waiting); --waiting-foreground:var(--d-waiting-foreground);
  --neutral:var(--d-neutral); --neutral-foreground:var(--d-neutral-foreground);
  --danger-bg:var(--d-danger-bg); --danger-foreground:var(--d-danger-foreground);
  --destructive:var(--d-destructive);
  --border:var(--d-border); --input:var(--d-input); --ring:var(--d-ring);
  --card-edge:var(--d-card-edge);
  --chart-1:var(--d-chart-1); --chart-2:var(--d-chart-2); --chart-3:var(--d-chart-3);
  --chart-4:var(--d-chart-4); --chart-5:var(--d-chart-5);
  --cat-1:var(--d-cat-1); --cat-1-text:var(--d-cat-1-text);
  --cat-2:var(--d-cat-2); --cat-2-text:var(--d-cat-2-text);
  --cat-3:var(--d-cat-3); --cat-3-text:var(--d-cat-3-text);
  --cat-4:var(--d-cat-4); --cat-4-text:var(--d-cat-4-text);
  --shadow:var(--d-shadow); --overlay:var(--d-overlay); --fade:var(--d-fade);
  color-scheme:dark;
  background:var(--background);
  color:var(--foreground);
}

/* ---------------------------------------------------------------------------
   2. APELIDOS --hub-*
   A especificação escrita do hub usa estes nomes. Eles existem aqui como
   APELIDO, nunca como segunda fonte de valor: cada um aponta para o token
   acima, então corrigir uma cor continua sendo uma edição só. Servem para
   quem estiver lendo a especificação de um lado e a folha do outro, e para
   HTML que precise sobrescrever um token em linha sem ter de traduzir.
   --------------------------------------------------------------------------- */
:root{
  --hub-fundo:var(--background);          --hub-superficie:var(--card);
  --hub-superficie-2:var(--muted);        --hub-superficie-3:var(--secondary);
  --hub-linha:var(--border);              --hub-linha-campo:var(--input);
  --hub-tx:var(--foreground);             --hub-tx-2:var(--muted-foreground);
  --hub-tx-inv:var(--primary-foreground);
  --hub-primaria:var(--primary);          --hub-primaria-tx:var(--primary-strong);
  --hub-primaria-forte:var(--primary-stronger);
  --hub-primaria-leve:var(--accent);      --hub-primaria-borda:var(--primary-edge);
  --hub-acao:var(--action);               --hub-acao-hover:var(--action-hover);
  --hub-acao-tx:var(--action-foreground); --hub-acao-letra:var(--action-text);
  --hub-ok-bg:var(--ok);                  --hub-ok-tx:var(--ok-foreground);
  --hub-esp-bg:var(--waiting);            --hub-esp-tx:var(--waiting-foreground);
  --hub-neu-bg:var(--neutral);            --hub-neu-tx:var(--neutral-foreground);
  --hub-err-bg:var(--danger-bg);          --hub-err-tx:var(--danger-foreground);
  --hub-destrutiva:var(--destructive);
  --hub-serie-1:var(--chart-1); --hub-serie-2:var(--chart-2); --hub-serie-3:var(--chart-3);
  --hub-serie-4:var(--chart-4); --hub-serie-5:var(--chart-5);
  --hub-cat-1:var(--cat-1); --hub-cat-1-tx:var(--cat-1-text);
  --hub-cat-2:var(--cat-2); --hub-cat-2-tx:var(--cat-2-text);
  --hub-cat-3:var(--cat-3); --hub-cat-3-tx:var(--cat-3-text);
  --hub-cat-4:var(--cat-4); --hub-cat-4-tx:var(--cat-4-text);
  --hub-raio:var(--radius);               --hub-raio-cartao:var(--radius-card);
  --hub-raio-azulejo:var(--radius-tile);  --hub-raio-caixa:var(--radius-box);
  --hub-raio-pilula:var(--radius-pill);
  --hub-sombra:var(--shadow);             --hub-mono:var(--mono);
}

/* ---------------------------------------------------------------------------
   3. BASE
   --------------------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}   /* copiado de estilo.css:196 */

html{-webkit-text-size-adjust:100%}

/* [MANUAL] 14px/20px é o padrão do painel deles, medido 122 vezes. Note o que
   NÃO está aqui: line-height sem unidade. 20px fixo é o que faz a linha da
   tabela fechar em 64px (16 + 20 + 16 + a borda) e o cabeçalho em 52px. */
body{
  margin:0;
  background:var(--background);
  color:var(--foreground);
  font-family:"Inter","Segoe UI",system-ui,-apple-system,Arial,sans-serif;
  font-size:14px;
  line-height:20px;
  font-weight:400;
  -webkit-font-smoothing:antialiased;
  accent-color:var(--primary);   /* caixa de seleção nativa na cor da casa, sem redesenhar controle */
}
img{max-width:100%; height:auto}
a{color:inherit}
button{font:inherit}

/* TRÊS DEGRAUS, E SÓ TRÊS [MANUAL].
   Não existe quarto degrau: o maior texto do hub é 16/24 peso 600. Quem
   precisar de mais presença num título pede o azulejo de ícone ao lado, nunca
   um tamanho maior. É essa regra que impede o hub de virar página de vendas. */
.hub-xs{font-size:12px; line-height:16px}   /* rótulo, legenda, pastilha, identificador */
.hub-sm{font-size:14px; line-height:20px}   /* O PADRÃO: tabela, botão, campo */
.hub-md{font-size:16px; line-height:24px}   /* aba, título de cartão, valor */

/* AS DUAS COR DE TEXTO, COMO CLASSE.
   [HARD] Estas duas linhas existem porque a auditoria de 22/08/2026 achou o
   contrário: o JS já aplicava `hub-tx-2` em cinco lugares e a folha só declarava
   `--hub-tx-2` como VARIÁVEL. Classe que não existe não dá erro: o elemento
   herda a cor do pai. O efeito era o pior possível numa tela de dado, porque a
   classe é justamente a que acinzenta a frase de dado AUSENTE ("a gente ainda
   não perguntou isto à Meta"). Sem ela, a explicação da ausência saía em
   #1f2a37, exatamente a cor de um dado real, e o cliente lia a desculpa como
   informação. */
.hub-tx{color:var(--hub-tx)}
.hub-tx-2{color:var(--hub-tx-2)}

.hub-container{width:100%; max-width:var(--hub-largura); margin-inline:auto; padding-inline:16px}

/* FOCO DE TECLADO, GLOBAL.
   Anel de 2px, não os 3px do site (estilo.css:222): num painel denso o anel
   grosso encosta no controle vizinho. O offset de 2px é o que separa o anel da
   borda do próprio controle. O componente que precisa de anel próprio (o
   campo, que põe o anel no invólucro) desliga este e explica por quê.
   Contraste do anel: 4,48:1 sobre o fundo e 3,98:1 sobre o accent, que é o
   pior caso. No escuro, 7,16:1 e 5,72:1.
   NÃO existe border-radius aqui, e é decisão, não esquecimento: border-radius
   numa regra de foco não arredonda só o anel, arredonda o ELEMENTO: o avatar
   redondo viraria um quadrado de cantos moles no instante em que recebesse
   foco. O contorno já acompanha o raio próprio de cada elemento em todos os
   navegadores atuais. */
:focus-visible{
  outline:2px solid var(--ring);
  outline-offset:2px;
}
/* Quem chega por ponteiro não leva anel; quem chega por teclado, leva sempre.
   Não existe regra que remova outline nesta folha sem devolver outra pista. */

/* MOVIMENTO. Bloco copiado de estilo.css:198-201. Vale para o pulso do
   esqueleto, o giro do botão ocupado e todas as transições de cor. */
@media (prefers-reduced-motion: reduce){
  *{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
}

/* Utilitários copiados literalmente da folha do site. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
[hidden]{display:none !important}

/* ---------------------------------------------------------------------------
   4. FAIXA DE AVISO, e é AQUI que mora o degradê
   [MEDIDO] em channel.png, y=10: #e8f7f9 em x=0, 100, 300 e 700; #f3fafc em
   1100; #fdfeff em 1400; #ffffff em 1483. Ou seja: a faixa fica CHEIA até a
   metade e só depois desvanece. Degradê que começa a apagar no pixel zero é
   outro efeito, e não é o deles.
   --------------------------------------------------------------------------- */
.hub-aviso{
  position:sticky; top:0; z-index:50;
  min-height:var(--hub-h-aviso);      /* [MEDIDO] 44px */
  display:flex; align-items:center; justify-content:center; gap:8px;
  padding:12px 48px 12px 16px;        /* 48px à direita é a folga do botão de fechar */
  font-size:14px; line-height:20px;
  color:var(--foreground);
  border-bottom:1px solid var(--primary-edge);
  background:linear-gradient(to right,
    var(--accent) 0%, var(--accent) 50%, transparent 100%);
}
.hub-aviso p{margin:0}
.hub-aviso b{font-weight:600}
.hub-aviso a{
  color:var(--primary-strong);        /* 5,71:1 no ponto mais saturado do degradê */
  font-weight:600;
  text-decoration:underline; text-underline-offset:2px;
}
.hub-aviso a:hover{color:var(--primary-stronger)}
.hub-aviso svg{width:16px; height:16px; color:var(--primary); flex:0 0 auto}

.hub-fechar{
  position:absolute; right:16px; top:50%; transform:translateY(-50%);
  width:24px; height:24px; display:grid; place-items:center;
  background:none; border:0; border-radius:var(--radius);
  color:var(--muted-foreground); cursor:pointer;
}
.hub-fechar:hover{background:var(--secondary); color:var(--secondary-foreground)}
.hub-fechar svg{width:16px; height:16px}

/* ---------------------------------------------------------------------------
   5. CABEÇALHO FIXO
   [MEDIDO] y=45..108, borda em 109 → 64px de altura.
   O cabeçalho é BRANCO LISO em toda a largura. O degradê é da faixa de cima, e
   confundir os dois é o erro nº 1 da lista de coisas que as fotos mostram e as
   medidas escritas não capturam.
   --------------------------------------------------------------------------- */
.hub-cabecalho{
  position:sticky; top:var(--hub-h-aviso); z-index:50;
  height:var(--hub-h-cabecalho);
  background:var(--background);
  border-bottom:1px solid var(--border);
}
/* Sem a faixa de aviso, o cabeçalho tem de grudar em 0. Herdar os 44px deixaria
   uma faixa morta mostrando o conteúdo da página por baixo. É bug que só
   aparece depois que alguém fecha o aviso, e por isso passa em revisão. As
   duas formas de o aviso sumir estão cobertas: escondido por atributo (o hub
   esconde bloco por [hidden]) e ausente do HTML. */
.hub-aviso[hidden] ~ .hub-cabecalho{top:0}
.hub-cabecalho:first-child{top:0}

.hub-cabecalho > .hub-container{height:100%; display:flex; align-items:center; gap:16px}

.hub-marca{display:flex; align-items:center; flex:0 0 auto; text-decoration:none}
.hub-marca svg, .hub-marca img{display:block; height:32px; width:auto}

.hub-contexto{display:flex; align-items:center; gap:8px; margin-inline:auto; min-width:0}
.hub-contexto__empresa{
  display:inline-flex; align-items:center; gap:8px;
  font-size:16px; line-height:24px; font-weight:600;
  color:var(--foreground);
  min-width:0;
  /* [MEDIDO 23/08/2026] "Zera Company" ocupa 137px no cabeçalho deles. Sem teto,
     "Padaria Barra Bakery Comercio de Alimentos" quebrava em DUAS LINHAS e
     esticava a barra inteira de 64px para 80. O `text-overflow` já estava
     escrito na linha de baixo e não valia de nada: reticências só aparecem
     quando existe uma largura para estourar. */
  max-width:260px;
  /* [HARD] O TRUNCAMENTO MORA AQUI, NÃO NUM FILHO.
     A regra abaixo (`> span`) está escrita desde o começo e NUNCA valeu de nada:
     o nome da conta entra por `document.createTextNode`, ou seja, é nó de texto
     puro dentro deste elemento, e não existe `<span>` nenhum para casar. Um
     seletor que não casa não dá erro, não aparece no console, e some no meio de
     mil linhas de folha de estilo: o sintoma era só "o nome quebra em duas
     linhas e a barra cresce", que qualquer um lê como falta de espaço.
     Ela fica logo abaixo, por causa do ícone: ele é irmão do texto, e um
     `nowrap` no pai basta para os dois. */
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.hub-contexto__empresa > span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* SELETOR DE CANAL. [MEDIDO] 160px no cabeçalho deles (x=672..832).
   O nosso era um `select` sem teto e ia a 540px, porque o `<select>` nativo se
   estica até o texto da opção mais longa, e a opção mais longa é
   "nome da empresa, +55 11 98765-4321". Resultado: o seletor sozinho ocupava um
   terço da barra e empurrava o botão para a beirada.
   O `text-overflow` NÃO funciona em `<select>` em navegador nenhum: quem corta é
   a largura fixa, e o texto some por baixo da seta.

   [MEDIDO 24/08/2026] O teto já estava aqui, mas o `padding-right` que este
   comentário prometia NÃO EXISTIA, e o `text-overflow:ellipsis` que estava
   escrito na linha de baixo não fazia nada. Medido no controle real: 198px de
   largura, `appearance: auto`, 16px de padding à direita, 166px de área de
   texto, e a opção mais longa pedindo 536px. A seta era a do sistema
   operacional, desenhada por cima dessa mesma área, e o texto passava por baixo
   dela. O comentário descrevia o conserto; o código tinha só metade dele.
   A regra da seta desceu para o bloco do campo (seção 12), porque o outro
   `<select>` do hub (o motivo do cancelamento, em `hub-conta.js`) tinha o mesmo
   defeito: é família, não instância. */
.hub-contexto .hub-campo{width:200px; flex:0 1 200px; min-width:120px}
.hub-contexto .hub-campo select{width:100%}
.hub-contexto__empresa svg{width:16px; height:16px; color:var(--muted-foreground); flex:0 0 auto}
.hub-contexto__sep{width:16px; height:16px; color:var(--input); flex:0 0 auto}

/* AVATAR. 40px, círculo. [MEDIDO] y=57..96.
   Duas trocas em relação ao que foi fotografado, e as duas têm número:
   1. eles usam #f1c40f, um amarelo que não pertence ao sistema deles. Aqui o
      azul da casa, que é cor de superfície e portanto uso legítimo.
   2. o fundo NÃO é --primary: iniciais brancas sobre #5B7A99 dão 4,48:1 e
      reprovam AA por dois centésimos (contraste é simétrico, então a mesma
      conta que proíbe o azul como letra proíbe o branco sobre ele).
      Com --primary-strong são 6,43:1. */
.hub-avatar{
  flex:0 0 auto;
  width:40px; height:40px; border-radius:var(--radius-pill);
  display:grid; place-items:center;
  font-size:12px; line-height:16px; font-weight:600; letter-spacing:.02em;
  background:var(--primary-strong); color:var(--primary-foreground);
  border:0; cursor:pointer;
}
.hub-avatar:hover{background:var(--primary-stronger)}

/* Abaixo de 900px o centro sai da barra e vira uma segunda linha, em vez de
   espremer três blocos que não cabem. */
@media (max-width:900px){
  .hub-cabecalho{height:auto}
  .hub-cabecalho > .hub-container{flex-wrap:wrap; padding-block:8px; gap:8px}
  .hub-contexto{order:3; width:100%; margin-inline:0; overflow-x:auto; scrollbar-width:none}
  .hub-contexto::-webkit-scrollbar{display:none}
  .hub-avatar{margin-left:auto}
  /* Achado da bancada, em 375px: dentro de uma fileira que ROLA, o nome da
     empresa encolhia até sobrar uma letra ("Grupo Solution" virava "G…")
     enquanto os dois botões ao lado mantinham a largura inteira. É o
     comportamento normal do flex, em que o item com min-width:0 é o primeiro a
     ceder, e é exatamente o contrário do que interessa: o nome da empresa é
     o contexto da tela toda; o botão a pessoa reconhece pelo formato.
     Aqui ninguém encolhe e a fileira rola, que é para isso que ela rola.
     No desktop o corte com reticências continua valendo, porque lá a fileira
     divide a barra com a marca e o avatar e não pode empurrá-los. */
  .hub-contexto > *{flex:0 0 auto}
  .hub-contexto__empresa > span{overflow:visible; text-overflow:clip}

  /* [HARD] A FILEIRA ROLA, E AGORA ELA DIZ QUE ROLA.
     Medido em 23/08/2026, num aparelho de 390px: o seletor de canal termina em
     691px e o botao de pedir mais um numero em 890px. Os dois estao dentro
     desta fileira e sao alcancaveis rolando ela, e a pagina nao rola de lado,
     entao nada esta quebrado. Mas `scrollbar-width:none` mais
     `::-webkit-scrollbar{display:none}` apagam a UNICA pista de que existe algo
     a direita: a pessoa com dois numeros abre o painel no celular, ve o nome da
     empresa, e conclui que nao da para trocar de canal.

     A sombra e a mesma solucao que `.hub-tabela-caixa` ja usa, pelo mesmo
     motivo e com a mesma mecanica: as duas primeiras camadas sao presas ao
     CONTEUDO (`local`) e cobrem a sombra quando aquele lado chega ao fim; as
     duas ultimas sao presas a CAIXA (`scroll`) e ficam paradas. Resultado: a
     sombra da direita some ao chegar ao fim, a da esquerda so aparece depois
     que a pessoa rolou, e quando tudo cabe nao aparece nenhuma. Sem uma linha
     de JavaScript.

     Esconder a barra continua certo: barra de rolagem dentro de um cabecalho
     de 8px de respiro come o proprio conteudo. O que estava errado era esconder
     a barra E nao por nada no lugar. */
  .hub-contexto{
    background-color:var(--background);
    background-image:
      linear-gradient(to right, var(--background), transparent),
      linear-gradient(to left,  var(--background), transparent),
      linear-gradient(to right, var(--fade), transparent),
      linear-gradient(to left,  var(--fade), transparent);
    background-position:left center, right center, left center, right center;
    background-size:24px 100%, 24px 100%, 12px 100%, 12px 100%;
    background-repeat:no-repeat;
    background-attachment:local, local, scroll, scroll;
  }
  /* Quem navega por teclado tambem precisa alcancar a fileira para rola-la. O
     atributo tabindex vai no HTML; aqui so se garante que o anel apareca por
     dentro e nao seja cortado pela propria rolagem. */
  .hub-contexto:focus-visible{outline-offset:-2px}
}

/* ---------------------------------------------------------------------------
   6. MIGALHA DUPLA, e ela é que carrega o estado
   Duas linhas por bloco. Fora de um canal só existe o bloco da esquerda;
   dentro de um canal aparece o segundo, com o azulejo do telefone no meio.
   [MEDIDO] o cabeçalho termina em 109 e a migalha começa em ~176 → 64px.
   --------------------------------------------------------------------------- */
.hub-migalha{
  display:flex; align-items:center; gap:16px;
  margin-top:64px;
  margin-bottom:40px;      /* [MANUAL] respiro entre blocos de 1º nível */
}
.hub-migalha__bloco{display:flex; flex-direction:column; gap:2px; min-width:0}
.hub-migalha__linha1{
  display:flex; align-items:center; gap:8px;
  font-size:14px; line-height:20px; font-weight:400;
  color:var(--muted-foreground);
}
.hub-migalha__linha2{
  display:flex; align-items:center; gap:8px;
  font-size:16px; line-height:24px; font-weight:600;
  color:var(--foreground);
  min-width:0;
}
.hub-migalha__linha2 > span{overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.hub-migalha__selo{width:16px; height:16px; border-radius:4px; display:block}
.hub-migalha__sep{width:16px; height:16px; color:var(--input); flex:0 0 auto}
.hub-migalha__azulejo{
  flex:0 0 auto; width:32px; height:32px; border-radius:var(--radius-tile);
  display:grid; place-items:center;
  background:var(--accent); color:var(--accent-foreground);
}
.hub-migalha__azulejo svg{width:16px; height:16px}
.hub-migalha a{text-decoration:none}
.hub-migalha a:hover .hub-migalha__linha2{text-decoration:underline; text-underline-offset:2px}

@media (max-width:640px){
  .hub-migalha{flex-wrap:wrap; gap:8px; margin-top:32px; margin-bottom:24px}
}

/* ---------------------------------------------------------------------------
   7. FILEIRA DE ABAS
   A régua é do CONTAINER e atravessa a largura toda; a linha da aba ativa fica
   SOBRE ela, não no lugar dela. [MEDIDO] em x=228 (dentro da aba): y=301-302 na
   primária e y=303 na linha neutra. Em x=700 (fora da aba): y=303 neutra. As
   duas empilham, e é isso que dá o degrau de 2px.
   --------------------------------------------------------------------------- */
.hub-abas{
  display:flex; gap:32px;                 /* [DERIVADO] */
  border-bottom:1px solid var(--border);
  margin-bottom:16px;                     /* [MEDIDO] régua em 303 → cartão em 320 */
  overflow-x:auto;
  scrollbar-width:none;
}
.hub-abas::-webkit-scrollbar{display:none}
/* [corrigido 31/08/2026] A BARRA ROLA E NADA DIZIA QUE ELA ROLA.
   `overflow-x:auto` com `scrollbar-width:none` esconde a barra de rolagem de
   propósito, para a régua ficar limpa no desktop. No celular isso deixava a
   navegação sem nenhum sinal: MEDIDO em 360, 390 e 430px, "Ajustes" e
   "Documentação" ficavam fora da vista nas três larguras, alcançáveis só por um
   arrasto que a tela não sugeria. Não era conteúdo perdido, era conteúdo
   invisível, que na prática dá no mesmo.

   A sombra usa a técnica das quatro camadas: as duas primeiras são da COR DE
   FUNDO e rolam junto com o conteúdo (`local`); as duas últimas são a sombra e
   ficam paradas (`scroll`). Quando não há o que rolar daquele lado, a camada de
   fundo cobre a sombra sozinha. Ou seja: o sinal aparece exatamente quando há
   mais coisa, e some quando não há, sem uma linha de JavaScript.

   `background-color` fica de fora de propósito: com ela, a barra ganharia um
   fundo próprio e o `border-bottom` da régua sumiria por baixo dela. */
.hub-abas{
  background-image:
    linear-gradient(to right, var(--background), transparent),
    linear-gradient(to left, var(--background), transparent),
    linear-gradient(to right, rgba(15, 23, 42, .16), transparent),
    linear-gradient(to left, rgba(15, 23, 42, .16), transparent);
  background-position: left center, right center, left center, right center;
  background-size: 28px calc(100% - 2px), 28px calc(100% - 2px),
                   14px calc(100% - 2px), 14px calc(100% - 2px);
  background-repeat: no-repeat;
  background-attachment: local, local, scroll, scroll;
}
.hub-aba{
  flex:0 0 auto;
  padding:8px 0 12px;                     /* [DERIVADO] */
  font-size:16px; line-height:24px; font-weight:400;
  color:var(--muted-foreground); text-decoration:none;
  background:none; border:0;
  border-bottom:2px solid transparent;    /* nasce transparente para a ativa não empurrar nada */
  cursor:pointer; white-space:nowrap;
  transition:color .15s ease, border-color .15s ease;
}
.hub-aba:hover{color:var(--foreground)}
/* DUAS coisas mudam na aba ativa, nunca uma: peso 600 E linha de 2px.
   Só a linha é fraca demais para quem lê rápido; só a cor exclui quem não
   distingue matiz. Marcar aba ativa apenas pela cor é o erro clássico. */
.hub-aba[aria-selected="true"],
.hub-aba[aria-current="page"]{
  color:var(--foreground);
  font-weight:600;
  border-bottom-color:var(--primary);
}
/* O anel de foco padrão sairia cortado pelo overflow da fileira; encolher o
   offset mantém o anel inteiro dentro da área rolável. */
.hub-aba:focus-visible{outline-offset:-2px}

@media (max-width:640px){
  .hub-abas{scroll-snap-type:x mandatory; gap:24px}
  .hub-aba{scroll-snap-align:start}      /* encaixa ao rolar, como o max-sm:snap-start deles */
}

/* ---------------------------------------------------------------------------
   8. CARTÃO
   [MEDIDO] raio y=320→331 = 12px; lateral 16px nas duas capturas.
   SEM SOMBRA. --shadow existe nesta folha, e não é aqui que ela entra: num
   painel a hierarquia vem da borda e do espaço.
   --------------------------------------------------------------------------- */
.hub-cartao{
  background:var(--card);
  color:var(--card-foreground);
  border:1px solid var(--border);
  border-radius:var(--radius-card);
  padding:24px 16px;        /* lateral [MEDIDO]; vertical [DERIVADO] da grade de 8 */
  overflow:hidden;
  box-shadow:none;
}
/* [MEDIDO] 30 a 36px nas quatro emendas da captura de detalhe → 32px na grade */
.hub-cartao + .hub-cartao,
.hub-cartao + .hub-destaque,
.hub-destaque + .hub-cartao,
.hub-destaque + .hub-destaque{margin-top:32px}

.hub-cartao__cabeca{display:flex; align-items:flex-start; gap:16px; margin-bottom:24px}
/* O AZULEJO. [MEDIDO] x=252..283 e y=344..375 (32px), raio 10px.
   É ele, e não um título maior, que dá presença ao cartão. Sem o azulejo, o
   quarto degrau tipográfico volta pela porta dos fundos. */
.hub-cartao__azulejo{
  flex:0 0 auto; width:32px; height:32px;
  border-radius:var(--radius-tile);
  display:grid; place-items:center;
  background:var(--accent); color:var(--accent-foreground);
}
.hub-cartao__azulejo svg{width:16px; height:16px}
.hub-cartao__titulo{font-size:16px; line-height:24px; font-weight:600; color:var(--foreground); margin:0}
.hub-cartao__sub{font-size:14px; line-height:20px; color:var(--muted-foreground); margin:2px 0 0}
.hub-cartao__acoes{margin-left:auto; display:flex; align-items:center; gap:8px; flex-wrap:wrap}
/* AÇÕES NO CORPO DO CARTÃO, e não no cabeçalho dele.
   `.hub-cartao__acoes` acima nasceu para o CABEÇALHO: o `margin-left:auto` é o
   que joga o botão para a direita do título, e ela não tem espaço em cima
   porque ali não precisa. Usada no corpo, ela cola o botão no bloco anterior --
   medido em 02/09/2026 na aba Cobrança: folga de 0px entre o fim dos pares e o
   botão, que passava a parecer parte da última coluna ("Próxima cobrança") em
   vez de uma ação do cartão. */
.hub-acoes-corpo{margin-top:20px; display:flex; align-items:center; gap:8px; flex-wrap:wrap}

/* PAR RÓTULO/VALOR.
   O RÓTULO é que tem peso 600, e o valor fica em 400. É o contrário do que
   quase todo painel faz, e é o que a captura mostra: "Display Name" escuro e
   forte, "Grupo Solution" abaixo em peso normal. */
.hub-pares{display:grid; grid-template-columns:1fr 1fr; gap:32px 48px}
@media (max-width:760px){ .hub-pares{grid-template-columns:1fr; gap:24px} }
.hub-par__rotulo{
  display:flex; align-items:center; gap:6px;
  font-size:14px; line-height:20px; font-weight:600;
  color:var(--foreground); margin:0 0 4px;
}
.hub-par__ajuda{width:14px; height:14px; color:var(--muted-foreground); cursor:help; flex:0 0 auto}
/* Estado, dado e ação no MESMO eixo horizontal: a pastilha vem colada ao valor
   e o lápis de editar fecha a linha. Foi assim que a captura mostrou. */
.hub-par__valor{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap;
  font-size:16px; line-height:24px; font-weight:400;
  color:var(--foreground); margin:0;
}
/* [MEDIDO 24/08/2026] O ÍCONE DO VALOR PRECISA DE TAMANHO, COMO TODOS OS OUTROS.
   `hub-cobranca.js` põe o ícone do cartão de crédito direto aqui dentro, e um
   <svg> sem width/height no atributo e sem regra na folha não tem tamanho
   intrínseco: ele cai no padrão de elemento substituído e, num contêiner flex
   que pode crescer, estica até o espaço acabar. Medido no meio de pagamento:
   1004 por 1004 pixels, com a linha inteira em 1004 por 1032. Nada de erro, nada
   no console: um cartão de crédito do tamanho do cartão da tela.
   Esta era a ÚNICA família de ícone da folha sem regra de tamanho; as outras
   catorze já tinham a delas (conferido família por família em 24/08/2026, e o
   portão passou a conferir isto sozinho).
   O combinador é `>` de propósito: a pastilha que vive nesta mesma linha traz o
   ícone dela em 12px por `.hub-pastilha svg`, e um seletor de descendente aqui
   entraria em empate de especificidade com ela, decidido por ordem no arquivo.
   Empate resolvido por ordem é regra que quebra quando alguém move um bloco. */
.hub-par__valor > svg{width:16px; height:16px; flex:0 0 auto}
.hub-par__editar{
  width:16px; height:16px; padding:0; background:none; border:0;
  color:var(--muted-foreground); cursor:pointer; flex:0 0 auto;
}
.hub-par__editar:hover{color:var(--primary-strong)}
.hub-par__editar:focus-visible{outline-offset:3px}

/* A AUSÊNCIA DENTRO DE UMA CÉLULA DE TABELA.
   `.hub-ausente` é um bloco com ícone, margem no topo e quebra livre, e está
   certo embaixo de um par rótulo/valor. Dentro de uma célula ele empilha: na
   aba Modelos, a mesma frase de 31 caracteres aparecia 24 vezes (duas colunas
   por doze linhas) e levava a linha de uma para três alturas.

   Aqui ela vira uma palavra em linha, sem ícone e sem margem. O texto inteiro
   fica no `title` e no `aria-label`, então a explicação continua alcançável
   pelo ponteiro e pelo leitor de tela. */
.hub-ausente--celula{
  display:inline; margin-top:0; gap:0;
  white-space:nowrap; font-style:italic; cursor:help;
}
.hub-ausente--celula > svg{display:none}

/* A PRÉVIA DO CARTÃO DE CONTATO, na aba Perfil.
   A referência mostra este cartão ao lado do formulário, e o motivo é que
   "Sobre" e "Descrição" só fazem sentido quando dá para ver onde eles aparecem.

   A foto vem de `/api/hub/canal/<id>/foto`, servida pela nossa própria origem:
   o endereço da Meta é descartado pela CSP EM SILÊNCIO, e foi por isso que esta
   tela passou meses explicando a ausência de uma foto que existia. */
.hub-previa{
  border:1px solid var(--hub-linha); border-radius:var(--hub-raio-caixa);
  padding:16px; margin-bottom:20px; background:var(--hub-superficie-2);
}
.hub-previa__cab{margin-bottom:12px}
.hub-previa__titulo{margin:0; font-size:14px; font-weight:600; color:var(--hub-tx)}
.hub-previa__sub{margin:4px 0 0; font-size:12px; line-height:16px; color:var(--hub-tx-2)}
.hub-previa__cartao{
  display:flex; align-items:center; gap:12px;
  background:var(--hub-superficie); border:1px solid var(--hub-linha);
  border-radius:var(--hub-raio); padding:12px;
}
/* O círculo é o contêiner posicionado: a imagem cobre as iniciais em vez de
   empurrá-las, então o troca-troca no `load`/`error` não mexe no layout. */
.hub-previa__foto{
  position:relative; flex:0 0 auto; width:56px; height:56px; overflow:hidden;
  border-radius:var(--hub-raio-pilula); background:var(--hub-superficie-3);
  display:flex; align-items:center; justify-content:center;
}
.hub-previa__iniciais{font-size:18px; font-weight:600; color:var(--hub-tx-2); line-height:1}
.hub-previa__img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover}
.hub-previa__lado{min-width:0}
.hub-previa__nome{
  margin:0; font-size:15px; font-weight:600; color:var(--hub-tx);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.hub-previa__numero{margin:2px 0 0; font-size:13px; color:var(--hub-tx-2)}
.hub-previa__link{margin-top:12px}
.hub-previa__link .hub-par__rotulo{margin-bottom:2px}

/* O QR do wa.me. O quadrado tem fundo branco fixo (ver qr.js): num tema escuro,
   QR claro sobre fundo escuro e o negativo do que a maioria dos leitores de
   celular espera. A moldura clara aqui existe para o branco do codigo nao
   parecer um buraco na tela escura. */
.hub-previa__qr{display:flex; gap:12px; align-items:flex-start; margin-top:12px; flex-wrap:wrap}
.hub-previa__qr > svg{
  flex:0 0 auto; background:#fff; padding:6px;
  border:1px solid var(--hub-linha); border-radius:var(--hub-raio);
}
.hub-previa__qr-lado{flex:1 1 180px; min-width:0}
.hub-previa__qr-lado .hub-par__rotulo{margin-bottom:2px}

/* A BARRA DE BUSCA E FILTRO DA TABELA DE MODELOS.
   A referência tem "Search by name" e "Add Filter" nesta tela, e o motivo é
   prático: a conta do Grupo Solution tem 19 modelos, e achar um deles rolando a
   lista inteira é o que se faz hoje.

   No celular os dois viram uma coluna: lado a lado em 390px, o seletor de
   situação fica com menos de 120px e o rótulo da opção não cabe. */
.hub-filtros{display:flex; gap:8px; margin-bottom:16px; flex-wrap:wrap}
.hub-filtros .hub-campo{flex:1 1 220px}
@media (max-width:640px){
  .hub-filtros{flex-direction:column}
  .hub-filtros .hub-campo{width:100%; flex:0 0 auto}
}

/* O FORMULÁRIO DO PERFIL PÚBLICO DO WHATSAPP.
   [MEDIDO 31/08/2026 na tela do 360dialog: campo de largura total, contador de
   caracteres colado embaixo à direita, e o par Salvar preenchido + Cancelar de
   contorno no fim.]

   `.hub-campo` sozinho não serve aqui: ele nasce com 200px de largura e altura
   fixa de controle, o que está certo para o seletor de canal do cabeçalho e
   errado para um campo de texto de três linhas. A variante abaixo solta os dois,
   e SÓ dentro deste formulário, para não mexer em nenhum outro campo do hub. */
.hub-form-perfil{display:flex; flex-direction:column; margin-top:16px}
.hub-form-perfil .hub-campo{width:100%; flex:0 0 auto; height:auto; min-height:var(--hub-h-controle)}
.hub-form-perfil .hub-campo textarea{height:auto; padding:10px 16px; resize:vertical}
.hub-form-perfil .hub-rotulo{margin-top:16px}
.hub-form-perfil .hub-rotulo:first-of-type{margin-top:0}

/* O contador fica alinhado à direita, embaixo do campo, como na referência.
   Ele conta para todo mundo, e não só quando aperta: saber que sobram 12
   caracteres ANTES de escrever a frase é o que evita reescrever depois. */
.hub-contador{
  align-self:flex-end; margin-top:4px;
  color:var(--muted-foreground); font-variant-numeric:tabular-nums;
}
.hub-contador--cheio{color:var(--danger-foreground)}

.hub-form-perfil__acoes{display:flex; gap:8px; margin-top:20px}

/* A confirmação de que salvou. Mesma caixa do erro, com a cor do estado bom:
   as duas mensagens ocupam o mesmo lugar da tela, e quem lê não precisa
   procurar em dois cantos diferentes para saber o que aconteceu. */
.hub-ok-campo{
  display:flex; align-items:flex-start; gap:6px; margin:8px 0 0;
  font-size:12px; line-height:16px; color:var(--ok-foreground);
}
/* O TERCEIRO ESTADO, e ele não é decoração.
   "A Meta aceitou, mas ainda não devolveu o valor novo" não é erro nem sucesso
   limpo. A primeira versão pintava esse caso com a cor de ERRO enquanto a frase
   dizia que havia sido aceito: a cor contradizia o texto, e quem lê acredita na
   cor. Reusa o par de espera das pastilhas, que já existe e já tem variante de
   tema escuro. */
.hub-aviso-campo{
  display:flex; align-items:flex-start; gap:6px; margin:8px 0 0;
  font-size:12px; line-height:16px; color:var(--waiting-foreground);
}

/* O MESMO DEFEITO, NA LINHA DA LISTA. Uma linha curta abaixo da pastilha de
   situação, e não no lugar dela: os dois estados são diferentes e os dois são
   verdade ao mesmo tempo. Antes disto a lista escrevia "No ar" em verde para um
   número que não recebe nem envia mensagem. */
.hub-aviso-linha{
  display:flex; align-items:center; gap:4px;
  margin-top:4px; color:var(--danger-foreground);
}
.hub-aviso-linha--media{color:var(--waiting-foreground)}
.hub-aviso-linha svg{width:12px; height:12px; flex:0 0 auto}

/* A FAIXA DE DEFEITO DO LADO DA META, no topo da aba Detalhes.
   Equivale à faixa "Your channel is no longer active / Fix channel setup" da
   referência, e existe porque os quatro campos que provam o defeito viviam só
   dentro do cartão "Estado na Meta", que nasce fechado. A pessoa só descobria
   que o número estava fora se abrisse um cartão que não tinha motivo para abrir.

   Duas gravidades, com as cores que a folha já tem: `alta` reusa o par de perigo
   das pastilhas de erro, e `media` reusa o de espera. Nenhuma cor nova entra
   aqui, e as duas já têm variante de tema escuro. */
.hub-alerta-meta{
  display:flex; align-items:flex-start; gap:12px;
  margin-top:24px; padding:16px;
  border:1px solid var(--destructive); border-radius:var(--radius);
  background:var(--danger-bg); color:var(--danger-foreground);
}
.hub-alerta-meta--media{
  border-color:var(--waiting-foreground);
  background:var(--waiting); color:var(--waiting-foreground);
}
.hub-alerta-meta > svg{width:20px; height:20px; flex:0 0 auto; margin-top:2px}
.hub-alerta-meta__texto{flex:1 1 auto; min-width:0}
.hub-alerta-meta__titulo{font-size:14px; line-height:20px; font-weight:600; margin:0}
.hub-alerta-meta__corpo{font-size:14px; line-height:20px; margin:4px 0 0}
/* A data da leitura fica presa ao alerta pelo mesmo motivo de todo valor da
   Meta nesta interface: alerta sem data mente por omissão, e leitura de três
   semanas atrás parece leitura de agora. */
.hub-alerta-meta__quando{font-size:12px; line-height:16px; opacity:.85; margin:6px 0 0}
.hub-alerta-meta .hub-btn{flex:0 0 auto}
@media (max-width:640px){
  .hub-alerta-meta{flex-wrap:wrap}
  .hub-alerta-meta .hub-btn{width:100%; justify-content:center}
}

/* COPIAR O IDENTIFICADOR COM UM CLIQUE.
   No hub do 360dialog cada identificador é clicável e copia sozinho; do nosso
   lado eles eram texto monoespaçado sem nenhuma afordância, e quem precisa do
   `phone_number_id` para configurar a Cloud API selecionava 15 dígitos à mão.

   O botão nasce com opacidade reduzida e ganha cor no hover e no foco: presente
   o bastante para se descobrir, discreto o bastante para não competir com o
   valor, que é o que a pessoa veio ler.

   `:focus-within` acende junto com o hover porque quem chega pelo teclado
   precisa do mesmo sinal de que ali existe uma ação. */
.hub-copiar{opacity:.55; transition:opacity .12s ease}
.hub-par__valor:hover .hub-copiar,
.hub-par__valor:focus-within .hub-copiar{opacity:1}
.hub-copiar:focus-visible{opacity:1}
/* O recado da confirmação vive ao lado do valor. Ele nasce em `sr-only` (só
   para leitor de tela) e sai de lá quando há o que dizer: a troca de ícone
   sozinha não é confirmação nenhuma para quem não enxerga. */
.hub-copiar__recado{font-size:12px; line-height:16px; color:var(--muted-foreground)}

/* INDICADOR (a variante do par, para número que a pessoa vem ver).
   [MEDIDO 31/08/2026 no produto do 360dialog, na aba Insights ao vivo: 20px,
   peso 700, cor rgb(10,10,10).]

   O par comum acima inverte a hierarquia de propósito, e está certo: no cartão
   de DADOS o rótulo é a pergunta ("Nome de exibição") e o valor é a resposta,
   e foi assim que a referência desenhou. Em MÉTRICAS a relação se inverte: a
   pessoa abriu a aba para ver 6.705, e não para ler a palavra "Enviadas".
   Com a regra comum, o rótulo saía em 14px peso 600 e o número em 16px peso
   400, ou seja, a palavra pesava mais que o número.

   Fica como modificador do contêiner, e não como classe nova de valor, para
   que o `ausente()` de "Recebidas" continue com o tamanho de texto: ele traz
   className próprio e por isso nunca recebe `hub-par__valor`.

   NÃO subir para 30 ou 32px: a referência está em 20px, e estourar isso afasta
   em vez de aproximar. */
.hub-pares--kpi .hub-par__rotulo{
  font-weight:400; color:var(--muted-foreground);
}
.hub-pares--kpi .hub-par__valor{
  font-size:20px; line-height:28px; font-weight:700;
  font-variant-numeric:tabular-nums;
}

/* BLOCO DE DESTAQUE, a faixa com o botão de ação.
   [MEDIDO] no detalhe, y=700: #eff8fa em x=236 indo a #f8fcfd em x=1300, com
   borda #d0edf2. Aqui o degradê vai do accent ao transparente. */
.hub-destaque{
  display:flex; align-items:center; gap:16px; flex-wrap:wrap;
  padding:24px;
  border-radius:var(--radius-card);
  border:1px solid var(--primary-edge);
  background:linear-gradient(to right, var(--accent), transparent);
}
.hub-destaque__texto{flex:1 1 320px; min-width:0}
.hub-destaque h3{font-size:16px; line-height:24px; font-weight:600; margin:0; color:var(--foreground)}
.hub-destaque p{font-size:14px; line-height:20px; color:var(--muted-foreground); margin:8px 0 0}

/* ---------------------------------------------------------------------------
   8.1 DOBRA: a sanfona do cartão

   POR QUE ELA EXISTE
   [MEDIDO 24/08/2026, ver-hub --local] Ajustes tinha cinco cartões, todos
   abertos, e a página fechava em 1954px a 1600 de largura e 2750px a 390. A
   mesma tela deles resolve em torno de 700px. A dobra não encurta o conteúdo:
   ela deixa a pessoa escolher o que abrir.

   ELA É <details>/<summary>, E ISSO NÃO É PREGUIÇA
   O elemento nativo já traz teclado (Enter e Espaço no <summary>), papel de
   revelação para o leitor de tela e o estado aberto ou fechado no acessível.
   Um <div> com onclick precisaria reimplementar os três, e a terceira sempre
   fica de fora. O JS acrescenta `aria-expanded` por cima, porque a exigência
   é explícita e o atributo custa uma linha.

   O TRIÂNGULO NATIVO SAI, A SETA DA CASA ENTRA
   `list-style:none` tira o marcador do Firefox e do Chrome; a regra
   ::-webkit-details-marker tira o do Safari antigo. As duas são necessárias:
   uma sozinha deixa dois marcadores em metade dos navegadores.

   O CARTÃO DOBRÁVEL PERDE O PADDING PARA O GATILHO
   .hub-cartao tem `padding:24px 16px`. Se ele ficasse, a área de clique
   pararia antes da borda e sobraria uma moldura morta em volta do cabeçalho.
   O padding desce para o gatilho e para o corpo, que somados dão a mesma
   caixa de antes.
   --------------------------------------------------------------------------- */
.hub-cartao--dobra{padding:0}
.hub-dobra{display:block}
.hub-dobra__gatilho{
  display:flex; align-items:flex-start; gap:16px;
  padding:24px 16px;
  cursor:pointer;
  list-style:none;                  /* Firefox e Chrome */
  border-radius:var(--radius-card);
}
.hub-dobra__gatilho::-webkit-details-marker{display:none}   /* Safari */
.hub-dobra__gatilho:hover{background:var(--secondary)}
.hub-dobra__gatilho:focus-visible{outline-offset:-2px}
/* O cabeçalho perde a margem de baixo aqui: quem separa passa a ser o padding
   do gatilho, senão o cartão fechado fica 24px mais alto sem nada dentro. */
.hub-dobra__gatilho > .hub-cartao__cabeca{margin-bottom:0; flex:1 1 auto; min-width:0}
.hub-dobra__seta{
  flex:0 0 auto; margin-left:auto;
  width:20px; height:20px;
  display:grid; place-items:center;
  color:var(--muted-foreground);
  transition:transform .18s ease;
}
.hub-dobra__seta svg{width:16px; height:16px}
.hub-dobra[open] > .hub-dobra__gatilho .hub-dobra__seta{transform:rotate(180deg)}
.hub-dobra__corpo{padding:0 16px 24px}

/* A DOBRA DE TEXTO, dentro de um cartão. O gatilho dela é uma frase, não um
   cabeçalho de cartão.
   [MEDIDO 24/08/2026] O texto integral do aceite já morava num <details> em
   hub-conta.js desde 23/08, e esta folha não tinha UMA regra para `details` ou
   `summary`: ele desenhava com o triângulo preto do navegador e a fonte padrão
   dentro de um painel todo em Inter 14/20. Só aparece para cliente que tem o
   aceite guardado, então ninguém tinha visto. */
.hub-dobra--texto{margin-top:16px}
/* O gatilho é `.hub-dobra__frase`, uma CLASSE, e não o seletor de elemento
   `> summary`. A conferência 10.5 do portão descobre o tamanho do svg pela
   classe de quem recebe o ícone: um <summary> sem classe cai na lista de "pai
   fora do alcance" e o ícone deixa de ser conferido, mesmo com a regra escrita
   logo abaixo. */
.hub-dobra__frase{
  display:flex; align-items:center; gap:8px;
  list-style:none; cursor:pointer;
  font-size:14px; line-height:20px; font-weight:600;
  color:var(--primary-strong);
  border-radius:var(--radius);
}
.hub-dobra__frase::-webkit-details-marker{display:none}
.hub-dobra__frase:hover{color:var(--primary-stronger)}
.hub-dobra__frase svg{
  width:16px; height:16px; flex:0 0 auto;
  transition:transform .18s ease;
}
.hub-dobra--texto[open] > .hub-dobra__frase svg{transform:rotate(180deg)}
.hub-dobra--texto > p{margin-top:8px}

/* O BOTÃO QUE ABRE TUDO DE UMA VEZ.
   [HARD] Ele não é enfeite: é a saída para o Ctrl+F. O Chrome já procura
   dentro de <details> fechado e abre sozinho; o Firefox e o Safari não. Sem
   este botão, num desses dois a pessoa procura uma palavra que ESTÁ na página
   e o navegador responde que não existe. Com ele, um clique devolve a página
   inteira à busca. */
.hub-dobras-controle{display:flex; justify-content:flex-end; margin:0 0 12px}

/* INTRODUÇÃO DE ABA: título e frase, sem caixa.
   [MEDIDO 24/08/2026] A aba Começar abria com um .hub-cartao de 120px que só
   tinha cabeçalho: borda, fundo e nada dentro. Caixa vazia lê como conteúdo
   que não carregou. Isto aqui é a mesma tipografia sem a caixa, que é o que a
   frase sempre foi: o título da aba. */
.hub-intro{display:flex; align-items:flex-start; gap:16px; margin:0 0 24px}
.hub-intro__titulo{font-size:16px; line-height:24px; font-weight:600; color:var(--foreground); margin:0}
.hub-intro__sub{font-size:14px; line-height:20px; color:var(--muted-foreground); margin:2px 0 0}

/* ---------------------------------------------------------------------------
   9. TABELA
   A tabela NÃO encosta na borda do cartão: ela vive numa caixa de fundo
   próprio, ligeiramente mais escuro. É o que faz a tabela ler como um objeto
   DENTRO do cartão, e não como o cartão inteiro.
   --------------------------------------------------------------------------- */
/* SOMBRA DE ROLAGEM, e ela existe por um achado da bancada: com a tabela
   larga demais para a caixa, a última coluna aparecia FATIADA na borda, o que
   lê como defeito de renderização e não como "tem mais coisa para o lado".
   O truque é o clássico de duas camadas por lado, e é CSS puro:
     - as duas primeiras camadas são TAMPAS na cor da própria caixa, presas ao
       CONTEÚDO (background-attachment:local). Elas andam junto com a rolagem
       e cobrem a sombra quando aquele lado chega ao fim;
     - as duas últimas são as SOMBRAS, presas à CAIXA (scroll), sempre no mesmo
       lugar.
   Resultado: a sombra da direita some ao chegar ao fim, a da esquerda só
   aparece depois que a pessoa rolou, e quando a tabela cabe inteira não
   aparece nenhuma das duas. Sem uma linha de JavaScript e sem medir nada. */
.hub-tabela-caixa{
  background-color:var(--muted);
  background-image:
    linear-gradient(to right, var(--muted), transparent),
    linear-gradient(to left,  var(--muted), transparent),
    linear-gradient(to right, var(--fade), transparent),
    linear-gradient(to left,  var(--fade), transparent);
  background-position:left center, right center, left center, right center;
  background-size:32px 100%, 32px 100%, 14px 100%, 14px 100%;
  background-repeat:no-repeat;
  background-attachment:local, local, scroll, scroll;
  border-radius:var(--radius-box);
  overflow-x:auto;                    /* a TABELA rola. O <body> nunca rola na horizontal */
  -webkit-overflow-scrolling:touch;
}
/* A caixa recebe foco quando o conteúdo transborda, senão quem navega por
   teclado não consegue rolar a tabela. O atributo tabindex="0" vai no HTML;
   aqui só se garante que o anel apareça por dentro e não seja cortado. */
.hub-tabela-caixa:focus-visible{outline-offset:-2px}

/* ---------------------------------------------------------------------------
   A COLUNA DE AÇÃO GRUDA NA DIREITA.
   [HARD] Escrita depois de OLHAR o print de 22/08/2026. A tabela de Canais
   tinha sete colunas e, em 1600px, ela passava da largura do cartão: a caixa
   rolava (o que está certo) e o botão "Abrir" da última coluna ficava CORTADO
   no limite direito, nas três linhas. A pessoa via a tabela inteira e não via a
   única coisa clicável dela.

   [MEDIDO 24/08/2026] Hoje são SEIS colunas e a tabela cabe: 1054px pedidos numa
   caixa de 1054px, em viewport de 1180 e 1484. A regra continua valendo, e não
   por herança: em 900px de viewport a tabela pede 1041 numa caixa de 834 e volta
   a rolar, que é exatamente o caso em que a coluna presa faz diferença.

   A medida não pegava isso: `overflow-x:auto` estava funcionando exatamente
   como escrito. Rolar é o comportamento certo para o DADO; para a AÇÃO, não:
   ação que exige rolar é ação que metade das pessoas não encontra.

   `position:sticky` dentro de um contêiner que rola prende a célula na borda.
   O fundo é opaco de propósito, senão o texto da coluna vizinha passa por
   baixo enquanto a pessoa rola. A sombra à esquerda separa a coluna presa do
   conteúdo que desliza sob ela, e some quando não há mais o que rolar.
   --------------------------------------------------------------------------- */
.hub-tabela th.hub-col-acao,
.hub-tabela td.hub-col-acao{
  position:sticky; right:0; z-index:1;
  /* O fundo da célula presa é o da CAIXA, não o do cartão: nesta tabela a linha
     não tem fundo próprio, quem pinta é a caixa. Pôr `--card` aqui deixaria a
     coluna da ponta branca sobre uma tabela cinza. */
  background:var(--muted);
  box-shadow:-8px 0 8px -8px rgb(0 0 0 / .10);
  text-align:right; white-space:nowrap;
}
/* DOIS BOTÕES NESTA CÉLULA, desde 23/08/2026: a ação primária mudou de lugar.
   Ela era empilhada dentro da célula de "Situação" (terceiro andar, linha a
   116px contra os ~59px deles) e passou a viver aqui, à esquerda do "Abrir o
   canal". Sem espaçamento os dois encostam, porque a célula é `nowrap` e
   `text-align:right`, e nenhum dos dois cria espaço entre irmãos.

   [HARD] O FLEX VAI NUM INVÓLUCRO, NUNCA NO `<td>`.
   `display:flex` num `td` tira a célula do layout de tabela: ela deixa de ser
   `table-cell`, para de participar do cálculo de largura da coluna, o
   `vertical-align:middle` do resto da tabela deixa de valer nela, e o
   `position:sticky` fica em cima de uma caixa que o navegador já não mede junto
   com as irmãs. Nada disso dá erro: dá uma coluna que desalinha de leve e uma
   ponta direita que treme ao rolar. */
.hub-acoes-linha{display:flex; gap:8px; justify-content:flex-end; align-items:center}
/* E ela acompanha o clareamento do hover, senão a linha inteira clareia e só a
   ponta direita fica para trás. */
.hub-tabela tbody tr:hover td.hub-col-acao{background:var(--card)}

.hub-tabela{width:100%; border-collapse:collapse; font-size:14px; line-height:20px}
/* [MEDIDO] cabeçalho de 52px = 16 + 20 + 16. Sem faixa cinza no <thead>: a
   caixa já É a faixa, e cinza sobre cinza vira duas faixas. */
.hub-tabela th{
  padding:16px;
  font-weight:600; color:var(--foreground); text-align:left; white-space:nowrap;
  border-bottom:1px solid var(--border);
  background:transparent;
}
.hub-tabela td{
  padding:16px; height:64px;          /* [MEDIDO] linha de 64px */
  font-weight:400; color:var(--foreground); vertical-align:middle;
  border-bottom:1px solid var(--border);
}
.hub-tabela tbody tr:last-child td{border-bottom:0}

/* NOME LONGO TRUNCA, NÃO EMPILHA. [MEDIDO 23/08/2026]
   A linha deles tem ~59px, uma linha de texto por célula. A nossa ia a 105px
   porque "Distribuidora de Materiais de Construcao Sao Jorge Limitada" quebrava
   em cinco linhas e esticava a linha inteira -- e com ela as outras cinco
   células, que ficavam com um mar de vazio em cima e embaixo.
   `max-width` com valor pequeno é o truque conhecido para `table-layout:auto`:
   ele não fixa a largura (o navegador continua distribuindo), só autoriza o
   truncamento. Sem ele, `text-overflow` em `td` não faz nada.
   O `title` com o nome inteiro é posto pelo JS: truncar sem deixar o valor
   completo em lugar nenhum seria esconder dado do dono dele. */
.hub-tabela td.hub-col-nome{
  max-width:200px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* O NOME DO MODELO: mesmo problema de largura da regra acima, solução oposta.
   Nome de canal pode ser cortado com reticências porque o telefone ao lado
   identifica a linha. Nome de MODELO é a própria chave -- é o que a pessoa
   procura no Gerenciador da Meta, e `renovacao_cert_d30` e `renovacao_cert_d60`
   diferem no último caractere. Então ele quebra e continua inteiro.
   `anywhere` e não `break-word` porque nome de modelo não tem espaço: é tudo
   `palavra_com_underline`, e `break-word` só quebra entre palavras. */
/* [HARD] O `min-width` É TÃO NECESSÁRIO QUANTO O `max-width`, e por um motivo
   que só apareceu na foto: com `overflow-wrap:anywhere` o mínimo de conteúdo da
   coluna passa a ser UM CARACTERE, e o navegador então dá a ela a menor largura
   possível. A tabela passou a caber, e `renovacao_cert_d30` quebrava em
   `renovacao_cert_d3` / `0` -- um nome de 18 caracteres partido ao meio numa
   tabela que tinha espaço de sobra. A medida dizia "sobra 0px" e estava certa.
   220px cabem os nomes usuais numa linha; o teto de 280px segura o exagero. */
.hub-tabela td.hub-col-modelo{min-width:220px; max-width:280px; overflow-wrap:anywhere}
@media (max-width:1099px){
  /* Abaixo do corte a folha vira cartão e a caixa da tabela encolhe: manter
     220px aqui devolveria a rolagem horizontal que esta regra veio tirar. */
  .hub-tabela td.hub-col-modelo{min-width:150px}
}
.hub-tabela tbody tr:hover td{background:var(--card)}   /* [DERIVADO] a linha CLAREIA sobre a caixa */
/* Número em coluna de tabela SEMPRE com tabular-nums, senão a coluna dança a
   cada carga: o "1" da Inter proporcional é mais estreito que os outros. */
.hub-tabela td.hub-num, .hub-tabela th.hub-num{font-variant-numeric:tabular-nums; text-align:right}

/* VALOR ATÔMICO NÃO QUEBRA, e esta regra apareceu na bancada, não na
   especificação: em 900px de largura o "+55 34 9154-0926" estava se partindo
   em TRÊS linhas ("+55 34" / "9154-" / "0926") em vez de a tabela rolar.
   A causa é que width:100% deixa o navegador espremer coluna por coluna até o
   texto caber de qualquer jeito, e telefone picado não é dado: é ruído.
   Proibindo a quebra desses valores, o mínimo de conteúdo da tabela sobe, ela
   passa de largo da caixa e a caixa ROLA, que é o comportamento que a
   especificação pede, obtido sem cravar um min-width mágico. É de propósito
   que isto NÃO é o table{min-width:820px} do estilo.css:785: aquilo obriga
   até uma tabela de duas colunas a rolar num celular.
   Vale para telefone, data, identificador e qualquer coisa que só faz sentido
   inteira. Frase (o motivo de uma célula vazia, por exemplo) continua
   quebrando normalmente.

   [HARD] E A CLASSE VAI NO PEDAÇO, NUNCA NO BLOCO QUE TEM UM PEDAÇO DENTRO.
   [MEDIDO 24/08/2026] As duas notas da tabela de canais nasceram com esta classe
   na FRASE inteira ("desde 13/08/2025", "lido em 23/08/2025, 21:46"), porque
   tinham uma data dentro. Só que "desde" e "lido em" são prosa: proibir a quebra
   ali travou a frase inteira numa palavra só de 157px e de 152px. Como
   `table-layout:auto` nunca desce abaixo do maior pedaço indivisível de uma
   coluna, essas três notas empurraram a tabela para 1275px dentro de uma caixa
   de 1054px, a coluna "Qualidade" sumiu por baixo da coluna presa e "Limite
   (Meta)" saiu cortada no meio da palavra. Nenhum erro, nenhum aviso.
   Hoje a nota é montada com a data num `<span class="hub-atomico">` e o resto em
   texto normal (`notaComData`, em hub-canais.js), e a tabela pede 1054px. */
.hub-tabela .hub-atomico,
.hub-tabela .hub-num,
/* `.hub-id` entra na regra de cima pelo mesmo motivo que `.hub-atomico`:
   identificador não parte no meio.

   [HARD] E NÃO NASCE UMA CLASSE NOVA PARA ISTO.
   Em 23/08/2026 eu inventei uma `.hub-nota--data` para dizer "esta data não
   quebra" sem ter visto que `.hub-atomico` existia, estava dez linhas acima, e
   trazia escrita a mesma frase: *"vale para telefone, data, identificador e
   qualquer coisa que só faz sentido inteira; frase continua quebrando"*. Duas
   classes para o mesmo papel é como uma regra nasce divergente: seis meses
   depois metade das datas usa uma, metade usa a outra, e o conserto de um lado
   não chega no outro.
   Quem precisar de "não quebra" numa célula usa `.hub-atomico`. */
.hub-tabela .hub-id{white-space:nowrap}
/* [corrigido 31/08/2026] AS RETICÊNCIAS PRECISAM ESTAR NO ELEMENTO QUE TEM O
   TEXTO, E ELE É O FILHO.
   `td.hub-col-nome` (seção 9) já traz `overflow:hidden`, `text-overflow:ellipsis`
   e `white-space:nowrap`, mas o nome do canal é escrito dentro de um
   `<div class="hub-forte">` (hub-canais.js, `celulaNome`). `text-overflow` age
   sobre o conteúdo do PRÓPRIO elemento e não é herdado por um filho de bloco:
   o `overflow:hidden` da célula cortava, e as reticências nunca apareciam.
   Medido na bancada: "Distribuidora de Materiais de Construcao Sao J" com 234px
   de sobra, cortado no meio da palavra e sem nenhum sinal de que havia mais.
   O `title` da célula continua guardando o nome inteiro. */
.hub-tabela td.hub-col-nome > .hub-forte{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* A regra de cartão para este mesmo filho existe, e mora DENTRO da media query
   da tabela responsiva, algumas centenas de linhas abaixo. Ela não pode ficar
   aqui: `hub-tabela--responsiva` está na tabela em toda largura. */
.hub-tabela td.hub-forte{font-weight:500}   /* ênfase em célula é 500, não 600: 600 é peso de rótulo */
.hub-tabela caption{
  text-align:left; padding:0 0 16px;
  font-size:14px; line-height:20px; color:var(--muted-foreground);
}

.hub-tabela-pe{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:24px 0 0;
  font-size:14px; line-height:20px; color:var(--muted-foreground);
}
.hub-paginacao{display:flex; align-items:center; gap:8px}
.hub-pag-btn{
  width:40px; height:36px; display:grid; place-items:center;
  background:var(--muted);
  border:1px solid var(--input);   /* --input e não --border: é controle, precisa do 3:1 */
  border-radius:var(--radius);
  color:var(--foreground); cursor:pointer;
}
.hub-pag-btn svg{width:16px; height:16px}
.hub-pag-btn:hover:not(:disabled){background:var(--secondary)}
/* Controle desligado é isento do mínimo de contraste (WCAG 1.4.3 e 1.4.11
   dispensam componente inativo), e a opacidade é o sinal de que ele não
   responde. O cursor confirma para quem usa ponteiro. */
.hub-pag-btn:disabled{opacity:.45; cursor:not-allowed}

/* ---------------------------------------------------------------------------
   9.1 TABELA -> CARTÕES, abaixo do ponto MEDIDO de quebra
   [HARD] A nota que ficava aqui dizia o contrário disto: "abaixo de 640px a
   tabela continua rolando, NÃO vira cartão empilhado, porque comparar linha
   com linha é o motivo dela existir". Essa decisão parava de valer no
   instante em que a rolagem passou a ESCONDER coluna: a coluna de ação é
   `sticky` e opaca (linha 1038), e com a caixa mais estreita que a tabela
   ela PASSA POR CIMA da vizinha em vez de só empurrá-la para o lado. Medido
   em 21/08/2026, com sessão real: em 845px de janela a coluna "Qualidade"
   ficava coberta, "Limite (Meta)" saía cortada no meio da palavra
   ("1.000 por d") e a nota de leitura saía picada ("lido er 24/08"). Não
   sobrou comparação nenhuma para proteger: sobrou dado ilegível.

   [MEDIDO 24/08/2026, sessão real em .hub-tabela--responsiva de Canais, seis
   colunas] A largura mínima real da tabela (scrollWidth, table-layout
   natural) é 1004px — é o piso que sobra depois que a coluna do nome
   (`.hub-col-nome`) já encolheu ao máximo e as demais (telefone, situação,
   limite, qualidade, ações) não cedem mais um pixel, todas com
   `white-space:nowrap` de propósito (ver a nota de `.hub-atomico`). A caixa
   alcança exatamente 1004px de largura útil em viewport 1070px (varredura
   binária real, passo a passo):
     1060px → caixa 994px  → ROLA (994 < 1004)
     1070px → caixa 1004px → NÃO rola (1004 = 1004)
     1080px → caixa 1014px → NÃO rola
   O corte abaixo fica em 1099px: 29px de folga sobre o breakpoint medido
   (1070px), para cobrir a variação de barra de rolagem entre navegador e
   sistema operacional — a própria medição já rodou com a barra vertical real
   do Chromium/Windows somada ao viewport, então a folga é margem extra, não
   compensação de uma medida que faltou.

   [HARD] A REGRA É `.hub-tabela--responsiva`, NUNCA `.hub-tabela` sozinha.
   `hub-tabela` também monta a lista de Modelos (hub.js), a de Faturas
   (hub-faturas.js), a da Conta (hub-conta.js) e as tabelas da ficha do canal
   (hub-canal.js) — nenhuma delas foi revisada para o modo cartão: não
   escrevem `data-rotulo` por célula nem os papéis `role` que a transformação
   abaixo exige para a tabela continuar acessível depois do `display`
   mudar (a blockificação do CSS tira o role implícito de `<tr>`/`<td>`/
   `<thead>` nos navegadores atuais, por isso os `role` explícitos ficam no
   JS que monta cada `<table>` que usar esta classe). Uma regra "sem classe
   nova" que passasse a valer para toda `.hub-tabela` mudaria essas quatro
   telas sem que ninguém tivesse olhado o resultado — o mesmo erro, do lado
   oposto, de uma regra que perde de especificidade e não pinta nada. Cada
   tabela que quiser este modo entra na classe de propósito depois de ganhar
   `data-rotulo` e `role` no HTML dela.
   --------------------------------------------------------------------------- */
@media (max-width:1099px){
  /* A CAIXA PARA DE SER CAIXA DE ROLAGEM: sem largura mínima para estourar,
     a sombra de rolagem (seção 9, `background-image`) e o fundo cinza que
     separava "a tabela" do cartão deixam de fazer sentido — cada linha já
     tem a própria borda e o próprio fundo abaixo. */
  .hub-tabela-caixa--responsiva{
    overflow-x:visible;
    background-color:transparent;
    background-image:none;
    border-radius:0;
  }

  .hub-tabela--responsiva{display:block; width:100%}

  /* O CABEÇALHO SOME DA VISTA, NUNCA DA ÁRVORE. `display:none` apaga o
     `<thead>` para o leitor de tela também; isto aqui só tira ele do
     retângulo visível (mesma receita de `.sr-only`, linha 515). */
  .hub-tabela--responsiva thead{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }

  .hub-tabela--responsiva tbody{display:block; width:100%}

  /* CADA `<tr>` VIRA UM CARTÃO. A borda e o raio que a caixa perdeu acima
     mudam de dono: agora é a LINHA que os carrega, uma por uma. */
  .hub-tabela--responsiva tr{
    display:block;
    border:1px solid var(--border);
    border-radius:var(--radius-box);
    padding:4px 16px;
    background:var(--card);
  }
  .hub-tabela--responsiva tbody tr + tr{margin-top:12px}
  /* O hover de LINHA de tabela ("a linha clareia sobre a caixa", seção 9) não
     tem para onde clarear num cartão parado sobre o próprio fundo. */
  .hub-tabela--responsiva tbody tr:hover td{background:transparent}

  /* [HARD] A CÉLULA DE AÇÃO PRECISA DE DUAS REGRAS PRÓPRIAS, E NÃO É REDUNDÂNCIA.
     Achado da bancada visual em 24/08/2026, com o ponteiro estacionado FORA da
     tabela — detalhe que importa, porque na primeira leva ele ficou parado sobre
     a primeira linha e o retrato saiu com um cartão claro e outro cinza, o que
     parecia diferença de dado e era hover.

     1. O FUNDO OPACO. `td.hub-col-acao` ganha fundo na seção 9 por um motivo que
        só existe no modo tabela: a coluna é presa (`sticky`) e precisa ser opaca
        para cobrir a vizinha enquanto a tabela rola. No cartão ela não é presa, e
        o fundo vira uma faixa cinza no rodapé que não é rodapé de nada — parando
        16px antes das bordas, com cantos retos dentro de um cartão arredondado.
        Contraste de 1,05 contra o cartão: sutil, e aparecia em todos os retratos.

     2. O HOVER DELA. `.hub-tabela tbody tr:hover td.hub-col-acao` (seção 9) tem
        especificidade (0,3,3) e VENCE a regra de hover logo acima, que é (0,2,3).
        Sem esta linha, passar o mouse no cartão pinta só a faixa de ação e mais
        nada. Zerar `background` na regra genérica não resolve: quem perde na
        cascata não pinta, e não avisa. */
  .hub-tabela--responsiva td.hub-col-acao,
  .hub-tabela--responsiva tbody tr:hover td.hub-col-acao{
    background:transparent;
    /* inerte com `position:static`, sai para não sugerir empilhamento que não há */
    z-index:auto;
  }
  /* Sobrescreve a regra de tabela que zera a borda só da ÚLTIMA linha
     (seção 9): aqui cada CARTÃO fecha a própria lista de pares sozinho, não
     só o último da página. A borda volta para toda linha... */
  .hub-tabela--responsiva tbody tr:last-child td{border-bottom:1px solid var(--border)}

  /* ...e sai de novo só do ÚLTIMO PAR de cada cartão — não do último cartão
     da página. */
  .hub-tabela--responsiva td{
    display:block;
    width:100%; max-width:none; height:auto;
    padding:10px 0;
    border-bottom:1px solid var(--border);
    text-align:left; white-space:normal; overflow:visible; text-overflow:clip;
  }
  .hub-tabela--responsiva td:last-child{border-bottom:0}
  /* O nome truncava por largura de coluna (seção 9, `.hub-col-nome`); num
     cartão a largura é a tela inteira, então o nome comprido volta a
     aparecer por extenso em vez de reticências. */
  .hub-tabela--responsiva td.hub-col-nome{
    max-width:none; overflow:visible; text-overflow:clip; white-space:normal;
  }
  /* O filho também, pelo mesmo motivo, e AQUI DENTRO da media query.
     [MEDIDO 31/08/2026] A primeira versão desta regra ficou solta lá fora, e
     `hub-tabela--responsiva` está na tabela SEMPRE: a media query muda o
     comportamento, não a classe. Solta, ela vencia o `nowrap` no DESKTOP e o
     nome comprido passou a quebrar em quatro linhas em vez de truncar, com a
     linha da tabela indo de 89px para 113px. Um seletor certo no lugar errado. */
  .hub-tabela--responsiva td.hub-col-nome > .hub-forte{
    overflow:visible; text-overflow:clip; white-space:normal;
  }

  /* O RÓTULO SAI DE `data-rotulo`, ESCRITO PELO JS A PARTIR DA MESMA
     `COLUNAS[]` QUE MONTA O `<th>` (ver `linha()` em hub-canais.js) — nunca
     de uma segunda lista escrita à mão aqui. Duas listas para o mesmo papel
     divergem sozinhas na primeira coluna nova: foi exatamente o erro que já
     aconteceu uma vez com `.hub-nota--data` (ver a nota de `.hub-atomico`).
     Sem o atributo, `attr()` devolve vazio e a regra abaixo nem desenha o
     pseudo-elemento: a célula aparece só com o valor, nunca com um rótulo
     em branco ocupando linha. */
  .hub-tabela--responsiva td::before{
    content:attr(data-rotulo);
    display:block;
    font-size:12px; line-height:16px; font-weight:600;
    color:var(--muted-foreground);
    margin-bottom:4px;
  }
  .hub-tabela--responsiva td:not([data-rotulo])::before{content:none}

  /* A COLUNA DE AÇÃO PARA DE SER STICKY. Sticky sem rolagem horizontal é só
     uma caixa flutuando sem motivo — e era esse mesmo sticky opaco que
     cobria a Qualidade acima deste corte. */
  .hub-tabela--responsiva td.hub-col-acao{
    position:static; box-shadow:none;
  }
  .hub-tabela--responsiva .hub-acoes-linha{justify-content:flex-start}
}

/* ---------------------------------------------------------------------------
   10. PASTILHA DE ESTADO
   Quatro variantes, e NENHUMA usa a cor da marca. É de propósito: azul-aço é
   superfície clicável e verde é ação; estado tem de ser lavado e inerte.
   A pastilha NUNCA é clicável. Se algo precisa ser apertado, é botão.
   --------------------------------------------------------------------------- */
.hub-pastilha{
  display:inline-flex; align-items:center; gap:4px;
  height:20px; padding:2px 10px;
  border-radius:var(--radius-pill);
  font-size:12px; line-height:16px; font-weight:500;
  white-space:nowrap; border:0;
}
/* Cada variante leva o ícone dela (check, ampulheta, tracinho, exclamação):
   cor sozinha não comunica estado para quem não distingue matiz, e a pastilha
   é justamente onde o estado mora. */
.hub-pastilha svg{width:12px; height:12px; flex:0 0 auto}
.hub-pastilha--ok{background:var(--ok); color:var(--ok-foreground)}                 /* 8,70:1 */
.hub-pastilha--esperando{background:var(--waiting); color:var(--waiting-foreground)} /* 8,75:1 */
.hub-pastilha--neutra{background:var(--neutral); color:var(--neutral-foreground)}    /* 7,17:1 */
.hub-pastilha--erro{background:var(--danger-bg); color:var(--danger-foreground)}     /* 7,60:1 */

/* ---------------------------------------------------------------------------
   11. BOTÃO, NOS QUATRO TIPOS
   --------------------------------------------------------------------------- */
.hub-btn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  height:var(--hub-h-controle); padding:8px 16px;
  border-radius:var(--radius);
  border:1px solid transparent;
  font-size:14px; line-height:20px; font-weight:400;
  cursor:pointer; text-decoration:none; white-space:nowrap;
  transition:background-color .15s ease, border-color .15s ease, color .15s ease;
}
.hub-btn svg{width:16px; height:16px; flex:0 0 auto}
.hub-btn:disabled, .hub-btn[aria-disabled="true"]{opacity:.55; cursor:not-allowed}

/* 1. AÇÃO (sólido). O único verde cheio da tela, um por bloco no máximo.
   Em duas capturas inteiras existe exatamente UM botão sólido, e ele está na
   faixa de venda. Todo o resto é contorno ou neutro, e é daí que vem a calma
   da tela. Letra ESCURA sobre o verde: branco sobre #25D366 dá 1,98:1 e está
   proibido. A borda mais escura existe pelo motivo anotado em --action-edge. */
.hub-btn--acao{
  background:var(--action);
  border-color:var(--action-edge);
  color:var(--action-foreground);
  font-weight:600;
}
.hub-btn--acao:hover:not(:disabled){background:var(--action-hover)}
.hub-btn--faixa{height:40px}    /* [MEDIDO] o botão dentro da faixa de destaque */

/* 2. CONTORNO. O tipo mais comum do hub. Fundo transparente, texto e borda no
   azul, e a LETRA usa a versão escurecida, senão são 4,48:1. */
.hub-btn--contorno{
  height:var(--hub-h-controle-p); padding:4px 8px;
  background:transparent;
  border-color:var(--primary);
  color:var(--primary-strong);
}
.hub-btn--contorno:hover:not(:disabled){background:var(--accent); color:var(--primary-stronger)}

/* 3. NEUTRO. */
.hub-btn--neutro{
  background:var(--muted);
  border-color:var(--input);      /* --input, não --border: contorno de controle exige 3:1 */
  color:var(--foreground);
}
.hub-btn--neutro:hover:not(:disabled){background:var(--secondary); border-color:var(--primary)}

/* 4. DISCRETO (só texto). Para desfazer, cancelar, voltar. */
.hub-btn--discreto{background:transparent; border-color:transparent; color:var(--muted-foreground)}
.hub-btn--discreto:hover:not(:disabled){background:var(--secondary); color:var(--secondary-foreground)}

/* DESTRUTIVA: contorno, nunca sólido. Vermelho cheio num painel de conta puxa
   o olho para a única coisa que ninguém deveria apertar sem querer. */
.hub-btn--perigo{background:transparent; border-color:var(--destructive); color:var(--danger-foreground)}
.hub-btn--perigo:hover:not(:disabled){background:var(--danger-bg)}

/* Tamanho pequeno, para qualquer um dos quatro tipos. [MEDIDO] 30px. */
.hub-btn--p{height:var(--hub-h-controle-p); padding:4px 8px}
/* Botão que só tem ícone continua precisando de nome acessível: use
   aria-label no HTML. O quadrado abaixo garante a área de 38px mesmo sem texto. */
.hub-btn--icone{width:var(--hub-h-controle); padding:0}
.hub-btn--icone.hub-btn--p{width:var(--hub-h-controle-p)}

/* ---------------------------------------------------------------------------
   12. CAMPO, COM O ANEL NO INVÓLUCRO
   O anel de foco NÃO fica no <input>: fica no invólucro, e o input perde o
   outline dele. É o que permite pôr o ícone dentro da moldura sem o anel
   recortar em volta do texto. Quem trocar isto por outline no input vai ver o
   anel cruzar a lupa.
   --------------------------------------------------------------------------- */
.hub-campo{
  position:relative; display:flex; align-items:center;
  height:var(--hub-h-controle);
  background:var(--background);
  border:1px solid var(--input);
  border-radius:var(--radius);
  transition:border-color .15s ease, box-shadow .15s ease;
}
.hub-campo:hover{border-color:var(--primary)}
/* A borda vira transparente e o anel de 2px assume o lugar dela: assim o
   controle não muda de tamanho ao receber foco (borda + anel somariam 3px e a
   linha inteira saltaria). */
.hub-campo:focus-within{
  border-color:transparent;
  box-shadow:0 0 0 2px var(--ring);
}
.hub-campo input,
.hub-campo select,
.hub-campo textarea{
  width:100%; min-width:0; height:100%;
  padding:8px 16px; border:0; background:transparent;
  font:inherit; font-size:14px; line-height:20px;
  color:var(--foreground);
}
.hub-campo input:focus,
.hub-campo select:focus,
.hub-campo textarea:focus{outline:none}   /* o anel é do invólucro, e ele existe */
.hub-campo input::placeholder{color:var(--muted-foreground)}   /* 5,33:1: placeholder é texto */
/* [MANUAL] 40px de folga à direita quando há ícone. E a lupa fica à DIREITA,
   não à esquerda: é o contrário do que a maioria faz, e é o que o hub deles
   faz. [MEDIDO] em channel.png o campo vai de x=1057 a 1279 com o glifo em
   x≈1258. */
.hub-campo--com-icone input{padding-right:40px}
/* ---------------------------------------------------------------------------
   O <select> DEIXA DE SER O CONTROLE DO SISTEMA OPERACIONAL.
   [MEDIDO 24/08/2026] Os dois `<select>` do hub (o seletor de canal no cabeçalho
   e o motivo do cancelamento) rodavam com `appearance: auto`: seta desenhada
   pelo sistema, tamanho e cor fora do sistema visual da tela, e, o que custa
   caro, desenhada POR CIMA da área de texto. Com 16px de padding à direita e
   nada reservado, o fim da opção passava por baixo da seta.
   Agora a seta é nossa, tem o traço de 1.5px e a cor dos outros ícones, e os
   40px de padding à direita reservam o lugar dela. Contas, para quem for mexer:
   o losango de 8px girado 45 graus ocupa 8 x raiz de 2 = 11,3px, começa a 14px
   da borda e portanto termina a 25px; o texto para aos 40px. Folga medida no
   navegador: 15px, contra o mínimo de 8px que a bancada cobra.
   A seta é pseudo-elemento, e não imagem de fundo, porque assim ela usa a mesma
   variável de cor dos ícones e acompanha o tema escuro sozinha. Um data URI
   traria a cor congelada e exigiria uma segunda cópia da regra dentro do
   `@media (prefers-color-scheme: dark)`, que é uma cópia para divergir.
   O `:has(select)` mantém a seta fora dos campos de texto, que não têm seta. */
.hub-campo select{appearance:none; -webkit-appearance:none; padding-right:40px}
/* [24/09/2026] A LISTA QUE ABRE TEM DE TER A COR DO TEMA, E NAO A DO SISTEMA.
   Visto pelo Albert no tema escuro: a lista do "Escolher um canal" abria com fundo
   BRANCO e a opcao "Numero 1 de 1" em letra quase branca, ilegivel. Com
   `appearance:none` e fundo transparente no <select>, o Chrome no Windows desenha
   a lista suspensa com o fundo padrao do sistema (branco), mas as <option> herdam a
   `color` do tema (clara no escuro). Letra clara em fundo branco. Dar as duas cores
   a <option> de forma explicita resolve nos dois temas, porque os tokens trocam
   juntos: no claro fica fundo claro com letra escura, no escuro o contrario. */
.hub-campo select option{background-color:var(--card); color:var(--card-foreground)}
.hub-campo:has(select)::after{
  content:'';
  position:absolute; right:14px; top:50%;
  width:8px; height:8px; margin-top:-6px;
  border-right:1.5px solid var(--muted-foreground);
  border-bottom:1.5px solid var(--muted-foreground);
  transform:rotate(45deg);
  pointer-events:none;
}
.hub-campo__icone{
  position:absolute; right:12px; top:50%; transform:translateY(-50%);
  width:16px; height:16px; color:var(--muted-foreground);
  pointer-events:none;
}
.hub-campo[data-invalido="true"]{border-color:var(--destructive)}
.hub-campo[data-invalido="true"]:focus-within{box-shadow:0 0 0 2px var(--destructive)}

.hub-rotulo{display:block; font-size:14px; line-height:20px; font-weight:600; margin-bottom:8px}
/* O erro do campo tem ícone além da cor, e o texto diz o que resolver. Ele é
   ligado ao input por aria-describedby no HTML: sem isso, quem usa leitor de
   tela ouve o campo e não ouve o motivo. */
.hub-erro-campo{
  display:flex; align-items:flex-start; gap:6px; margin:8px 0 0;
  font-size:12px; line-height:16px; color:var(--danger-foreground);
}
.hub-erro-campo svg{width:14px; height:14px; flex:0 0 auto; margin-top:1px}

/* ---------------------------------------------------------------------------
   13. ESTADO VAZIO
   Vive dentro da caixa da tabela, no lugar do <tbody>.
   Vazio NÃO é erro: não pede desculpa, não usa vermelho, e o azulejo é o mesmo
   azul apagado do resto. Os textos estão na especificação e no /js/textos.js.
   --------------------------------------------------------------------------- */
.hub-vazio{
  display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:8px; padding:64px 24px;
}
.hub-vazio__azulejo{
  width:40px; height:40px; border-radius:var(--radius-tile);
  display:grid; place-items:center; margin-bottom:8px;
  background:var(--accent); color:var(--accent-foreground);
}
.hub-vazio__azulejo svg{width:20px; height:20px}
.hub-vazio__titulo{font-size:16px; line-height:24px; font-weight:600; color:var(--foreground); margin:0}
.hub-vazio__texto{
  font-size:14px; line-height:20px; color:var(--muted-foreground);
  margin:0; max-width:52ch;      /* linha longa demais quebra a leitura de texto centralizado */
}
.hub-vazio .hub-btn{margin-top:16px}

/* Falha ao carregar é o ÚNICO vazio que vira vermelho, porque é o único em que
   algo deu errado. A cor não vai sozinha: o ícone e o texto dizem o que houve
   e o que fazer. */
.hub-vazio--erro .hub-vazio__azulejo{background:var(--danger-bg); color:var(--danger-foreground)}

/* ---------------------------------------------------------------------------
   14. CARREGANDO, em duas formas, e cada uma tem a vez dela
   --------------------------------------------------------------------------- */

/* ESQUELETO: quando o FORMATO do que vem já é conhecido (tabela, cartão).
   Nunca giro nesse caso: o esqueleto já diz quantas linhas vêm e evita o
   salto de layout. Uma linha de esqueleto por linha real esperada, com <td> de
   64px, senão a tabela pula quando o dado chega.
   O container leva aria-busy="true" e um .sr-only dizendo o que carrega. */
.hub-esqueleto{
  background:var(--secondary);
  border-radius:var(--radius);
  height:20px; width:100%;
  animation:hub-pulso 1.6s ease-in-out infinite;
}
.hub-esqueleto--curto{width:40%}
.hub-esqueleto--pastilha{width:72px; height:20px; border-radius:var(--radius-pill)}
@keyframes hub-pulso{0%,100%{opacity:1} 50%{opacity:.45}}
/* Com movimento reduzido a barra para e fica ligeiramente apagada: sem o
   pulso, um bloco cinza opaco pareceria conteúdo de verdade. */
@media (prefers-reduced-motion: reduce){
  .hub-esqueleto{animation:none; opacity:.7}
}

/* GIRO: quando a espera é de uma AÇÃO que a pessoa disparou.
   Padrão herdado de estilo.css:1024-1029, com 14px em vez de 15px para fechar
   com o ícone de 16 do botão. */
.hub-girando{display:inline-flex; align-items:center; gap:8px}
.hub-girando::before{
  content:""; width:14px; height:14px; border-radius:50%; flex:0 0 auto;
  border:2px solid currentColor; border-top-color:transparent;
  animation:hub-girar .7s linear infinite;
}
@keyframes hub-girar{to{transform:rotate(360deg)}}
/* Aqui o movimento não some, desacelera: o giro é o que informa que a ação
   está em curso, e parar de girar mentiria dizendo que acabou. */
@media (prefers-reduced-motion: reduce){
  .hub-girando::before{animation-duration:2.4s}
}
/* O botão ocupado troca o rótulo para o gerúndio da própria ação e MANTÉM a
   largura, senão a linha inteira se rearranja no clique. Nunca "Aguarde" nem
   "Carregando": "Adicionar canal" vira "Adicionando o canal".
   A largura o CSS não tem como segurar sozinho, porque ele não sabe quanto o
   rótulo antigo media. Este é o contrato com o /js/: antes de trocar o texto,
   medir o botão e gravar a medida no próprio elemento, em linha:
       alvo.style.setProperty('--hub-largura-ocupado', alvo.offsetWidth + 'px')
   e limpar a propriedade quando a ação terminar. Sem a chamada, o valor de
   reserva "auto" mantém o comportamento de hoje e nada quebra. */
.hub-btn.hub-girando{min-width:var(--hub-largura-ocupado, auto)}

/* ---------------------------------------------------------------------------
   15. ERRO E AUSÊNCIA
   --------------------------------------------------------------------------- */

/* Faixa de erro dentro de um cartão ou no topo de um bloco. role="alert" no
   HTML. O texto explica o que deu errado E como resolver, sem pedir desculpa. */
.hub-faixa-erro{
  display:flex; align-items:flex-start; gap:8px;
  padding:16px;
  border-radius:var(--radius-box);
  background:var(--danger-bg);
  color:var(--danger-foreground);
  font-size:14px; line-height:20px;
}
.hub-faixa-erro svg{width:16px; height:16px; flex:0 0 auto; margin-top:2px}
.hub-faixa-erro p{margin:0}
.hub-faixa-erro p + p{margin-top:8px}
.hub-faixa-erro b{font-weight:600}
.hub-faixa-erro .hub-btn{margin-top:8px}

/* As três variantes de faixa informativa reusam a mesma caixa. A de espera
   existe porque "a Meta limitou as consultas agora" não é erro nosso nem
   sucesso: é um terceiro estado, e pintá-lo de vermelho assusta à toa. */
.hub-faixa-ok{background:var(--ok); color:var(--ok-foreground)}
.hub-faixa-espera{background:var(--waiting); color:var(--waiting-foreground)}
.hub-faixa-ok, .hub-faixa-espera{
  display:flex; align-items:flex-start; gap:8px;
  padding:16px; border-radius:var(--radius-box);
  font-size:14px; line-height:20px;
}
.hub-faixa-ok svg, .hub-faixa-espera svg{width:16px; height:16px; flex:0 0 auto; margin-top:2px}
.hub-faixa-ok p, .hub-faixa-espera p{margin:0}

/* EXPLICAR A AUSÊNCIA DE UM CONTROLE.
   Quando um botão ou ícone some, a tela diz por quê. Quase ninguém faz isso, e
   é uma das coisas que mais vale a pena copiar do hub deles (item 18 da lista
   de cópia). Fica logo abaixo do lugar onde o controle estaria, em 12/16.
       "O ícone de editar sumiu porque outro nome já está em análise."
   Não é aviso e não é erro: é cinza secundário, tom de nota de rodapé. */
.hub-ausente{
  display:flex; align-items:flex-start; gap:8px;
  font-size:12px; line-height:16px; color:var(--muted-foreground);
  margin-top:8px;
}
.hub-ausente svg{width:14px; height:14px; flex:0 0 auto; margin-top:1px}

/* Célula sem valor NUNCA é "--". Ela recebe a frase do código de vazio, e a
   frase é texto normal em cinza secundário: itálico e travessão dariam ao
   "não sabemos" a aparência de um dado. */
.hub-sem-valor{color:var(--muted-foreground); font-size:12px; line-height:16px}

/* ---------------------------------------------------------------------------
   16. GRÁFICO
   Duas regras, e a distinção decide se a tela de Insights vende ou não:
     ORDENADA   → --chart-1..5, cinco degraus de UMA cor.
     CATEGÓRICA → --cat-1..4, matizes diferentes. Empilhar categorias em tons do
                  mesmo azul deixa ilegível justamente a tela que mais vende.
   Legenda e rótulo usam o -text da categoria, NUNCA a cor de preenchimento:
   cor de barra é superfície e não passa em contraste como letra.

   LIMITE CONHECIDO, e ele é de contraste, não de gosto: os degraus claros da
   rampa não alcançam 3:1 contra o fundo (--chart-4 dá 2,64:1 e --chart-5 dá
   1,71:1 sobre branco), e duas categóricas têm luminância quase idêntica
   (--cat-2 contra --cat-4 dá 1,00:1, verde e cinza que só se distinguem pelo
   matiz). Por isso duas coisas são OBRIGATÓRIAS em todo gráfico desta folha:
     1. separador de 1px na cor da superfície entre marcas vizinhas (a regra
        .hub-gr-marca abaixo), que é o que torna barras coladas distinguíveis
        sem depender de matiz;
     2. rótulo de valor direto na marca ou legenda com texto. A marca nunca é
        o único portador da informação.
   Gráfico de série ÚNICA usa só --chart-1, 2 ou 3, que passam o 3:1 sozinhos. */
.hub-gr-eixo{font-size:12px; line-height:16px; fill:var(--muted-foreground)}
.hub-gr-grade{stroke:var(--border); stroke-width:1}
.hub-gr-marca{stroke:var(--card); stroke-width:1}   /* o separador obrigatório */
.hub-gr-legenda{display:flex; flex-wrap:wrap; gap:8px 16px; font-size:12px; line-height:16px}
.hub-gr-legenda__item{display:inline-flex; align-items:center; gap:8px}
.hub-gr-legenda__marca{width:8px; height:8px; border-radius:2px; flex:0 0 auto}
/* Sem grade vertical. Sem 3D. Sem degradê dentro da barra. Sem sombra na
   barra. As quatro coisas custam legibilidade e não devolvem nada. */

/* ---------------------------------------------------------------------------
   17. UTILITÁRIOS
   --------------------------------------------------------------------------- */
/* Identificador técnico em monoespaçada e um degrau MENOR. Muda muito a
   leitura de um painel: ID, namespace e chave param de competir com o nome. */
.hub-id{font-family:var(--mono); font-size:12px; line-height:16px; color:var(--muted-foreground)}

/* ===========================================================================
   [HARD] QUEBRA DE VALOR COMPRIDO FORA DE TABELA. A CLASSE ESTAVA MORTA.
   ===========================================================================
   Até 24/08/2026 `.hub-atomico` só existia nesta folha dentro de
   `.hub-tabela .hub-atomico` (bloco 12). FORA de tabela ela não declarava nada:
   `class="hub-atomico"` num telefone, numa lista de sites e no <code> da
   credencial da Cloud API não mudava um pixel. O portão não pegava porque a
   conferência 5 procura o NOME da classe na folha, e o nome existia.

   O QUE ISSO CUSTAVA, MEDIDO na aba Detalhes com uma credencial de 201
   caracteres (tamanho real de token da Meta), clicando em "Mostrar a
   credencial" e contando caractere a caractere quantos ficam dentro da borda
   direita do cartão:
       1600px -> 152 dentro,  49 ESCONDIDOS
        900px -> 120 dentro,  81 ESCONDIDOS
        375px ->  46 dentro, 155 ESCONDIDOS  (o cliente via 23% da credencial)
   O <code> media 1413px dentro de um cartão de 343px, `.hub-cartao` tem
   `overflow:hidden` e `documentElement.scrollWidth == clientWidth` nos três:
   o excedente era CORTADO, não rolável, sem reticência e sem barra. Nada na
   tela dizia que aquilo estava pela metade, e o bloco promete, antes do clique,
   que "a credencial aparece aqui inteira, para você copiar". O caminho de
   reserva da cópia ("o texto acima já está selecionado") apontava para um texto
   155 caracteres fora da tela.

   SÃO DOIS PAPÉIS OPOSTOS, E POR ISSO SÃO DUAS CLASSES:
     .hub-atomico  = NÃO PARTE (telefone, data, identificador curto). Agora ela
                     declara isso fora de tabela também, que é o que o bloco 12
                     sempre disse que ela fazia.
     .hub-quebravel = PARTE EM QUALQUER PONTO (credencial, URL de webhook, site
                     do cliente). Valor sem espaço e sem hífen não quebra com
                     `overflow-wrap:normal`, e o cartão corta.
   Trocar o valor comprido para `.hub-atomico` seria pedir para ele ser cortado.
   =========================================================================== */
.hub-atomico{white-space:nowrap}
.hub-quebravel{white-space:normal; overflow-wrap:anywhere; word-break:break-word}
/* `.hub-id` carrega identificador opaco, e um deles é o endereço de webhook do
   cliente, que aceita 300 caracteres. Sem esta linha ele é cortado pela borda
   do cartão do mesmo jeito que a credencial era. `anywhere` só age quando a
   palavra não cabe, então id curto continua numa linha só. */
.hub-id{overflow-wrap:anywhere}

/* NOTA DE APOIO. Mesmo degrau da `.hub-id` e a MESMA cor, sem a monoespaçada.
   [MEDIDO 23/08/2026] Eles usam mono também, e isso corrige um julgamento que eu
   tinha escrito antes: no Settings deles o id do parceiro (`srMmqpPA`) está em
   mono. O que eles NÃO fazem é pôr data em mono. A nossa `.hub-id` estava
   carregando as duas coisas, e "desde 24/07/2026" e "lido em 23/08/2026, 11:46"
   saíam em Consolas dentro de uma tabela em Inter, cada dígito com a mesma
   largura, parecendo saída de terminal no meio do painel.
   A regra: mono para IDENTIFICADOR OPACO (id da WABA, chave, namespace); esta
   classe para tudo que a pessoa lê como frase. */
.hub-nota{font-size:12px; line-height:16px; color:var(--muted-foreground)}

.hub-num{font-variant-numeric:tabular-nums}

/* Camadas: só a faixa de aviso e o cabeçalho têm z-index, e é o mesmo (50).
   Todo o resto vive em zero. Modal, quando existir, usa <dialog> nativo, que
   sobe sozinho para a camada superior e dispensa disputa de z-index. */
.hub-caixa{
  border:1px solid var(--border); border-radius:var(--radius-card);
  background:var(--card); color:var(--foreground);
  padding:0; box-shadow:var(--shadow);
  max-width:560px; width:calc(100% - 32px);
}
/* O valor aparece duas vezes de propósito, e é a única exceção à regra de não
   ter cor literal fora do :root. Motivo: ::backdrop só passou a herdar variável
   do elemento de origem em 2024 (Chrome 122). Sem o valor de reserva, num
   navegador anterior a var(--overlay) não resolve, o fundo cai para
   transparente e o modal aparece sem véu nenhum, e a caixa flutua sobre a
   página inteira ainda legível, que é pior que feio: é confuso. */
.hub-caixa::backdrop{background:var(--overlay, rgba(4,8,12,.72))}

/* A página termina em branco. Depois do último cartão há centenas de pixels de
   nada: não existe rodapé e não existe faixa de preenchimento. Não inventar um.
   Este respiro é [DERIVADO] da grade e existe para o último cartão não colar
   na borda da janela em telas curtas. */
.hub-fim{height:64px}

/* Espaço entre blocos de 1º nível [MANUAL]: 40px. */
.hub-bloco + .hub-bloco{margin-top:40px}

/* ---------------------------------------------------------------------------
   18. A JORNADA DE CONTA (entrar, começar, ficha, ativar, obrigado)

   [HARD] POR QUE ESTA SEÇÃO EXISTE, e por que ela chegou tão tarde.
   Até 23/08/2026 esta folha vestia só /painel/*. As cinco telas da jornada
   ainda carregavam /estilo.css, que é a folha do SITE: pastilha de campanha,
   título de 57,6px, botão verde de 49px, rodapé institucional de três colunas.
   A bancada dizia "zero divergência" porque media só /painel/* e /descobrir,
   exatamente a parte que já tinha sido construída neste sistema. Instrumento
   escopado no que você construiu não mede o produto: mede você. As cinco rotas
   entraram na lista, e apareceram 51 divergências em 340 medidas.

   DOIS MOLDES, E A DISTINÇÃO É DELES, NÃO NOSSA:
     PORTÃO  um cartão de 400px flutuando numa página vazia, sem cabeçalho e
             sem rodapé. É o 01-login.png e o 05-enviado.png. Serve à tela cuja
             única tarefa é provar quem você é.
     FLUXO   cabeçalho de produto de 64px, barra de passos, conteúdo em 1120px.
             É o add-channel.png. Serve à tela que faz trabalho de produto.

   [HARD] O PRIMÁRIO DA JORNADA É AZUL-AÇO, NÃO VERDE. O #25D366 continua sendo
   a cor de AÇÃO no contexto de conversa. Na jornada de conta o primário é a cor
   da MARCA, como é neles. Duas línguas na mesma jornada é o que faz o produto
   parecer costurado de pedaços.

   E o preenchimento é --primary-strong #47617B, não --primary #5B7A99: branco
   sobre #5B7A99 dá 4,48:1 e reprova o 4,5:1 por 0,02.
   --------------------------------------------------------------------------- */

/* A página da jornada é cinza claro; o cartão e o cabeçalho é que são brancos.
   [ESCOLHA, não cópia] o fundo do login deles é #FFFFFF. O cinza claro é o que
   /descobrir já faz e é a ordem escrita do brief. */
.hub-corpo-jornada{background:var(--secondary)}
/* A jornada nunca tem a faixa de aviso, então o cabeçalho gruda em 0. Sem esta
   linha ele herdaria os 44px da faixa e deixaria uma faixa morta no topo. */
.hub-corpo-jornada .hub-cabecalho{top:0}

/* [HARD] TODO CARTÃO QUE POUSA NESTA PÁGINA USA A ARESTA DA JORNADA.
   O escopo é por CLASSE DE CORPO, e não por classe de cartão, porque
   .hub-cartao mora nos dois lugares: no hub ele pousa em --background #0B1119,
   onde a borda comum já mede 1,63:1 e a aresta se vê; na jornada ele pousa em
   --secondary #1B2735, onde a mesma borda cai para 1,31:1 e o cartão vira uma
   mancha só. É o fundo que muda, não o cartão, então quem decide é o fundo.
   Medido nas cinco telas em 23/08/2026: 1,31:1 -> 2,02:1 contra a página.
   No tema claro --card-edge É --border, então esta regra não muda um pixel. */
.hub-corpo-jornada .hub-cartao,
.hub-corpo-jornada .hub-destaque{border-color:var(--card-edge)}
/* O .hub-portao__cartao recebe a mesma aresta na declaração dele, logo abaixo,
   e NÃO aqui: ele nasce com a forma abreviada `border:1px solid var(--border)`,
   que vem depois nesta folha e apagaria um border-color escrito aqui em cima.
   Abreviada tardia comendo propriedade longa anterior é silenciosa: o navegador
   não avisa, e a tela sai com a borda velha. */

/* 18.1 MOLDE PORTÃO ------------------------------------------------------- */
.hub-portao{
  min-height:100vh;
  display:grid; place-items:center;
  padding:48px 16px;      /* [DERIVADO] da grade de 8: 48px de folga mínima */
}
.hub-portao__cartao{
  width:400px; max-width:100%;   /* 400px [MEDIDO] no 01-login.png */
  background:var(--card); color:var(--card-foreground);
  border:1px solid var(--card-edge);
  border-radius:var(--radius-card);
  /* Eles usam só sombra. Sobre fundo cinza a sombra sozinha não segura a
     aresta, então aqui vão as duas: borda de 1px MAIS a sombra a 5%. */
  box-shadow:var(--shadow);
  padding:40px;                  /* [MEDIDO] 320 = 400 - 80, fecha no pixel */
}
/* [HARD] NO ESCURO ESTE CARTÃO TAMBÉM PERDE A SOMBRA, e o motivo é medida.
   No escuro --shadow vira `0 1px 2px rgba(0,0,0,.4)`. Medido em 23/08/2026 na
   /ficha e na /ativar com prefers-color-scheme: dark: aquela sombra escurece a
   página #1B2735 até cerca de #101720 numa faixa de 2px SÓ EMBAIXO do cartão.
   Ou seja, ela não separa o cartão: ela engorda o rodapé dele e deixa topo e
   laterais sem aresta nenhuma. É exatamente o "sombra forte em cartão de dado"
   que a bancada reprova acima de 5% de opacidade, e o gêmeo desta caixa, o
   .jor-cartao, já tinha perdido a sombra por este mesmo motivo. Quem separa nos
   quatro lados é --card-edge, que no escuro sobe para 2,02:1 contra a página.
   As duas guardas são as mesmas do resto da folha: preferência do sistema e
   classe .escuro no <html>. */
@media (prefers-color-scheme: dark){
  :root:not(.claro) .hub-portao__cartao{box-shadow:none}
}
.escuro .hub-portao__cartao{box-shadow:none}
@media (max-width:479px){ .hub-portao__cartao{padding:24px} }

.hub-portao__marca{display:flex; justify-content:center; margin:0 0 32px}
/* [HARD] A ALTURA PRECISA VIR DAQUI. Até 23/08/2026 a marca destas caixas era
   um par de <img> com width e height no atributo, e o tamanho vinha do HTML.
   Agora é o mesmo SVG do resto do aplicativo, e SVG sem largura declarada e sem
   regra de folha ocupa a linha inteira do contêiner: a marca de 32px viraria uma
   faixa do tamanho do cartão, sem erro nenhum aparecer. Mesmo par de valores do
   .hub-marca da linha 543 e do .jor-marca, de propósito. */
.hub-portao__marca svg{display:block; height:32px; width:auto}
/* Na tela de CONFIRMAÇÃO a marca sai de dentro do cartão e vira assinatura da
   página, e é o que o 05-enviado.png faz. A distinção separa "faça algo" de
   "algo aconteceu", e vale a pena copiar. */
.hub-portao__marca--acima{margin:0 0 24px}

.hub-portao__titulo{
  font-size:24px; line-height:32px; font-weight:400;
  color:var(--foreground); text-align:center; margin:0;
}
.hub-portao__apoio{
  font-size:14px; line-height:20px; color:var(--muted-foreground);
  text-align:center; margin:8px 0 0;
}
.hub-portao__corpo{font-size:14px; line-height:20px; margin:24px 0 0}
.hub-portao__corpo p{margin:0}
.hub-portao__corpo p + p{margin-top:8px}
.hub-portao__acao{margin:24px 0 0}
/* A linha de saída no rodapé do cartão é o lugar exato da linha de conta nova
   que fecha o cartão de login deles. */
.hub-portao__pe{
  margin:24px 0 0; padding-top:24px;
  border-top:1px solid var(--border);
  font-size:14px; line-height:20px; text-align:center;
}

/* 18.2 BARRA DE PASSOS ---------------------------------------------------- */
/* [MEDIDO] no add-channel.png: fileira de blocos finos, o corrente na cor
   primária, os demais em --border.
   [HARD] O NÚMERO DE BLOCOS É O NÚMERO REAL DE PASSOS DA ZERA. Eles têm sete;
   inventar blocos vazios para igualar o desenho é mentir sobre o comprimento
   do caminho, e a pessoa conta os blocos. */
.hub-passos{padding:24px 0}
.hub-passos__conta{
  font-size:12px; line-height:16px; color:var(--muted-foreground); margin:0 0 8px;
}
.hub-passos__fila{display:flex; gap:8px; margin:0; padding:0; list-style:none}
/* O <li> e a coluna; o bloco pintado e um <span> dentro dele. O rotulo do passo
   (.sr-only) fica no <li>, FORA da cor: dentro do bloco corrente ele dava
   2,26:1 de contraste, e recortar texto com clip nao o torna legivel, so o
   esconde. */
.hub-passos__item{flex:1 1 0; display:flex; position:relative}
.hub-passos__bloco{
  flex:1 1 0; height:4px;
  border-radius:var(--radius-pill);
  background:var(--border);
}
.hub-passos__bloco[data-estado="feito"]{background:var(--primary)}
.hub-passos__bloco[data-estado="corrente"]{background:var(--primary-strong)}

/* 18.3 RESUMO EM LINHAS (rótulo à esquerda, valor à direita) --------------- */
/* O par rótulo/valor de .hub-par empilha; aqui o resumo do pedido precisa das
   duas colunas numa linha só, que é como o add-channel.png mostra o plano. */
.hub-linhas{margin:0}
.hub-linha{
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
  padding:12px 0; border-top:1px solid var(--border);
}
.hub-linha:first-child{border-top:0; padding-top:0}
.hub-linha__rotulo{
  font-size:14px; line-height:20px; font-weight:600;
  color:var(--foreground); margin:0;
}
/* O termo do produto no resumo do pedido não pode aparecer nu na hora do
   pagamento: quem chega aqui pelo funil pode nunca ter lido o que ele é. O
   texto sai de perguntas.html, que é a fonte, e não de redação nova. */
.hub-linha__nota{
  display:block; margin-top:2px; max-width:46ch;
  font-size:13px; line-height:18px; font-weight:400;
  color:var(--muted-foreground);
}
.hub-linha__valor{
  font-size:16px; line-height:24px; font-weight:400;
  color:var(--foreground); margin:0; text-align:right;
}
/* O total é peso 600, e NÃO um degrau maior: 24px é o quarto degrau e ele não
   existe nesta folha. */
.hub-linha--total .hub-linha__valor{font-weight:600}

/* 18.4 FORMULÁRIO --------------------------------------------------------- */
.hub-grupo{margin:0 0 24px}
.hub-grupo:last-child{margin-bottom:0}
/* O aceite vem DEPOIS do container dos campos, e o ultimo campo (a senha) e
   :last-child dele: a regra acima zera a margem e o aceite encostava na ajuda
   "Pelo menos 12 caracteres". Medido em 03/09/2026 pelo juri: 2px de folga, e a
   linha da regra da senha lia como parte do bloco de consentimento. */
.hub-grupo--aceite{margin-top:16px}
.hub-grupo--secao{border:0; padding:0; min-width:0}   /* usado no <fieldset> */
.hub-grupo__legenda{
  font-size:14px; line-height:20px; font-weight:600;
  color:var(--foreground); padding:0; margin:0 0 8px;
}
.hub-dica{font-size:12px; line-height:16px; color:var(--muted-foreground); margin:8px 0 0}
/* Erro vazio não pode ocupar espaço: .hub-erro-campo é flex e a margem de 8px
   valeria mesmo sem texto, empurrando o campo seguinte. */
.hub-erro-campo:empty{display:none}

.hub-grade{display:grid; grid-template-columns:1fr 1fr; gap:24px}
@media (max-width:900px){ .hub-grade{grid-template-columns:1fr} }
.hub-grade__largo{grid-column:1 / -1}
/* O ultimo campo da grade perde a margem de baixo por ser :last-child, entao
   quem vem depois dela tem que trazer a propria folga. Sem isto a dica de
   12px do ultimo campo encosta na legenda da pergunta seguinte. */
.hub-grade + .hub-grupo{margin-top:24px}

/* 18.5 CARTÕES DE ESCOLHA ------------------------------------------------- */
/* Molde dos dois cartões de plano do add-channel.png e dos quatro cartões do
   /descobrir. O controle é um <input type="radio"> DE VERDADE dentro do
   <label>: seta do teclado navega, leitor de tela anuncia o estado, o foco
   aparece. Cartão clicável feito de <div> é o erro clássico deste padrão.
   O radio real fica em 1x1, e não em display:none, porque display:none tira o
   elemento do foco e da árvore de acessibilidade. */
.hub-escolhas{display:grid; gap:8px; margin:0}
.hub-escolha{display:block; position:relative; cursor:pointer}
.hub-escolha__radio{
  position:absolute; opacity:0; pointer-events:none;
  width:1px; height:1px; margin:0;
}
.hub-escolha__corpo{
  display:flex; align-items:flex-start; gap:12px;
  padding:16px;
  background:var(--card);
  border:1px solid var(--border);
  border-radius:var(--radius-card);
  transition:border-color .15s ease, background-color .15s ease;
}
.hub-escolha:hover .hub-escolha__corpo{border-color:var(--primary)}
.hub-escolha__radio:focus-visible + .hub-escolha__corpo{
  outline:2px solid var(--ring); outline-offset:2px;
}
.hub-escolha__radio:checked + .hub-escolha__corpo{
  border-color:var(--primary); background:var(--accent);
}
/* A bolinha desenhada não é o controle, é o retrato dele: o radio real está
   escondido, então o estado precisa aparecer em algum lugar. */
.hub-escolha__bolinha{
  width:18px; height:18px; flex:0 0 auto; margin-top:1px;
  border-radius:var(--radius-pill);
  border:2px solid var(--input);
  display:grid; place-items:center;
}
.hub-escolha__radio:checked + .hub-escolha__corpo .hub-escolha__bolinha{
  border-color:var(--primary);
  background:radial-gradient(circle, var(--primary) 0 4px, transparent 4px);
}
.hub-escolha__titulo{font-size:14px; line-height:20px; font-weight:600; color:var(--foreground)}
.hub-escolha__apoio{font-size:14px; line-height:20px; color:var(--muted-foreground); margin:4px 0 0}

/* 18.6 ACEITE (a caixa de marcar) ----------------------------------------- */
.hub-aceite{display:block; position:relative; cursor:pointer}
.hub-aceite__caixa{
  position:absolute; opacity:0; pointer-events:none;
  width:1px; height:1px; margin:0;
}
.hub-aceite__corpo{display:flex; align-items:flex-start; gap:12px; font-size:14px; line-height:20px}
.hub-aceite__marca{
  width:18px; height:18px; flex:0 0 auto; margin-top:1px;
  border-radius:4px; border:2px solid var(--input);
  display:grid; place-items:center; color:transparent;
}
.hub-aceite__marca svg{width:12px; height:12px}
.hub-aceite__caixa:checked + .hub-aceite__corpo .hub-aceite__marca{
  background:var(--primary); border-color:var(--primary); color:var(--primary-foreground);
}
.hub-aceite__caixa:focus-visible + .hub-aceite__corpo .hub-aceite__marca{
  outline:2px solid var(--ring); outline-offset:2px;
}

/* 18.7 O BOTÃO DA JORNADA ------------------------------------------------- */
/* Sólido na cor da MARCA. É o par do botão de 52px do 01-login e do Continue do
   add-channel, com a altura do nosso sistema (38 no portão, 40 dentro de
   faixa). Preenchimento --primary-strong porque branco sobre --primary reprova
   o contraste por 0,02. */
.hub-btn--marca{
  background:var(--primary-strong);
  border-color:var(--primary-strong);
  color:var(--primary-foreground);
  font-weight:600;
}
.hub-btn--marca:hover:not(:disabled){
  background:var(--primary-stronger); border-color:var(--primary-stronger);
}
/* [MEDIDO] o botão que avança o fluxo ocupa a largura inteira: 320 de 320 no
   01-login, largura cheia do diálogo no add-channel. Botão estreito no fim de
   um cartão largo é da landing, não do produto. */
/* [03/09/2026] OS CARTÕES DE PLANO DA TROCA (aba Cobrança). É o par do
   "Select a pricing plan" do 360dialog: cartões lado a lado, um marcado, com o
   preço por número e o que cada um inclui. O <input type=radio> fica dentro do
   <label> e some visualmente (não `display:none`, que tira do teclado); quem
   marca a borda é `data-marcado`, escrito pelo /js/hub-cobranca.js. */
.hub-planos{display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:12px; margin-top:16px}
.hub-plano{
  position:relative; display:block; padding:14px 16px;
  border:1px solid var(--border); border-radius:var(--radius); background:var(--card);
  cursor:pointer; transition:border-color .15s ease, box-shadow .15s ease;
}
.hub-plano:hover{border-color:var(--primary)}
.hub-plano[data-marcado="true"]{border-color:var(--primary); box-shadow:0 0 0 1px var(--primary)}
.hub-plano[aria-disabled="true"]{cursor:not-allowed; opacity:.6}
.hub-plano:focus-within{outline:2px solid var(--ring); outline-offset:2px}
.hub-plano input{position:absolute; width:1px; height:1px; margin:-1px; padding:0; border:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}
.hub-plano__nome{font-size:14px; line-height:20px; font-weight:600; color:var(--foreground); margin:0}
.hub-plano__preco{font-size:20px; line-height:28px; font-weight:600; color:var(--foreground); margin:6px 0 0}
.hub-plano__preco small{font-size:12px; line-height:16px; font-weight:400; color:var(--muted-foreground)}
.hub-plano__nota{font-size:12px; line-height:16px; color:var(--muted-foreground); margin:8px 0 0}
.hub-plano__atual{font-size:12px; line-height:16px; font-weight:600; color:var(--primary-strong); margin:8px 0 0}
.hub-troca__linha{display:flex; flex-wrap:wrap; gap:16px 24px; align-items:flex-end; margin-top:16px}
.hub-troca__linha .hub-grupo{margin:0; min-width:180px}

/* [03/09/2026] O BOTÃO LARGO QUEBRA LINHA. `.hub-btn` traz `white-space:nowrap`
   (certo para botão de largura própria), e o largo herdava: em 390px o rótulo
   "Abrir a minha plataforma e conectar o meu número" saía cortado nas duas
   bordas ("brir a minha plataforma... númer"), sem erro e sem rolagem, porque o
   botão tem overflow escondido. Visto na bancada visual, não pela medida.
   `height:auto` + `min-height` mantém os 40px de uma linha no desktop. */
.hub-btn--largo{
  width:100%; white-space:normal; height:auto; min-height:var(--hub-h-controle);
  padding-top:8px; padding-bottom:8px; line-height:20px; text-align:center;
}
.hub-btn--faixa.hub-btn--largo{min-height:40px}

/* 18.8 TEXTO CORRIDO DA JORNADA ------------------------------------------- */
.hub-nota{font-size:12px; line-height:16px; color:var(--muted-foreground); margin:16px 0 0}
.hub-elo{color:var(--primary-strong); text-decoration:underline; text-underline-offset:2px}
.hub-elo:hover{color:var(--primary-stronger)}
.hub-corpo{font-size:14px; line-height:20px; margin:0}
.hub-corpo + .hub-corpo{margin-top:8px}
.hub-lista{font-size:14px; line-height:20px; margin:16px 0 0; padding-left:20px}
.hub-lista li + li{margin-top:8px}
.hub-cartao__pe{
  margin:24px 0 0; padding-top:24px;
  border-top:1px solid var(--border);
  font-size:14px; line-height:20px;
}
/* O bloco de destaque com ícone à esquerda e um parágrafo alto: o alinhamento
   ao centro do .hub-destaque encostaria o ícone no meio do texto. */
.hub-destaque--topo{align-items:flex-start}
.hub-destaque__icone{width:16px; height:16px; flex:0 0 auto; color:var(--primary); margin-top:4px}

/* 18.9 PONTE PARA O QUE O /js/conectar.js DESENHA -------------------------- */
/* [HARD] O `conectar.js` cria os avisos dele com `className = 'aviso ' + classe`
   e ele NÃO é desta jornada: é compartilhado. Trocar a classe lá dentro sem ser
   dono do arquivo é um pedaço chamando um nome que outro não publica, que é a
   família de defeito que o portao-hub.py existe para pegar. Então a folha
   aprende os quatro nomes dele, escopados no corpo da jornada para não vazar
   sobre o resto do hub. */
.hub-corpo-jornada .aviso{
  display:block;
  padding:16px; border-radius:var(--radius-box);
  font-size:14px; line-height:20px;
  background:var(--muted); color:var(--foreground);
}
.hub-corpo-jornada .aviso p{margin:0}
.hub-corpo-jornada .aviso p + p{margin-top:8px}
.hub-corpo-jornada .aviso strong, .hub-corpo-jornada .aviso b{font-weight:600}
.hub-corpo-jornada .aviso.info{background:var(--accent); color:var(--foreground)}
.hub-corpo-jornada .aviso.ok{background:var(--ok); color:var(--ok-foreground)}
.hub-corpo-jornada .aviso.atencao{background:var(--waiting); color:var(--waiting-foreground)}
.hub-corpo-jornada .aviso.erro{background:var(--danger-bg); color:var(--danger-foreground)}

/* 18.10 A MARCA, EM UMA TINTA SÓ ------------------------------------------ */
/* [HARD] AQUI HAVIA UM MECANISMO DE DUAS IMAGENS, E ELE NÃO FUNCIONAVA.
   Até 23/08/2026 a marca da jornada era um par de <img> (uma azul, uma branca) e
   esta seção escondia a errada com `.hub-marca__escuro{display:none}`. Essa
   regra tem especificidade (0,1,0) e a da linha 543,
   `.hub-marca svg, .hub-marca img{display:block}`, tem (0,2,0): a de baixo
   perdia, e as DUAS versões da marca desenhavam lado a lado no cabeçalho de
   /comecar, /ficha e /ativar, nos três idiomas e nos dois temas. Medido no
   navegador: dois desenhos visíveis dentro do mesmo <a>.

   O conserto não foi empatar a especificidade: foi tirar o mecanismo. O desenho
   passou a ser o mesmo SVG inline das outras telas, com `fill="currentColor"`,
   então ele já acompanha o tema pela cor do texto do cabeçalho, sem segunda
   cópia para esconder, sem duas requisições de PNG e sem regra que possa perder
   de novo. Uma marca que se esconde por CSS é uma marca que aparece duas vezes
   no dia em que o CSS mudar de ordem.

   As classes .hub-marca__claro e .hub-marca__escuro não existem mais em nenhum
   HTML deste projeto (conferido por varredura). */

/* 18.11 REMENDOS DE ESPAÇO DA JORNADA ------------------------------------- */
/* A dica que explica a pergunta cola na legenda, e não no primeiro cartão de
   escolha: sem isto ela ganha 8px em cima e nenhum embaixo, e passa a parecer
   legenda do cartão de baixo. */
.hub-grupo__legenda + .hub-dica{margin:0 0 8px}
/* As faixas informativas com duas frases: a regra de origem zera a margem de
   todo <p> e as duas frases ficavam grudadas numa linha só. */
.hub-faixa-ok p + p,
.hub-faixa-espera p + p{margin-top:8px}
/* Título de bloco dentro de um cartão. Mesmo degrau do título do cartão: não
   existe um degrau intermediário nesta folha. */
.hub-subtitulo{
  font-size:16px; line-height:24px; font-weight:600;
  color:var(--foreground); margin:24px 0 0;
}
/* A armadilha de robô. Fora da tela, e não display:none: campo com display
   none o robô também ignora, e aí a armadilha não arma. Altura de 1px é o que
   diz à bancada que este campo está escondido de propósito. */
.hub-armadilha{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden}
/* So o CAMPO encolhe. O rotulo fica no degrau normal de 14px: encolher a
   letra dele o tiraria da escala tipografica, e a bancada mede tamanho de
   fonte em todo texto visivel, inclusive o que esta fora da tela. */
.hub-armadilha input{width:1px; height:1px; padding:0; border:0}
/* O aviso que aparece DEPOIS de uma escolha. A margem não pode vir de
   `.hub-escolhas + .hub-destaque`: são três avisos irmaos e só um aparece por
   vez, então o seletor de irmao adjacente acertaria sempre o primeiro do HTML e
   nunca o que esta na tela. */
.hub-destaque--apos{margin-top:16px}

/* 18.12 ESPERA E RESPIRO --------------------------------------------------- */
/* A linha de espera do cartão-portao. O `.hub-girando` e inline-flex e não se
   centra sozinho dentro de um bloco; este e o involucro que o centra sem
   mexer no componente do giro. */
.hub-portao__espera{
  display:flex; justify-content:center;
  margin:24px 0 0;
  font-size:14px; line-height:20px; color:var(--muted-foreground);
}
/* Dois degraus de respiro entre blocos irmaos, para quando a margem não cabe
   no componente porque ela depende de quem esta ao lado. */
.hub-espaco{margin-top:16px}
.hub-espaco-g{margin-top:24px}

/* 18.13 O SELETOR DE IDIOMA ------------------------------------------------ */
/* [HARD] E UM <div>, e NÃO um <nav>. A bancada procura aba por `nav a`, e um
   <nav> com três links viraria "três abas" para ela, que então exigiria uma aba
   ativa com peso 600 E linha de 2px embaixo. Seletor de idioma não e fileira de
   abas, e forcar a aparência de aba nele só para calar o instrumento seria
   mentir para a própria ferramenta.
   Ele não existe no hub do 360dialog, e existe aqui porque as telas da jornada
   são servidas em três idiomas: tirar o seletor deixaria quem leu em ingles sem
   caminho de volta. */
.hub-idiomas{
  display:flex; align-items:center; gap:4px; margin-left:auto;
  font-size:12px; line-height:16px;
}
.hub-idiomas a{
  color:var(--muted-foreground); text-decoration:none;
  padding:2px 6px; border-radius:var(--radius);
}
.hub-idiomas a:hover{color:var(--foreground); background:var(--secondary)}
.hub-idiomas a[aria-current]{color:var(--primary-strong); font-weight:600}
/* No cartão-portao ele vira uma linha centrada no rodape do cartão, porque ali
   não existe cabeçalho onde encostar. */
.hub-portao__pe .hub-idiomas{justify-content:center; margin:8px 0 0}

/* As faixas informativas tambem sao usadas como <p>, e ai o <p> traz a margem
   de 1em do navegador. Com o corpo em 14px isso da 14px, que nao esta na grade
   de 8 e foi o unico aviso que sobrou na medida de /comecar. */
p.hub-faixa-ok, p.hub-faixa-espera, p.hub-faixa-erro{margin:0}

/* ---------------------------------------------------------------------------
   LINHA LEGAL. Fora do cartão, embaixo dele, no degrau menor.

   [HARD] POR QUE ELA EXISTE, JÁ QUE O 360DIALOG NÃO TEM.
   Ao adotar o molde "portão" deles, as 15 telas da jornada perderam o rodapé
   institucional inteiro, e DOZE ficaram sem nenhum link para os Termos de Uso e
   a Política de Privacidade. A pessoa abre /ativar ou /obrigado logo depois de
   PAGAR, ou /ficha para digitar o CNPJ, e não tem como abrir nenhum dos dois.

   O login deles também não tem rodapé, e copiar essa ausência seria copiar uma
   decisão tomada noutro contexto: eles não vendem sob o Código de Defesa do
   Consumidor brasileiro, e a tela deles carrega o Cookiebot, que injeta os
   avisos por cima.

   O que NÃO volta é o rodapé de 250px com três colunas, assinatura de marca e
   aviso de marcas da Meta: aquilo é do site que VENDE. Estas telas são do app
   que ENTREGA, e uma linha basta.
   --------------------------------------------------------------------------- */
.jor-legal{
  margin:24px auto 0;
  /* [MEDIDO] 400px é a largura do cartão do molde PORTÃO, e alinhar por ela
     centraliza a linha embaixo do cartão. No molde de FLUXO o conteúdo tem a
     largura do container (--hub-largura, 1120px), e a linha acompanha: a regra
     abaixo sobe o teto quando a página é de fluxo. Sem isso, a linha ficava
     centrada em 400px embaixo de um conteúdo de 1120 e parecia deslocada. */
  max-width:400px;
  padding-inline:16px;
  text-align:center;
  font-size:12px; line-height:16px;
  color:var(--muted-foreground);
}
.jor-legal a{color:inherit; text-decoration:underline; text-underline-offset:2px}
.jor-legal a:hover{color:var(--foreground)}
.jor-legal a:focus-visible{outline:2px solid var(--ring); outline-offset:2px; border-radius:2px}

/* No molde de fluxo (as telas que NÃO carregam jornada.css, e que usam o
   cabeçalho de 64px com o container de 1120) a linha acompanha o conteúdo. */
.hub-corpo-jornada .jor-legal,
body:has(.hub-cabecalho) .jor-legal{max-width:var(--hub-largura)}
