/* ===========================================================================
   naveg-ui.css — Naveg UI: o design system do painel.
   ---------------------------------------------------------------------------
   POR QUE EXISTE, medido antes de escrever uma linha:

     53 receitas diferentes de `.btn{` espalhadas pelas paginas
     10 raios de borda em uso simultaneo (8px x191, 6px x105, 10px x102...)
     12 paginas declarando a propria paleta :root

   Cada pagina reinventa o proprio botao. O que um framework entregaria
   (tokens + componentes prontos + um catalogo para olhar) e o que esta
   camada entrega em vanilla — sem build, compativel com o deploy por copia
   de arquivo.

   O CATALOGO VIVO e o styleguide.html: cada componente, em cada estado,
   escuro e claro lado a lado. Ele e a referencia — na duvida, olhe la,
   nao invente por pagina.

   COMO ADOTAR NUMA PAGINA: <link> por ULTIMO, depois das camadas
   corretivas. As classes sao aditivas (prefixo .ui-, sem colisao com nada
   existente) — ligar o arquivo nao muda nenhum pixel ate alguem usar uma
   classe.

   ===========================================================================
   COMO MUDAR ALGUMA COISA — a esquematica, em uma frase:

       mexa no token AQUI, e as 63 paginas seguem. Nao edite pagina.

   O fluxo tem DUAS pernas, e a segunda foi aprendida na marra:

     1. `:root, .ui-dark { --ui-X: <valor> }`   <- a fonte, literal
     2. `:root:root { --<token-da-pagina>: var(--ui-X) }`  <- a PONTE

   A ponte existe porque as paginas nao usam `--ui-*`: elas usam `--bg`,
   `--border`, `--text`, `--accent`, escritos antes deste arquivo existir e
   presentes em milhares de regras. Sem a ponte, mudar um token do sistema
   arruma os componentes `.ui-*` e deixa o resto de cada pagina na cor antiga.

   O SENTIDO IMPORTA. Na primeira versao a seta apontava para o outro lado —
   `--ui-acento: var(--accent, #2dd4bf)`, o sistema LENDO a pagina. Parecia
   flexivel e era o contrario: cinco telas declaravam outro `--accent` e
   rodavam um Naveg UI de outra cor, sem nada no console. Com os neutros era
   pior — 10 fundos, 8 bordas, 7 cores de texto entre as paginas. Hoje os 19
   tokens sao LITERAIS e a pagina e que aponta para eles.

   COMO CONFERIR QUE PEGOU (o teste que encontrou o furo): troque um token
   por uma cor absurda, abra 3 ou 4 paginas e meca o `body`. Se o guia mudar
   e as paginas nao, falta uma entrada na ponte. Foi assim que se descobriu
   que `--bg` tinha ficado de fora e 49 paginas ignoravam o sistema.

   O QUE NAO SEGUE, de proposito:
     - `styleguide.html` tem cromo proprio: hospeda os DOIS temas lado a
       lado, entao nao pode vestir nenhum dos dois;
     - gradiente com parada opaca cravada cobre o token por baixo. Tres
       paginas faziam isso (`comando`, `mapa`, `wall`); a base delas agora
       deriva de `--ui-fundo`. Se criar gradiente de fundo, use o token como
       parada — senao a pagina sai do sistema em silencio.

   TEMAS: `.ui-dark` / `.ui-light` escopam um CONTAINER — e o que permite o
   styleguide mostrar os dois temas lado a lado na mesma tela.
   ===========================================================================

   O que esta camada NAO faz (do `donts` do .impeccable/design.json):
   sem emoji, sem glassmorphism, sem texto-gradiente, accent em ate 10%
   da tela. "Hightech" aqui e feito com luz contida, tipografia tabular e
   consistencia — nao com efeito.
   =========================================================================== */

/* ============================================================== estrutura ==
   Tokens que nao mudam com o tema. TRES raios, e nao dez: controle,
   cartao, pilula. Quem precisar de um quarto esta desenhando fora do
   sistema.                                                                  */
:root {
  --ui-r-ctl: 8px;         /* botoes, campos, chips retangulares */
  --ui-r-card: 12px;       /* cartoes, modais, paineis            */
  --ui-r-pill: 999px;      /* chips, contadores, switch           */

  --ui-s1: 4px;  --ui-s2: 8px;  --ui-s3: 12px;
  --ui-s4: 16px; --ui-s5: 24px; --ui-s6: 32px;

  --ui-ctrl-h: 32px;       /* altura padrao de controle (naveg-padrao ja usa) */
  --ui-ctrl-h-peq: 26px;

  --ui-fonte: 'Inter', 'Segoe UI', system-ui, sans-serif;
  --ui-mono: 'JetBrains Mono', Consolas, monospace;

  --ui-t-toque: 110ms;     /* mesmos tempos do naveg-vida.css */
  --ui-t-estado: 200ms;
  --ui-curva: cubic-bezier(.4, 0, .2, 1);

  /* sombras em tres niveis: repouso, cartao, flutuante */
  --ui-sombra-1: 0 1px 3px rgba(0, 0, 0, .22);
  --ui-sombra-2: 0 2px 10px rgba(0, 0, 0, .28);   /* a dos cartoes aprovados */
  --ui-sombra-3: 0 18px 50px rgba(0, 0, 0, .5);

  --ui-anel-foco: 0 0 0 3px rgba(45, 212, 191, .18);
  --ui-brilho-acao: 0 0 16px rgba(45, 212, 191, .22);
}

/* ================================================================= temas ==
   Escuro e o padrao (:root) e tambem um escopo (.ui-dark). O claro entra
   pelo atributo da pagina OU pelo escopo. A indirecao var(--bg, ...) faz a
   camada herdar a paleta que a pagina ja declara; os literais sao a escada
   estabelecida no naveg-vida.css.                                           */
:root, .ui-dark {
  --ui-fundo:       #182130;
  /* DECISAO DO DONO (styleguide, 18/08): o cartao estava mais escuro que o
     painel (#1f2937 < #26313f) e lia como buraco. Passa a ser a superficie
     MAIS ALTA da escada — fundo < painel < eleva < cartao — um degrau acima
     do hover, no mesmo matiz slate.
     SEM a indirecao var(--bg-card): a pagina/vida ainda declara #1f2937, e
     ler a variavel reintroduziria em silencio o tom escuro rejeitado.      */
  --ui-cartao:      #344256;
  /* REGRA QUE VEM JUNTO, medida: sobre o cartao (a superficie mais clara),
     o --ui-texto-3 cai a 3,64:1. Rotulo apoiado em cartao usa --ui-texto-2
     (5,0+). O texto corrente segue em --ui-texto: 8,73:1 ali.              */
  --ui-painel:      #26313f;
  --ui-eleva:       #2c3849;
  --ui-linha:       #2c4260;
  --ui-linha-forte: #3b5578;

  --ui-texto:   #e7eef7;
  --ui-texto-2: #93a4bb;
  /* MEDIDO: #7e8ea6 dava 3,96:1 sobre o painel — abaixo do piso de 4,5
     para o micro-rotulo de 10px. Um degrau mais claro resolve sem tirar
     o carater de texto de apoio.                                        */
  --ui-texto-3: #8b9cb3;
  --ui-titulo:  #f1f5f9;

  /* LITERAL, nao `var(--accent, ...)`. Ceder ao token da pagina fazia o
     sistema inteiro trocar de cor onde a pagina declarava outro accent:
     aoc/aoc3d ciano, comando e login azul, relatorio azul-petroleo. Cinco
     paginas rodando um Naveg UI de outra cor, sem nada no console.       */
  --ui-acento:       #2dd4bf;
  /* TEXTO x PREENCHIMENTO sao papeis diferentes, e no claro divergem.
     `--ui-acento` e `--ui-alerta` respondem pela IDENTIDADE (o preenchimento,
     o pino, o filete). Como TEXTO sobre branco, o verde #10b981 da 2,54:1 e
     o ambar #f59e0b da 2,15:1 — ilegiveis. Os tons abaixo sao da MESMA
     familia, so mais fundos, e e neles que o texto se apoia.
     No escuro nao ha divergencia: a cor de identidade ja e legivel la.    */
  --ui-acento-txt:   #2dd4bf;
  --ui-alerta-txt:   #fbbf24;
  --ui-perigo-txt:   #f87171;
  --ui-acento-2:     #14b8a6;
  --ui-acento-tinta: #052723;

  --ui-ok: #34d399; --ui-alerta: #fbbf24; --ui-perigo: #f87171; --ui-info: #38bdf8;
  /* Tinta PARA quando a semantica vira preenchimento, e nao texto. Existe
     pelo mesmo motivo de `--ui-acento-tinta`: `--ui-perigo` significa
     claridades OPOSTAS nos dois temas (vermelho claro aqui, escuro la), e
     um `color: #fff` fixo sobre ele da 2,77:1 no escuro. MEDIDO.        */
  --ui-perigo-tinta: #2b0708;   /* 6,68:1 sobre #f87171 */
  --ui-alerta-tinta: #422006;   /* 8,73:1 sobre #fbbf24 */
}

:root[data-theme="light"], .ui-light {
  --ui-fundo:       #eef2f7;
  --ui-cartao:      #ffffff;
  --ui-painel:      #ffffff;
  --ui-eleva:       #f1f5f9;
  --ui-linha:       #e2e8f0;
  --ui-linha-forte: #cbd5e1;

  --ui-texto:   #0f172a;
  --ui-texto-2: #475569;
  --ui-texto-3: #5c6b80;   /* 4,8:1 sobre o fundo claro */
  --ui-titulo:  #0f172a;

  /* MEDIDO: branco sobre #0d9488 da 3,74:1 — abaixo do piso para o rotulo
     de 12,5px do botao. O teal-700 (#0f766e) da 5,47:1 e mantem o matiz.   */
  --ui-acento:       #10b981;   /* rgb(16,185,129) — identidade */
  --ui-acento-txt:   #065f46;   /* emerald-800: 7,68:1 no branco, 6,8 na propria tinta */
  --ui-alerta-txt:   #92400e;   /* amber-800:   7,09:1 no branco, 6,4 na propria tinta */
  --ui-perigo-txt:   #991b1b;   /* red-800:     6,64:1 na propria tinta */
  --ui-acento-2:     #059669;   /* emerald-600: tinta escura da 4,74:1 */
  --ui-acento-tinta: #0f172a;   /* 7,04:1 sobre #10b981 — o branco dava 2,54 */

  /* Semanticas escurecidas de proposito: os tons do escuro caem para
     2-3:1 sobre branco e viram decoracao ilegivel.                          */
  /* MEDIDO por composicao (canvas): sobre o tom de 13% no tema claro,
     #059669 dava 2,87:1, #b45309 3,75 e #0369a1 4,39. Um degrau mais
     escuro em cada um poe todos acima de 4,5 sem perder a leitura de cor. */
  --ui-ok: #065f46; --ui-alerta: #f59e0b; --ui-perigo: #b91c1c; --ui-info: #075985;   /* alerta rgb(245,158,11) */
  --ui-perigo-tinta: #ffffff;   /* 6,47:1 sobre #b91c1c */
  --ui-alerta-tinta: #0f172a;   /* 8,31:1 sobre #f59e0b — o branco dava 2,15 */

  --ui-sombra-1: 0 1px 3px rgba(15, 23, 42, .1);
  --ui-sombra-2: 0 2px 10px rgba(15, 23, 42, .12);
  --ui-sombra-3: 0 18px 40px rgba(15, 23, 42, .16);
  --ui-anel-foco: 0 0 0 3px rgba(13, 148, 136, .2);
  --ui-brilho-acao: 0 0 14px rgba(13, 148, 136, .25);
}

/* Um escopo pinta o proprio chao — sem isso o lado claro do styleguide
   ficaria com componentes claros sobre pagina escura.                       */
.ui-dark, .ui-light {
  background: var(--ui-fundo);
  color: var(--ui-texto);
  font-family: var(--ui-fonte);
}

/* ============================================================ utilitarios == */
/* O micro-rotulo: a marca tipografica do painel (cabecalho de cartao, th de
   tabela, rotulo de KPI e de campo). Um so lugar decide como ele e.         */
.ui-rotulo {
  font: 700 10px/1.4 var(--ui-fonte);
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--ui-texto-3);
}
.ui-mono { font-family: var(--ui-mono); font-variant-numeric: tabular-nums; }
.ui-kbd {
  font: 600 11px/1 var(--ui-mono);
  padding: 3px 6px;
  border: 1px solid var(--ui-linha-forte);
  border-bottom-width: 2px;
  border-radius: 5px;
  background: var(--ui-fundo);
  color: var(--ui-texto-2);
}

/* ================================================================= botao ==
   QUATRO variantes, e acabou: padrao, primario, fantasma, perigo.
   O primario e o UNICO com o accent preenchido — e a regra dos 10% de
   accent na tela que mantem o "um ponto que puxa o olho".                   */
.ui-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ui-s2);
  min-height: var(--ui-ctrl-h);
  padding: 0 14px;
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--ui-linha-forte);
  background: var(--ui-painel);
  color: var(--ui-texto-2);
  font: 600 12.5px/1 var(--ui-fonte);
  letter-spacing: .2px;
  cursor: pointer;
  user-select: none;
  transition: background var(--ui-t-estado) var(--ui-curva),
              border-color var(--ui-t-estado) var(--ui-curva),
              color var(--ui-t-estado) var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva),
              transform var(--ui-t-toque) var(--ui-curva);
}
.ui-btn:hover:not(:disabled) { background: var(--ui-eleva); color: var(--ui-titulo); border-color: var(--ui-acento-txt); }
.ui-btn:active:not(:disabled) { transform: scale(.97); }
.ui-btn:focus-visible { outline: 2px solid var(--ui-acento); outline-offset: 2px; box-shadow: var(--ui-anel-foco); }
.ui-btn:disabled { opacity: .45; cursor: not-allowed; }

.ui-btn--primario {
  background: linear-gradient(180deg, var(--ui-acento), var(--ui-acento-2));
  border-color: transparent;
  color: var(--ui-acento-tinta);
}
.ui-btn--primario:hover:not(:disabled) {
  background: linear-gradient(180deg, var(--ui-acento), var(--ui-acento-2));
  color: var(--ui-acento-tinta);
  border-color: transparent;
  box-shadow: var(--ui-brilho-acao);
}

.ui-btn--fantasma { background: transparent; border-color: var(--ui-linha); }

.ui-btn--perigo { color: var(--ui-perigo-txt); border-color: color-mix(in srgb, var(--ui-perigo) 45%, transparent); background: transparent; }
.ui-btn--perigo:hover:not(:disabled) {
  background: color-mix(in srgb, var(--ui-perigo) 14%, transparent);
  color: var(--ui-perigo-txt);
  border-color: var(--ui-perigo-txt);
}

.ui-btn--peq { min-height: var(--ui-ctrl-h-peq); padding: 0 10px; font-size: 11.5px; }
.ui-btn--icone { width: var(--ui-ctrl-h); padding: 0; }
.ui-btn--icone svg { width: 15px; height: 15px; }

/* ================================================================ cartao ==
   A mesma linguagem aprovada nas telas de conversa: fundo proprio, raio de
   12, SOMBRA e nao borda — duas linguagens de separacao ao mesmo tempo
   (linha E sombra) leem como falha de alinhamento.                          */
.ui-card {
  background: var(--ui-painel);
  border-radius: var(--ui-r-card);
  box-shadow: var(--ui-sombra-2);
}
.ui-card__cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ui-s3);
  padding: var(--ui-s3) var(--ui-s4);
  border-bottom: 1px solid var(--ui-linha);
}
.ui-card__corpo { padding: var(--ui-s4); }
.ui-card__rodape {
  display: flex;
  justify-content: flex-end;
  gap: var(--ui-s2);
  padding: var(--ui-s3) var(--ui-s4);
  border-top: 1px solid var(--ui-linha);
}

/* =================================================================== KPI ==
   Numero em mono tabular — numa sala de controle o valor E a interface.
   A cor entra por modificador e pinta numero e filete, nunca o fundo.       */
.ui-kpi {
  background: var(--ui-painel);
  border-radius: var(--ui-r-card);
  box-shadow: var(--ui-sombra-2);
  padding: var(--ui-s4);
  position: relative;
  overflow: hidden;
}
.ui-kpi__n {
  font: 800 34px/1 var(--ui-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ui-titulo);
}
.ui-kpi__l { margin-top: var(--ui-s2); }
.ui-kpi::after {           /* o filete de estado, embaixo */
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 3px;
  background: currentColor;
  opacity: 0;
}
.ui-kpi--ok     { color: var(--ui-ok); }
.ui-kpi--alerta { color: var(--ui-alerta-txt); }
.ui-kpi--perigo { color: var(--ui-perigo-txt); }
.ui-kpi--info   { color: var(--ui-info); }
.ui-kpi--ok .ui-kpi__n, .ui-kpi--alerta .ui-kpi__n,
.ui-kpi--perigo .ui-kpi__n, .ui-kpi--info .ui-kpi__n { color: currentColor; }
.ui-kpi--ok::after, .ui-kpi--alerta::after,
.ui-kpi--perigo::after, .ui-kpi--info::after { opacity: .6; }

/* ================================================================ campos ==
   O campo fica UM degrau abaixo da superficie (fundo da pagina) — decisao
   ja validada no dialogo do vida: campo no mesmo tom do painel some.

   ARMADILHA DE WINDOWS (do CLAUDE.md): <select> com fundo rgba faz o
   Chrome/Edge renderizar o menu nativo branco ignorando o tema. O fundo do
   select PRECISA resolver para cor solida — e a indirecao aqui resolve para
   os hex da paleta, nunca rgba.                                             */
.ui-campo { display: flex; flex-direction: column; gap: 6px; }
.ui-campo > .ui-rotulo { color: var(--ui-texto-2); }
.ui-campo__dica { font: 400 11.5px/1.4 var(--ui-fonte); color: var(--ui-texto-3); }

.ui-input, .ui-select, .ui-area {
  min-height: var(--ui-ctrl-h);
  padding: 0 11px;
  border: 1px solid var(--ui-linha);
  border-radius: var(--ui-r-ctl);
  background: var(--ui-fundo);
  color: var(--ui-texto);
  font: 400 13px/1.4 var(--ui-fonte);
  outline: none;
  transition: border-color var(--ui-t-estado) var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva);
}
.ui-area { padding: 9px 11px; min-height: 72px; resize: vertical; }
.ui-input:focus, .ui-select:focus, .ui-area:focus {
  border-color: var(--ui-acento-txt);
  box-shadow: var(--ui-anel-foco);
}
.ui-input::placeholder, .ui-area::placeholder { color: var(--ui-texto-3); }
.ui-input:disabled, .ui-select:disabled { opacity: .5; cursor: not-allowed; }
.ui-select { appearance: none; padding-right: 30px;
  background-image: linear-gradient(45deg, transparent 50%, var(--ui-texto-3) 50%),
                    linear-gradient(135deg, var(--ui-texto-3) 50%, transparent 50%);
  background-position: right 14px top 14px, right 9px top 14px;
  background-size: 5px 5px;
  background-repeat: no-repeat; }

/* O switch: estado binario com corpo — checkbox estilizado, sem JS.        */
.ui-troca {
  appearance: none;
  width: 38px; height: 20px;
  border-radius: var(--ui-r-pill);
  border: 1px solid var(--ui-linha-forte);
  background: var(--ui-fundo);
  position: relative;
  cursor: pointer;
  transition: background var(--ui-t-estado) var(--ui-curva),
              border-color var(--ui-t-estado) var(--ui-curva);
}
.ui-troca::before {
  content: "";
  position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--ui-texto-3);
  transition: transform var(--ui-t-estado) var(--ui-curva),
              background var(--ui-t-estado) var(--ui-curva);
}
.ui-troca:checked { background: var(--ui-acento); border-color: var(--ui-acento-txt); }
.ui-troca:checked::before { transform: translateX(18px); background: var(--ui-acento-tinta); }
.ui-troca:focus-visible { outline: 2px solid var(--ui-acento); outline-offset: 2px; }

/* ================================================================= chips ==
   Estado como cor + texto, nunca cor sozinha. O tom vem da semantica do
   tema — no claro as cores ja nascem escurecidas para passar em contraste. */
.ui-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
  padding: 0 10px;
  border-radius: var(--ui-r-pill);
  font: 700 11.5px/1 var(--ui-fonte);
  color: var(--ui-texto-2);
  background: color-mix(in srgb, var(--ui-texto-2) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--ui-texto-2) 25%, transparent);
}
.ui-chip::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.ui-chip--ok     { color: var(--ui-ok);     background: color-mix(in srgb, var(--ui-ok) 13%, transparent);     border-color: color-mix(in srgb, var(--ui-ok) 30%, transparent); }
.ui-chip--alerta { color: var(--ui-alerta-txt); background: color-mix(in srgb, var(--ui-alerta) 13%, transparent); border-color: color-mix(in srgb, var(--ui-alerta) 30%, transparent); }
.ui-chip--perigo { color: var(--ui-perigo-txt); background: color-mix(in srgb, var(--ui-perigo) 13%, transparent); border-color: color-mix(in srgb, var(--ui-perigo) 30%, transparent); }
.ui-chip--info   { color: var(--ui-info);   background: color-mix(in srgb, var(--ui-info) 13%, transparent);   border-color: color-mix(in srgb, var(--ui-info) 30%, transparent); }
.ui-chip--acento { color: var(--ui-acento-txt); background: color-mix(in srgb, var(--ui-acento) 13%, transparent); border-color: color-mix(in srgb, var(--ui-acento) 30%, transparent); }
.ui-chip--sem-ponto::before { display: none; }

/* Contador numerico (o "300 alertas"): mono, peso maximo.                  */
.ui-conta {
  display: inline-flex;
  align-items: center;
  min-height: 20px;
  padding: 0 8px;
  border-radius: var(--ui-r-pill);
  font: 800 12px/1 var(--ui-mono);
  background: var(--ui-eleva);
  color: var(--ui-titulo);
}
.ui-conta--perigo { background: color-mix(in srgb, var(--ui-perigo) 18%, transparent); color: var(--ui-perigo-txt); }

/* ================================================================== abas ==
   A pilula aprovada nas telas de conversa: container com raio e RECORTE
   (sem o overflow o efeito de clique cobre o canto — medido), abas em
   partes iguais, ativa marcada por cor + sublinhado (dois canais).          */
.ui-abas {
  display: flex;
  background: var(--ui-painel);
  border-radius: calc(var(--ui-r-card) - 2px);
  box-shadow: var(--ui-sombra-1);
  overflow: hidden;
  min-height: var(--ui-ctrl-h);
}
.ui-aba {
  flex: 1 1 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 0 12px;
  border: 0;
  background: none;
  color: var(--ui-texto-2);
  font: 600 12.5px/1 var(--ui-fonte);
  cursor: pointer;
  box-shadow: inset 0 -2px 0 transparent;
  transition: color var(--ui-t-estado) var(--ui-curva),
              background var(--ui-t-estado) var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva);
}
.ui-aba:hover { color: var(--ui-titulo); background: var(--ui-eleva); }
.ui-aba.ativa { color: var(--ui-acento-txt); box-shadow: inset 0 -2px 0 var(--ui-acento); }
.ui-aba:focus-visible { outline: 2px solid var(--ui-acento); outline-offset: -2px; }

/* ================================================================ tabela ==
   45 paginas tem tabela — e o componente mais usado do painel. Cabecalho em
   micro-rotulo, so linhas horizontais, numero em mono alinhado a direita
   (classe .num na celula).                                                  */
.ui-tabela { width: 100%; border-collapse: collapse; font: 400 12.5px/1.45 var(--ui-fonte); }
.ui-tabela th {
  text-align: left;
  padding: 8px 12px;
  font: 700 10px/1.4 var(--ui-fonte);
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--ui-texto-3);
  border-bottom: 1px solid var(--ui-linha-forte);
  white-space: nowrap;
}
.ui-tabela td {
  padding: 9px 12px;
  color: var(--ui-texto);
  border-bottom: 1px solid color-mix(in srgb, var(--ui-linha) 55%, transparent);
}
.ui-tabela tbody tr { transition: background var(--ui-t-estado) var(--ui-curva); }
.ui-tabela tbody tr:hover { background: color-mix(in srgb, var(--ui-eleva) 55%, transparent); }
.ui-tabela .num { font-family: var(--ui-mono); font-variant-numeric: tabular-nums; text-align: right; }

/* ================================================================= lista ==
   O item de lista das conversas, generalizado: marca de ativo na borda de
   ataque (a mesma do vida), titulo + apoio, meta a direita.                 */
.ui-lista { display: flex; flex-direction: column; gap: var(--ui-s1); }
.ui-item {
  display: flex;
  align-items: center;
  gap: var(--ui-s3);
  padding: 10px 12px;
  border-radius: calc(var(--ui-r-card) - 4px);
  cursor: pointer;
  position: relative;
  transition: background var(--ui-t-estado) var(--ui-curva);
}
.ui-item:hover { background: var(--ui-eleva); }
.ui-item::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 2px; height: 0;
  background: var(--ui-acento);
  border-radius: 0 2px 2px 0;
  transform: translateY(-50%);
  transition: height var(--ui-t-estado) var(--ui-curva);
}
.ui-item.ativo { background: color-mix(in srgb, var(--ui-acento) 10%, transparent); }
.ui-item.ativo::before { height: 100%; }
.ui-item__corpo { flex: 1; min-width: 0; }
.ui-item__titulo { font: 700 13px/1.3 var(--ui-fonte); color: var(--ui-titulo);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-item__apoio { font: 400 11.5px/1.4 var(--ui-fonte); color: var(--ui-texto-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-item__meta { font: 600 11px/1 var(--ui-mono); color: var(--ui-texto-3); flex: none; }

/* ================================================================= modal ==
   Mesma anatomia do dialogo aprovado no vida (nv-dlg): veu com desfoque
   leve, painel flutuante, cabecalho em micro-rotulo.                        */
.ui-veu {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(3, 7, 18, .62);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
}
.ui-veu.aberto { display: flex; }
.ui-modal {
  width: min(440px, 100%);
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--ui-painel);
  color: var(--ui-texto);
  border-radius: var(--ui-r-card);
  box-shadow: var(--ui-sombra-3);
}
.ui-modal__cab {
  padding: 11px 14px;
  border-bottom: 1px solid var(--ui-linha);
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.ui-modal__corpo { padding: var(--ui-s4); overflow-y: auto;
  font: 400 13.5px/1.5 var(--ui-fonte); }
.ui-modal__rodape {
  display: flex;
  justify-content: flex-end;
  gap: var(--ui-s2);
  padding: var(--ui-s3) 14px;
  border-top: 1px solid var(--ui-linha);
}

/* ================================================================= toast ==
   Aviso transitorio, embaixo no centro. A pagina controla o texto e a
   classe .no-ar; o componente cuida do resto.                               */
.ui-toast {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translate(-50%, 8px);
  z-index: 2100;
  padding: 10px 18px;
  border-radius: 10px;
  background: var(--ui-painel);
  color: var(--ui-texto);
  border: 1px solid var(--ui-acento);
  box-shadow: var(--ui-sombra-3);
  font: 600 13px/1.4 var(--ui-fonte);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ui-t-estado) var(--ui-curva),
              transform var(--ui-t-estado) var(--ui-curva);
}
.ui-toast.no-ar { opacity: 1; transform: translate(-50%, 0); }

/* ========================================================== estado vazio ==
   47 paginas tem estado vazio. Ele diz o que NAO ha e o que fazer — nunca
   so um espaco em branco.                                                   */
.ui-vazio {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ui-s3);
  padding: var(--ui-s6) var(--ui-s4);
  text-align: center;
  color: var(--ui-texto-3);
}
.ui-vazio svg { width: 44px; height: 44px; opacity: .55; }
.ui-vazio__titulo { font: 700 14px/1.4 var(--ui-fonte); color: var(--ui-texto-2); }
.ui-vazio__apoio { font: 400 12.5px/1.5 var(--ui-fonte); max-width: 340px; }

/* ============================================================== skeleton ==
   Ossos de carregamento. O brilho anda; quem pediu menos movimento recebe
   o osso parado — a informacao ("carregando") nao mora na animacao.         */
.ui-osso {
  border-radius: var(--ui-r-ctl);
  background: linear-gradient(90deg,
    var(--ui-eleva) 25%,
    color-mix(in srgb, var(--ui-eleva) 55%, var(--ui-painel)) 42%,
    var(--ui-eleva) 60%);
  background-size: 220% 100%;
  animation: ui-osso 1.3s linear infinite;
  min-height: 12px;
}
@keyframes ui-osso { from { background-position: 130% 0; } to { background-position: -90% 0; } }

/* =============================================================== tooltip ==
   Por atributo (data-dica), sem JS. Aparece acima, centrado.                */
[data-dica] { position: relative; }
[data-dica]::after {
  content: attr(data-dica);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 7px);
  transform: translateX(-50%) translateY(2px);
  padding: 5px 9px;
  border-radius: 7px;
  background: #0b1220;
  color: #e7eef7;
  font: 600 11.5px/1.3 var(--ui-fonte);
  white-space: nowrap;
  box-shadow: var(--ui-sombra-1);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ui-t-estado) var(--ui-curva),
              transform var(--ui-t-estado) var(--ui-curva);
  z-index: 500;
}
[data-dica]:hover::after, [data-dica]:focus-visible::after {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ==================================================== movimento reduzido == */
@media (prefers-reduced-motion: reduce) {
  .ui-osso { animation: none; }
  .ui-btn, .ui-aba, .ui-item, .ui-toast, .ui-troca, .ui-troca::before,
  .ui-input, .ui-select, .ui-area, [data-dica]::after { transition: none; }
}

/* ===========================================================================
   ADOCAO — o sistema alcanca as paginas que nao foram convertidas
   ---------------------------------------------------------------------------
   POR QUE ESTE BLOCO EXISTE, e ele e a correcao de um erro meu:

   Ligar o naveg-ui.css nas 63 paginas nao mudou nada, e nao podia mesmo: as
   classes `.ui-*` so valem no markup que as usa, e o markup delas usa os
   nomes de sempre. Trocar os VALORES de origem (literal -> token) tambem nao
   bastou: um botao continuou com 27px de altura e peso 500 enquanto o sistema
   diz 32px e 600. Medido lado a lado:

     styleguide  32px | raio 8 | peso 600 | 12,5px
     devices     27px | raio 7 | peso 500 | 12px
     admin       29px | raio 7 | peso 500 | 12px

   A peca que faltava e esta: aplicar a ESPECIFICACAO do sistema aos nomes de
   classe que as paginas ja usam. `.btn` esta em 55 paginas, `.empty` em 46,
   `.card` em 27 — sao os componentes do painel, so que com outro nome.

   COMO ISTO CONVIVE COM O QUE JA EXISTE
   - carrega por ULTIMO, entao vence o empate de especificidade igual;
   - NAO usa !important: uma pagina que precise divergir ainda consegue, com
     uma regra mais especifica. A camada e um piso, nao uma algema;
   - so toca propriedades de APARENCIA (altura, raio, peso, cor, superficie).
     Layout (display, flex, grid, position) fica com a pagina — e o que evita
     que um botao dentro de uma barra flex mude de lugar;
   - as paginas ja convertidas usam `.ui-btn` e nao sao afetadas por `.btn`.

   O ESCOPO E DELIBERADAMENTE ESTREITO: so os nomes levantados por contagem no
   markup real. Nao vale adivinhar nome de classe.
   =========================================================================== */

/* --- botao: o componente mais espalhado do painel (55 paginas) -----------
   ESPECIFICIDADE TRIPLICADA (`.btn.btn.btn`), e o motivo e medido: 51 paginas
   tem 256 regras locais que miram estes mesmos componentes com dois ou mais
   niveis de classe (`.top a.btn`, `.chat-header .btn`). Com um nivel so, a
   camada perdia o empate e a pagina continuava com a cara antiga — foi
   exatamente o que aconteceu.

   Repetir a classe NAO muda o que o seletor casa: `.btn.btn.btn` acerta os
   mesmos elementos que `.btn`. So sobe o peso na cascata, de 1 classe para 3.
   Preferido ao `!important` de proposito: com `!important` nenhuma pagina
   conseguiria mais divergir nem em caso legitimo; com peso 3, quem precisar
   ainda pode, sendo explicito.                                             */.btn.btn.btn{
  min-height: var(--ui-ctrl-h);
  height: auto;   /* .btn{height:37px} em acs_pendentes */
  padding: 0 14px;
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--ui-linha-forte);
  background: var(--ui-painel);
  color: var(--ui-texto-2);
  font-family: var(--ui-fonte);
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .2px;
  line-height: 1;
  cursor: pointer;
  /* `inline-flex` e a UNICA propriedade de layout aqui, e ela e o que faz o
     rotulo centralizar na altura nova — sem isso o texto encosta no topo. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ui-s2);
  transition: background var(--ui-t-estado) var(--ui-curva),
              border-color var(--ui-t-estado) var(--ui-curva),
              color var(--ui-t-estado) var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva),
              transform var(--ui-t-toque) var(--ui-curva);
}.btn.btn.btn:hover:not(:disabled){ background: var(--ui-eleva); color: var(--ui-titulo); border-color: var(--ui-acento-txt); }.btn.btn.btn:active:not(:disabled){ transform: scale(.97); }.btn.btn.btn:focus-visible{ outline: 2px solid var(--ui-acento); outline-offset: 2px; box-shadow: var(--ui-anel-foco); }.btn.btn.btn:disabled{ opacity: .45; cursor: not-allowed; }a.btn.btn.btn{ text-decoration: none; }

/* Os modificadores que aparecem no markup real, mapeados ao sistema.       */.btn.btn.btn.btn-sm,
.btn.btn.btn.sm,
.btn.btn.btn.mini{ min-height: var(--ui-ctrl-h-peq); padding: 0 10px; font-size: 11.5px; }.btn.btn.btn.icon{ width: var(--ui-ctrl-h); padding: 0; }.btn.btn.btn.icon svg{ width: 15px; height: 15px; }.btn.btn.btn.on,
.btn.btn.btn.active,
.btn.btn.btn.save,
.btn.btn.btn.btn-apply,
.btn.btn.btn.btn-primary{
  background: linear-gradient(180deg, var(--ui-acento), var(--ui-acento-2));
  border-color: transparent;
  color: var(--ui-acento-tinta);
}.btn.btn.btn.on:hover:not(:disabled),
.btn.btn.btn.active:hover:not(:disabled),
.btn.btn.btn.save:hover:not(:disabled),
.btn.btn.btn.btn-apply:hover:not(:disabled),
.btn.btn.btn.btn-primary:hover:not(:disabled){
  background: linear-gradient(180deg, var(--ui-acento), var(--ui-acento-2));
  color: var(--ui-acento-tinta);
  border-color: transparent;
  box-shadow: var(--ui-brilho-acao);
}.btn.btn.btn.danger,
.btn.btn.btn.btn-danger,
.btn.btn.btn.red{
  color: var(--ui-perigo-txt);
  border-color: color-mix(in srgb, var(--ui-perigo) 45%, transparent);
  background: transparent;
}.btn.btn.btn.danger:hover:not(:disabled),
.btn.btn.btn.btn-danger:hover:not(:disabled),
.btn.btn.btn.red:hover:not(:disabled){
  background: color-mix(in srgb, var(--ui-perigo) 14%, transparent);
  color: var(--ui-perigo-txt);
  border-color: var(--ui-perigo-txt);
}.btn.btn.btn.green,
.btn.btn.btn.ok{
  color: var(--ui-ok);
  border-color: color-mix(in srgb, var(--ui-ok) 45%, transparent);
  background: transparent;
}.btn.btn.btn.btn-secondary,
.btn.btn.btn.ghost{ background: transparent; border-color: var(--ui-linha); }

/* --- superficies: cartao e painel (27 e 18 paginas) --------------------- */.card.card.card,
.panel.panel.panel{
  background: var(--ui-painel);
  border-radius: var(--ui-r-card);
  box-shadow: var(--ui-sombra-2);
  /* A borda sai: o vao e a sombra ja separam. Linha E sombra ao mesmo tempo
     leem como falha de alinhamento — mesma decisao dos cartoes aprovados.  */
  border: 0;
}

/* --- KPI (21 paginas): o numero e a interface ---------------------------- */.kpi.kpi.kpi{
  background: var(--ui-painel);
  border-radius: var(--ui-r-card);
  box-shadow: var(--ui-sombra-2);
  border: 0;
}

/* --- chips, selos e etiquetas (23/20/19 paginas) ------------------------- */.chip.chip.chip,
.badge.badge.badge,
.tag.tag.tag,
.pill.pill.pill{
  border-radius: var(--ui-r-pill);
  font-family: var(--ui-fonte);
  font-weight: 700;
  letter-spacing: .2px;
  /* REDEFINE O TOKEN, e nao pinta o fundo.

     A primeira versao desta regra dava `background` a todo chip/selo/etiqueta
     — larga demais. Um `.pill` que era transparente e vivia sobre a topbar
     clara ganhou tinta por cima e caiu para 4,24:1: o selo "OBSERVE", que e
     justamente o indicador da Regra Maxima. Regressao criada pela propria
     correcao, em quatro paginas.

     O problema real era so quem PINTA com `var(--bg-card)`: a ponte de tokens
     passou a resolver isso para o `--ui-cartao` claro, e as cores semanticas,
     calibradas para superficie escura, caiam sobre ela — MEDIDO em sinal.html:
     perigo 3,69:1 e texto-fraco 3,64:1, contra o piso de 4,5.

     Entao aqui muda so o que `--bg-card` SIGNIFICA dentro de um chip. Quem
     pinta com ele recebe a tinta do guia (12% de texto-2, a mesma do
     `.ui-chip`); quem nao pinta continua transparente e intocado.           */
  --bg-card: color-mix(in srgb, var(--ui-texto-2) 12%, transparent);
}

/* --- estado vazio (46 paginas) ------------------------------------------ */.empty.empty.empty{
  color: var(--ui-texto-3);
  font-family: var(--ui-fonte);
}

/* --- numero e texto tecnico --------------------------------------------- */.mono.mono.mono,
.num.num.num,
.val.val.val{
  font-family: var(--ui-mono);
  font-variant-numeric: tabular-nums;
}.muted.muted.muted,
.dim.dim.dim,
.hint.hint.hint{ color: var(--ui-texto-3); }

/* --- campos (6 paginas usam .finput/.fld; o resto usa a tag) ------------- */.finput.finput.finput,
.fld.fld.fld input,
.fld.fld.fld select,
.fld.fld.fld textarea{
  min-height: var(--ui-ctrl-h);
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--ui-linha);
  /* Fundo SOLIDO por exigencia do Windows: <select> com appearance:none e
     fundo rgba faz o Chrome/Edge desenhar o menu nativo branco, ignorando o
     tema. `--ui-fundo` resolve para hex, nunca rgba.                       */
  background: var(--ui-fundo);
  color: var(--ui-texto);
  font-family: var(--ui-fonte);
  font-size: 13px;
}.finput.finput.finput:focus,
.fld.fld.fld input:focus,
.fld.fld.fld select:focus,
.fld.fld.fld textarea:focus{
  border-color: var(--ui-acento-txt);
  box-shadow: var(--ui-anel-foco);
  outline: none;
}

/* --- vocabulario das telas de conversa ----------------------------------
   whatsapp.html e chat.html nomeiam os proprios componentes (`.conv-item`,
   `.user-item`, `.stab`, `.main-tab`). Sao os mesmos componentes do guia com
   outro nome — entao recebem a mesma especificacao.

   MEDIDO antes: o item de conversa vinha com raio 0 e fundo solido proprio,
   enquanto `.ui-item` pede raio de controle, fundo transparente e elevacao no
   hover. Era a divergencia mais visivel da tela.

   A marca teal na borda de ataque do item ativo NAO entra aqui: o
   naveg-vida.css ja a desenha (`.conv-item::after`), e repetir criaria duas
   fontes para o mesmo tracinho.                                            */
.conv-item.conv-item.conv-item,
.user-item.user-item.user-item,
.ticket-item.ticket-item.ticket-item {
  border-radius: var(--ui-r-ctl);
  background: transparent;
  transition: background var(--ui-t-estado) var(--ui-curva);
}
.conv-item.conv-item.conv-item:hover,
.user-item.user-item.user-item:hover,
.ticket-item.ticket-item.ticket-item:hover { background: var(--ui-eleva); }
.conv-item.conv-item.conv-item.active,
.user-item.user-item.user-item.active,
.ticket-item.ticket-item.ticket-item.active {
  background: color-mix(in srgb, var(--ui-acento) 10%, transparent);
}

/* Chip de setor e aba: os pesos e tamanhos exatos do guia (eram 600/11px e
   600/12px; o guia diz 700/11,5px no chip e 600/12,5px na aba).            */
.stab.stab.stab { min-height: 22px; font-weight: 700; font-size: 11.5px; }
.main-tab.main-tab.main-tab { font-size: 12.5px; }

/* Aba: a especificacao do guia, ponta a ponta.
   MEDIDO antes: o ativo mudava so de cor (`box-shadow: none`) e o hover
   pintava a aba inteira de accent pelo efeito de origem — um terceiro
   vocabulario de estado, que so existia aqui. O guia usa DOIS canais no ativo
   (cor + sublinhado) porque cor sozinha nao basta para indicar estado, e
   reserva o preenchimento accent ao botao primario.
   O padding de 4px lateral tambem entra na conta: com o rotulo maior do
   sistema, "Diagnostico" encostava nas bordas.                             */
/* A .main-tab entrou no bloco unificado abaixo. Ficou aqui so a neutralizacao
   do `::before`: escrever a aba em DOIS lugares foi o que quebrou — o bloco
   solto nao tinha `min-height`, e ao trocar o padding de `8px 4px` por
   `0 12px` a aba perdeu os 16px verticais que o padding dava e colapsou para
   a altura da linha (32px -> 16px). Uma aba, uma regra.                     */
/* O `::before` do efeito de origem pode ter sido injetado numa sessao antiga
   que ficou em cache; neutraliza sem depender da ordem de carga do JS.     */
.main-tab.main-tab.main-tab::before { content: none; }

/* Aba, o resto do painel. Levantamento: NOVE vocabularios de aba, com CINCO
   tratamentos de ativo diferentes — preenchimento solido (.mtab), gradiente
   (.ctab-btn), caixa accent-soft (.ctab, .vstab), e sublinhado (.dd-tab,
   .ftab, .opa-tab). O guia tem UM: cor + sublinhado, fundo transparente. O
   preenchimento accent e do botao primario; usa-lo na aba faz a aba ativa
   competir com a acao principal da tela.
   O sublinhado vem do `inset` do guia, e a borda de baixo das paginas que ja
   tinham uma vai a transparente (.mx-tab NAO entra: e <table>, nao aba)
   — se as duas ficassem, sairiam 4px de linha. */
.ctab.ctab.ctab, .vstab.vstab.vstab, .mtab.mtab.mtab, .ctab-btn.ctab-btn.ctab-btn,
.dd-tab.dd-tab.dd-tab, .ftab.ftab.ftab, .opa-tab.opa-tab.opa-tab,
.tab-btn.tab-btn.tab-btn, .tabbtn.tabbtn.tabbtn, .tab.tab.tab,
.main-tab.main-tab.main-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: var(--ui-ctrl-h);
  /* acs_pendentes fixava height:38px, que vence min-height em silencio. */
  height: auto;
  padding: 0 12px;
  border: 0;
  border-radius: 0;
  background: none;
  color: var(--ui-texto-2);
  font: 600 12.5px/1 var(--ui-fonte);
  text-decoration: none;
  cursor: pointer;
  box-shadow: inset 0 -2px 0 transparent;
  transition: color var(--ui-t-estado) var(--ui-curva),
              background var(--ui-t-estado) var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva);
}
.ctab.ctab.ctab:hover, .vstab.vstab.vstab:hover, .mtab.mtab.mtab:hover,
.ctab-btn.ctab-btn.ctab-btn:hover, .dd-tab.dd-tab.dd-tab:hover,
.ftab.ftab.ftab:hover, .opa-tab.opa-tab.opa-tab:hover,
.tab-btn.tab-btn.tab-btn:hover, .tabbtn.tabbtn.tabbtn:hover, .tab.tab.tab:hover,
.main-tab.main-tab.main-tab:hover {
  color: var(--ui-titulo);
  background: var(--ui-eleva);
}
.ctab.ctab.ctab.on, .ctab.ctab.ctab.active,
.vstab.vstab.vstab.on, .vstab.vstab.vstab.active,
.mtab.mtab.mtab.on, .mtab.mtab.mtab.active,
.ctab-btn.ctab-btn.ctab-btn.on, .ctab-btn.ctab-btn.ctab-btn.active,
.dd-tab.dd-tab.dd-tab.on, .dd-tab.dd-tab.dd-tab.active,
.ftab.ftab.ftab.on, .ftab.ftab.ftab.active,
.opa-tab.opa-tab.opa-tab.on, .opa-tab.opa-tab.opa-tab.active,
.tab-btn.tab-btn.tab-btn.on, .tab-btn.tab-btn.tab-btn.active,
.tabbtn.tabbtn.tabbtn.on, .tabbtn.tabbtn.tabbtn.active,
.tab.tab.tab.on, .tab.tab.tab.active,
.main-tab.main-tab.main-tab.on, .main-tab.main-tab.main-tab.active {
  color: var(--ui-acento-txt);
  background: none;
  border-bottom-color: transparent;
  box-shadow: inset 0 -2px 0 var(--ui-acento);
}

/* .stab NAO e aba: e filtro em pilula (Todas / Financeiro / Suporte). O guia
   ja tem o componente — .ui-chip, e .ui-chip--acento para o selecionado.
   A pagina usava 14%/40%; o guia usa 13%/30%. Uma so fonte para o par.      */
.stab.stab.stab {
  min-height: 22px;
  padding: 0 10px;
  border-radius: var(--ui-r-pill);
  font: 700 11.5px/1 var(--ui-fonte);
  color: var(--ui-texto-2);
  background: color-mix(in srgb, var(--ui-texto-2) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--ui-texto-2) 25%, transparent);
  cursor: pointer;
}
.stab.stab.stab:hover { color: var(--ui-titulo); }
.stab.stab.stab.active {
  color: var(--ui-acento-txt);
  background: color-mix(in srgb, var(--ui-acento) 13%, transparent);
  border-color: color-mix(in srgb, var(--ui-acento) 30%, transparent);
}

/* Hover de botao: o guia diz `eleva` + borda acento, e NUNCA preenchimento.
   Seis controles se pintavam de accent inteiro ao passar o ponteiro — o mesmo
   defeito da aba: o secundario passa a parecer o primario, e a tela perde a
   hierarquia justo no momento em que o usuario esta escolhendo.             */
.dbtn.dbtn.dbtn:hover, .vbtn.vbtn.vbtn:hover, .expbtn.expbtn.expbtn:hover,
.btnwide.btnwide.btnwide:hover, .voip-key.voip-key.voip-key:hover {
  background: var(--ui-eleva);
  border-color: var(--ui-acento-txt);
  color: var(--ui-titulo);
}

/* Movimento: o guia move UMA vez, no toque — `.ui-btn:active { scale(.97) }`.
   Nenhum componente dele sobe no hover: `.ui-card` nao tem hover e
   `.ui-item:hover` e so mudanca de fundo. O painel tinha nove elementos
   subindo 1-2px ou crescendo 6% so por o ponteiro em cima; com varios lado a
   lado a grade inteira treme. A resposta de hover fica com a borda/fundo que
   esses elementos ja tem — o canal do guia.                                 */
.btn-new.btn-new.btn-new:hover, .btn-send.btn-send.btn-send:hover,
.n1-btn.n1-btn.n1-btn:hover, .dom.dom.dom:hover, .tcard.tcard.tcard:hover,
.node.node.node:hover, .card.card.card.hover:hover,
.btn.btn.btn:hover, #navisFab#navisFab:hover {
  transform: none;
}
/* O toque, com o valor unico do guia (campo.html usava .98).               */
.btn.btn.btn:active:not(:disabled), .dbtn.dbtn.dbtn:active,
.vbtn.vbtn.vbtn:active, .btn-new.btn-new.btn-new:active,
.btn-send.btn-send.btn-send:active, .n1-btn.n1-btn.n1-btn:active:not(:disabled) {
  transform: scale(.97);
}

/* Mao inversa: agora que --ui-acento e fixo, a familia de accent da PAGINA
   passa a apontar para ele. Sao 715 usos de var(--accent) espalhados pelas
   paginas — sem esta ponte, corrigir o token do sistema arrumaria so os
   componentes .ui-* e deixaria o resto de cada pagina na cor antiga.
   `:root:root` (0,2,0) para vencer o `:root` que as paginas declaram, sem
   depender da ordem de carga das folhas.                                    */
:root:root, .ui-dark {
  --accent:       var(--ui-acento);
  --accent2:      var(--ui-acento-2);
  --accent-2:     var(--ui-acento-2);
  --accent-deep:  var(--ui-acento-2);
  --accent-press: var(--ui-acento-2);
  --accent-ink:   var(--ui-acento-tinta);
  --teal:         var(--ui-acento);
  --accent-soft:  color-mix(in srgb, var(--ui-acento) 13%, transparent);
}

/* O container tambem tem piso de altura no guia (`.ui-abas`). Sem ele o
   cartao acompanha o colapso do filho e a barra fica com metade da caixa.   */
.main-tabs.main-tabs, .diag-tabs.diag-tabs, .ctabs.ctabs, .mtabs.mtabs,
.opa-tabs.opa-tabs, .dd-tabs.dd-tabs, .feed-tabs.feed-tabs, .vstabs.vstabs,
.sector-tabs.sector-tabs, .tabbar.tabbar, .tab-nav.tab-nav {
  min-height: var(--ui-ctrl-h);
  align-items: stretch;
}

/* Neutros: mesma ponte do accent, e pelo mesmo motivo medido — as paginas
   declaravam 10 fundos, 8 bordas, 7 cores de texto e 5 de texto-fraco
   diferentes. Como o sistema CEDIA a esses tokens, cada pagina rodava um
   Naveg UI proprio; e como as regras das paginas usam --border/--text
   direto, corrigir so o lado --ui-* arrumaria os componentes e deixaria o
   resto de cada pagina na cor antiga.
   `--bg` (a tela de fundo) NAO entra: e o unico que responde por identidade
   de pagina, e o guia mostra painel sobre fundo, nao o fundo em si.         */
:root:root, .ui-dark {
  /* `--bg` ENTROU. Ficou de fora na primeira volta com o argumento de que a
     tela de fundo responde por identidade de pagina — e o teste desmontou
     isso: trocar `--ui-fundo` no guia mudava o guia e mais nada, porque sao
     49 paginas pintando `body { background: var(--bg) }` com valor proprio
     (10 valores distintos entre elas). Um guia que nao alcanca o fundo da
     tela nao governa a tela; descreve.                                      */
  --bg:             var(--ui-fundo);
  --bg2:            var(--ui-painel);
  --ground:         var(--ui-fundo);
  --bg-primary:     var(--ui-fundo);
  --sup2:           var(--ui-painel);
  --bg-panel:       var(--ui-painel);
  --bg-card:        var(--ui-cartao);
  --bg-hover:       var(--ui-eleva);
  --border:         var(--ui-linha);
  --border-2:       var(--ui-linha);
  --border-light:   var(--ui-linha-forte);
  --text:           var(--ui-texto);
  --text-secondary: var(--ui-texto-2);
  --sec:            var(--ui-texto-2);
  --text-muted:     var(--ui-texto-3);
  --muted:          var(--ui-texto-3);
  --title:          var(--ui-titulo);
}

/* Indicadores do topo (OPA / IXC): sao CONTROLES — clicaveis, com cursor de
   ponteiro — entao seguem `.ui-btn`, nao um tamanho proprio. Estavam em 40px
   de altura, raio 10 e rotulo 13px contra os 32 / 8 / 12,5 do guia, e a
   severidade usava borda a 55% contra os 45% de `.ui-btn--perigo`.
   O `min-width` fica: e igualdade de largura entre o par, nao estilo.       */
.nv-ind.nv-ind.nv-ind {
  height: var(--ui-ctrl-h);
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--ui-linha-forte);
  background: var(--ui-painel);
  color: var(--ui-texto-2);
  font-size: 12.5px;
  box-shadow: none;
}
.nv-ind-opa.nv-ind-opa.nv-ind-opa,
.nv-ind-ixc.nv-ind-ixc.nv-ind-ixc,
.nv-ind-men.nv-ind-men.nv-ind-men {
  height: var(--ui-ctrl-h);
  padding: 0 14px;
  font-size: 12.5px;
  border-radius: var(--ui-r-ctl);
  box-shadow: none;
}
.nv-ind-opa.nv-ind-opa .nv-ind-n,
.nv-ind-ixc.nv-ind-ixc .nv-ind-n,
.nv-ind-men.nv-ind-men .nv-ind-n { font-size: 12.5px; }
/* Severidade pelo padrao de `.ui-btn--perigo`: cor semantica no numero,
   borda a 45% e fundo transparente. Duas correcoes de paleta junto:
   o OPA usava #fb923c (laranja), uma QUINTA cor que o guia nao tem — vai para
   `--ui-alerta`; e as bordas estavam a 55%/75%, nao aos 45% do guia.        */
.nv-ind.nv-ind.sev-atencao, .nv-ind.nv-ind.sev-critico, .nv-ind.nv-ind.sev-alarme {
  border-color: color-mix(in srgb, var(--nv-alerta, var(--ui-perigo)) 45%, transparent);
  background: transparent;
}
.nv-ind.nv-ind.sev-atencao .nv-ind-n,
.nv-ind.nv-ind.sev-critico .nv-ind-n,
.nv-ind.nv-ind.sev-alarme  .nv-ind-n { color: var(--nv-alerta, var(--ui-perigo)); }
.nv-ind.nv-ind.sev-atencao .nv-ind-marca,
.nv-ind.nv-ind.sev-critico .nv-ind-marca,
.nv-ind.nv-ind.sev-alarme  .nv-ind-marca { color: var(--nv-alerta, var(--ui-perigo)); }
/* O ponto pulsante do alarme herda a mesma cor — era vermelho fixo.         */
.nv-ind.nv-ind.sev-alarme .nv-ind-pulso {
  background: var(--nv-alerta, var(--ui-perigo));
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--nv-alerta, var(--ui-perigo)) 70%, transparent);
}

/* ================================================================ alerta ==
   A faixa que avisa sem interromper: 5 telas ja tinham uma, com 5 desenhos
   diferentes. Fica entre o chip (rotula) e o toast (some sozinho) — o alerta
   PERSISTE ate alguem dispensar, entao carrega texto de apoio e uma acao.

   Uma variavel local `--al` guarda a semantica, e borda, fundo, icone e
   titulo saem toda dela: trocar de gravidade e trocar UMA cor, nao quatro
   regras. A escala de tinta e a MESMA do chip (13% de fundo, 30% de borda);
   o painel tinha 8%/38% e 8%/42% conforme a origem — tres escalas para o
   mesmo gesto.                                                              */
.ui-alerta {
  /* Dois canais, e nao um: `--al` PINTA (borda, fundo, icone) e `--al-txt`
     ESCREVE. No escuro sao a mesma cor; no claro o titulo em #f59e0b sobre a
     propria tinta dava 1,74:1 — a cor de identidade e clara demais para texto,
     que e exatamente o motivo de os dois papeis existirem separados.        */
  --al: var(--ui-info);
  --al-txt: var(--ui-info);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  column-gap: var(--ui-s3);
  padding: var(--ui-s3) 14px;
  border-radius: var(--ui-r-card);
  border: 1px solid color-mix(in srgb, var(--al) 30%, transparent);
  background: color-mix(in srgb, var(--al) 13%, transparent);
}
.ui-alerta--ok     { --al: var(--ui-ok); --al-txt: var(--ui-ok); }
.ui-alerta--alerta { --al: var(--ui-alerta); --al-txt: var(--ui-alerta-txt); }
.ui-alerta--perigo { --al: var(--ui-perigo); --al-txt: var(--ui-perigo-txt); }
.ui-alerta--info   { --al: var(--ui-info); --al-txt: var(--ui-info); }

.ui-alerta__ic {
  grid-column: 1; grid-row: 1 / -1;
  display: grid; place-items: center;
  width: 26px; height: 26px;
  flex: none;
  color: var(--al);
}
.ui-alerta__ic svg { width: 20px; height: 20px; }
/* Fonte de icone dimensiona por font-size, nao por width/height.           */
.ui-alerta__ic i { font-size: 17px; line-height: 1; }

.ui-alerta__t {
  grid-column: 2;
  font: 700 12px/1.2 var(--ui-fonte);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--al-txt, var(--al));
}
.ui-alerta__d {
  grid-column: 2;
  margin-top: 3px;
  font: 400 12.5px/1.45 var(--ui-fonte);
  color: var(--ui-texto-2);
}
/* A acao e um `.ui-btn` de verdade — o alerta nao inventa botao proprio.
   Aqui so a colocacao na grade e a cor de foco, que segue a gravidade.      */
.ui-alerta__x { grid-column: 3; grid-row: 1 / -1; }
.ui-alerta__x:hover:not(:disabled) { border-color: var(--al); color: var(--al); }

/* Empilhado: mesmo alerta em coluna estreita (balao, painel lateral). O
   icone volta para a linha do titulo e a acao cai para baixo.               */
.ui-alerta--empilhado {
  grid-template-columns: auto 1fr;
  align-items: start;
}
.ui-alerta--empilhado .ui-alerta__ic { grid-row: 1; }
.ui-alerta--empilhado .ui-alerta__x {
  grid-column: 2; grid-row: auto;
  margin-top: var(--ui-s2);
  justify-self: start;
}

/* Adocao: a faixa de avisos do painel (`.nv-painel-alerta`) passa a ser o
   `.ui-alerta`. Ela tinha TRES escalas de tinta conforme a origem do aviso
   (8%/38% no balao, 8%/42% na faixa) e pintava o OPA com #fb923c — o laranja
   fora da paleta que ja saiu dos indicadores. Aqui a origem so escolhe a
   semantica; borda, fundo, icone e titulo saem dela sozinhos.               */
.nv-painel-alerta.nv-painel-alerta.nv-painel-alerta {
  --al: var(--ui-alerta);
  border-radius: var(--ui-r-card);
  border: 1px solid color-mix(in srgb, var(--al) 30%, transparent);
  background: color-mix(in srgb, var(--al) 13%, transparent);
}
.nv-painel-alerta.nv-painel-alerta.nv-painel-alerta[data-fonte="OPA"] { --al: var(--ui-alerta); }
.nv-painel-alerta.nv-painel-alerta.nv-painel-alerta[data-fonte="IXC"] { --al: var(--ui-perigo); }
.nv-painel-alerta.nv-painel-alerta .nv-painel-alerta-ic { color: var(--al); }
.nv-painel-alerta.nv-painel-alerta .nv-painel-alerta-t {
  font: 700 12px/1.2 var(--ui-fonte);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--al);
}
.nv-painel-alerta.nv-painel-alerta .nv-painel-alerta-d {
  font: 400 12.5px/1.45 var(--ui-fonte);
  color: var(--ui-texto-2);
}
/* O dispensar vira `.ui-btn--peq`: 26px, raio de controle, borda do sistema. */
.nv-painel-alerta.nv-painel-alerta .nv-painel-alerta-x {
  min-height: var(--ui-ctrl-h-peq);
  height: auto;
  padding: 0 10px;
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--ui-linha-forte);
  background: transparent;
  color: var(--ui-texto-2);
  font: 600 12px/1 var(--ui-fonte);
}
.nv-painel-alerta.nv-painel-alerta .nv-painel-alerta-x:hover {
  border-color: var(--al);
  color: var(--al);
  background: transparent;
}

/* ================================================================ icones ==
   MEDIDO: 555 SVG embutidos em 56 paginas, zero Font Awesome no markup, zero
   emoji (o DESIGN.md ja proibia). O que variava era a espessura: OITO valores
   em uso — 2 em 338 casos, e 1.5, 1.6, 1.8, 2.2, 2.4, 2.5 e 3 no resto. Numa
   fileira de botoes isso le como icones de familias diferentes.

   O contrato: viewBox 24, traco 2, sem preenchimento, `currentColor` — o
   icone herda a cor do texto e nunca declara a sua, entao acompanha estado,
   tema e semantica de graca. Tres tamanhos, e nao "o que couber".           */
:root, .ui-dark, :root[data-theme="light"], .ui-light {
  --ui-ic-peq: 14px;   /* dentro de botao pequeno, chip, celula de tabela */
  --ui-ic:     16px;   /* padrao: botao, campo, item de lista             */
  --ui-ic-gr:  20px;   /* alerta, cabecalho de cartao, estado vazio       */
}
.ui-ic {
  width: var(--ui-ic); height: var(--ui-ic);
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* O SVG assenta na linha de base do texto; sem isto ele flutua ~1px acima
     do rotulo em qualquer botao com icone + texto.                         */
  vertical-align: -.125em;
}
.ui-ic--peq { width: var(--ui-ic-peq); height: var(--ui-ic-peq); }
.ui-ic--gr  { width: var(--ui-ic-gr);  height: var(--ui-ic-gr); }
/* Traco mais fino so no tamanho grande: a 20px o traco 2 fica pesado.      */
.ui-ic--gr  { stroke-width: 1.8; }

/* ============================================================== mapa ==
   Duas telas de mapa (frota ao vivo, calor) mais o mapa embutido do hub. O
   que o guia fixa e a MOLDURA, nao o motor: a superficie, a legenda, o pino,
   o balao e o carimbo de frescor. O motor (Leaflet aqui, Google la) troca
   sem que nada disto mude.

   Regra de leitura: no mapa a cor JA esta ocupada pelo territorio, entao o
   estado do pino vem de cor + FORMA (anel), nunca so de cor.               */
.ui-mapa {
  position: relative;
  border-radius: var(--ui-r-card);
  overflow: hidden;
  background: var(--ui-painel);
  box-shadow: var(--ui-sombra-2);
  min-height: 220px;
}
/* Contexto de empilhamento proprio: sem isto o controle de zoom do motor
   (z-index 1000) sobe por cima da topbar do painel — foi o que sumiu com os
   menus em fleet.html.                                                     */
.ui-mapa > .leaflet-container, .ui-mapa__tela { position: relative; z-index: 0; height: 100%; }

.ui-mapa__legenda {
  position: absolute;
  right: var(--ui-s3); bottom: var(--ui-s3);
  z-index: 2;
  display: flex; flex-direction: column; gap: 6px;
  padding: 10px var(--ui-s3);
  border-radius: var(--ui-r-ctl);
  /* Fundo SOLIDO, nao translucido: sobre ladrilho a legenda translucida fica
     ilegivel exatamente onde o mapa e mais detalhado.                      */
  background: var(--ui-painel);
  border: 1px solid var(--ui-linha);
  box-shadow: var(--ui-sombra-2);
  font: 400 12px/1.3 var(--ui-fonte);
  color: var(--ui-texto-2);
}
.ui-mapa__legenda-item { display: flex; align-items: center; gap: 8px; }
.ui-mapa__legenda-cor {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--lc, var(--ui-acento));
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--lc, var(--ui-acento)) 28%, transparent);
  flex: none;
}
/* Cluster de controles do motor, reposicionado longe da topbar.            */
.ui-mapa__ctrl { position: absolute; right: var(--ui-s3); top: var(--ui-s3); z-index: 2; display: flex; flex-direction: column; gap: 6px; }

.ui-pino {
  --pc: var(--ui-acento);
  display: grid; place-items: center;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--pc);
  color: var(--ui-acento-tinta);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--pc) 30%, transparent), var(--ui-sombra-2);
}
.ui-pino--ok     { --pc: var(--ui-ok); }
.ui-pino--alerta { --pc: var(--ui-alerta); }
.ui-pino--perigo { --pc: var(--ui-perigo); }
/* Parado/offline: sem anel e dessaturado — a FORMA muda junto com a cor.   */
.ui-pino--inativo { --pc: var(--ui-texto-3); box-shadow: var(--ui-sombra-1); opacity: .75; }
.ui-pino svg { width: 14px; height: 14px; }

.ui-mapa__balao {
  min-width: 190px; max-width: 260px;
  padding: 10px var(--ui-s3);
  border-radius: var(--ui-r-ctl);
  background: var(--ui-painel);
  border: 1px solid var(--ui-linha);
  box-shadow: var(--ui-sombra-3);
  font: 400 12.5px/1.45 var(--ui-fonte);
  color: var(--ui-texto-2);
}
.ui-mapa__balao b { display: block; margin-bottom: 3px; font: 700 12.5px/1.3 var(--ui-fonte); color: var(--ui-titulo); }

/* Carimbo de frescor: um mapa "ao vivo" que congelou nao avisa sozinho.    */
.ui-frescor {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 22px; padding: 0 10px;
  border-radius: var(--ui-r-pill);
  font: 700 11.5px/1 var(--ui-mono);
  color: var(--ui-ok);
  background: color-mix(in srgb, var(--ui-ok) 13%, transparent);
  border: 1px solid color-mix(in srgb, var(--ui-ok) 30%, transparent);
}
.ui-frescor::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: currentColor;
  animation: ui-pulsa 2s ease-out infinite;
}
.ui-frescor--velho { color: var(--ui-alerta-txt); background: color-mix(in srgb, var(--ui-alerta) 13%, transparent); border-color: color-mix(in srgb, var(--ui-alerta) 30%, transparent); }
.ui-frescor--morto { color: var(--ui-perigo-txt); background: color-mix(in srgb, var(--ui-perigo) 13%, transparent); border-color: color-mix(in srgb, var(--ui-perigo) 30%, transparent); }
.ui-frescor--velho::before, .ui-frescor--morto::before { animation: none; }
@keyframes ui-pulsa { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
@media (prefers-reduced-motion: reduce) { .ui-frescor::before { animation: none } }

/* ====================================================== mapa de rede ==
   A topologia POP -> OLT -> PON -> CTO -> assinante. Nao e o mapa
   geografico: aqui a posicao nao significa lugar, significa NIVEL. Por isso
   o no carrega o nivel escrito, e nao so a altura na tela — quem chega pelo
   meio da arvore (um link de incidente) nao ve a hierarquia inteira.       */
.ui-rede { display: flex; flex-direction: column; gap: var(--ui-s4); }
.ui-rede__nivel { display: flex; flex-wrap: wrap; align-items: stretch; gap: var(--ui-s2); }
.ui-rede__rotulo {
  flex: 0 0 68px;
  align-self: center;
  font: 700 10px/1 var(--ui-fonte);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ui-texto-3);
}
.ui-no {
  --nc: var(--ui-linha-forte);
  display: flex; flex-direction: column; gap: 2px;
  min-width: 108px;
  padding: 9px var(--ui-s3);
  border-radius: var(--ui-r-ctl);
  border: 1px solid var(--nc);
  background: var(--ui-painel);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--ui-t-estado) var(--ui-curva),
              background var(--ui-t-estado) var(--ui-curva);
}
.ui-no:hover { background: var(--ui-eleva); border-color: var(--ui-acento-txt); }
.ui-no__t { font: 700 12.5px/1.2 var(--ui-fonte); color: var(--ui-titulo); }
.ui-no__m { font: 400 11.5px/1.3 var(--ui-mono); color: var(--ui-texto-3); }
.ui-no--ok     { --nc: color-mix(in srgb, var(--ui-ok) 45%, transparent); }
.ui-no--alerta { --nc: color-mix(in srgb, var(--ui-alerta) 45%, transparent); }
.ui-no--perigo { --nc: color-mix(in srgb, var(--ui-perigo) 45%, transparent); }
.ui-no--perigo .ui-no__t { color: var(--ui-perigo-txt); }
/* Fora do ar: risca o rotulo. Cor sozinha nao sobrevive a impressao nem a
   quem nao separa vermelho de cinza.                                       */
.ui-no--fora { --nc: var(--ui-linha); opacity: .6; }
.ui-no--fora .ui-no__t { text-decoration: line-through; }
/* O tronco que liga um nivel ao proximo.                                   */
.ui-rede__elo { height: 14px; margin-left: 68px; border-left: 1px solid var(--ui-linha); }

/* ============================================================ painel ==
   A grade de dashboard. 37 telas ja tinham uma, com quatro vocabularios
   (`kpi`, `metric`, `stat`, `state-box`). A grade e `auto-fill` e nao um
   numero de colunas: o painel roda de 1280 a 3840px e uma grade fixa deixa
   canaleta vazia numa ponta e aperta na outra.                             */
.ui-painel-grade {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--ui-s3);
}
.ui-painel-grade--largo { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }

/* Variacao: o numero sozinho nao diz se 143 e bom. O delta diz.            */
.ui-delta {
  display: inline-flex; align-items: center; gap: 4px;
  font: 700 11.5px/1 var(--ui-mono);
  color: var(--ui-texto-3);
}
.ui-delta--sobe { color: var(--ui-ok); }
.ui-delta--desce { color: var(--ui-perigo-txt); }
/* A seta e o segundo canal — quem nao separa as duas cores le a direcao.   */
.ui-delta--sobe::before  { content: "\2191"; }
.ui-delta--desce::before { content: "\2193"; }
.ui-delta--nulo::before  { content: "\2013"; }
/* Inverte onde subir e ruim (churn, chamados, tempo de espera).            */
.ui-delta--invertido.ui-delta--sobe  { color: var(--ui-perigo-txt); }
.ui-delta--invertido.ui-delta--desce { color: var(--ui-ok); }

/* Moldura de grafico: titulo, corpo com altura propria, legenda embaixo.
   O corpo tem altura FIXA porque grafico sem altura declarada colapsa a
   zero antes do dado chegar, e a tela pula quando ele chega.               */
.ui-grafico { display: flex; flex-direction: column; gap: var(--ui-s2); }
.ui-grafico__cab { display: flex; align-items: baseline; justify-content: space-between; gap: var(--ui-s3); }
.ui-grafico__corpo { position: relative; height: 180px; min-height: 180px; }
.ui-grafico__corpo > canvas, .ui-grafico__corpo > svg { width: 100%; height: 100%; display: block; }
.ui-grafico__nota { font: 400 11.5px/1.4 var(--ui-fonte); color: var(--ui-texto-3); }

/* ================================================================ GRAFICOS ==
   Os tipos que EXISTEM no projeto, e so eles. Cada um nasceu solto numa
   pagina: sparkline em `produtividade`/`ponto`/`vendas`/`devices`, area e
   rosca e pizza em `vendas`, barras em quatro telas, anel em `devices`,
   escore em `saude`, mapa de calor em `mapa_calor`. Reunidos aqui, a mesma
   serie deixa de ter quatro aparencias conforme a pagina em que caiu.

   AS PAGINAS AINDA NAO USAM ESTAS CLASSES — continuam com a implementacao
   propria. Este bloco e o destino; a migracao e trabalho a parte. Dizer o
   contrario faria o catalogo mentir sobre o que ja esta no ar.

   Nao ha biblioteca de grafico no projeto e este bloco nao introduz uma: e
   SVG e CSS, como tudo o mais. Chart.js sozinho pesa mais que o painel.
   ========================================================================= */

/* A COR DA SERIE. Nao e o acento, e a razao tem numero: `--ui-acento` no tema
   claro e #10b981, que como TRACO FINO sobre cartao branco da 2,5:1 — abaixo
   do piso de 3:1 que um grafico precisa para ser lido. `--ui-acento-2` da 3,5
   no claro e 5,5 no escuro. O token existe com nome proprio porque "use o -2
   aqui" e uma regra que ninguem lembra em um mes; "a serie tem cor de serie"
   se sustenta sozinha.

   Vale para TRACO. Preenchimento grande (fatia de pizza, celula de calor) se
   separa pela propria fronteira e nao esta sujeito ao mesmo piso.         */
:root, .ui-dark, :root[data-theme="light"], .ui-light {
  --ui-serie: var(--ui-acento-2);
  /* A serie 1 E a cor de serie: um grafico que ganha uma segunda serie nao
     muda a cor da primeira.                                              */
  --ui-serie-1: var(--ui-serie);
}

/* A RAMPA DE SERIE — para grafico de mais de uma serie ao mesmo tempo.
   Tokens proprios, e nao os de estado, pela mesma razao da rampa de calor:
   verde, amarelo e vermelho DIZEM estado da rede. Uma serie que calhasse de
   ser a segunda ficaria amarela sem estar em alerta nenhum. As tres matizes
   daqui evitam o semaforo de proposito.

   CATEGORICO, e nao sequencial: tres tons da mesma cor — que e o que um ramp
   monocromatico faz — so se separam pela claridade, e no tema claro o terceiro
   tom cai para 1,3:1 sobre o cartao branco e some como traco. Matiz distinta
   sobrevive nos dois temas, e e o que a serie precisa: ela nao tem ordem, tem
   nome.

   Os numeros abaixo sao MEDIDOS contra o cartao onde a peca vive (`--ui-painel`
   no escuro, branco no claro), e nao contra o fundo da pagina: o traco fica
   sobre o cartao, e e desse par que o piso de 3:1 trata.

   As tres primeiras servem a TRACO e por isso carregam o numero medido. A 4 e a
   5 entraram para a rosca de gomos, que e PREENCHIMENTO: fatia grande se separa
   pela propria fronteira e nao esta sujeita ao piso de 3:1 — o par que importa
   nelas e o do numero escrito por dentro, que sai em `--ui-fundo`. Usar uma
   das duas como traco fino exige medir antes.

   A 5 e neutra de proposito: "Outros" nao e uma categoria como as outras, e
   dar-lhe uma cor propria faz o olho procurar significado onde nao ha.      */
:root, .ui-dark {
  /* serie-1 (#14b8a6) da 5,29:1 aqui */
  --ui-serie-2: #38bdf8;   /* 6,15:1 */
  --ui-serie-3: #a78bfa;   /* 4,84:1 */
  --ui-serie-4: #e879f9;
  --ui-serie-5: #94a3b8;
}
:root[data-theme="light"], .ui-light {
  /* A serie-1 do tema claro NAO e `--ui-serie`, e a excecao tem numero: o
     emerald-600 (#059669) da 3,77:1 como traco sobre o cartao branco e apenas
     3,35:1 sob o numero escrito por dentro do gomo — abaixo do piso de texto.
     Um passo mais escuro (emerald-700) leva os dois a 5,48:1. No tema escuro a
     identidade se mantem: la a serie-1 continua sendo a cor de serie.      */
  --ui-serie-1: #047857;   /* 5,48:1 */
  --ui-serie-2: #0369a1;   /* 5,93:1 */
  --ui-serie-3: #6d28d9;   /* 7,10:1 */
  --ui-serie-4: #a21caf;
  --ui-serie-5: #475569;
}

/* A TINTA QUE SE ESCREVE POR CIMA DE UMA SERIE — o numero dentro do gomo.
   Nao e `--ui-texto`: no escuro os gomos sao claros e no claro sao escuros,
   entao a tinta de texto do tema acertaria num e sumiria no outro. Aqui ela e
   sempre o oposto do preenchimento. Medida contra as cinco: 5,94 a 7,55 no
   escuro, 5,48 a 7,58 no claro.                                            */
:root, .ui-dark { --ui-serie-tinta: #182130; }
:root[data-theme="light"], .ui-light { --ui-serie-tinta: #ffffff; }

/* --- partes do SVG: servem a linha e a area ------------------------------ */
.ui-graf-grade { stroke: var(--ui-linha); stroke-width: 1; }
.ui-graf-eixo  { fill: var(--ui-texto-3); font: 500 10px/1 var(--ui-mono); }
.ui-graf-linha {
  fill: none;
  stroke: var(--ui-serie);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
/* O periodo anterior e tracejado, e nao so mais claro: quem imprime em preto
   e branco, ou nao separa as duas cores, continua lendo qual e qual.       */
.ui-graf-linha--anterior { stroke: var(--ui-linha-forte); stroke-dasharray: 4 4; }
.ui-graf-ponto { fill: var(--ui-serie); }

/* --- sparkline: a serie sem eixo, dentro do KPI -------------------------- */
/* Nao e um grafico pequeno — e outro tipo. Nao tem eixo, grade nem valor: so
   a FORMA da serie ao lado do numero. Por isso `preserveAspectRatio:none` no
   uso: a proporcao nao carrega informacao aqui, o perfil carrega.          */
.ui-spark { display: block; width: 100%; height: 30px; }
.ui-spark path {
  fill: none;
  stroke: var(--ui-serie);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.ui-spark--ok path     { stroke: var(--ui-ok); }
.ui-spark--alerta path { stroke: var(--ui-alerta); }
.ui-spark--perigo path { stroke: var(--ui-perigo); }

/* --- camadas: a area de varias series ------------------------------------ */
/* CAMADAS, e nao "area": `.ui-area` JA E a <textarea> (ver a secao de campos
   la em cima). Batizar o grafico assim daria 200px de altura e
   `position:relative` a toda caixa de texto do painel, sem nada no console —
   o pior tipo de colisao, porque a peca quebrada fica longe da que mudou.
   Antes de batizar componente novo: `grep -n "\.ui-<nome>" naveg-ui.css`.

   Varias series no MESMO par de eixos, sobrepostas e nao empilhadas: cada
   fonte e lida contra o mesmo zero. Empilhar somaria o que nao se soma — um
   evento do Zabbix e um do IXC costumam ser o MESMO incidente visto de dois
   lados, e a pilha entregaria o dobro.

   Sem eixo Y, por escolha e nao por esquecimento: o valor exato de cada dia
   vem na dica do hover, e a grade sozinha da a inclinacao, que e o que se le
   a distancia num monitor de parede. Um eixo Y a mais roubaria largura de uma
   peca que costuma viver em meia coluna.

   NAO HA SCRIPT NENHUM AQUI, e e o ponto: o hover e `:hover` numa faixa por
   periodo e a entrada e animacao CSS. Quem copiar o HTML para outra pagina
   leva a peca inteira funcionando, sem lembrar de inicializar nada.        */
.ui-camadas {
  position: relative;
  /* Altura propria, e maior que a de `.ui-grafico__corpo`: esta peca carrega
     rotulo de eixo embaixo. Fixa pelo mesmo motivo de sempre — grafico sem
     altura declarada colapsa a zero antes do dado chegar.                 */
  height: 200px; min-height: 200px;
}
.ui-camadas__svg { display: block; width: 100%; height: 100%; }

.ui-camadas__serie--1 { --c: var(--ui-serie-1); }
.ui-camadas__serie--2 { --c: var(--ui-serie-2); }
.ui-camadas__serie--3 { --c: var(--ui-serie-3); }
.ui-camadas__linha {
  fill: none;
  stroke: var(--c);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}

/* --- a leitura no hover --------------------------------------------------- */
/* `fill: transparent` e NAO `fill: none`: com `none` o retangulo nao existe
   para o ponteiro e a faixa nunca recebe hover — o mesmo alvo, invisivel dos
   dois jeitos, so um deles clicavel.                                       */
.ui-camadas__alvo { fill: transparent; }
.ui-camadas__guia { stroke: var(--ui-linha-forte); stroke-width: 1; stroke-dasharray: 3 3; }
.ui-camadas__ponto { stroke: var(--ui-cartao); stroke-width: 1.5; }
.ui-camadas__ponto--1, .ui-camadas__dica-cor--1 { fill: var(--ui-serie-1); }
.ui-camadas__ponto--2, .ui-camadas__dica-cor--2 { fill: var(--ui-serie-2); }
.ui-camadas__ponto--3, .ui-camadas__dica-cor--3 { fill: var(--ui-serie-3); }

.ui-camadas__dica-caixa { fill: var(--ui-eleva); stroke: var(--ui-linha); stroke-width: 1; }
.ui-camadas__dica-t { fill: var(--ui-texto);   font: 700 8px/1 var(--ui-fonte); }
.ui-camadas__dica-l { fill: var(--ui-texto-2); font: 500 7.5px/1 var(--ui-fonte); }
.ui-camadas__dica-v { fill: var(--ui-texto);   font: 700 8px/1 var(--ui-mono); }

.ui-camadas__guia, .ui-camadas__ponto, .ui-camadas__dica {
  opacity: 0;
  /* So o alvo participa da caca ao ponteiro. Sem isto a propria dica, ao
     aparecer sob o cursor, viraria alvo e produziria pisca-pisca.        */
  pointer-events: none;
  transition: opacity var(--ui-t-estado) var(--ui-curva);
}
.ui-camadas__faixa:hover .ui-camadas__guia,
.ui-camadas__faixa:hover .ui-camadas__ponto,
.ui-camadas__faixa:hover .ui-camadas__dica { opacity: 1; }

/* --- a entrada ------------------------------------------------------------ */
/* O ESTADO BASE E O ESTADO FINAL, sempre. Escrever o quadro inicial no CSS
   base (cortina fechada, traco por desenhar) e depois desligar a animacao em
   `prefers-reduced-motion` deixaria o grafico VAZIO exatamente para quem pediu
   menos movimento — o pior tipo de regressao, porque nunca aparece em teste.

   `pathLength="1"` no traco normaliza o comprimento do caminho: o mesmo par
   dasharray/dashoffset serve a qualquer curva, sem medir nada com JS.      */
.ui-camadas__cortina {
  transform-box: fill-box; transform-origin: left center;
  animation: ui-camadas-cortina 900ms var(--ui-curva) both;
}
.ui-camadas__linha {
  stroke-dasharray: 1; stroke-dashoffset: 0;
  animation: ui-camadas-traco 900ms var(--ui-curva) both;
}
.ui-camadas__preenche { animation: ui-camadas-entra 700ms var(--ui-curva) both; }
/* A defasagem por serie: as tres chegando juntas viram um borrao so.      */
.ui-camadas__serie--2 .ui-camadas__linha, .ui-camadas__serie--2 .ui-camadas__preenche { animation-delay: 90ms; }
.ui-camadas__serie--3 .ui-camadas__linha, .ui-camadas__serie--3 .ui-camadas__preenche { animation-delay: 180ms; }

@keyframes ui-camadas-cortina { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes ui-camadas-traco   { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes ui-camadas-entra   { from { opacity: 0; } to { opacity: 1; } }

/* --- as metricas de rodape ------------------------------------------------ */
/* A lista que fecha o cartao de relatorio: rotulo com icone, valor em mono e o
   selo de tendencia. Linha divisoria entre as metricas, e nao moldura em cada
   uma — sao um bloco, nao tres cartoes.                                     */
.ui-metricas { display: flex; flex-direction: column; margin-top: var(--ui-s3); }
.ui-metrica {
  display: flex; align-items: center; gap: var(--ui-s3);
  padding: 10px 0;
  border-top: 1px solid var(--ui-linha);
  animation: ui-metrica-entra 420ms var(--ui-curva) both;
}
.ui-metrica:first-child { border-top: 0; }
.ui-metrica:nth-child(2) { animation-delay: 70ms; }
.ui-metrica:nth-child(3) { animation-delay: 140ms; }
@keyframes ui-metrica-entra {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.ui-metrica__rot {
  display: flex; align-items: center; gap: 7px;
  flex: 1; min-width: 0;          /* `min-width:0` e o que deixa o nome cortar */
  font: 500 12.5px/1.3 var(--ui-fonte);
  color: var(--ui-texto-2);
}
.ui-metrica__nome { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ui-metrica__val {
  font: 700 17px/1 var(--ui-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ui-titulo);
}

/* O SELO DE TENDENCIA. Mesmo vocabulario do `.ui-delta`, `--invertido`
   inclusive: em tempo de resposta e em taxa de escalonamento, subir e ruim. A
   seta e o segundo canal — cor sozinha nao diz direcao a quem nao a separa,
   nem sobrevive a impressao.                                               */
.ui-tend {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; flex: none;
  border-radius: var(--ui-r-pill);
  color: var(--ui-texto-3);
  background: color-mix(in srgb, currentColor 22%, transparent);
}
.ui-tend svg {
  width: 14px; height: 14px;
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: square;
}
.ui-tend--sobe  { color: var(--ui-ok); }
.ui-tend--desce { color: var(--ui-perigo-txt); }
.ui-tend--invertido.ui-tend--sobe  { color: var(--ui-perigo-txt); }
.ui-tend--invertido.ui-tend--desce { color: var(--ui-ok); }

@media (prefers-reduced-motion: reduce) {
  .ui-camadas__cortina, .ui-camadas__linha, .ui-camadas__preenche, .ui-metrica { animation: none; }
  .ui-camadas__guia, .ui-camadas__ponto, .ui-camadas__dica { transition: none; }
}

/* --- barras horizontais -------------------------------------------------- */
/* Horizontais, e nao verticais, porque o rotulo e nome de plano, de equipe ou
   de vendedor — texto que na vertical vira etiqueta girada ou cortada.     */
.ui-barras { display: flex; flex-direction: column; gap: var(--ui-s2); }
.ui-barra { display: flex; flex-direction: column; gap: 3px; }
.ui-barra__rot {
  font: 500 11.5px/1.3 var(--ui-fonte);
  color: var(--ui-texto-2);
  overflow-wrap: anywhere;
}
.ui-barra__linha { display: flex; align-items: center; gap: var(--ui-s2); }
.ui-barra__trilho {
  flex: 1; min-width: 0;
  height: 14px;
  border-radius: var(--ui-r-pill);
  background: var(--ui-eleva);
  overflow: hidden;
}
.ui-barra__preenche {
  /* `display:block` NAO e decorativo: a peca costuma ser um `<span>`, e o pai
     (`__trilho`) e item flex mas nao CONTEINER flex — entao o filho continua
     inline, e `width`/`height` simplesmente nao se aplicam. O trilho aparece
     vazio, sem nada no console. */
  display: block;
  height: 100%;
  border-radius: var(--ui-r-pill);
  /* Fio por dentro, pelo mesmo motivo da amostra da legenda: o COMPRIMENTO e
     o dado, e sem contraste contra o trilho nao se enxerga onde a barra
     termina. No tema claro `--ui-alerta` sobre o trilho da 1,96:1. Recolorir
     resolveria o contraste e criaria outro problema — no claro a tinta de
     alerta (#92400e) e a de perigo (#991b1b) sao quase o mesmo marrom, e
     alerta deixaria de se distinguir de perigo.                           */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-texto) 28%, transparent);
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--ui-acento) 72%, black),
              var(--ui-acento));
  transition: width var(--ui-t-lento) var(--ui-curva);
}
.ui-barra--ok     .ui-barra__preenche { background: var(--ui-ok); }
.ui-barra--alerta .ui-barra__preenche { background: var(--ui-alerta); }
.ui-barra--perigo .ui-barra__preenche { background: var(--ui-perigo); }
.ui-barra__val {
  flex: none;
  min-width: 46px;
  text-align: right;
  font: 700 12px/1 var(--ui-mono);
  color: var(--ui-texto);
  font-variant-numeric: tabular-nums;
}

/* --- rosca: proporcao entre poucas categorias, com total no meio --------- */
.ui-rosca { position: relative; width: 150px; height: 150px; flex: none; }
.ui-rosca svg { width: 100%; height: 100%; display: block; transform: rotate(-90deg); }
.ui-rosca__trilho { fill: none; stroke: var(--ui-eleva); }
.ui-rosca__arco { fill: none; stroke-linecap: round; }
.ui-rosca__centro {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  text-align: center;
}
.ui-rosca__n { font: 800 23px/1 var(--ui-mono); color: var(--ui-texto); letter-spacing: -.02em; }
.ui-rosca__l {
  font: 600 10px/1 var(--ui-fonte);
  letter-spacing: .05em; text-transform: uppercase;
  color: var(--ui-texto-3); margin-top: 4px;
}

/* --- pizza: so quando o TOTAL importa tanto quanto as partes ------------- */
/* Acima de cinco fatias a leitura cai e a barra le melhor. Fica aqui porque
   `vendas` a usa para a divisao por vendedor, nao como recomendacao geral. */
.ui-pizza { display: block; flex: none; }
.ui-pizza path { stroke: var(--ui-painel); stroke-width: 1.5; }

/* --- gomos: a rosca de categorias separadas ------------------------------ */
/* Nem `.ui-rosca` nem `.ui-pizza`, e a diferenca e de LEITURA:
   - `.ui-rosca` e um anel continuo, as partes se tocam e o total e a soma;
   - `.ui-pizza` sao fatias em bico, ancoradas no centro;
   - gomos separa as fatias por uma folga, e a folga carrega dado: da para
     contar quantas categorias existem sem seguir a legenda. O valor de cada
     uma fica DENTRO do proprio gomo, entao ler nao exige ir e voltar ate a
     lateral -- que e o custo real da rosca num monitor grande.

   O CANTO ARREDONDADO NAO E UM `rx`. Cada canto e um arco de raio rc tangente
   ao arco (interno ou externo) e a aresta radial; o `d` vem calculado no HTML
   porque isto e geometria, nao estilo. Um gomo estreito exige ENCOLHER o rc:
   sem isso os dois cantos internos se cruzam e o caminho vira um no. Ao trocar
   os valores, gere o `d` de novo -- nao ajuste a mao.

   Acima de seis gomos a folga come a fatia e a barra le melhor.            */
.ui-gomos { position: relative; width: 100%; max-width: 300px; margin-inline: auto; }
.ui-gomos__svg { display: block; width: 100%; height: auto; }
.ui-gomos__arco { fill: var(--c); }
.ui-gomos__fatia--1 { --c: var(--ui-serie-1); }
.ui-gomos__fatia--2 { --c: var(--ui-serie-2); }
.ui-gomos__fatia--3 { --c: var(--ui-serie-3); }
.ui-gomos__fatia--4 { --c: var(--ui-serie-4); }
.ui-gomos__fatia--5 { --c: var(--ui-serie-5); }

/* O numero mora DENTRO do gomo, entao a tinta e a da serie (ver
   `--ui-serie-tinta` la em cima), e nao a de texto do tema. Ele e redundante
   de proposito: o mesmo valor esta na legenda e na dica — numero escrito sobre
   cor nunca deve ser o unico canal.                                        */
.ui-gomos__num { fill: var(--ui-serie-tinta); font: 600 12px/1 var(--ui-mono); }

/* --- a leitura no hover --------------------------------------------------- */
/* Apagar as outras, e nao so acender a de baixo do ponteiro: num anel de cinco
   cores o olho precisa de contraste contra as vizinhas para saber onde uma
   acaba.                                                                    */
.ui-gomos__fatia { transition: opacity var(--ui-t-estado) var(--ui-curva); }
.ui-gomos__fatias:hover .ui-gomos__fatia { opacity: .32; }
.ui-gomos__fatias:hover .ui-gomos__fatia:hover { opacity: 1; }
.ui-gomos__dica {
  opacity: 0;
  /* Sem isto a dica, ao aparecer por cima do gomo vizinho, roubaria o hover
     dele e o par piscaria.                                                 */
  pointer-events: none;
  transition: opacity var(--ui-t-estado) var(--ui-curva);
}
.ui-gomos__fatia:hover .ui-gomos__dica { opacity: 1; }
.ui-gomos__dica-caixa { fill: var(--ui-eleva); stroke: var(--ui-linha); stroke-width: 1; }
.ui-gomos__dica-cor { fill: var(--c); }
.ui-gomos__dica-l { fill: var(--ui-texto-2); font: 500 9px/1 var(--ui-fonte); }
.ui-gomos__dica-v { fill: var(--ui-texto);   font: 700 9px/1 var(--ui-mono); }

/* A entrada do gomo e a MESMA das outras pecas redondas — ver "entrada dos
   graficos redondos", logo abaixo. O atraso de cada um sai do angulo inicial,
   escrito no HTML como `--atraso`.                                          */
@media (prefers-reduced-motion: reduce) {
  .ui-gomos__dica { transition: none; }
}

/* ============================ A ENTRADA DOS GRAFICOS REDONDOS ==============
   Rosca, pizza, anel e gomos entram do mesmo jeito, e isso e a regra e nao um
   detalhe: quatro pecas redondas na mesma tela, cada uma chegando de um jeito,
   parece defeito — nao variedade. Antes so o gomo animava, e era ele que
   destoava.

   Cada parte gira e cresce em torno do CENTRO DA PECA. Dai `view-box` e nao
   `fill-box`: com `fill-box` cada fatia giraria em torno de si mesma e a peca
   se desmontaria no ar. As quatro tem o centro do desenho no centro do
   `viewBox`, entao `50% 50%` serve para todas.

   O atraso vem por posicao, entao a entrada VARRE o circulo em vez de piscar
   tudo de uma vez. No gomo ele e calculado (o angulo de inicio, no HTML); nas
   outras sai do proprio indice, para que a peca funcione so com o HTML colado,
   sem ninguem lembrar de escrever atraso nenhum.

   `backwards` e NAO `both`, e a diferenca importa: com `both` a animacao
   continuaria valendo DEPOIS de terminar e venceria o `opacity` do hover — as
   outras fatias do gomo nunca apagariam. Com `backwards` ela so vale antes e
   durante, e o repouso volta a ser o do CSS base.

   O rotate nao desalinha o trilho da rosca nem o do anel: trilho e circulo
   inteiro, e circulo inteiro e indiferente a rotacao.                      */
@keyframes ui-redondo-entra {
  from { opacity: 0; transform: rotate(-16deg) scale(.82); }
  to   { opacity: 1; transform: none; }
}
.ui-rosca__arco, .ui-pizza path, .ui-anel__arco, .ui-gomos__fatia {
  transform-box: view-box;
  transform-origin: 50% 50%;
  animation: ui-redondo-entra 560ms var(--ui-curva) backwards;
  animation-delay: var(--atraso, 0ms);
}
/* `nth-of-type` conta entre os irmaos da MESMA tag: na rosca e no anel o
   primeiro <circle> e o trilho, entao o arco comeca no 2.                  */
.ui-rosca__arco:nth-of-type(3) { --atraso: 90ms; }
.ui-pizza path:nth-of-type(2)  { --atraso: 70ms; }
.ui-pizza path:nth-of-type(3)  { --atraso: 140ms; }
.ui-pizza path:nth-of-type(4)  { --atraso: 210ms; }
.ui-pizza path:nth-of-type(5)  { --atraso: 280ms; }

/* O numero do meio entra depois do desenho, e so em opacidade: os centros sao
   sobreposicoes em `position:absolute` centradas por flex, e um `scale` aqui
   brigaria com a centralizacao em vez de acompanhar o anel.                */
@keyframes ui-redondo-centro { from { opacity: 0; } to { opacity: 1; } }
.ui-rosca__centro, .ui-anel__centro {
  animation: ui-redondo-centro 420ms var(--ui-curva) 260ms backwards;
}

@media (prefers-reduced-motion: reduce) {
  .ui-rosca__arco, .ui-pizza path, .ui-anel__arco, .ui-gomos__fatia,
  .ui-rosca__centro, .ui-anel__centro { animation: none; }
  .ui-gomos__fatia { transition: none; }
}

/* --- legenda: uma so, para rosca, pizza e mapa de calor ------------------ */
.ui-legenda { display: flex; flex-wrap: wrap; gap: 6px var(--ui-s3); }
.ui-legenda__item {
  display: inline-flex; align-items: center; gap: 6px;
  font: 500 11.5px/1 var(--ui-fonte); color: var(--ui-texto-2);
}
/* A amostra leva um fio por dentro, e nao uma cor diferente: a cor DELA tem
   de ser a mesma da fatia, senao a legenda para de mapear. No tema claro
   `--ui-alerta` sobre cartao branco da 2,15:1 — a amostra somia. O fio a
   delimita sem tocar no que ela informa.                                  */
.ui-legenda__cor {
  width: 10px; height: 10px; border-radius: 3px; flex: none;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ui-texto) 30%, transparent);
}
.ui-legenda__val { color: var(--ui-texto); font-family: var(--ui-mono); font-weight: 700; }

/* --- anel de progresso / medidor ---------------------------------------- */
/* Um valor contra um teto conhecido — velocidade contratada, ping, uso. A
   rosca compara categorias entre si; o anel compara UM valor com o seu
   maximo. Trocar um pelo outro faz o leitor somar o que nao soma.          */
.ui-anel { position: relative; width: 120px; height: 120px; flex: none; }
.ui-anel svg { width: 100%; height: 100%; display: block; transform: rotate(-90deg); }
.ui-anel__trilho { fill: none; stroke: var(--ui-eleva); stroke-width: 8; }
.ui-anel__arco {
  fill: none; stroke: var(--ui-serie); stroke-width: 8; stroke-linecap: round;
  transition: stroke-dashoffset var(--ui-t-lento) var(--ui-curva);
}
.ui-anel--ok     .ui-anel__arco { stroke: var(--ui-ok); }
.ui-anel--alerta .ui-anel__arco { stroke: var(--ui-alerta); }
.ui-anel--perigo .ui-anel__arco { stroke: var(--ui-perigo); }
.ui-anel__centro {
  position: absolute; inset: 0;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
}
.ui-anel__n { font: 800 26px/1 var(--ui-mono); color: var(--ui-texto); }
.ui-anel__l { font: 600 10px/1 var(--ui-fonte); color: var(--ui-texto-3);
              letter-spacing: .05em; text-transform: uppercase; }

/* --- escore: um numero grande que resume o resto ------------------------- */
/* `saude` mostra 0-100 assim. E grafico de um dado so, e e legitimo: numa
   parede, o numero lido a quatro metros vale mais que a serie que o gerou. */
.ui-escore { text-align: center; }
.ui-escore__n {
  font: 800 64px/1 var(--ui-mono);
  color: var(--ui-texto);
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
.ui-escore__l { font: 500 12px/1.3 var(--ui-fonte); color: var(--ui-texto-3); margin-top: 6px; }
/* TINTA, e nao a cor de estado. `--ui-alerta` e a cor do SINAL — pensada para
   preencher pilula e traçar série, sobre fundo escuro. Como texto num cartao
   claro ela da 2,15:1, e o escore e justamente o numero maior da tela. Os
   tokens `-txt` existem para este caso e ja estao aferidos.               */
.ui-escore--ok     .ui-escore__n { color: var(--ui-ok); }
.ui-escore--alerta .ui-escore__n { color: var(--ui-alerta-txt); }
.ui-escore--perigo .ui-escore__n { color: var(--ui-perigo-txt); }

/* --- mapa de calor: a RAMPA, que e a parte reutilizavel ------------------ */
/* O mapa de calor de `mapa_calor` e um canvas de blobs radiais desenhado por
   cima do mapa — nao ha o que catalogar em CSS. O que se repete, e o que
   costuma sair errado, e a RAMPA e a sua legenda: sem escala visivel, cor
   quente nao quer dizer nada.

   Os seis valores sao os que estao em producao. Ficam como token proprio, e
   nao remendados aos de estado: `--ui-alerta` pode mudar de amarelo sem que
   a rampa de densidade deva mudar junto.                                   */
:root, .ui-dark, :root[data-theme="light"], .ui-light {
  --ui-calor-1: #2563eb;
  --ui-calor-2: #14b8a6;
  --ui-calor-3: #22c55e;
  --ui-calor-4: #eab308;
  --ui-calor-5: #f97316;
  --ui-calor-6: #ef4444;
}
.ui-calor__escala {
  height: 12px;
  border-radius: var(--ui-r-pill);
  border: 1px solid var(--ui-linha);
  background: linear-gradient(90deg,
    var(--ui-calor-1) 0%, var(--ui-calor-2) 30%, var(--ui-calor-3) 50%,
    var(--ui-calor-4) 70%, var(--ui-calor-5) 85%, var(--ui-calor-6) 100%);
}
.ui-calor__eixo {
  display: flex; justify-content: space-between;
  margin-top: 5px;
  font: 500 10px/1 var(--ui-mono); color: var(--ui-texto-3);
}
/* A grade de amostra existe para o guia poder mostrar a rampa aplicada sem
   um mapa por baixo. Em producao quem pinta e o canvas.                    */
.ui-calor__grade {
  display: grid; grid-template-columns: repeat(12, 1fr); gap: 2px;
}
.ui-calor__cel { aspect-ratio: 1; border-radius: 3px; }

/* Seletor de periodo: um grupo, uma escolha. E `.ui-abas` deitado, entao
   usa o mesmo desenho — nao um terceiro tipo de botao-grupo.               */
.ui-periodo { display: inline-flex; border-radius: var(--ui-r-ctl); background: var(--ui-eleva); padding: 2px; gap: 2px; }
.ui-periodo__op {
  min-height: var(--ui-ctrl-h-peq); padding: 0 11px;
  border: 0; border-radius: calc(var(--ui-r-ctl) - 2px);
  background: none; color: var(--ui-texto-2);
  font: 600 12px/1 var(--ui-fonte); cursor: pointer;
  transition: background var(--ui-t-estado) var(--ui-curva), color var(--ui-t-estado) var(--ui-curva);
}
.ui-periodo__op:hover { color: var(--ui-titulo); }
.ui-periodo__op.ativo { background: var(--ui-painel); color: var(--ui-acento-txt); }

/* ========================================================== carrossel ==
   O videowall: uma TV na parede do NOC, lida de 3 a 5 metros, sem ninguem
   por perto. Tudo o que o painel assume deixa de valer — nao ha ponteiro,
   nao ha hover, nao ha rolagem, e ninguem vai chegar perto para ler.

   Por isso a escala e OUTRA e mora aqui em tokens proprios: 12,5px de
   rotulo a 60cm equivale a nada a 4 metros. E o unico lugar do sistema onde
   o tamanho de fonte cresce com a distancia do leitor, nao com a
   importancia do conteudo.                                                 */
.ui-wall {
  --w-esc: 1;                         /* 1 = 1080p; use 1.5 em 4K          */
  display: flex; flex-direction: column;
  height: 100%;
  padding: calc(22px * var(--w-esc));
  background: var(--ui-fundo);
  color: var(--ui-texto);
  /* Deriva lenta contra queima de tela em painel que fica ligado 24h.      */
  animation: ui-antiqueima 180s ease-in-out infinite;
}
@keyframes ui-antiqueima { 0%,100% { transform: translate(0,0) } 50% { transform: translate(3px,2px) } }
@media (prefers-reduced-motion: reduce) { .ui-wall { animation: none } }

.ui-wall__cab { display: flex; align-items: center; gap: calc(14px * var(--w-esc)); padding-bottom: calc(14px * var(--w-esc)); }
.ui-wall__t {
  font: 800 calc(26px * var(--w-esc))/1 var(--ui-fonte);
  letter-spacing: .02em;
  color: var(--ui-titulo);
}
.ui-wall__sub { font: 600 calc(15px * var(--w-esc))/1 var(--ui-fonte); color: var(--ui-texto-3); }
.ui-wall__corpo { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: calc(12px * var(--w-esc)); }

/* O KPI da parede: rotulo pequeno em cima, numero enorme embaixo. A ordem e
   invertida em relacao ao painel de propria vontade — de longe o olho acha o
   numero primeiro e so entao procura o que ele mede.                       */
.ui-wall__kpi {
  display: flex; flex-direction: column; gap: calc(4px * var(--w-esc));
  padding: calc(16px * var(--w-esc));
  border-radius: var(--ui-r-card);
  background: var(--ui-painel);
}
.ui-wall__kpi-r { font: 700 calc(13px * var(--w-esc))/1 var(--ui-fonte); letter-spacing: .1em; text-transform: uppercase; color: var(--ui-texto-3); }
.ui-wall__kpi-n { font: 800 calc(44px * var(--w-esc))/1 var(--ui-mono); color: var(--ui-titulo); font-variant-numeric: tabular-nums; }
.ui-wall__kpi--ok     .ui-wall__kpi-n { color: var(--ui-ok); }
.ui-wall__kpi--alerta .ui-wall__kpi-n { color: var(--ui-alerta-txt); }
.ui-wall__kpi--perigo .ui-wall__kpi-n { color: var(--ui-perigo-txt); }

/* Pips: quantas telas existem e em qual estamos. Substituem a barra de
   progresso porque de longe uma barra fina some, e porque o operador precisa
   saber QUANTAS faltam, nao so quanto falta desta.                         */
.ui-pips { display: flex; gap: calc(8px * var(--w-esc)); flex: 1; }
.ui-pip {
  flex: 1; height: calc(4px * var(--w-esc));
  border-radius: var(--ui-r-pill);
  background: var(--ui-eleva);
  overflow: hidden;
}
.ui-pip__enche { display: block; height: 100%; width: 0; background: var(--ui-acento); }
.ui-pip.ativo .ui-pip__enche { width: 100%; transition: width linear; }
.ui-pip.visto .ui-pip__enche { width: 100%; background: var(--ui-texto-3); }

/* A grade de O.S. da parede: um cartao por ordem de servico, com o FUNDO
   dizendo em que estado ela esta agora. E o unico componente do sistema em
   que a cor carrega significado no fundo, e nao so na borda ou no texto —
   de 4 metros a borda de 1px some, o fundo nao.

   A Honest Status Rule vale aqui mais do que em qualquer outro lugar: o
   rotulo vai SEMPRE escrito ao lado da cor (`.ui-wall__os-e`). Ninguem vai
   chegar perto para conferir, e quem nao distingue verde de laranja fica
   sem a informacao inteira.

   Quatro colunas e o padrao para 1080p: com cinco o nome do cliente comeca
   a ser cortado, que e o dado que identifica a visita.                     */
.ui-wall__osgrade {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: calc(18px * var(--w-esc));
  align-content: start;
}
.ui-wall__os {
  --os-c: var(--ui-texto-3);          /* a cor do estado; ver os modificadores */
  /* `overflow:hidden` e a garantia final: espremido alem do que o componente
     aguenta, o texto e CORTADO no proprio cartao em vez de ser pintado por
     cima do vizinho — que e o defeito que nao se ve ate estar na parede.   */
  position: relative; min-width: 0; overflow: hidden;
  display: flex; flex-direction: column; gap: calc(7px * var(--w-esc));
  padding: calc(14px * var(--w-esc));
  border-radius: var(--ui-r-card);
  background: color-mix(in srgb, var(--os-c) 16%, var(--ui-eleva));
  border: 1px solid color-mix(in srgb, var(--os-c) 45%, transparent);
}
/* faixa no topo: some do olho de perto, organiza a grade de longe          */
.ui-wall__os::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0;
  height: calc(4px * var(--w-esc));
  background: var(--os-c);
  border-radius: var(--ui-r-card) var(--ui-r-card) 0 0;
}
.ui-wall__os-l1 { display: flex; align-items: center; gap: calc(10px * var(--w-esc)); min-width: 0; }
.ui-wall__os-h {
  font: 700 calc(24px * var(--w-esc))/1 var(--ui-mono);
  color: var(--ui-titulo); font-variant-numeric: tabular-nums;
}
.ui-wall__os-e {
  margin-left: auto; white-space: nowrap;
  font: 800 calc(13px * var(--w-esc))/1 var(--ui-fonte);
  letter-spacing: .06em; text-transform: uppercase; color: var(--os-c);
}
.ui-wall__os-c {
  font: 400 calc(21px * var(--w-esc))/1.2 var(--ui-fonte); color: var(--ui-titulo);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ui-wall__os-a {
  font: 400 calc(15px * var(--w-esc))/1.25 var(--ui-fonte); color: var(--ui-texto-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.ui-wall__os-l2 {
  display: flex; align-items: center; gap: calc(10px * var(--w-esc)); min-width: 0;
  padding-top: calc(6px * var(--w-esc));
  font: 400 calc(15px * var(--w-esc))/1.2 var(--ui-fonte); color: var(--ui-texto-3);
  border-top: 1px solid color-mix(in srgb, var(--os-c) 24%, transparent);
}
/* `min-width:0` nos dois: sem isso o item flex nao encolhe abaixo do
   conteudo, e um nome longo de colaborador empurra o bairro para fora.     */
.ui-wall__os-t { flex: 1 1 auto; min-width: 0; font-family: var(--ui-mono);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ui-wall__os-b { flex: 0 1 auto; min-width: 0; margin-left: auto; text-align: right;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Os cinco estados. As cores sao as mesmas que o quadro da agenda usa no
   painel — cartao e card falam da MESMA O.S. e nao podem discordar.        */
.ui-wall__os--execucao     { --os-c: var(--ui-info,     #38bdf8); }
.ui-wall__os--deslocamento { --os-c: var(--ui-alerta,   #fbbf24); }
.ui-wall__os--agendada     { --os-c: var(--ui-texto-3); }
.ui-wall__os--reagendada   { --os-c: #f97316; }
.ui-wall__os--finalizada   { --os-c: var(--ui-ok,       #34d399); }

/* O TOTAL nao e um estado: e a soma dos outros. Por isso sai da familia de
   cores de status e usa o acento -- confundi-lo com um estado faria procurar
   na parede uma fila de O.S. "totais", que nao existe. */
.ui-wall__os--todas { --os-c: var(--ui-acento, #12d3bf); }

/* Cartao de ALERTA. A cor vem do NIVEL da regra, nao do estado da O.S.: o
   cartao descreve o alerta, e sao as MESMAS tres cores que tingem a linha na
   tabela do ciclo -- ver a mesma O.S. amarela na linha e amarela no cartao e o
   que liga uma coisa a outra a distancia de TV. */
.ui-wall__os--alerta-amarelo  { --os-c: var(--ui-atencao, #ffe27a); }
.ui-wall__os--alerta-laranja  { --os-c: #ff7a2f; }
.ui-wall__os--alerta-vermelho { --os-c: var(--ui-erro, #ff3b46); }
/* Fundo tingido: no cartao de alerta a barra lateral sozinha nao basta -- ele
   divide a grade com outros tres e precisa se separar do bloco inteiro. */
.ui-wall__os[class*="ui-wall__os--alerta-"] {
  background: color-mix(in srgb, var(--os-c) 14%, var(--ui-cartao, #121c2b));
  border-color: color-mix(in srgb, var(--os-c) 45%, transparent); }
/* O POR QUE: o numero que violou a regra e o limite contra o qual foi medido.
   Quebra em duas linhas de proposito -- e frase, nao rotulo, e cortar com
   reticencias justamente aqui esconderia a razao do alerta. */
.ui-wall__os-m { font: 400 calc(15px * var(--w-esc))/1.3 var(--ui-fonte);
  color: var(--ui-texto-2); white-space: normal; }
.ui-wall__os-p { font: 400 calc(13px * var(--w-esc))/1.2 var(--ui-mono);
  color: var(--ui-texto-3); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* O cartao de alerta e mais APERTADO que o de O.S.: carrega seis linhas em vez
   de quatro e divide a aba com tres tabelas. Com a folga do cartao normal ele
   passava de 190px, e duas fileiras estouravam a aba no pior momento do dia --
   o mecanismo de corte entao comia a fileira inteira. Apertado, cabe. */
.ui-wall__os[class*="ui-wall__os--alerta-"] {
  gap: calc(3px * var(--w-esc)); padding: calc(10px * var(--w-esc)); }
.ui-wall__os[class*="ui-wall__os--alerta-"] .ui-wall__os-c {
  font-size: calc(17px * var(--w-esc)); }

/* Estado inteiro condensado numa contagem. Serve para o que ja passou: 30
   cartoes de servico encerrado empurram para fora da tela justamente o que
   ainda tem de andar. Mesma forma e mesma trilha — muda o conteudo.        */
.ui-wall__os--total { align-items: center; justify-content: center; text-align: center; gap: calc(2px * var(--w-esc)); }
.ui-wall__os-n { font: 800 calc(56px * var(--w-esc))/1 var(--ui-mono); color: var(--os-c); font-variant-numeric: tabular-nums; }
.ui-wall__os-r { font: 800 calc(17px * var(--w-esc))/1.2 var(--ui-fonte); letter-spacing: .08em; text-transform: uppercase; color: var(--ui-titulo); }
.ui-wall__os-u { font: 400 calc(13px * var(--w-esc))/1.2 var(--ui-fonte); color: var(--ui-texto-3); }

/* Contador compacto: para quando o placar divide a tela com outras faixas.
   O numero encolhe e a linha de unidade sai -- com um cartao TOTAL ao lado e o
   titulo da faixa em cima, repetir "O.S. no dia" em cada cartao gasta altura
   para dizer o que ja esta dito. Sao ~40px por cartao, que numa grade de duas
   linhas devolvem quase 100px para a lista abaixo. */
.ui-wall__os--compacto { gap: 0; padding: calc(10px * var(--w-esc)); }
.ui-wall__os--compacto .ui-wall__os-n { font-size: calc(42px * var(--w-esc)); }
.ui-wall__os--compacto .ui-wall__os-r { font-size: calc(15px * var(--w-esc)); }
.ui-wall__os--compacto .ui-wall__os-u { display: none; }

@media (max-width: 1100px) { .ui-wall__osgrade { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* Faixa de dado velho: a TV nao tem quem reclame, entao ela precisa gritar
   sozinha quando o feed parou. Fica no rodape, largura inteira.            */
.ui-wall__parado {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: calc(10px * var(--w-esc)) calc(18px * var(--w-esc));
  background: var(--ui-perigo);
  color: var(--ui-perigo-tinta);
  font: 800 calc(15px * var(--w-esc))/1.2 var(--ui-fonte);
  letter-spacing: .04em;
  text-align: center;
}

/* ================================================================= app ==
   A superficie de campo: telefone, na rua, com uma das maos ocupada e sol na
   tela. Tres coisas mudam em relacao ao painel.                            */
.ui-app { --ui-ctrl-h: 44px; --ui-ctrl-h-peq: 36px; }
/* 1. O alvo de toque tem piso de 44px. Nao e estetica: abaixo disso o erro
      de toque sobe, e aqui o erro custa uma visita refeita.                */
.ui-app .ui-btn, .ui-app .ui-app__item { min-height: 44px; }
/* 2. O texto nao encolhe. 12,5px se le a 60cm num monitor; no sol, na rua,
      nao se le.                                                            */
.ui-app { font-size: 15px; }
.ui-app .ui-app__item-t { font: 600 15px/1.3 var(--ui-fonte); color: var(--ui-titulo); }
.ui-app .ui-app__item-m { font: 400 13px/1.4 var(--ui-fonte); color: var(--ui-texto-2); }
.ui-app__item {
  display: flex; align-items: center; gap: var(--ui-s3);
  padding: var(--ui-s3);
  border-radius: var(--ui-r-ctl);
  background: var(--ui-painel);
  border: 1px solid var(--ui-linha);
}
.ui-app__lista { display: flex; flex-direction: column; gap: var(--ui-s2); }
/* 3. A acao principal fica ao alcance do polegar — embaixo, nao no topo —
      e respeita a barra do sistema (`safe-area`).                          */
.ui-app__acao {
  position: sticky; bottom: 0;
  display: flex; gap: var(--ui-s2);
  padding: var(--ui-s3);
  padding-bottom: calc(var(--ui-s3) + env(safe-area-inset-bottom, 0px));
  background: var(--ui-fundo);
  box-shadow: 0 -1px 0 var(--ui-linha);
}
.ui-app__acao > .ui-btn { flex: 1; }

/* Lista do painel "Mais opcoes" (`.nv-painel-op`) segue o `.ui-item` do guia.
   Sao 47 linhas numa coluna estreita; o que o guia da para isso e uma borda
   esquerda unica, fundo no hover e o trilho de acento marcando onde o
   ponteiro esta. Nada de preenchimento — ver o comentario em naveg-vida.js. */
.nv-painel-op.nv-painel-op.nv-painel-op {
  position: relative;
  min-height: var(--ui-ctrl-h);
  padding: 0 var(--ui-s3) 0 34px;   /* 34 = trilho + coluna do icone */
  border-radius: var(--ui-r-ctl);
  color: var(--ui-texto-2);
  font: 400 13px/1.3 var(--ui-fonte);
  transition: background var(--ui-t-estado) var(--ui-curva),
              color var(--ui-t-estado) var(--ui-curva);
}
.nv-painel-op.nv-painel-op.nv-painel-op:hover {
  background: var(--ui-eleva);
  color: var(--ui-titulo);
}
/* O icone sai do fluxo e vira COLUNA: no fluxo, um glifo mais largo empurrava
   o rotulo e a borda de texto voltava a serrilhar de linha para linha.      */
.nv-painel-op.nv-painel-op > i {
  position: absolute;
  left: 12px; top: 50%;
  transform: translateY(-50%);
  width: 16px;
  text-align: center;
  font-size: 13px;
  opacity: .8;
}
.nv-painel-op.nv-painel-op:hover > i { opacity: 1; color: var(--ui-acento-txt); }
/* Trilho: cresce do centro, como no `.ui-item`.                             */
.nv-painel-op.nv-painel-op::after {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  width: 2px; height: 0;
  background: var(--ui-acento);
  border-radius: 0 2px 2px 0;
  transform: translateY(-50%);
  transition: height var(--ui-t-estado) var(--ui-curva);
}
.nv-painel-op.nv-painel-op:hover::after { height: 60%; }

/* Cabecalho de secao da lista: micro-rotulo do guia, e nao mais uma linha
   parecida com as opcoes.                                                   */
.nv-painel-info.nv-painel-info {
  font: 700 10px/1 var(--ui-fonte);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ui-texto-3);
  padding: var(--ui-s3) var(--ui-s3) 6px;
}
/* Divisor entre grupos da lista: linha de 1px com respiro dos dois lados. Sem
   rotulo de propria vontade — ver o comentario no naveg-foco.js.            */
.nv-painel-div {
  height: 1px;
  margin: var(--ui-s2) var(--ui-s3);
  background: var(--ui-linha);
}

/* --- arvore do painel "Mais opcoes" -------------------------------------- */
/* O no pai carrega TRES sinais de que abre: a seta, a contagem do que ha
   dentro e o giro da seta ao abrir. Só a seta seria pouco num item que, de
   resto, e identico aos irmaos que executam acao direta.                    */
.nv-painel-pai.nv-painel-pai { color: var(--ui-texto); }
.nv-painel-seta {
  margin-left: auto;
  font-size: 10px;
  opacity: .7;
  transition: transform var(--ui-t-estado) var(--ui-curva);
}
.nv-painel-pai[aria-expanded="true"] .nv-painel-seta { transform: rotate(90deg); }
.nv-painel-conta {
  min-width: 20px;
  padding: 0 6px;
  margin-left: auto;
  border-radius: var(--ui-r-pill);
  background: var(--ui-eleva);
  color: var(--ui-texto-3);
  font: 700 11px/18px var(--ui-mono);
  text-align: center;
}
.nv-painel-pai[aria-expanded="true"] .nv-painel-conta { color: var(--ui-acento-txt); }
/* Com a contagem ocupando o `margin-left:auto`, a seta perde o dele. */
.nv-painel-conta + .nv-painel-seta { margin-left: 8px; }

/* A subarvore recua e ganha um fio vertical: o recuo sozinho se perde quando
   a lista rola e o primeiro filho visivel nao tem o pai na tela junto.      */
.nv-painel-sub {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 2px 0 2px 16px;
  padding-left: 10px;
  border-left: 1px solid var(--ui-linha);
}
.nv-painel-sub[hidden] { display: none; }
/* Filho e menor que o pai — a hierarquia se le tambem sem o recuo. */
.nv-painel-sub .nv-painel-op.nv-painel-op.nv-painel-op {
  min-height: 28px;
  padding-left: 30px;
  font-size: 12.5px;
}
.nv-painel-sub .nv-painel-op.nv-painel-op > i { left: 8px; font-size: 12px; }

/* ========================================================= CardLocation ==
   Comportamento: a linha responde ao ponteiro com inclinacao 3D e, ao
   clique, ABRE revelando o mapa do endereco.

   Portado de um componente React (framer-motion). Aqui nao ha React nem
   bundler, entao o que la eram `useMotionValue`/`useSpring` sao duas custom
   properties escritas pelo JS (`--cl-rx`, `--cl-ry`) e uma transicao com
   curva de mola. O resultado e o mesmo e nao entra dependencia nenhuma —
   `framer-motion` sozinho pesa mais que o painel inteiro.

   POR QUE A INCLINACAO E PEQUENA (6deg, nao 8): a linha do videowall tem
   1.500px de largura contra os 240 do cartao original. O mesmo angulo numa
   peca 6x mais larga levanta a ponta dezenas de pixels e a lista inteira
   parece descolar. Angulo menor, mesma leitura.

   REDUCED MOTION: quem pediu menos animacao no sistema recebe a abertura
   sem inclinacao e sem desenho progressivo. O conteudo continua todo la.
   ========================================================================= */
/* DUAS DURACOES, e ambas sao token porque sao ajuste de ritmo, nao estrutura:

   `--cl-mola`  quanto a inclinacao demora para ALCANCAR o alvo. Nao e a
                velocidade do percurso — e o amortecimento. Subir este valor
                deixa o movimento mais preguicoso sem mexer no tempo do bloco,
                que e o que faz a borda inteira ser percorrida.
   `--cl-abre`  quanto a gaveta leva para crescer. O `naveg-vida.js` LE este
                valor em vez de repetir o numero: o passeio precisa reservar o
                recolhimento dentro do bloco, e dois numeros que precisam bater
                sem nada que os obrigue divergem no primeiro ajuste.        */
.ui-cardloc {
  --cl-rx: 0deg;
  --cl-ry: 0deg;
  --cl-mola: 820ms;
  --cl-abre: 900ms;
  position: relative;
  cursor: pointer;
  user-select: none;
  transform-style: preserve-3d;
  transform: perspective(1200px) rotateX(var(--cl-rx)) rotateY(var(--cl-ry));
  transition: transform var(--cl-mola) cubic-bezier(.34, 1.3, .64, 1),
              box-shadow var(--ui-t-estado) var(--ui-curva);
}
.ui-cardloc:hover { box-shadow: var(--ui-sombra-3); z-index: 2; }
.ui-cardloc:focus-visible { outline: 2px solid var(--ui-acento); outline-offset: 2px; }

/* O filete que corre por baixo no hover — o `animated underline` do
   original. Cresce da esquerda, e nao aparece de uma vez.                */
.ui-cardloc::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  background: linear-gradient(90deg,
              color-mix(in srgb, var(--ui-acento) 55%, transparent),
              color-mix(in srgb, var(--ui-acento) 25%, transparent),
              transparent);
  transform: scaleX(.28);
  transform-origin: left;
  transition: transform 400ms var(--ui-curva);
  pointer-events: none;
}
.ui-cardloc:hover::after,
.ui-cardloc.aberto::after { transform: scaleX(1); }

/* --- a gaveta do mapa ---------------------------------------------------- */
/* `grid-template-rows: 0fr -> 1fr` anima altura sem numero magico: a gaveta
   se mede pelo proprio conteudo. `max-height` chutado ou corta ou deixa
   sobra, e as duas falham calado.                                          */
.ui-cardloc__mapa {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--cl-abre) var(--ui-curva);
}
.ui-cardloc.aberto .ui-cardloc__mapa { grid-template-rows: 1fr; }
.ui-cardloc__mapa > * { overflow: hidden; }

/* A altura e TOKEN, e nao numero fixo, porque quem decide o quanto o mapa
   pode ocupar e o layout que o hospeda: numa pagina que rola, 148px; numa
   grade de altura fechada — o videowall — o valor tem de sair da conta entre
   a altura das linhas e a folga reservada embaixo, senao ou o cartao encolhe
   demais ou a gaveta nao cabe.                                            */
.ui-cardloc__tela {
  position: relative;
  height: var(--ui-cardloc-tela-h, 148px);
  margin-top: var(--ui-s3);
  border-radius: var(--ui-r-ctl);
  overflow: hidden;
  background: var(--ui-fundo);
  border: 1px solid var(--ui-linha);
}
/* As ruas: desenhadas com gradiente repetido, e nao com um <svg> por linha.
   Sao seis tracos que nao carregam informacao — sao cenario. Um SVG por
   item multiplicaria nos por linha da lista sem nada em troca.            */
.ui-cardloc__tela::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(0deg, transparent 0 33px,
      color-mix(in srgb, var(--ui-texto-3) 22%, transparent) 33px 36px),
    repeating-linear-gradient(90deg, transparent 0 52px,
      color-mix(in srgb, var(--ui-texto-3) 16%, transparent) 52px 54px);
  opacity: 0;
  transition: opacity calc(var(--cl-abre) * .8) var(--ui-curva) calc(var(--cl-abre) * .3);
}
.ui-cardloc.aberto .ui-cardloc__tela::before { opacity: 1; }

/* O pino, com o mesmo desenho do `.ui-pino` do guia — nao um segundo
   vocabulario de marcador de mapa.                                        */
.ui-cardloc__pino {
  position: absolute;
  left: 50%; top: 50%;
  width: 26px; height: 26px;
  margin: -13px 0 0 -13px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--ui-acento);
  color: var(--ui-acento-tinta);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ui-acento) 30%, transparent);
  transform: scale(0) translateY(-14px);
  transition: transform calc(var(--cl-abre) * .8) cubic-bezier(.34, 1.56, .64, 1)
              calc(var(--cl-abre) * .5);
}
.ui-cardloc.aberto .ui-cardloc__pino { transform: scale(1) translateY(0); }
.ui-cardloc__pino svg { width: 14px; height: 14px; }

.ui-cardloc__coord {
  position: absolute;
  left: var(--ui-s3); bottom: 10px;
  font: 400 11.5px/1 var(--ui-mono);
  color: var(--ui-texto-2);
  opacity: 0;
  transition: opacity calc(var(--cl-abre) * .55) var(--ui-curva) calc(var(--cl-abre) * .7);
}
.ui-cardloc.aberto .ui-cardloc__coord { opacity: 1; }

/* --- selo "ao vivo" ------------------------------------------------------ */
.ui-cardloc__vivo {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 20px; padding: 0 8px;
  border-radius: var(--ui-r-pill);
  background: color-mix(in srgb, var(--ui-texto-2) 10%, transparent);
  font: 700 10px/1 var(--ui-fonte);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ui-texto-3);
}
.ui-cardloc__vivo::before {
  content: "";
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--ui-acento);
}
.ui-cardloc:hover .ui-cardloc__vivo { color: var(--ui-texto-2); }

/* --- a dica, que so aparece com o ponteiro em cima ----------------------- */
.ui-cardloc__dica {
  position: absolute;
  left: 50%; bottom: -18px;
  transform: translate(-50%, 4px);
  font: 400 10px/1 var(--ui-fonte);
  color: var(--ui-texto-3);
  white-space: nowrap;
  opacity: 0;
  transition: opacity 200ms var(--ui-curva), transform 200ms var(--ui-curva);
  pointer-events: none;
}
.ui-cardloc:hover:not(.aberto) .ui-cardloc__dica { opacity: 1; transform: translate(-50%, 0); }

/* --- variante: a gaveta FLUTUA em vez de empurrar ------------------------ */
/* Numa lista comum a gaveta empurra os itens de baixo e tudo cabe. Numa
   GRADE de linhas fixas — o videowall — nao ha para onde empurrar: o
   cartao mede 225px, a gaveta pede 160, e 43px saem cortados; na linha de
   baixo a tela do mapa cai inteira fora do palco. Aqui ela sai do fluxo e
   se ancora no cartao (o `perspective` ja faz dele bloco de contencao).

   Ela abre SEMPRE PARA BAIXO. Houve uma inversao automatica para o cartao
   da ultima linha, e ela resolvia o corte — mas punha duas direcoes de
   abertura na mesma tela. Quem garante a folga agora e o layout de quem
   hospeda: no wall, um `padding-bottom` na grade do tamanho da tela do mapa,
   que encolhe as linhas o bastante para a gaveta da ultima caber abaixo dela
   sem que nenhum bloco precise sair.                                      */
.ui-cardloc--flutuante .ui-cardloc__mapa {
  position: absolute;
  left: 0; right: 0; top: 100%;
  z-index: 1;
}
.ui-cardloc--flutuante.aberto { z-index: 20; }
.ui-cardloc--flutuante .ui-cardloc__tela {
  margin-top: 6px;
  box-shadow: var(--ui-sombra-3);
}

/* --- a luz que acompanha o percurso -------------------------------------- */
/* Um halo do acento em volta do cartao enquanto ele esta no passeio. O
   deslocamento sai das MESMAS custom properties da inclinacao, entao a luz
   nao e enfeite parado: ela orbita junto, como se viesse de onde o ponteiro
   esta — a sombra cai para o lado oposto ao da luz.

   `/ 1deg` converte o angulo em numero puro. Sem isso o `calc` nao fecha e a
   declaracao inteira e descartada em silencio: some a luz e some a sombra.  */
.ui-cardloc.ui-cardloc--luz {
  box-shadow:
    calc(var(--cl-ry) / 1deg * -1.1px) calc(var(--cl-rx) / 1deg * 1.1px) 26px 2px
      color-mix(in srgb, var(--ui-acento) 24%, transparent),
    0 0 0 1px color-mix(in srgb, var(--ui-acento) 26%, transparent),
    var(--ui-sombra-3);
  /* `transform` PRECISA estar aqui. A abreviada substitui, e nao soma: com so
     `box-shadow` nesta regra, o cartao perdia a mola da inclinacao durante
     TODO o passeio — a luz fica acesa o bloco inteiro — e o movimento corria
     sem amortecimento nenhum, que e justamente o que arredonda as viradas de
     canto. O defeito nao aparecia em teste de classe: so medindo o
     `transition-property` com a luz acesa.                                */
  transition: transform var(--cl-mola) cubic-bezier(.34, 1.3, .64, 1),
              box-shadow 420ms var(--ui-curva);
}

/* --- o recuo do clique -------------------------------------------------- */
/* Numa parede sem mouse o clique nao tem quem o mostre: o cartao simplesmente
   aparece aberto, o gesto some e fica so o resultado. Este e o mesmo recuo que
   um `:active` daria a quem esta clicando de verdade — e por ser classe, serve
   tanto ao ponteiro real quanto ao passeio automatico.

   `scale` ANTES do `perspective` faria a escala virar parte da projecao 3D;
   aqui ela entra depois das rotacoes, entao encolhe o cartao ja inclinado.  */
.ui-cardloc.ui-cardloc--pressionado {
  transform: perspective(1200px) rotateX(var(--cl-rx)) rotateY(var(--cl-ry)) scale(.972);
  /* As duas propriedades, sempre. `transition` na forma abreviada SUBSTITUI a
     declaracao anterior inteira — nao acumula. Listar so `transform` aqui
     apagava a transicao de `box-shadow` que o cartao tinha.               */
  transition: transform 120ms var(--ui-curva),
              box-shadow var(--ui-t-estado) var(--ui-curva);
}

@media (prefers-reduced-motion: reduce) {
  .ui-cardloc { transform: none; transition: none; }
  .ui-cardloc__mapa, .ui-cardloc__tela::before,
  .ui-cardloc__pino, .ui-cardloc__coord, .ui-cardloc::after,
  .ui-cardloc.ui-cardloc--luz { transition: none; }
}
