/* ===========================================================================
   PONTE DE VOCABULÁRIO — "Pro-Enterprise Dark" (MD3) → tokens do E-Store
   ---------------------------------------------------------------------------
   Estes nomes `--color-*` NÃO declaram mais cor própria: cada um aponta para o
   token equivalente de sdk-ui/tokens.css, seguindo o mapa de tradução do
   DESIGN.md ("Como isso vive no apps-proxy"). O "Pro-Enterprise Dark" está
   descontinuado desde a sessão 96 — este bloco é o que faz o portal inteiro
   passar para a identidade do E-Store sem reescrever, de uma vez, as ~360
   chamadas espalhadas por styles.css, index.html (inline) e app.js.

   POR QUE PONTE, E NÃO REESCRITA DAS 360 CHAMADAS: não existe teste de UI
   cobrindo essas telas. Reescrever tudo num commit é mudança grande sem rede,
   e o DESIGN.md já define "legado migra quando for tocado". A ponte é um ÚNICO
   lugar para mudar — mesmo argumento que justificou o ApiClient no front-end.

   ⚠️ Todo HTML que carrega este arquivo PRECISA carregar sdk-ui/tokens.css
   também. `var()` de token inexistente resolve para VAZIO, sem erro no
   console: texto invisível e fundo transparente, exatamente o modo de falha
   que o DESIGN.md registra para `--sidebar`. Guarda automática em
   public/js/__tests__/tema-shell.test.js.

   COMO ESTA PONTE MORRE: arquivo migrado deixa de usar `--color-*` e passa a
   usar os tokens direto (foi o que `#clientes-panel`, `.cf-field` e a aba
   Integrações › Regras já fizeram). Quando a contagem chegar a zero, este
   bloco sai. Não adicione nome novo aqui.

   Os três seletores existem porque substituição de custom property acontece
   onde a propriedade é DECLARADA, não onde é usada: sem repetir o bloco em
   `.dark`/`[data-theme='dark']`, uma subárvore em tema escuro herdaria os
   valores já resolvidos no claro e não escureceria.
   =========================================================================== */
:root,
.dark,
[data-theme='dark'] {
  /* Texto e traços */
  --color-on-surface: var(--foreground);
  --color-on-surface-variant: var(--muted-foreground);
  /* O mapa do DESIGN.md manda os DOIS traços para `--border`. Aqui `outline`
     vai para `--muted-foreground` de propósito: ele não é só borda — é o
     preenchimento do polegar do interruptor (`background: var(--color-outline)`)
     e o tracejado da área de upload. Colapsado em `--border` (oklch 0.922), o
     polegar ficaria cinza-claro sobre trilho cinza-claro, invisível e sem erro.
     A distinção "traço forte / traço sutil" que o MD3 codificava é preservada. */
  --color-outline: var(--muted-foreground);
  --color-outline-variant: var(--border);

  /* Marca. A paleta do E-Store é NEUTRA de propósito (`--primary` é quase
     preto) — a cor de marca é injetada em runtime pelo consumidor. Não cravar
     verde da AppsToB aqui: ver DESIGN.md, "A paleta não tem cor de marca". */
  --color-primary: var(--primary);
  --color-on-primary: var(--primary-foreground);

  --color-error: var(--destructive);

  /* ── O que SAIU da ponte, e por quê (sessões 108, 109 e 149) ───────────────
     Eram 23 nomes; são 7. É assim que a ponte morre: painel componentizado
     deixa de usar `--color-*`, e o nome que fica sem NENHUM consumidor sai.

     Sessão 149 — "Manutenção de Usuários" virou classe
     (`users-panel.js` + o bloco `#users-panel` deste arquivo) e levou os
     ÚLTIMOS usos de `surface` (o fundo do formulário antigo) e
     `surface-container` (idem) — a tela nova usa os tokens do E-Store
     direto, sem passar pela ponte.

     Sessão 109 — `#settings-panel` virou classe (`configuracoes-panel.js` +
     bloco `#settings-panel` deste arquivo), e levou os DOIS últimos usos:

       surface-container-high → era o fundo do cartão da URL (1 uso);
       surface-bright         → era o trilho dos interruptores de nível (5 usos)

     Os dois só existiam nessa tela, o que faz dela o primeiro caso em que
     componentizar um painel encolheu a ponte — o mecanismo previsto na 104,
     agora comprovado.

     Sessão 108 — saíram os 12 que ficaram sem consumidor depois que
     `styles.css` (115 usos) e `admin-prices.html` (33) passaram a apontar
     direto para os tokens do E-Store:

       surface-dim · surface-container-lowest · surface-container-highest ·
       secondary · on-secondary · secondary-container · on-error
         → já não tinham consumidor antes desta sessão;
       surface-container-low · surface-variant · primary-container ·
       on-primary-container · on-secondary-container
         → o último uso de cada um estava justamente nesses dois arquivos.

     `--color-tertiary`/`--color-on-tertiary` nunca tiveram ponte: o único
     consumidor era `.cache-badge`, que passou a usar a família
     `*-bg-soft`/`*-strong` — a que existe justamente para badge pequeno
     alcançar contraste AA.

     Os 9 que ficam têm consumidor vivo em `index.html` (inline `style=`) e em
     `app.js` (`el.style.x = ...`), e morrem conforme cada painel restante é
     componentizado — as abas do `#integrations-panel` primeiro, o detalhe do
     chamado por último. **Não adicione nome novo aqui** — a ponte só encolhe, e
     há teste prendendo a contagem. */
}

:root {
  /* Layout — não são cor, continuam nossos */
  --sidebar-width: 280px;
  --max-content-width: 1200px;

  /* Forma. A escala `--radius-sm/md/lg/xl` do MD3 saiu daqui: tokens.css
     declara as mesmas quatro em `:root` e, sendo carregado depois com a mesma
     especificidade, JÁ vencia — as linhas daqui eram código morto desde a
     sessão 96. `--radius-default` não existe no E-Store; aponta para o
     `--radius-md`, que é o raio de botão do ZardUI (`rounded-md`). */
  --radius-default: var(--radius-md);

  /* Tema claro é o padrão do E-Store. `color-scheme` governa o que o NAVEGADOR
     pinta e o CSS não alcança: date picker nativo, scrollbar padrão, autofill.
     Deixar `dark` aqui daria calendário preto dentro de painel branco. */
  color-scheme: light;
  /* Nenhuma webfont: o E-Store usa a pilha do sistema (DESIGN.md, "Tipografia").
     O 'Inter' do MD3 saiu junto com os <link> do Google Fonts — uma requisição
     de rede a menos e zero flash de texto sem estilo. */
  font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
    'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  background: var(--background);
  color: var(--foreground);
}

* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  background: var(--background);
  color: var(--foreground);
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  overflow-y: auto;
}

/* Typography Scale */
h1 { font-size: 32px; font-weight: 600; line-height: 40px; margin: 0; }
h2 { font-size: 22px; font-weight: 500; line-height: 28px; margin: 0; }
h3 { font-size: 16px; font-weight: 500; line-height: 24px; margin: 0; }
/* `font-family: inherit` nos controles de formulário não é preferência, é
   conserto: campo de formulário NÃO herda `font-family` do documento por
   padrão — o navegador impõe a fonte dele. Sem esta linha o composer do chat
   (`#chatInput`, um textarea) renderiza na fonte padrão do navegador, quase
   sempre monoespaçada. Era assim desde sempre, e não era erro do tema escuro:
   só ficou visível quando o resto da página passou a ter a pilha do sistema. */
p, body, input, textarea, select { font-family: inherit; font-size: 14px; font-weight: 400; line-height: 20px; letter-spacing: 0.25px; }
.label-md { font-size: 12px; font-weight: 500; line-height: 16px; letter-spacing: 0.5px; }

/* Layout Shell */
.app-shell {
  display: grid;
  grid-template-columns: 1fr;
  flex: 1 0 auto;
  width: 100%;
}

/* Shell migrado para os tokens do E-Store na sessão 104 — daqui para baixo
   este arquivo mistura os dois vocabulários de propósito: o shell usa os
   tokens direto, o resto continua na ponte até ser tocado. */

/* Moldura de painel. O nome `.logs-panel` é histórico — é a moldura de TODOS
   os nove painéis do portal (logs, preços, integrações, contratos, clientes,
   usuários, suporte…), não só do histórico de requisições. Renomear tocaria
   nove marcações e o AlternadorDePaineis; fica como está e o comentário paga a
   dívida de nome.

   No E-Store a hierarquia vem de SUPERFÍCIE + BORDA, nunca de sombra
   (DESIGN.md, "Forma, foco e elevação"). Por isso `card` + borda + raio em vez
   do `border-top` solto que existia aqui: no tema claro `card` e `background`
   são o mesmo branco, então sem a borda o painel não teria contorno nenhum. */
.logs-panel {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 16px 24px;
  margin: 0 24px 24px;
  overflow: hidden;
  background: var(--card);
  color: var(--card-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

#logs-table-container {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

#logs-loading {
  color: var(--muted-foreground);
  font-size: 14px;
}

#logs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  color: var(--foreground);
}

#logs-table th {
  padding: 6px 10px;
  border-bottom: 2px solid var(--border);
  text-align: left;
  font-weight: 600;
  color: var(--muted-foreground);
  white-space: nowrap;
  position: sticky;
  top: 0;
  background: var(--card);
  z-index: 2;
}

#logs-table th:nth-child(1),
#logs-table td:nth-child(1) {
  white-space: nowrap;
  min-width: 130px;
}

#logs-table th:nth-child(2),
#logs-table td:nth-child(2) {
  white-space: nowrap;
  min-width: 160px;
}

#logs-table td {
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  word-break: break-word;
}

#logs-table .log-row:hover td {
  background: var(--muted);
  cursor: pointer;
}

.log-row .expand-icon {
  display: inline-block;
  font-size: 9px;
  color: var(--primary);
  margin-right: 5px;
  transition: transform 0.15s;
  user-select: none;
}

.log-detail-row {
  display: none;
}

.log-detail-row.visible {
  display: table-row;
}

.log-detail-row td {
  padding: 0 10px 12px 28px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
}

.phase-breakdown {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 0 4px 0;
}

.phase-card {
  background: var(--muted);
  border-radius: var(--radius-md);
  padding: 8px 14px;
  flex: 1;
  min-width: 140px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.phase-card-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted-foreground);
}

.phase-card-value {
  font-size: 12px;
  color: var(--foreground);
}

.phase-card-time {
  font-size: 13px;
  font-weight: 600;
  color: var(--primary);
}

.phase-breakdown-total {
  width: 100%;
  text-align: right;
  font-size: 11px;
  color: var(--muted-foreground);
  padding-top: 2px;
}

/* Filter inputs (logs panel) */
.filter-input {
  background: var(--muted);
  border: 1px solid var(--border);
  color: var(--foreground);
  border-radius: var(--radius-default);
  padding: 0.375rem 0.625rem;
  font-size: 0.875rem;
  font-family: inherit;
  height: 34px;
}
.filter-input:focus {
  outline: none;
  border-color: var(--primary);
}

/* A dica do documento (#cDocDica) — a cor de cada estado; o texto vem do pessoa-identidade.js. */
.cf-ident-dica--ok { color: var(--success-strong); }
.cf-ident-dica--erro { color: var(--error-strong); }
.cf-ident-dica--aviso { color: var(--warning-strong); }

/* A faixa da consulta à Receita (#cConsultaFaixa, PS3): um estado por vez, só com os
   tokens do DESIGN. `[hidden]` próprio porque `.cf-faixa` fixa o display e a regra de
   autor venceria o atributo (a armadilha do `.zr-btn`, CLAUDE.md). */
.cf-faixa {
  grid-column: 1 / -1;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--muted);
  padding: 0.6rem 0.75rem;
  font-size: 0.8rem;
  line-height: 1.4;
}
.cf-faixa[hidden] { display: none; }
.cf-faixa--ok { background: color-mix(in srgb, var(--success-bg-soft) 70%, var(--background)); border-color: color-mix(in srgb, var(--success) 35%, var(--border)); }
.cf-faixa--aviso { background: color-mix(in srgb, var(--warning-bg-soft) 70%, var(--background)); border-color: color-mix(in srgb, var(--warning) 35%, var(--border)); }
.cf-faixa--erro { background: color-mix(in srgb, var(--error-bg-soft) 70%, var(--background)); border-color: color-mix(in srgb, var(--error-strong) 35%, var(--border)); }
.cf-faixa--busca { background: var(--muted); }
.cf-faixa-corpo { display: flex; flex-direction: column; gap: 0.35rem; }
.cf-faixa-corpo > span { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.cf-faixa-dica { color: var(--muted-foreground); }
.cf-faixa-etiqueta {
  align-self: flex-start;
  background: var(--success-bg-soft);
  color: var(--success-strong);
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.05rem 0.5rem;
}
.cf-faixa-acoes { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.cf-faixa-mantidos { display: flex; flex-direction: column; gap: 0.3rem; }
.cf-faixa-mantido { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; }
.cf-faixa .zr-btn { min-height: 28px; }
/* O ícone girando da consulta — o bloco global de `prefers-reduced-motion` do
   styles.css neutraliza a animação para quem pediu menos movimento. */
.cf-faixa-gira { animation: cf-faixa-girar 1s linear infinite; }
@keyframes cf-faixa-girar { to { transform: rotate(360deg); } }

/* Preenchido pela Receita (R5): a marca sai quando a pessoa edita o campo — quem
   tira é o app.js; aqui é só o desenho, com tokens. */
.cf-preenchido {
  background: color-mix(in srgb, var(--success-bg-soft) 65%, var(--background)) !important;
  border-color: color-mix(in srgb, var(--success) 45%, var(--input)) !important;
}

.collapsible-header.open .collapsible-arrow {
  transform: rotate(90deg);
}
.collapsible-body {
  display: none;
  padding: 0.9rem;
}
.collapsible-body.open {
  display: block;
}

/* Prices table (prices panel) */
.prices-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.prices-table th {
  text-align: left;
  padding: 0.625rem 0.75rem;
  background: var(--muted);
  color: var(--muted-foreground);
  font-weight: 500;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
}
.prices-table td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border);
  color: var(--foreground);
  vertical-align: middle;
}
.prices-table tr:last-child td { border-bottom: none; }
.prices-table tr:hover td { background: var(--card); }
.prices-table input[type="text"],
.prices-table input[type="number"] {
  background: var(--muted);
  border: 1px solid var(--border);
  color: var(--foreground);
  border-radius: var(--radius-default);
  padding: 0.375rem 0.5rem;
  font-size: 0.875rem;
  font-family: inherit;
  width: 100%;
  min-width: 80px;
  transition: border-color 0.15s;
}
.prices-table input:focus {
  outline: none;
  border-color: var(--primary);
}
/* ===========================================================================
   Painel de Configurações › Inferência (#settings-panel)
   ---------------------------------------------------------------------------
   Saiu de 41 tokens `--color-*` em `style=` inline no index.html (sessão 109). Os
   nomes aqui apontam DIRETO para os tokens do E-Store — não para a ponte:
   painel migrado deixa de usar `--color-*`, e foi isto que tirou
   `--color-surface-container-high` e `--color-surface-bright` da ponte, que
   caiu de 11 nomes para 9.

   O comportamento está em public/js/panels/configuracoes-panel.js. O que este
   arquivo guarda é só aparência — inclusive o estado ATIVO do interruptor, que
   antes era `rgba(111,238,225,0.25)` cravado no app.js: o verde-água do tema
   escuro descontinuado, fora do alcance da ponte e do teste que proíbe essa
   paleta em styles.css.
   =========================================================================== */
.cfg-body {
  flex: 1;
  overflow: auto;
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 1100px;
}
.cfg-modelo--fraco { opacity: 0.6; }

/* Campo recusado pelo servidor. A borda é a única sinalização — o motivo vai no
   alerta do formulário, que já existe e é lido por leitor de tela. */
.cfg-modelo-input--erro { border-color: var(--destructive); }
.cfg-provedor { opacity: 0.6; }


.btn-remove {
  background: transparent;
  border: 1px solid var(--destructive);
  color: var(--destructive);
  border-radius: var(--radius-default);
  padding: 0.25rem 0.5rem;
  cursor: pointer;
  font-size: 0.8rem;
  transition: background 0.15s;
}
.btn-remove:hover { background: color-mix(in oklch, var(--destructive) 12%, transparent); }

/* Sidebar / Contextual Panel */
.panel-side {
  display: none; /* Remove completamente a sidebar do layout */
}

/* Main Content Area */
.content {
  background: var(--background);
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Cabeçalho. Especificação do E-Store (DESIGN.md, "Layout e espaçamento"):
   `bg-background`, `border-b border-border`, `shrink-0`. Desde o menu lateral (MENU_LATERAL.md) ele
   leva só o CAMINHO da tela, o sino, o tema e o perfil — o título longo e o logo foram para a barra. */
.topbar {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  height: 56px;
  padding: 0 16px;
  background: var(--background);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0; /* Impede que o cabeçalho encolha */
}

.topbar .topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

/* Delegar ao dev (só com chamado aberto): ao lado das ações, sem encolher. O selo "Delegado ao dev · TAR-1" corta com reticências. */
.topbar .topbar-delegar {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  min-width: 0;
}
.topbar .topbar-delegar .zr-badge {
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* O caminho da tela ("Consultoria › Atividades › Agrupar em ordem"): o único trecho de largura
   VARIÁVEL do cabeçalho, então `min-width: 0` é obrigatório — item de flex nasce com
   `min-width: auto` (a maior palavra) e o cabeçalho passaria a rolar a página de lado. */
.migalhas {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  font-size: 13px;
  color: var(--muted-foreground);
}
.migalhas ol {
  display: flex;
  gap: 6px;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}
.migalhas li + li::before { content: "›"; margin-right: 6px; }
.migalhas li:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; color: var(--foreground); font-weight: 500; }

/* O perfil: foto e nome abrem um menu com o "Sair". */
.perfil { position: relative; }
.perfil-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 8px 0 4px;
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.perfil-btn:hover { background: var(--accent); }
.perfil .profile-avatar { width: 28px; height: 28px; border-width: 1px; }
.perfil-nome { font-size: 13px; font-weight: 500; }
.perfil-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 50;
  min-width: 220px;
  padding: 4px;
  background: var(--popover);
  color: var(--popover-foreground);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 4px 12px color-mix(in oklch, var(--foreground) 12%, transparent);
}
.perfil-menu[hidden] { display: none; }
.perfil-quem { display: flex; flex-direction: column; padding: 8px 10px 10px; margin-bottom: 4px; border-bottom: 1px solid var(--border); }
.perfil-quem strong { font-weight: 600; }
.perfil-quem span { font-size: 12px; color: var(--muted-foreground); }

/* 🔴 `logo-appstob.png` é BRANCO sobre transparente — foi desenhado para o
   painel escuro. Com o shell claro ele desaparece: some no cabeçalho E no card
   de login, sem erro nenhum, porque é imagem e não CSS. Foi a única regressão
   real da migração do shell (sessão 104), e apareceu só olhando a tela.

   A inversão é o mesmo tratamento que `public/suporte-painel.html` já usava
   desde que aquela página migrou — reusado aqui em vez de inventar outro. Vale
   para a marca em QUALQUER página que carregue este arquivo, presente ou
   futura, porque o seletor casa pelo nome do asset e não por classe.

   ⚠️ Funciona porque a marca é monocromática. **Se o logo ganhar cor, a
   inversão passa a mentir** — aí o certo é um asset escuro próprio
   (`logo-appstob-dark.png`), trocar o `src` e remover esta regra.
   `logo-preto.jpg` NÃO serve: é JPEG, não tem transparência, e apareceria como
   um retângulo branco dentro do card. */
img[src$="logo-appstob.png"] {
  filter: invert(1);
}
.dark img[src$="logo-appstob.png"],
[data-theme='dark'] img[src$="logo-appstob.png"] {
  filter: none;
}

#logo {
  display: block;
  height: 100% !important; /* [AJUSTE] Ocupa toda a altura permitida */
  max-height: 60px !important; /* [AJUSTE] Limite de segurança para o header */
  width: auto !important;
  object-fit: contain; /* [AJUSTE] Garante que a logo não distorça */
  margin: 0;
  padding: 0;
}

#docAnalysisButton {
    display: flex;
    align-items: center;
    gap: 8px; /* Espaço entre ícone e texto */
}

/* Auth Screen Styles */
.auth-screen {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  overflow: auto;
  padding: 24px;
}

.auth-card {
  background: var(--card);
  color: var(--card-foreground);
  border-radius: var(--radius-lg);
  padding: 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
  max-width: 480px;
  width: 100%;
  border: 1px solid var(--border);
  min-width: 0; /* Previne que o conteúdo estique o card durante a renderização */
}

.auth-help {
  font-size: 12px;
  color: var(--muted-foreground);
  max-width: 380px;
  line-height: 1.5;
}

/* Workspace Grid Fix */
.workspace {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 61px);
  width: 100%;
  max-width: var(--max-content-width);
  margin: 0 auto;
}

/* Só o `#workspace` do index.html leva `workspace--lateral` (o `bi-painel.html` reusa `.workspace` como
   coluna simples e continua assim): uma COLUNA de conteúdo (`.workspace-conteudo`) que leva o teto de
   largura (1200 px) e o `workspace--cheia`. O nome ficou do tempo do menu lateral (substituído pela
   tela inicial por módulos, TELA_INICIAL_LOOP.md). */
.workspace--lateral {
  --largura-barra: 256px;
  --largura-barra-recolhida: 64px;
  flex-direction: row;
  align-items: stretch;
  min-height: 100vh;
  max-width: none;
  margin: 0;
}

.workspace-conteudo {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  width: 100%;
  max-width: var(--max-content-width);
  margin: 0 auto;
}

/* Chat Window */
.chat-window {
  flex: 1 1 auto;
  min-height: 0;
  max-height: calc(100vh - 220px);
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 24px 0 0;
}

.message {
  max-width: 85%;
  padding: 12px 16px;
  border-radius: var(--radius-lg);
  position: relative;
  word-wrap: break-word;
}

.message.user {
  align-self: flex-end;
  background: var(--secondary);
  color: var(--secondary-foreground);
  border-bottom-right-radius: var(--radius-sm);
}

.message.assistant {
  align-self: flex-start;
  background: var(--card);
  color: var(--foreground);
  border: 1px solid var(--border);
  border-bottom-left-radius: var(--radius-sm);
}

.message.system {
  align-self: center;
  background: none;
  border: none;
  color: var(--muted-foreground);
  font-style: italic;
  font-size: 12px;
  max-width: 100%;
  text-align: center;
  padding: 4px 16px;
}

/* Barra "Exportar .md / .txt / Copiar" do retorno de Whisper/MarkItDown.
   Mora nos dois lugares do chat onde esse retorno cai: o cartão do MarkItDown
   e a bolha de transcrição pura (ver criarBarraDeExportacao em app.js).

   O divisor no topo é o que sustenta o contrato do recurso: acima dele está o
   texto como sempre foi, abaixo está o único acréscimo. `flex-wrap` porque a
   bolha tem `max-width: 85%` e em janela estreita os três botões não caberiam
   numa linha — sem isso eles vazariam a borda do cartão. */
.export-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.export-bar__rotulo {
  font-size: 12px;
  color: var(--muted-foreground);
}

/* `--color-tertiary` não tem equivalente no E-Store (DESIGN.md). Como o único
   consumidor era este badge, ele foi para a família `info`: `*-bg-soft` de
   fundo com `*-strong` de texto, que é exatamente o par que existe porque
   badge de 10-12px não alcança contraste AA com a cor cheia. */
.cache-badge {
  position: absolute;
  top: -10px;
  right: -10px;
  background-color: var(--info-bg-soft);
  color: var(--info-strong);
  border: 1px solid color-mix(in oklch, var(--info) 35%, transparent);
  padding: 3px 8px;
  border-radius: 9999px;
  font-size: 10px;
  font-weight: 600;
}

/* ── TEMPLATE PREVIEW ────────────────────────────────────────────────── */
.tpl-preview {
  flex: 1;
  padding: 2.5rem 3rem;
  overflow-y: auto;
  font-size: 0.95rem;
  line-height: 1.9;
  color: var(--foreground);
  min-height: 0;
  word-break: break-word;
  background: var(--background);
}

.tpl-preview > *:first-child { margin-top: 0; }
.tpl-preview > *:last-child  { margin-bottom: 0; }

.tpl-preview p { margin: 0.6em 0; }

.tpl-preview h1,
.tpl-preview h2,
.tpl-preview h3,
.tpl-preview h4 {
  font-weight: 700;
  line-height: 1.3;
  margin: 1.1em 0 0.4em;
  color: var(--foreground);
}
.tpl-preview h1 { font-size: 1.5em; border-bottom: 2px solid var(--border); padding-bottom: 0.2em; }
.tpl-preview h2 { font-size: 1.25em; border-bottom: 1px solid var(--border); padding-bottom: 0.15em; }
.tpl-preview h3 { font-size: 1.1em; }
.tpl-preview h4 { font-size: 1em; color: var(--muted-foreground); }

.tpl-preview ul,
.tpl-preview ol { margin: 0.5em 0 0.5em 1.6em; padding: 0; }
.tpl-preview li { margin: 0.3em 0; }
.tpl-preview li > ul,
.tpl-preview li > ol { margin: 0.15em 0 0.15em 1.2em; }

.tpl-preview strong { font-weight: 700; }
.tpl-preview em     { font-style: italic; }
.tpl-preview u      { text-decoration: underline; }

.tpl-preview code {
  font-family: 'Consolas', 'Fira Code', monospace;
  font-size: 0.85em;
  background: var(--muted);
  border-radius: 4px;
  padding: 0.15em 0.45em;
}
.tpl-preview pre {
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.85em 1em;
  margin: 0.7em 0;
  overflow-x: auto;
  font-size: 0.85em;
  line-height: 1.55;
}
.tpl-preview pre code { background: none; padding: 0; }

.tpl-preview blockquote {
  border-left: 4px solid var(--border);
  margin: 0.7em 0;
  padding: 0.3em 1em;
  color: var(--muted-foreground);
  font-style: italic;
}

.tpl-preview table {
  border-collapse: collapse;
  width: 100%;
  margin: 0.8em 0;
  font-size: 0.9em;
  display: block;
  overflow-x: auto;
}
.tpl-preview th,
.tpl-preview td {
  border: 1px solid var(--border);
  padding: 0.45em 0.8em;
  text-align: left;
}
.tpl-preview th { background: var(--muted); font-weight: 700; }
.tpl-preview tr:nth-child(even) td { background: color-mix(in oklch, var(--muted) 50%, transparent); }

.tpl-preview hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 1em 0;
}

.tpl-preview-chip,
.ql-field-chip {
  display: inline-flex;
  align-items: center;
  background: var(--foreground);
  color: var(--background);
  border-radius: 4px;
  padding: 1px 7px;
  font-family: 'Consolas', 'Fira Code', monospace;
  font-size: 0.78em;
  font-weight: 700;
  white-space: nowrap;
  vertical-align: middle;
  margin: 0 1px;
  letter-spacing: 0.01em;
  cursor: default;
  user-select: none;
}

/* ── EDITOR DE TEMPLATE — QUILL (sessão 85) ─────────────────────────────── */
#ctTplEditor .ql-toolbar.ql-snow {
  border: none;
  border-bottom: 1px solid var(--border);
  background: var(--card);
  flex-shrink: 0;
}
#ctTplEditor .ql-toolbar.ql-snow .ql-stroke { stroke: var(--muted-foreground); }
#ctTplEditor .ql-toolbar.ql-snow .ql-fill   { fill: var(--muted-foreground); }
#ctTplEditor .ql-toolbar.ql-snow .ql-picker { color: var(--muted-foreground); }
#ctTplEditor .ql-toolbar.ql-snow button:hover .ql-stroke,
#ctTplEditor .ql-toolbar.ql-snow button.ql-active .ql-stroke { stroke: var(--primary); }
#ctTplEditor .ql-toolbar.ql-snow button:hover .ql-fill,
#ctTplEditor .ql-toolbar.ql-snow button.ql-active .ql-fill { fill: var(--primary); }
#ctTplEditor .ql-picker-options {
  background: var(--muted);
  border-color: var(--border);
}
#ctTplEditor .ql-picker-item { color: var(--foreground); }

#ctTplEditorBody.ql-container {
  flex: 1;
  overflow-y: auto;
  border: none;
  min-height: 0;
}
#ctTplEditorBody .ql-editor {
  padding: 2.5rem 3rem;
  font-size: 0.95rem;
  line-height: 1.9;
  color: var(--foreground);
  caret-color: var(--primary);
  min-height: 100%;
}
#ctTplEditorBody .ql-editor.ql-blank::before {
  color: var(--muted-foreground);
  font-style: normal;
}

/* Fontes restritas (só as 3 famílias com Bold/Italic nativos no pdf-lib) */
.ql-font-helvetica { font-family: Helvetica, Arial, sans-serif; }
.ql-font-times      { font-family: 'Times New Roman', Times, serif; }
.ql-font-courier    { font-family: 'Courier New', Courier, monospace; }
#ctTplEditor .ql-picker.ql-font .ql-picker-label[data-value="helvetica"]::before,
#ctTplEditor .ql-picker.ql-font .ql-picker-item[data-value="helvetica"]::before { content: 'Helvetica'; font-family: Helvetica, sans-serif; }
#ctTplEditor .ql-picker.ql-font .ql-picker-label[data-value="times"]::before,
#ctTplEditor .ql-picker.ql-font .ql-picker-item[data-value="times"]::before { content: 'Times New Roman'; font-family: 'Times New Roman', serif; }
#ctTplEditor .ql-picker.ql-font .ql-picker-label[data-value="courier"]::before,
#ctTplEditor .ql-picker.ql-font .ql-picker-item[data-value="courier"]::before { content: 'Courier New'; font-family: 'Courier New', monospace; }
#ctTplEditor .ql-picker.ql-font .ql-picker-label:not([data-value])::before,
#ctTplEditor .ql-picker.ql-font .ql-picker-item:not([data-value])::before { content: 'Padrão'; }

.tpl-preview .ql-field-chip { margin: 0 1px; }

/* ── MARKDOWN RENDERING ───────────────────────────────────────────────── */
.message.assistant .message-content { line-height: 1.65; }

.message.assistant .message-content > *:first-child { margin-top: 0; }
.message.assistant .message-content > *:last-child  { margin-bottom: 0; }

.message.assistant .message-content p { margin: 0.55em 0; }

.message.assistant .message-content h1,
.message.assistant .message-content h2,
.message.assistant .message-content h3,
.message.assistant .message-content h4 {
  font-weight: 700;
  line-height: 1.3;
  margin: 1em 0 0.35em;
  color: var(--primary);
}
.message.assistant .message-content h1 { font-size: 1.25em; border-bottom: 1px solid var(--border); padding-bottom: 0.2em; }
.message.assistant .message-content h2 { font-size: 1.1em; }
.message.assistant .message-content h3 { font-size: 1em; }
.message.assistant .message-content h4 { font-size: 0.95em; color: var(--muted-foreground); }

.message.assistant .message-content ul,
.message.assistant .message-content ol {
  margin: 0.5em 0 0.5em 1.4em;
  padding: 0;
}
.message.assistant .message-content li { margin: 0.25em 0; }
.message.assistant .message-content li > ul,
.message.assistant .message-content li > ol { margin: 0.15em 0 0.15em 1.2em; }

.message.assistant .message-content strong { font-weight: 700; color: var(--foreground); }
.message.assistant .message-content em     { font-style: italic; opacity: 0.9; }

.message.assistant .message-content code {
  font-family: 'Consolas', 'Fira Code', monospace;
  font-size: 0.85em;
  background: var(--muted);
  border-radius: 4px;
  padding: 0.15em 0.45em;
}

.message.assistant .message-content pre {
  background: var(--muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0.85em 1em;
  margin: 0.7em 0;
  overflow-x: auto;
  font-size: 0.82em;
  line-height: 1.55;
}
.message.assistant .message-content pre code {
  background: none;
  padding: 0;
  border-radius: 0;
  font-size: inherit;
}

.message.assistant .message-content blockquote {
  border-left: 3px solid var(--primary);
  margin: 0.65em 0;
  padding: 0.3em 0.9em;
  color: var(--muted-foreground);
  font-style: italic;
  background: color-mix(in oklch, var(--muted) 60%, transparent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.message.assistant .message-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 0.7em 0;
  font-size: 0.88em;
  overflow-x: auto;
  display: block;
}
.message.assistant .message-content th,
.message.assistant .message-content td {
  border: 1px solid var(--border);
  padding: 0.4em 0.75em;
  text-align: left;
}
.message.assistant .message-content th {
  background: var(--muted);
  font-weight: 700;
}
.message.assistant .message-content tr:nth-child(even) td {
  background: color-mix(in oklch, var(--muted) 50%, transparent);
}

.message.assistant .message-content hr {
  border: none;
  border-top: 1px solid var(--border);
  margin: 0.9em 0;
}

.message.assistant .message-content a {
  color: var(--primary);
  text-decoration: underline;
}

#telemetry-model {
  color: var(--primary);
  font-weight: 600;
}


/* Chat Input */
.chat-input-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
  flex-shrink: 0;
}

.chat-input-area {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 8px 12px;
}

#chatInput {
  flex-grow: 1;
  background: transparent;
  border: none;
  padding: 8px 0;
  color: var(--foreground);
  resize: vertical;
  font-size: 14px;
  line-height: 1.5;
  max-height: 400px;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}

#chatInput::-webkit-scrollbar {
  width: 6px;
}
#chatInput::-webkit-scrollbar-track {
  background: transparent;
}
#chatInput::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}
#chatInput::-webkit-scrollbar-thumb:hover {
  background: var(--muted-foreground);
}

#chatInput:focus {
  outline: none;
  box-shadow: none;
}

/* Data Cards */
.data-card {
  background: var(--card);
  border-radius: var(--radius-md);
  padding: 12px;
  font-family: monospace;
  font-size: 12px;
  color: var(--foreground);
  word-wrap: break-word; /* CORREÇÃO: Garante que dados em JSON/Massa não quebrem o card */
}

/* Status Bar */
.status-bar {
  font-size: 12px;
  color: var(--muted-foreground);
  padding-top: 8px;
}

/* Overlay for modal sidebar */
.sidebar-overlay {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0,0,0,0.5);
  z-index: 89; /* Below the sidebar */
  cursor: pointer;
}

/* Responsive Breakpoints */
@media (max-width: 1240px) {
  /* A lógica responsiva da sidebar foi removida, então não são necessárias regras aqui. */
}

@media (max-width: 600px) {
  .app-shell {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr; /* Remove a linha extra para a barra de navegação móvel */
  }
  .panel-side {
    /* On mobile, the sidebar takes most of the width but not all */
    width: 85%;
    max-width: 320px;
  }
  .panel-side.open {
    transform: translateX(0); /* Slide in from the very edge */
  }

  /* ── Cabeçalho em tela estreita ────────────────────────────────────────
     🔴 Até a sessão 124 a `.topbar` era a ÚNICA coisa do portal que rolava a
     página de lado, e o "Piso de qualidade" do DESIGN.md proíbe exatamente
     isso. Medido a 375px, logado: `body.scrollWidth` = 549 contra um viewport
     de 375. A conta fecha em `.topbar-actions`, que sozinha ocupava 281px —
     sino (44) + LOGO (169) + hambúrguer (36) + os dois `gap` de 16.

     Não aparecia deslogado: a `.topbar` vive dentro do `#workspace`, que só
     é exibido depois do login. Por isso ninguém viu — a tela de entrada, que
     é a que se abre por acidente, está correta.

     As três decisões abaixo são explícitas, não ajuste até parar de doer. */

  .topbar {
    padding: 0 12px;
    gap: 4px;
  }

  /* O NOME do usuário sai, a foto fica: o nome é largura variável e a foto já diz quem está logado
     (o nome completo continua no menu do perfil). */
  .topbar .perfil-nome {
    display: none;
  }

  /* O texto do botão Delegar ao dev sai, o ícone fica (o aria-label continua dizendo o que é); o selo encurta. */
  .topbar .delegar-rotulo {
    display: none;
  }
  .topbar .topbar-delegar .zr-badge {
    max-width: 7rem;
  }
}
/* Garante que o avatar da Google não fique distorcido */
#userAvatar {
    border-radius: 50%;
    object-fit: cover;
}

.profile-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--border);
}
/* =========================================
   RESTAURO C-LEVEL: UI MÚLTIPLOS ANEXOS
   ========================================= */
#attachments-container {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 10px;
    width: 100%;
}

.attachment-pill {
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--muted);
    border: 1px solid var(--border);
    padding: 4px 10px;
    border-radius: 16px;
    font-size: 12px;
    color: var(--foreground);
}

.remove-pill-btn {
    background: transparent;
    border: none;
    color: var(--destructive);
    cursor: pointer;
    font-weight: bold;
    font-size: 14px;
    padding: 0 4px;
    margin-left: 4px;
}

.remove-pill-btn:hover {
    color: color-mix(in oklch, var(--destructive) 85%, black);
}


/* Exemplo de como destacar um item específico, se necessário */
#telemetry-model {
  color: var(--primary);
  font-weight: 500;
}

/* Modal Styles */
.modal {
  position: fixed;
  z-index: 1000;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(0,0,0,0.6);
  display: flex;
  align-items: center;
  justify-content: center;
}

/* `display:flex` acima é de autor e vence o `[hidden] { display:none }` do
   navegador mesmo com o atributo presente — por isso .modal[hidden] precisa
   forçar de volta (especificidade maior por ter dois seletores). */
.modal[hidden] {
  display: none;
}

.colab-dica { font-size: 0.78rem; color: var(--muted-foreground); }

.colab-prestador-linha { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.colab-prestador-linha .zr-input { flex: 1 1 12rem; min-width: 0; }
.colab-prestador-faixa-linha { display: block; }
.colab-prestador-faixa-linha + .colab-prestador-faixa-linha { margin-top: 0.25rem; }


/* Termo de confidencialidade: o gate do primeiro acesso (OC7; Telas 6 e 7 do mockup) e a leitura
   na ficha. O fundo cobre a plataforma inteira — z-index ACIMA do modal da senha (60): enquanto
   o termo está aberto, ele é o mais bloqueante dos dois. `.termo-fundo[hidden]` é obrigatório:
   a classe fixa `display` e a regra de autor venceria o `[hidden]` do navegador (CLAUDE.md). */
.termo-fundo { position: fixed; inset: 0; z-index: 70; background: rgba(15, 23, 42, 0.55); display: flex; align-items: center; justify-content: center; padding: 1rem; }
.termo-fundo[hidden] { display: none; }
.termo-modal { background: var(--background); color: var(--foreground); border-radius: var(--radius-md); box-shadow: 0 18px 40px rgba(0, 0, 0, 0.25); width: min(680px, 100%); max-height: 92vh; display: flex; flex-direction: column; gap: 0.6rem; padding: 1rem 1.1rem; }
.termo-cab { display: flex; flex-direction: column; gap: 0.15rem; }
.termo-texto { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--background); overflow-y: auto; max-height: 46vh; padding: 0.9rem; white-space: pre-wrap; font-size: 0.86rem; line-height: 1.45; }
.termo-texto:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.termo-marcar { display: flex; gap: 0.5rem; align-items: flex-start; font-size: 0.85rem; cursor: pointer; }
.termo-rodape { display: flex; gap: 0.5rem; justify-content: flex-end; flex-wrap: wrap; }
.termo-modal--suspenso { max-width: 520px; }

.modal-content {
  background: var(--card);
  margin: auto;
  padding: 24px;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  width: 90%;
  max-width: 500px;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.close-button {
  color: var(--muted-foreground);
  position: absolute;
  top: 16px;
  right: 16px;
  font-size: 28px;
  font-weight: bold;
  cursor: pointer;
}

.close-button:hover,
.close-button:focus {
  color: var(--foreground);
}

.upload-area {
    border: 2px dashed var(--muted-foreground);
    border-radius: var(--radius-md);
    padding: 30px;
    text-align: center;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease;
}

.upload-area:hover, .upload-area.drag-over {
    background-color: var(--card);
    border-color: var(--primary);
}

.upload-label p {
    margin: 0;
    font-size: 14px;
    color: var(--muted-foreground);
}

/* =========================================
   BUTTON SYSTEM — Brand Identity
   ========================================= */
button {
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
}

/* `.primary` e `.secondary` são o sistema de botão LEGADO, usado em ~40 lugares
   entre index.html e app.js. Em vez de trocar classe em cada um deles, as duas
   passam a reproduzir `.zr-btn--default` e `.zr-btn--outline` de
   sdk-ui/components.css: mesma altura (2.25rem, o `h-9` do ZardUI), mesmo raio,
   mesma tipografia, mesmos tokens. Um lugar para mudar, e todos os call sites
   convergem sem serem tocados — o mesmo movimento que as 4 cópias de
   `authHeaders()` sofreram ao passar a delegar ao ApiClient (sessão 102).

   ⚠️ Tela NOVA nasce com `.zr-btn zr-btn--default`, nunca com `.primary`
   (CLAUDE.md). Estas duas existem só para o legado convergir. */
.primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.25rem;
  background: var(--primary);
  color: var(--primary-foreground);
  border: 1px solid transparent;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.25;
  white-space: nowrap;
  outline: none;
}
.primary:hover:not(:disabled) {
  opacity: 0.9;
}

.secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.25rem;
  background: var(--background);
  color: var(--foreground);
  border: 1px solid var(--border);
  padding: 0.5rem 1rem;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.25;
  white-space: nowrap;
  outline: none;
}
.secondary:hover:not(:disabled) {
  background: var(--accent);
  color: var(--accent-foreground);
}

/* Foco de teclado: anel de 3px, igual ao `.zr-btn:focus-visible`. É o único
   indicador de teclado do sistema — nunca `outline: none` sem substituto
   (DESIGN.md, "Forma, foco e elevação"). */
.primary:focus-visible,
.secondary:focus-visible {
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
}

/* Estado desabilitado genérico. O ZardUI usa `opacity-50` +
   `pointer-events-none`; aqui mantemos `cursor: not-allowed`, que o legado já
   tinha e comunica melhor num painel interno. */
button:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}
.primary:disabled,
.secondary:disabled {
    background: var(--muted);
    border-color: var(--border);
    color: var(--muted-foreground);
}

/* Specific button disabled states to prevent overrides */
.chat-actions button:disabled,
#docAnalysisButton:disabled {
    cursor: not-allowed;
    opacity: 0.45;
    border-color: var(--border);
    color: var(--muted-foreground);
    background: transparent;
}


.integrations-subtab,
.senders-subtab,
.docs-subtab {
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted-foreground);
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  margin-bottom: -1px;
  border-radius: 0;
  transition: color 0.15s, border-color 0.15s;
  white-space: nowrap;
}

.integrations-subtab:hover:not(:disabled),
.senders-subtab:hover:not(:disabled),
.docs-subtab:hover:not(:disabled) {
  color: var(--foreground);
  background: transparent;
}

.integrations-subtab.active,
.senders-subtab.active,
.docs-subtab.active {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

/* Avatar do usuário / fallback */
#userAvatar,
.profile-avatar,
.user-avatar-fallback {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--border);
  background-color: var(--muted);
  display: block;
}


.user-avatar-fallback {
  object-fit: contain;
  padding: 4px;
  box-sizing: border-box;
}
/* =========================================================================
   PISO DE QUALIDADE — movimento reduzido
   =========================================================================
   Ver DESIGN.md, "Piso de qualidade". Preferência do SISTEMA OPERACIONAL, não
   do produto: não existe tela onde ligá-la.

   ⚠️ O bloco se repete em `sdk-ui/components.css` e no `<style>` de
   `public/suporte.html`, e a duplicação é DELIBERADA — as três são folhas de
   PÁGINA, e nem toda página carrega as três. `public/suporte.html` (o portal do
   cliente, único com público externo) carrega só `tokens.css`; um bloco só, em
   qualquer um dos arquivos, deixaria alguma página descoberta em silêncio.
   `tokens.css` não serve de casa para isto: ele espelha 1:1 as variáveis do
   E-Store e só contém valores.

   O portão não é esta nota — é `public/js/__tests__/piso-qualidade.test.js`,
   que exige o bloco de TODA folha que declare `transition`/`animation`. Folha
   nova nasce coberta ou o teste falha.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ============================================================================
   BI de Vendas — aba "Aproveitamento de Carteira"
   Portado do mockup aprovado (docs/specs/bi-dashboard-demo-vendas.html), que
   já nasceu no vocabulário do DESIGN.md — nenhum token novo entra aqui.
   ============================================================================ */

.bi-carteira-card { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 0.9rem; }

/* Nota "Fábrica / venda direta" (sessão 179) — o rodapé ADMIN do card de
   representantes, com o colapsável dos pedidos. Discreta: é rodapé, não alerta.
   O `[hidden]` explícito porque o bloco é flex (regra de autor vence o
   `[hidden]` do navegador — a armadilha do CLAUDE.md). */
.bi-nota-excluidos {
  display: flex; flex-direction: column; gap: 0.5rem; align-items: flex-start;
  margin-top: 0.4rem; padding-top: 0.7rem; border-top: 1px solid var(--border);
}
.bi-nota-excluidos[hidden] { display: none; }
.bi-nota-excluidos-texto {
  margin: 0; font-size: 0.8rem; line-height: 1.45; color: var(--muted-foreground);
  border-left: 2px solid var(--warning); padding-left: 0.6rem;
}
.bi-nota-excluidos-lista { width: 100%; margin-top: 0.2rem; }
.bi-nota-excluidos-lista[hidden] { display: none; }
.bi-card-title-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.bi-card-title-row h3 { margin: 0; }

/* ── O botão "?" e o modal de explicação (sessão 169) ───────────────────────
   Mudança 1 do redesenho do Cockpit: o texto explicativo sai do corpo da tela
   e passa a viver atrás de um `?`. Componente em `public/js/bi-ajuda.js`; o
   catálogo dos 18 verbetes está lá, e é fonte única.

   🔴 O modal reaproveita `.modal`/`.modal-content`, que JÁ têm o override de
   `[hidden]` acima — `.modal` declara `display:flex` como regra de autor, e
   sem o override o "escondido" continuaria na tela. Não crie um primitivo
   novo aqui: seria o sexto da mesma família a precisar redescobrir isso. */
.bi-ajuda-btn {
  appearance: none; cursor: pointer; flex: none; width: 1.35rem; height: 1.35rem;
  border-radius: 999px; border: 1px solid var(--border); background: var(--secondary);
  color: var(--muted-foreground); font: inherit; font-size: 0.78rem; font-weight: 700;
  line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.bi-ajuda-btn:hover { background: var(--accent); color: var(--foreground); border-color: var(--ring); }
.bi-ajuda-btn:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
/* O rodapé VIVO do verbete — hoje as duas datas de carga (170). Discreto de
   propósito: é procedência, não a explicação; quem abriu o `?` veio ler a
   prosa acima e leva a data de brinde. */
.bi-ajuda-rodape { margin-top: 0.9rem; padding-top: 0.7rem; border-top: 1px solid var(--border);
                   color: var(--muted-foreground); font-size: 0.8rem; }

/* O cabeçalho que abriga título + `?`. `> div` com `min-width: 0` para o
   título longo quebrar em vez de empurrar o botão para fora do cartão.
   🔴 `[hidden]` PRÓPRIO (achado na sessão 170, ao esconder a legenda de
   "Duas procedências"): `display: flex` é regra de autor, e sem o override
   `elemento.hidden = true` não esconde nada — é a 7ª ocorrência da mesma
   armadilha (`.zr-btn`/`.zr-badge`/`.zr-empty`/`.bi-frescor`/`.bi-recolhivel`/
   `.bi-aba`/`.modal`, ver os comentários deles). */
.bi-cab-com-ajuda { display: flex; align-items: flex-start; gap: 0.45rem; }
.bi-cab-com-ajuda[hidden] { display: none; }
.bi-cab-com-ajuda > div { min-width: 0; }
.bi-cab-com-ajuda h3 { margin: 0; }

.bi-ajuda-caixa { max-width: 34rem; max-height: 80vh; overflow-y: auto; }
.bi-ajuda-cab { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; margin-bottom: 0.6rem; }
.bi-ajuda-cab h3 { margin: 0; font-size: 1rem; }
#bi-ajuda-corpo p { margin: 0 0 0.65rem; }
#bi-ajuda-corpo p:last-child { margin-bottom: 0; }

/* Números em coluna alinham à direita e usam algarismo de largura fixa — sem
   isso, "R$ 1.234,50" e "R$ 903.482,55" não formam uma coluna legível. */
.zr-table .col-num { text-align: right; font-variant-numeric: tabular-nums; }

.toggle-metrica { display: inline-flex; border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; flex-shrink: 0; }
.toggle-metrica button { background: var(--background); color: var(--muted-foreground); border: none; padding: 0.35rem 0.8rem; font-size: 0.78rem; font-weight: 600; cursor: pointer; }
.toggle-metrica button.active { background: var(--primary); color: var(--primary-foreground); }
.toggle-metrica button:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--ring); }

.analise-chip { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; font-weight: 600; padding: 0.12rem 0.5rem; border-radius: 999px; white-space: nowrap; }
.analise-chip.positiva { background: var(--success-bg-soft); color: var(--success-strong); }
.analise-chip.novo { background: var(--info-bg-soft); color: var(--info-strong); }
.analise-chip.risco { background: var(--warning-bg-soft); color: var(--warning-strong); }
/* 04 em laranja a 30% (pedido do Eder, 16/09). A opacidade vai no FUNDO, via
   color-mix contra a superfície — nunca `opacity` no chip inteiro, que
   apagaria o texto junto e derrubaria o contraste da leitura. O texto fica no
   laranja forte, que é o que mantém a legibilidade sobre o fundo claro. */
.analise-chip.risco-04 {
  background: color-mix(in srgb, var(--warning) 30%, var(--background));
  color: var(--warning-strong);
}
.analise-chip.perdido { background: var(--muted); color: var(--muted-foreground); }

.analise-bar-cell { display: flex; align-items: center; gap: 0.55rem; }
.analise-bar-track { flex: 1; height: 0.5rem; border-radius: 999px; background: var(--muted); overflow: hidden; min-width: 3rem; }
.analise-bar-fill { height: 100%; border-radius: 999px; }

.reativacao-kpis { display: flex; gap: 1.5rem; flex-wrap: wrap; }
.reativacao-kpi { display: flex; flex-direction: column; gap: 0.2rem; }
.reativacao-kpi .v { font-size: 1.3rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.reativacao-kpi .l { font-size: 0.72rem; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.03em; }
/* 🔴 Indicadores + "Ver clientes" à DIREITA do título (print do Thiago, 23/09).
   `align-items: flex-start` no título alinha o `h3` pelo topo dos números, em
   vez de centralizá-lo contra o bloco de 2 linhas do KPI. `flex-wrap` no resumo
   faz o botão cair sob os números em tela estreita, sem estourar a linha. */
.reativacao-title-row { align-items: flex-start; }
.reativacao-resumo { display: flex; align-items: center; gap: 1.25rem; flex-wrap: wrap; }

/* 🆕 A conta da Projeção por rótulo e os inadimplentes FORA dela (mockup aprovado
   em 08/10/2026). Duas colunas lado a lado; abaixo de 900px empilham. O bloco de
   inadimplentes usa o MESMO vermelho da tag `.bi-tag-inad` — é a mesma gente. */
.reativacao-decomp-wrap { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 0.75rem; min-width: 0; }
.reativacao-decomp-wrap[hidden] { display: none; }
.reativacao-decomp { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 1rem; }
@media (max-width: 900px) { .reativacao-decomp { grid-template-columns: minmax(0, 1fr); } }
.reativacao-bloco { display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; padding: 0.75rem 0.9rem;
  border: 1px solid var(--border); border-radius: var(--radius-md); }
.reativacao-bloco--fora { background: var(--error-bg-soft); border-color: color-mix(in srgb, var(--destructive) 22%, transparent); }
.reativacao-bloco-cab { display: flex; justify-content: space-between; align-items: baseline; gap: 0.25rem 0.75rem; flex-wrap: wrap; }
.reativacao-bloco-cab h4 { margin: 0; font-size: 0.85rem; }
.reativacao-bloco--fora h4, .reativacao-bloco--fora .reativacao-regua { color: var(--error-strong); }
.reativacao-regua { font-size: 0.72rem; color: var(--muted-foreground); }
.reativacao-tabela td, .reativacao-tabela th { white-space: nowrap; font-variant-numeric: tabular-nums; }
.reativacao-bloco--fora .reativacao-tabela { background: transparent; }
.reativacao-total td { font-weight: 700; border-bottom: 0; }
.reativacao-atraso { color: var(--error-strong); font-weight: 600; }
.reativacao-sem-carga { margin: 0; font-size: 0.8rem; color: var(--error-strong); }
.reativacao-conta { display: flex; flex-wrap: wrap; gap: 0.3rem 0.75rem; margin: 0; padding-top: 0.4rem;
  border-top: 1px dashed var(--border); font-size: 0.8rem; color: var(--muted-foreground); }
.reativacao-conta b { color: var(--foreground); font-variant-numeric: tabular-nums; }
.reativacao-conta-menos b { color: var(--error-strong); }

/* O drill-down nasce fechado e é o `open` que o revela — nunca `hidden`
   alternado no JS, para o `aria-expanded` do botão e o estado visual não
   poderem divergir. */
.expand-clientes-wrap { display: none; flex-direction: column; gap: 0.6rem; }
.expand-clientes-wrap.open { display: flex; }

/* Nome do representante acima do cabeçalho da tabela — ele saiu da COLUNA,
   onde repetia o mesmo valor em todas as linhas gastando a largura que o
   município precisava. Discreto de propósito: é contexto, não dado da linha. */
.bi-tabela-representante {
  margin: 0; font-size: 0.82rem; font-weight: 600;
  color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.02em;
}
.bi-tabela-representante:empty { display: none; }

/* 🔴 `<p class="hint">` vazio ocupava um GAP inteiro (Eder, 23/09: "queime todo
   esse espaçamento"). O aviso de corte da carteira fica vazio no caso comum —
   só aparece quando o servidor trunca — e, mesmo com altura zero, seguia sendo
   um item flex do `.bi-carteira-card` (`gap: 0.9rem`): custava 14,4px de
   espaço morto entre o título e a tabela.

   ⚠️ A margem NÃO era a causa (ela já era zero) — medido na tela, não deduzido.
   Quem cobra o espaço é o `gap` do container, e de `gap` só escapa quem sai do
   fluxo.

   ⚠️ `position: absolute`, e não `display: none`: estes parágrafos são regiões
   vivas (`role="status"`), e leitor de tela não anuncia de forma confiável um
   elemento que sai de `display:none` já com o texto dentro. Fora do fluxo, ele
   continua renderizado e na árvore de acessibilidade; ao ganhar texto deixa de
   ser `:empty` e volta ao lugar sozinho. */
.hint:empty { position: absolute; margin: 0; }

/* "carteira dividida": o rótulo daquela linha descreve só a parte de um
   representante, então a marca fica junto do cliente. 🆕 Desde 23/09 vive na
   linha de tags sob o nome (`.bi-cliente-meta`), ao lado da tag de município —
   por isso virou PÍLULA inline (era `display:block` sob a coluna Município, que
   deixou de existir). */
.bi-tag-dividida {
  display: inline-flex; align-items: center;
  padding: 0.05rem 0.45rem; border-radius: 999px;
  background: var(--warning-bg-soft); color: var(--warning-strong);
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.02em;
  text-transform: uppercase; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
}

/* Filtro por rótulo da tabela larga. Multi-seleção: cada chip liga/desliga,
   mesmo gesto da legenda do mapa na aba Geografia. */
.bi-rotulos-filtro { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* 🔴 A legenda fica NUMA LINHA SÓ (pedido do Thiago, print 23/09 — "responsivas
   o suficiente para caberem numa linha"). `flex: 1 1 100%` joga a fila de chips
   para a própria linha (o rótulo de representante fica na de cima), dando a ela
   a largura inteira do card; `nowrap` impede a quebra que punha o 6º chip
   embaixo. `overflow-x: auto` é rede de segurança para telas estreitas: a fila
   ROLA no eixo X em vez de quebrar — nunca esconde opção no desktop, onde os 6
   chips compactos cabem folgados. */
.bi-rotulos-filtro > div {
  flex: 1 1 100%; min-width: 0;
  display: flex; gap: 0.3rem; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: thin;
}
.analise-filtro-chip {
  display: inline-flex; align-items: center; gap: 0.28rem; flex: 0 0 auto;
  padding: 0.18rem 0.5rem; border-radius: 999px; cursor: pointer;
  font-size: 0.72rem; font-family: inherit; white-space: nowrap;
  background: var(--background); color: var(--foreground);
  border: 1px solid var(--border);
}
.analise-filtro-chip:hover { background: var(--muted); }
/* O ativo precisa ser distinguível SEM depender só de cor — o anel some em
   monocromático e para quem não separa os tons, e o estado do filtro é o que
   explica por que a tabela tem menos linhas. */
.analise-filtro-chip.ativo {
  border-color: var(--foreground); box-shadow: inset 0 0 0 1px var(--foreground); font-weight: 600;
}

/* ── A linha de tendência sob as peças e a coluna Tendência (BT4, 19/10) ─────
   Desenho do mockup aprovado (B2): a 2ª linha da tabela atravessa as SEIS
   colunas de coleção, e a coluna Tendência resume o período com a cor do
   veredito. A cor NUNCA é o único canal — o veredito está escrito em letras
   ao lado (piso de qualidade do DESIGN.md). */
/* A borda do `zr-table` é do `tr` (`.zr-table tbody tr`), não da célula: a
   regra abaixo precisa da MESMA especificidade + a classe para vencê-la, senão
   o traço do fim do cliente puxaria uma linha extra entre os dados e a linha
   de tendência. */
.zr-table tbody tr.bi-tend-dados { border-bottom: 0; }
.bi-tend-cel { padding-top: 0; padding-bottom: 0.45rem; }
.bi-tend-linha { display: block; width: 100%; height: 24px; }
.bi-tend-col { vertical-align: top; white-space: nowrap; }
.bi-tend-rot { display: block; font-weight: 600; font-size: 0.82rem; line-height: 1.25; }
.bi-tend-det { display: block; font-weight: 400; color: var(--muted-foreground); font-size: 0.7rem; }
/* Cor de TEXTO nas variantes `-strong` (AA sobre o fundo do card); a cor cheia
   é a do traço do gráfico, que é objeto gráfico e não texto. */
.bi-tend--crescimento { color: var(--success-strong); }
.bi-tend--estagnacao { color: var(--warning-strong); }
.bi-tend--queda { color: var(--error-strong); }
.bi-tend--poucos { color: var(--muted-foreground); }

/* O quadro B1 (BT5): chips nas cores da tendência — o molde dos chips de
   análise, com as famílias soft/strong do design system. */
.analise-chip.bi-tend-chip--crescimento { background: var(--success-bg-soft); color: var(--success-strong); }
.analise-chip.bi-tend-chip--estagnacao { background: var(--warning-bg-soft); color: var(--warning-strong); }
.analise-chip.bi-tend-chip--queda { background: var(--error-bg-soft); color: var(--error-strong); }
.analise-chip.bi-tend-chip--poucos { background: var(--secondary); color: var(--muted-foreground); }

/* A linha do quadro é um FILTRO: clicável e operável pelo teclado (Enter/
   espaço), então precisa de cursor e de foco visível. A linha ligada é
   marcada sem depender só de cor — o fundo e o `aria-pressed` dizem o
   estado, e o foco ganha o anel do sistema. */
.bi-tend-quadro-linha { cursor: pointer; }
.bi-tend-quadro-linha:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--ring); }
.bi-tend-quadro-linha.ativo { background: color-mix(in oklch, var(--primary) 6%, transparent); }
.bi-tend-quadro-linha.ativo > td:first-child { font-weight: 700; }
.analise-filtro-chip:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* Injetor de parâmetros do BI — portado do mockup aprovado. */
.injector-panel { display: flex; flex-direction: column; }
/* 🔴 Mesma armadilha de `.zr-btn[hidden]`/`.zr-badge[hidden]` (sdk-ui/components.css):
   `display: flex` de autor vence o `[hidden] { display:none }` do navegador por
   especificidade+ordem. Sem isto, esconder o injetor no portal por SSO
   (bi-painel.html, sessão 150) deixava o painel visível mesmo com `hidden`
   setado — achado por teste real no navegador, não por leitura de código. */
.injector-panel[hidden] { display: none; }
.injector-head { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.85rem 1.05rem; cursor: pointer; }
.injector-head h3 { margin: 0; font-size: 0.85rem; font-weight: 600; display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; }
.injector-head .hint { font-size: 0.74rem; color: var(--muted-foreground); font-weight: 400; }
.injector-toggle { font-size: 0.72rem; color: var(--muted-foreground); flex-shrink: 0; }
.injector-head:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--ring); border-radius: var(--radius-lg); }
.injector-body { display: none; flex-direction: column; gap: 0.9rem; padding: 0 1.05rem 1.05rem; }
/* A previa entrou no corpo colapsavel em 2026-09-11 (antes vivia no cabecalho
   das abas, e recolher o injetor a deixava orfa na tela). Herda o tom de apoio
   do resto do bloco em vez de competir com o primeiro numero da tela. */
.injector-previa { margin: 0; font-size: 0.78rem; color: var(--muted-foreground); }
.injector-body.open { display: flex; }
.injector-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0.7rem; }
.injector-field { display: flex; flex-direction: column; gap: 0.3rem; }
.injector-field label { font-size: 0.72rem; font-weight: 600; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.03em; font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.injector-field select { width: 100%; }
.injector-preview { background: var(--muted); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.75rem 0.9rem; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 0.78rem; overflow-x: auto; white-space: pre; }
.injector-colecao-auto { margin: 0; font-size: 0.78rem; color: var(--muted-foreground); display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.injector-carga-vinculo { margin: 0.4rem 0 0; font-size: 0.78rem; color: var(--muted-foreground); line-height: 1.5; }
.injector-carga-vinculo code { font-family: ui-monospace, "SF Mono", Menlo, monospace; background: var(--muted); padding: 0.05rem 0.32rem; border-radius: var(--radius-sm); font-size: 0.92em; }
.injector-body code, .fine-print code { font-family: ui-monospace, "SF Mono", Menlo, monospace; background: var(--muted); padding: 0.05rem 0.32rem; border-radius: var(--radius-sm); font-size: 0.92em; }
.fine-print { font-size: 0.74rem; color: var(--muted-foreground); line-height: 1.5; margin: 0; }
.injector-visao { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.78rem; font-weight: 600; padding: 0.3rem 0.7rem; border-radius: 999px; width: fit-content; }
.injector-visao.admin { background: var(--info-bg-soft); color: var(--info-strong); }
.injector-visao.restrito { background: var(--warning-bg-soft); color: var(--warning-strong); }
.injector-visao.irrestrito { background: var(--success-bg-soft); color: var(--success-strong); }


/* ============================================================================
   BI de Vendas — abas "Visão Geral" e "Clientes & Representantes" (sessão 124)
   Mesmo vocabulário do bloco acima: nenhum token novo, só composição.
   ============================================================================ */

.bi-panel-head { display: flex; flex-direction: column; gap: 0.75rem; }
.bi-panel-head p { margin: 0; color: var(--muted-foreground); font-size: 0.85rem; max-width: 72ch; }
/* Faixa do 2º processo de carga (Geografia indisponível) — discreta (amarela,
   não vermelha): não é erro do relatório de vendas, é só a Geografia atrasada.
   🔴 `[hidden]` próprio: a classe declara `display`, e sem o override o
   elemento "escondido" continuaria ocupando espaço (a armadilha recorrente). */
.bi-geo-faixa {
  display: block; margin: 0.25rem 0 0; padding: 0.6rem 0.85rem;
  border: 1px solid #fde68a; border-radius: var(--radius-md);
  background: var(--warning-bg-soft); color: var(--warning-strong); font-size: 0.83rem;
}
.bi-geo-faixa[hidden] { display: none; }

/* 🔴 `.bi-aba[hidden]` NÃO é redundante com o `hidden` do HTML: `display: flex`
   é regra de AUTOR e vence o `[hidden] { display: none }` do navegador. Sem
   esta linha, as três abas apareceriam empilhadas de uma vez, com o atributo
   `hidden` corretamente no DOM e nenhum erro em lugar nenhum — é o mesmo
   defeito que a sessão 124 corrigiu no `.zr-alert`, e antes dela no `.modal`.
   Terceira ocorrência do mesmo mecanismo neste projeto. */
.bi-aba { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.bi-aba[hidden] { display: none; }

/* ═══ Procedência do dado — colapsada por padrão (sessão 145) ══════════════
   O selo ("banco — N clientes · gerado em … · carregado em …") continua sendo a
   resposta de maior risco desta tela: a carga do piloto é MANUAL, e sem ele
   dado de semana passada tem a mesma aparência de dado de hoje. Mas ele abria a
   aba em texto corrido, e numa demonstração a primeira linha da tela passou a
   ser infraestrutura NOSSA em vez do número do cliente. Decisão do Eder,
   2026-09-11: fica, e fica FECHADO — um clique o revela, nada foi removido.

   ⚠️ O `class="hint"` que os três selos usavam era INERTE: não existe `.hint`
   global neste projeto (só `.injector-head .hint` e `.bi-ing-card-head .hint`),
   então o selo saía no corpo de texto, do tamanho de um título de bloco.
   `.bi-frescor-texto` dá a ele o tamanho de apoio que a intenção original pedia.

   🔴 `.bi-frescor` declara `display`, então leva o seu `[hidden]` na MESMA
   linha. Nenhuma tela o esconde por atributo hoje — e é exatamente assim que a
   armadilha volta pela 18ª vez (`DESIGN.md`, "Regra de autor com `display`
   vence o `[hidden]` do navegador").

   O corpo é escondido por CLASSE (`.aberto`), não por `hidden`, no mesmo
   mecanismo de `.injector-body`/`.open` do injetor de parâmetros acima. */
.bi-frescor { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; }
.bi-frescor[hidden] { display: none; }

/* Sem carga não há procedência: os painéis escrevem `textContent = ''` enquanto
   carregam, e em private label (onde a sintética não responde) não há carimbo
   nenhum. O disparador sozinho prometeria conteúdo que não existe. */
.bi-frescor:has(.bi-frescor-texto:empty) { display: none; }

.bi-frescor-toggle {
  display: inline-flex; align-items: center; gap: 0.3rem;
  background: none; border: 0; margin: 0; padding: 0.1rem 0;
  font: inherit; font-size: 0.74rem; color: var(--muted-foreground);
  cursor: pointer; border-radius: var(--radius-sm);
}
.bi-frescor-toggle:hover { color: var(--foreground); }
/* 🔴 O disparador é escondido por atributo quando não há nada para recolher (o
   bloco de modalidade só ganha botão sob filtro de representante), e
   `inline-flex` é regra de AUTOR: vence o `[hidden]` do navegador. Achado pelo
   portão `hidden-vence-display.test.js` ANTES de subir — é a 19ª ocorrência do
   mesmo mecanismo, e a primeira que o portão pegou em código ainda não
   mesclado. */
.bi-frescor-toggle[hidden] { display: none; }

/* Aviso do que a Ficha do Cliente ainda não faz. Fica ABAIXO da série, onde a
   pessoa chega depois de clicar numa barra e não ver nada acontecer — é ali que
   a pergunta nasce, não no topo. Tom de apoio: informa, não se desculpa. */
.bi-ficha-proximo-nivel { margin: 0.2rem 0 0; font-size: 0.76rem; color: var(--muted-foreground); }

/* ═══ Nível 2 da Ficha do Cliente — pedidos da coleção (sessão 145) ════════
   A barra do Nível 1 virou `<button>`: precisa perder a aparência de botão do
   navegador e ganhar a pista de que é clicável. Só as coleções COM id são
   botão (ver `#pintarFichaCliente`), então `.bi-serie-col--clicavel` é o que
   distingue as duas — e uma coleção sem id continua inerte, sem prometer
   clique que não funciona. */
.bi-serie-col--clicavel {
  background: none; border: 0; padding: 0; margin: 0; font: inherit;
  color: inherit; cursor: pointer; text-align: center;
}
.bi-serie-col--clicavel:hover .bi-serie-trilho { outline: 2px solid var(--border); outline-offset: 2px; }
.bi-serie-col--clicavel:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }

#bi-ficha-n2[hidden] { display: none; }
.bi-ficha-n2-titulo { margin: 0.6rem 0 0.1rem; font-size: 0.95rem; }
.bi-ficha-voltar {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: none; border: 0; margin: 0; padding: 0.15rem 0;
  font: inherit; font-size: 0.76rem; color: var(--primary);
  cursor: pointer; border-radius: var(--radius-sm);
}
.bi-ficha-voltar:hover { text-decoration: underline; }
/* 🔴 A série do Nível 1 é escondida por atributo quando o Nível 2 abre, e
   `.bi-serie` declara `display: grid` — regra de autor, que vence o `[hidden]`
   do navegador. Sem esta linha os dois níveis apareceriam empilhados, com
   valores de fontes diferentes na mesma tela. */
.bi-serie[hidden] { display: none; }
.bi-frescor-seta { font-size: 0.62rem; }
.bi-frescor .bi-frescor-texto { display: none; margin: 0; font-size: 0.76rem; color: var(--muted-foreground); }
.bi-frescor.aberto .bi-frescor-texto { display: block; }

/* ═══ Bloco recolhível genérico (sessão 145) ═══════════════════════════════
   Mesmo mecanismo do selo de procedência — classe `.aberto`, nunca `hidden` —
   mas para conteúdo qualquer. Hoje agrupa o aviso de modalidade e o filtro
   cruzado da Visão Geral: os dois explicam COMO a tela recorta, não mostram
   número nenhum do cliente, e juntos empurravam os KPIs para baixo da dobra.

   🔴 Declara `display`, então leva o seu `[hidden]` na mesma linha. */
.bi-recolhivel { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; }
.bi-recolhivel[hidden] { display: none; }
.bi-recolhivel-corpo { display: none; width: 100%; flex-direction: column; gap: 1rem; }
.bi-recolhivel.aberto .bi-recolhivel-corpo { display: flex; }

/* "Recorte e observações" — deixou de ser recolhível na sessão 173 (Eder):
   sempre aberto. O título mantém a tipografia do disparador antigo para a aba
   não mudar de ritmo, só sem o cursor de clique. 🔴 Declara `display`, então
   leva o seu `[hidden]` na mesma linha. */
.bi-recorte { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; width: 100%; }
.bi-recorte[hidden] { display: none; }
.bi-recorte > .zr-card { width: 100%; }
/* 🔴 A caixa do filtro em UMA linha (173, Eder no tablet): rótulo ao lado do
   seletor, e a instrução na mesma linha. Com o rótulo em cima ela media 93px; o
   gestor precisa ver até o "Prêmio se bater" dos medidores sem rolar. Em tela
   estreita a linha quebra pelo `flex-wrap` de `.filtro-linha`. */
#bi-geral-filtro { padding-block: 0.45rem; margin-bottom: 0.85rem; }
#bi-geral-filtro .filtro-campo { flex-direction: row; align-items: center; gap: 0.6rem; }
#bi-geral-filtro .filtro-instrucao { align-self: center; padding-bottom: 0; }

/* 🔴 Topo compacto do ADMIN (173): os dois vãos que o Eder marcou no print —
   filtro → "O que a empresa realizou" e cartões → "Acompanhamento de Metas" —
   caem de 44px para o `gap` da aba (16px). SÓ na persona ADMIN: o
   representante vê os mesmos dois blocos, e a tela dele não muda nesta
   sessão. Quem põe a classe é `#aplicarPersona`, pela MESMA decisão pura
   (`topoCompactoPorPersona`). */
.bi-aba--topo-compacto > #bi-geral-realizado-secao,
.bi-aba--topo-compacto > #bi-metas-patamares-secao { margin-top: 0; }
/* Medido no tablet do Eder (1280×620): só os vãos pedidos deixavam o fim do
   "Prêmio se bater" em 620px, colado no limite. Um ganho a mais, sem tirar
   nada de vista: o arco do medidor cai de 117 para 100px (o desenho escala
   por inteiro; número e rótulos não mudam de tamanho). A região viva vazia
   VOLTOU a ocupar o seu `gap` (173, 2ª rodada): com o seletor abaixo da
   coleção, ele é o respiro entre as abas e "O que a empresa realizou". */
.bi-aba--topo-compacto #bi-metas-medidores svg { max-height: 6.25rem; }

/* ═══ Modalidade de venda — 1º bloco da Visão Geral (sessão 141) ═══════════
   Decisão do Eder: as 3 modalidades abrem a aba, e os blocos abaixo seguem a
   escolha. Private label mostra só total por coleção e total por cliente.

   🔴 **SEXTA ocorrência da armadilha do `[hidden]` neste projeto, e esta foi
   achada ANTES de subir — medindo o CSS, não olhando a tela.** A regra de
   exibição por modalidade esconde os três `.bi-carteira-card` (`display: flex`),
   que é regra de AUTOR e vence o `[hidden] { display:none }` do navegador por
   empate de especificidade + ordem — sem a linha abaixo, "esconder os blocos da
   sintética em private label" não esconderia NADA, com o atributo `hidden`
   corretamente no DOM e nenhum erro em lugar nenhum. Antecessores: `.zr-btn`,
   `.zr-badge`, `.zr-alert`, `.modal`, `.bi-aba`. (Desde a KP4 o bloco dos KPIs
   é um contêiner SEM classe, com a grade `.zr-kpi-grade` dentro — o `hidden`
   dele vale nativo, e o override do `.bi-kpi-grid` deixou de existir junto com
   a classe.)

   ⚠️ `.zr-tab` é a exceção conferida: não declara `display`, então o `hidden`
   dos botões de aba funciona sem override. */
.bi-carteira-card[hidden] { display: none; }

.bi-mod-card-wrap { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 0.9rem; }
.bi-mod-card-wrap[hidden] { display: none; }
.bi-mod-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.85rem; }
@media (max-width: 900px) { .bi-mod-grid { grid-template-columns: 1fr; } }

/* Cartão-botão: é `<button>` porque escolher a modalidade é uma AÇÃO, e botão
   já vem com foco de teclado, Enter/Espaço e papel de acessibilidade. Um
   `<div>` com `onclick` exigiria reimplementar os três à mão. */
.bi-mod-card {
  display: flex; flex-direction: column; gap: 0.3rem; align-items: flex-start;
  padding: 0.95rem 1.05rem; text-align: left; font: inherit; cursor: pointer;
  border: 1px solid var(--border); background: var(--card); color: var(--card-foreground);
}
.bi-mod-card[hidden] { display: none; }
.bi-mod-card:hover { border-color: var(--ring); }
/* O estado ativo não é só cor: a borda mais grossa sobrevive a daltonismo e a
   `prefers-contrast`, e o `aria-pressed` do botão diz o mesmo a leitor de tela. */
.bi-mod-card.ativa { border-color: var(--primary); border-width: 2px; background: var(--accent); }
.bi-mod-rotulo { font-size: 0.72rem; font-weight: 500; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.04em; }
.bi-mod-valor { font-size: 1.35rem; font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.bi-mod-apoio { font-size: 0.78rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-mod-selo { font-size: 0.68rem; font-weight: 600; color: var(--warning-strong); background: var(--warning-bg-soft); padding: 0.05rem 0.35rem; border-radius: var(--radius-sm); }
.bi-mod-fonte { margin: 0; max-width: 88ch; }
.bi-mod-aviso {
  margin: 0; padding: 0.7rem 0.9rem; border-radius: var(--radius-md);
  background: var(--warning-bg-soft); color: var(--warning-strong); border: 1px solid var(--warning);
  max-width: 100%;
}
.bi-mod-aviso[hidden] { display: none; }

/* 🔴 `min-width: 0` nos filhos, senão o `overflow: auto` do `.zr-table-wrap`
   NÃO clipa nada: item de flex nasce com `min-width: auto`, que é o tamanho do
   CONTEÚDO — então a tabela larga estica o cartão, o cartão estica a aba e a
   PÁGINA passa a rolar de lado. Medido a 375px na sessão 124: a tabela de Top
   5 clientes tem 406px e empurrava o body inteiro. O container com `overflow`
   parece resolver e não resolve; é preciso deixá-lo encolher primeiro. */
.bi-aba > * { min-width: 0; }

/* ---- KPIs da Visão Geral ----
   Desde a KP4 (item 9) os cartões são o `.zr-kpi` do design system, pela função
   única (`public/js/cartao-indicador.js`) — o CSS próprio `bi-kpi-*` saiu. O
   tom negativo da venda (devolução maior que venda) virou `zr-kpi__valor--negativo`. */

/* ---- Série de coleções ----
   🔴 `align-items: start`, nunca `end` — achado com dado real (17
   representantes, sessão 128). O nome embaixo do trilho tem comprimento
   VARIÁVEL (uma linha ou três), então a coluna inteira (valor+trilho+nome)
   também varia de altura; com `end`, a grade alinha cada coluna pelo
   RODAPÉ, e a coluna mais curta ganha um respiro em CIMA para compensar —
   empurrando justamente o trilho para baixo, fora do alinhamento das
   demais. O usuário lê isso como "ordenação errada", quando é só a barra
   de um item começando mais abaixo que a do vizinho. `start` prende o
   valor+trilho sempre no topo da célula; só o nome, que não compara nada,
   estica para baixo em quantidades diferentes — e isso não confunde. */
.bi-serie { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.6rem; align-items: start; }
@media (max-width: 700px) { .bi-serie { grid-template-columns: repeat(3, 1fr); } }
.bi-serie-col { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; min-width: 0; }
.bi-serie-valor { font-size: 0.76rem; font-weight: 600; font-variant-numeric: tabular-nums; }
/* Altura fixa do trilho: as barras são percentuais do maior valor, e sem uma
   referência comum a comparação visual entre coleções não significaria nada. */
.bi-serie-trilho { width: 100%; height: 7rem; display: flex; align-items: flex-end; background: var(--muted); border-radius: var(--radius-sm); overflow: hidden; }
.bi-serie-barra { width: 100%; background: var(--chart-2); border-radius: var(--radius-sm) var(--radius-sm) 0 0; min-height: 2px; }
.bi-serie-barra.corrente { background: var(--primary); }
.bi-serie-nome { font-size: 0.72rem; text-align: center; color: var(--foreground); overflow-wrap: anywhere; }
.bi-serie-apoio { font-size: 0.68rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-serie-selo { font-size: 0.64rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--warning-strong); background: var(--warning-bg-soft); padding: 0.05rem 0.4rem; border-radius: 999px; }
.bi-serie-selo.filtro { color: var(--info); background: var(--info-bg-soft); }
/* Barra/coluna clicável (filtro cruzado) — foco de teclado e destaque de
   seleção, sem depender de cor sozinha (o selo "filtrando" já diz isso). */
.bi-serie-col[data-colecao], .bi-serie-col[data-rep] { cursor: pointer; border-radius: var(--radius-sm); }
.bi-serie-col[data-colecao]:hover .bi-serie-trilho, .bi-serie-col[data-rep]:hover .bi-serie-trilho { outline: 2px solid var(--border); outline-offset: 2px; }
.bi-serie-col[data-colecao]:focus-visible, .bi-serie-col[data-rep]:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.bi-serie-col.selecionada .bi-serie-barra { background: var(--info); }
.bi-serie-col.selecionada .bi-serie-trilho { box-shadow: inset 0 0 0 2px var(--info); }

/* ---- Série de representantes (Vendas por representante / Aproveitamento) ----
   Mesmo papel do .bi-serie, mas para uma contagem VARIÁVEL de representantes
   (dezenas, não seis fixos). 🔴 8 colunas FIXAS, não `auto-fill` — pedido do
   Eder (sessão 128): com `auto-fill`, um cartão largo empacotava ~12 por
   linha, e mais colunas por linha só piora a comparação visual (rótulo mais
   apertado, barra mais fina). `repeat(8, 1fr)` deixa o excedente estourar
   para a linha de baixo sozinho — grid não precisa de JS para paginar.
   `align-items: start` pela mesma razão do `.bi-serie` acima. */
.rep-series { display: grid; grid-template-columns: repeat(8, 1fr); gap: 0.55rem; align-items: start; padding-bottom: 0.2rem; }
@media (max-width: 700px) { .rep-series { grid-template-columns: repeat(4, 1fr); } }

/* ---- Só a primeira linha, o resto sob "Mostrar os outros N" (sessão 145) ----
   Decisão do Eder, 2026-09-11: com 19 representantes cada cartão ocupava 3
   linhas — 6 linhas de barra só nos dois, e a aba não caberia numa tela de
   apresentação. Nada é removido: um clique abre.

   🔴 O corte é por CLASSE na coluna excedente, nunca por `max-height` no
   contêiner. A altura de uma coluna varia com o nome do representante (há
   rótulos que quebram em duas linhas, "JAIR ALTO VALE E MEDIO SC"), então
   qualquer altura fixa cortaria barra no meio em vez de por linha inteira.

   ⚠️ As 8 colunas vivem em DOIS lugares que precisam concordar: a grade acima
   e `PainelBiVendasGeral.POR_LINHA`, que decide quais colunas recebem a
   classe. Divergirem não dá erro — corta no lugar errado. Preso por
   `bi-vendas-ver-mais.test.js`, que lê o número deste arquivo.

   🔴 `.bi-ver-mais` declara `display` E é escondido por `hidden` (some quando
   não há excedente), então leva o seu override — a armadilha de sempre. */
.rep-series:not(.expandida) .bi-serie-col--extra { display: none; }

/* O mesmo corte numa TABELA (aba "Clientes & Representantes", 10 primeiros).
   ⚠️ `<tr>` ganha `display: table-row` do NAVEGADOR, não de uma regra de autor
   — então aqui `display: none` de autor vence sem precisar de override, ao
   contrário de `.bi-ver-mais`/`.bi-frescor` logo acima. A diferença é de onde
   vem a regra que se está sobrepondo, não de qual propriedade. */
tbody:not(.expandida) .bi-linha--extra { display: none; }

.bi-ver-mais {
  display: inline-flex; align-items: center; gap: 0.3rem; align-self: flex-start;
  background: none; border: 0; margin: 0.45rem 0 0; padding: 0.15rem 0;
  font: inherit; font-size: 0.74rem; font-weight: 500; color: var(--primary);
  cursor: pointer; border-radius: var(--radius-sm);
}
.bi-ver-mais[hidden] { display: none; }
.bi-ver-mais:hover { text-decoration: underline; }
.bi-ver-mais-seta { font-size: 0.62rem; }

/* ---- Filtro cruzado (Representante × Coleção) — aba Visão Geral ----
   🔴 `display:flex` é regra de autor e vence o `[hidden]{display:none}`
   nativo (mesma armadilha já documentada para `.zr-alert`/`.zr-btn`/
   `.zr-badge`/`.zr-empty` no projeto) — sem o override abaixo, esconder o
   cartão para tipoUsuario=REPRESENTANTE não teria efeito nenhum. */
.filtro-card[hidden] { display: none; }
.filtro-card { padding: 0.85rem 1.1rem; display: flex; flex-direction: column; gap: 0.6rem; }
/* 🔴 `.zr-card` (sdk-ui/components.css) fixa `border: 1px solid var(--border)`
   — o SHORTHAND, que reseta border-color inteiro. components.css carrega
   DEPOIS de styles.css (index.html), então `.filtro-card { border-color }`
   sozinho perde a cascata por ORDEM DE ARQUIVO, mesmo com a mesma
   especificidade (medido: computava a cor padrão, não a tingida). O composto
   `.zr-card.filtro-card` (2 classes) tem especificidade MAIOR que `.zr-card`
   (1 classe) e vence independente de ordem — é o que os outros `.bi-*-card`
   nunca precisaram porque nenhum deles toca `border-color`.

   🔴 `:not(.zr-moldura-ia)` (correção 2 da sessão, KP4): o filtro do Cockpit
   voltou a ter a classe `.filtro-card` pelo ESPAÇAMENTO (padding, flex e gap
   abaixo), mas a cor de borda daqui NÃO pode valer sobre a moldura — ela
   pintaria por cima do `border: 2px solid transparent` do `.zr-moldura-ia` e
   apagaria o gradiente. A cor tingida fica só para um `.filtro-card` SEM
   moldura (nenhum hoje); o espaçamento vale nos dois. */
.zr-card.filtro-card:not(.zr-moldura-ia) { border-color: color-mix(in srgb, var(--info) 30%, var(--border)); }
.filtro-linha { display: flex; align-items: center; gap: 0.9rem; flex-wrap: wrap; }
.filtro-campo { display: flex; flex-direction: column; gap: 0.25rem; }

/* ============================================================================
   Filtro de Representante do Cockpit — o realce "Modo IA" virou PRIMITIVO.

   Até a KP3, a borda multicolor girava por uma regra composta daqui
   (`.zr-card.filtro-card.modo-ia`). Desde a KP4 (item 9, D-P3) ela é o
   `.zr-moldura-ia` de `sdk-ui/components.css` — o MESMO componente que envolve
   a faixa de filtros de toda tela de lista. O `@property`, o `@keyframes` e o
   conic-gradient (tokens `--ia-1..4`) mudaram para lá; só a BORDA pinta, o
   miolo fica no `--card` (pedido do Thiago), e o movimento reduzido continua
   coberto pelo bloco `*` global.

   🔴 O card continua só de ADMIN: `#pintarFiltroCruzado` faz
   `cartao.hidden = !admin`, e o `hidden` agora vale nativo (o contêiner não
   fixa `display` por classe própria). Como a borda deixou de ser sinal
   exclusivo do administrador (D9), o rótulo ganhou o selo "Visão Admin" — desde
   o item 47 (20/10/2026) ele é o badge padrão do projeto
   (`.zr-badge .zr-badge--default`), sem classe própria.
   ============================================================================ */
.filtro-rotulo-ia { display: inline-flex; align-items: center; gap: 0.35rem; flex-wrap: wrap; }
.filtro-ia-faisca { width: 0.9rem; height: 0.9rem; }
/* A instrução ao lado do seletor (173): alinhada à CAIXA, não ao rótulo acima
   dela, e quebrando para baixo em tela estreita pelo `flex-wrap` da linha. */
.filtro-instrucao { margin: 0; align-self: flex-end; padding-bottom: 0.6rem; flex: 1 1 18rem; }
.filtro-campo label { font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); }
/* O selo "Visão Admin" (item 47, D1–D4): é o `.zr-badge--default` como está — fundo
   `--primary`, letra `--primary-foreground`, `nowrap` do próprio primitivo. O rótulo pai
   é caixa-alta e espaçado; o selo desliga os dois para o texto sair como foi escrito. */
.filtro-rotulo-ia .zr-badge { text-transform: none; letter-spacing: normal; }
.filtro-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.filtro-chip { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.75rem; font-weight: 500; background: var(--info-bg-soft); color: var(--info); padding: 0.25rem 0.45rem 0.25rem 0.65rem; border-radius: 999px; }
.filtro-chip button { border: none; background: transparent; color: inherit; cursor: pointer; font: inherit; font-size: 0.85em; line-height: 1; padding: 0.1rem; }

/* ---- Clientes & Representantes ---- */
.bi-busca { max-width: 22rem; flex: 1 1 14rem; }
/* Segunda linha dentro de uma célula (a coleção da última compra, a base do
   percentual). Bloco, para não empurrar o número principal da coluna. */
.bi-sub { display: block; font-size: 0.68rem; color: var(--muted-foreground); font-weight: 400; }


/* ============================================================================
   BI de Vendas — aba "Geografia" (mapa de cobertura de praça, sessão 124)
   ============================================================================ */

.bi-geo-legenda { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.bi-geo-legenda-item {
  display: inline-flex; align-items: center; gap: 0.45rem;
  background: var(--background); border: 1px solid var(--border); border-radius: 999px;
  padding: 0.25rem 0.7rem; font-size: 0.76rem; color: var(--foreground); cursor: pointer;
}
.bi-geo-legenda-item:hover { background: var(--accent); }
.bi-geo-legenda-item:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
/* Categoria oculta continua LEGÍVEL, só apagada — sumir com o item faria a
   pessoa perder o botão que a traz de volta. */
.bi-geo-legenda-item.oculto { opacity: 0.45; text-decoration: line-through; }
.bi-geo-legenda-item strong { font-variant-numeric: tabular-nums; }
.bi-geo-bolinha { width: 0.7rem; height: 0.7rem; border-radius: 50%; flex-shrink: 0; }

/* As cinco cores do mapa. `--chart-*` NÃO serve aqui: elas são uma paleta
   categórica sem ordem, e estes cinco estados têm significado — verde é bom,
   âmbar é alvo, cinza é irrelevante, azul é informativo. */
.bi-geo-bolinha.vendeu,                 .bi-geo-ponto.vendeu,
.bi-geo-area.vendeu                     { fill: var(--success); background: var(--success); }
.bi-geo-bolinha.nao-coberta-principal,  .bi-geo-ponto.nao-coberta-principal,
.bi-geo-area.nao-coberta-principal      { fill: var(--warning); background: var(--warning); }
.bi-geo-bolinha.nao-coberta-secundaria, .bi-geo-ponto.nao-coberta-secundaria,
.bi-geo-area.nao-coberta-secundaria     { fill: var(--muted-foreground); background: var(--muted-foreground); }
.bi-geo-bolinha.fora-de-area,           .bi-geo-ponto.fora-de-area,
.bi-geo-area.fora-de-area               { fill: var(--info); background: var(--info); }
.bi-geo-bolinha.desconhecida,           .bi-geo-ponto.desconhecida,
.bi-geo-area.desconhecida               { fill: var(--foreground); background: var(--foreground); }

.bi-geo-mapa-wrap { position: relative; width: 100%; }
.bi-geo-mapa {
  display: block; width: 100%; height: auto;
  background: var(--muted); border-radius: var(--radius-md);
}
/* ---- Zoom dentro do mapa (sessão 145) ----
   Roda aproxima, arrastar move, e os três botões são o caminho de teclado. O
   cursor é a única pista de que o mapa se move: `grab` no estado ampliado,
   `grabbing` durante o arraste. No nível 1 fica o `crosshair` de sempre, porque
   arrastar ali não faz nada — prometer interação que não existe é pior que não
   oferecê-la. */
.bi-geo-mapa.ampliado { cursor: grab; }
.bi-geo-mapa.arrastando { cursor: grabbing; }
.bi-geo-zoom {
  position: absolute; top: 0.6rem; left: 0.6rem; z-index: 2;
  display: flex; align-items: center; gap: 0.3rem;
  padding: 0.25rem; border-radius: var(--radius-md);
  background: color-mix(in oklch, var(--background) 88%, transparent);
}
.bi-geo-zoom[hidden] { display: none; }
.bi-geo-zoom-nivel {
  font-size: 0.72rem; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums; padding: 0 0.25rem; min-width: 2.6rem;
}
/* Contorno claro para pontos vizinhos não virarem uma mancha só, e opacidade
   para a sobreposição mostrar densidade em vez de esconder. */
.bi-geo-ponto { stroke: var(--background); stroke-width: 0.8; opacity: 0.82; cursor: crosshair; }
.bi-geo-ponto:hover { opacity: 1; stroke-width: 1.6; }

/* A divisa de município. O traço é FINO (0.4) e da cor do fundo: municípios
   vizinhos de mesma categoria precisam continuar sendo dois, mas um traço
   grosso viraria uma grade preta por cima do mapa — com 850 municípios em MG,
   a linha ocuparia mais pixels que a área. */
.bi-geo-area {
  stroke: var(--background); stroke-width: 0.4; stroke-linejoin: round;
  opacity: 0.88; cursor: crosshair;
  /* 🔴 O zoom é feito no `viewBox`, e sem isto o traço da divisa escalaria com
     ele: a 12× uma linha de 0.4 vira 4.8 e o mapa some sob a própria grade —
     exatamente o efeito que a escolha de 0.4 existia para evitar. Com
     `non-scaling-stroke` a divisa tem a mesma espessura em qualquer nível. */
  vector-effect: non-scaling-stroke;
}
/* No hover o município é destacado pela BORDA, não por mudança de cor — a cor
   é o dado (a categoria), e alterá-la faria o mapa mentir enquanto o mouse
   passa. */
.bi-geo-area:hover { opacity: 1; stroke: var(--foreground); stroke-width: 1.2; }

/* O quadro da ilha oceânica. Fundo próprio e borda: sem eles, um polígono
   solto sobre o mapa pareceria território de verdade naquele lugar — que é
   exatamente a leitura errada que o quadro existe para evitar. */
.bi-geo-quadro-fundo { fill: var(--background); stroke: var(--border); stroke-width: 1; opacity: 0.97; }
.bi-geo-quadro-marca { fill: var(--muted-foreground); font-size: 15px; font-weight: 700; }

.bi-geo-dica {
  position: absolute; z-index: 5; pointer-events: none;
  display: flex; flex-direction: column; gap: 0.1rem;
  background: var(--popover); color: var(--popover-foreground);
  border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 0.5rem 0.7rem; font-size: 0.76rem;
  /* Mesma sombra do dropdown do `sdk-ui/components.css` — o design system
     não tem token de sombra, então o valor é literal e copiado de lá em vez
     de inventar um `--shadow-*` que não existe. */
  box-shadow: 0 8px 24px color-mix(in oklch, black 20%, transparent);
  max-width: 18rem;
}
/* Mesma armadilha do `.zr-alert` e do `.bi-aba`: `display: flex` é regra de
   autor e vence o `[hidden]` do navegador. Sem esta linha a dica fica colada
   na tela para sempre, com o atributo `hidden` corretamente no DOM. */
.bi-geo-dica[hidden] { display: none; }
.bi-geo-dica-rep { color: var(--muted-foreground); }

.bi-geo-check {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.78rem; color: var(--muted-foreground); cursor: pointer; user-select: none;
}

/* O tom de alerta do KPI de praças sem venda é o `zr-kpi__valor--alerta` do
   design system desde a KP4 — o número que a aba existe para destacar não é um
   erro (por isso `warning`, nunca `destructive`). */


/* ── etiquetas, compartilhadas pelas duas telas ─────────────────────────── */

.crm-tag {
  display: inline-flex; align-items: center; gap: 0.25rem;
  font-size: 0.69rem; font-weight: 500; line-height: 1.3;
  padding: 0.15rem 0.45rem; border-radius: var(--radius-sm); white-space: nowrap;
}
.crm-tag--frio     { background: var(--error-bg-soft);   color: var(--error-strong); }
.crm-tag--info     { background: var(--info-bg-soft);    color: var(--info-strong); }
/* Os totais de ganhas/perdidas são BOTÕES (abrem a lista das negociações do período): tira o visual de botão do navegador. */
button.crm-tag { border: 0; font: inherit; cursor: pointer; }
button.crm-tag:hover { box-shadow: 0 0 0 2px color-mix(in oklch, var(--ring) 45%, transparent); }
button.crm-tag:focus-visible { outline: 3px solid var(--ring); outline-offset: 2px; }
/* T-A4 — direção da conversa (crm-conversas-panel.js). A cor é reforço, nunca
   o único sinal: o rótulo já diz "Recebida"/"Enviada" por palavra. */
.crm-tag--entrada  { background: var(--success-bg-soft); color: var(--success-strong); }
.crm-tag--saida    { background: var(--muted); color: var(--secondary-foreground); }

.crm-conversas-item--entrada::before { border-color: var(--success); }
/* Recebida = superfície de cartão (o que o cliente disse importa mais na
   leitura). Enviada = recuada, mesma lógica de bolha "nossa" mais discreta. */
.crm-conversas-item--saida { background: var(--muted); border-color: transparent; }

/* Só a PRIMEIRA fila é forte — se tudo é alarme, nada é (ver o painel). */
.crm-contagem--urgente { background: var(--error-bg-soft);   color: var(--error-strong); }
.crm-contagem--atencao { background: var(--warning-bg-soft); color: var(--warning-strong); }
.crm-contagem--neutro  { background: var(--muted); color: var(--muted-foreground); }


/* A borda diz de que fila a linha veio, sem gastar uma etiqueta por item. */
.crm-item--esperando { border-left-color: var(--error-strong); }
.crm-item--tarefas   { border-left-color: var(--warning-strong); }
.crm-item--escapando { border-left-color: var(--muted-foreground); }

/* T-F1 (LinkedIn): "próximo toque" atrasado/hoje pede a mesma urgência visual
   que `.crm-tag--frio`/`.crm-tag--atencao` já usam para o resto do CRM. */
.crm-item-quando--frio    { color: var(--error-strong);   font-weight: 600; }
.crm-item-quando--atencao { color: var(--warning-strong); font-weight: 600; }

.crm-coluna-alerta { color: var(--warning-strong); font-weight: 500; }


/* Imagem de anexo ampliada sobre o chamado (X, Esc ou clique fora fecham). Sem animação de propósito. */
.imagem-ampliada {
  position: fixed; inset: 0; z-index: 60; background: rgba(0, 0, 0, 0.85);
  display: flex; align-items: center; justify-content: center; padding: 3.5rem 1rem 1rem;
}
.imagem-ampliada img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 6px; background: var(--card); }
.imagem-ampliada-fechar { position: absolute; top: 0.75rem; right: 0.75rem; }

/* Arrastar uma negociação entre etapas: o cartão fica fantasma e a coluna de destino ganha contorno. */
.crm-cartao[draggable="true"] { cursor: grab; }

.crm-cartao {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 0.65rem 0.7rem 0.7rem; cursor: pointer;
}
.crm-cartao:focus-visible {
  outline: none; border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 45%, transparent);
}
.crm-cartao-valor { font-size: 0.92rem; font-weight: 600; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
/* "Sem valor estimado" NÃO é R$ 0,00 — zero e "ninguém estimou" são estados
   diferentes, e confundi-los é o que faz o cabeçalho da coluna mentir. */
.crm-cartao-valor--vazio { font-size: 0.78rem; font-weight: 500; color: var(--warning-strong); }
.crm-cartao-meta { display: flex; flex-direction: column; gap: 0.1rem; font-size: 0.71rem; color: var(--muted-foreground); }
.crm-cartao-tags { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.5rem; }
.crm-cartao-rodape { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
.crm-cartao-rodape .zr-btn { margin-left: auto; }

.crm-quem {
  width: 1.35rem; height: 1.35rem; border-radius: 999px; flex-shrink: 0;
  background: var(--secondary); color: var(--secondary-foreground);
  font-size: 0.62rem; font-weight: 600; display: grid; place-items: center;
}

/* ── CRM · barra do funil (Quadro/Lista, ⋮, filtros e chips) ──────────────
   Estrutura levantada da ferramenta atual; tokens e formas são os nossos.
   Nenhuma cor literal — só token declarado em sdk-ui/tokens.css. */


.crm-icon-btn { padding: 0; width: 2.25rem; }

/* ── CRM · Relatórios (13/10/2026; panels/crm-relatorios-panel.js) ───────────
   Só token declarado em sdk-ui/tokens.css, nenhuma cor literal. A cor das barras é reforço: cada número também está
   escrito (título da coluna, valor ao lado da barra). Nada anima. */
.crm-rel-filtros { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; padding: 0.75rem 1rem 0; }
/* Os controles do filtro ficam do tamanho do conteúdo (o .zr-select nasce com a largura toda). */
.crm-rel-filtros .zr-select, .crm-rel-filtros .zr-input { width: auto; min-width: 10rem; }

.crm-rel-numero {
  display: flex; flex-direction: column; gap: 0.15rem; padding: 0.75rem 0.85rem;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-md);
}

.crm-rel-cartao {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-md);
  padding: 0.85rem 0.95rem; min-width: 0;
}
.crm-rel-cartao--largo { grid-column: 1 / -1; }
.crm-rel-cartao header { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.6rem; margin-bottom: 0.65rem; }
.crm-rel-cartao h4 { margin: 0; font-size: 0.9rem; font-weight: 600; }


.crm-rel-cartao h5 { margin: 0.9rem 0 0.4rem; font-size: 0.8rem; font-weight: 600; }
.crm-rel-preenchimento--em_dia { background: var(--success-strong); }
.crm-rel-preenchimento--atencao { background: var(--warning-strong); }
.crm-rel-preenchimento--escapando { background: var(--muted-foreground); }
.crm-rel-preenchimento--frio { background: var(--error-strong); }

.crm-campo { display: flex; flex-direction: column; gap: 0.2rem; }
.crm-campo > span { font-size: 0.7rem; font-weight: 500; color: var(--muted-foreground); }
.crm-campo .zr-select { min-width: 11rem; }

.crm-funil-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-left: auto; }


@media (max-width: 600px) {
  .crm-funil-chips { margin-left: 0; }
  .crm-campo .zr-select { min-width: 0; width: 100%; }
  .crm-campo { flex: 1 1 100%; }
}

/* ── Funil · modais de escrita (T-B2b) ─────────────────────────────────────
   Mockup aprovado 2026-09-02 (CRM_APPSTOB.md §12) — "Nova oportunidade",
   abrir/editar oportunidade, "Criar tarefa" a partir de um cartão. Mesmo
   padrão visual de `.crm-config-funil-modal` (ação pontual, ver o comentário
   daquela classe) — próprio em vez de reaproveitado porque a marcação das
   duas telas não deveria ficar acoplada à mesma classe CSS por coincidência
   de estilo; se um dia convergirem de verdade, a unificação é dessas classes,
   não da marcação. */
.crm-modal-overlay {
  position: fixed; inset: 0; background: rgba(0, 0, 0, 0.45);
  display: none; align-items: center; justify-content: center;
  padding: 1.25rem; z-index: 50;
}
.crm-modal-overlay.aberto { display: flex; }
.crm-modal {
  width: 100%; max-width: 30rem; max-height: calc(100vh - 3rem); overflow-y: auto;
  background: var(--card); color: var(--card-foreground); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.25rem;
  box-shadow: 0 24px 60px -16px rgba(0, 0, 0, 0.4);
}
/* 🔴 `.logs-panel` corta com `overflow: hidden`, e isso o faz o "contêiner de rolagem" mais próximo: o `position: sticky` da barra não gruda na janela. `clip` corta
   igual, sem virar contêiner de rolagem. Só neste painel. */
#crm-funil-panel.logs-panel { overflow: clip; }
.crm-cartao--fechada { cursor: pointer; }
.crm-motivo-novo[hidden] { display: none; }
.crm-motivo-novo { display: flex; gap: 0.5rem; align-items: center; }
.crm-motivo-novo .zr-input { flex: 1 1 auto; min-width: 0; }

.crm-modal-topo { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; margin-bottom: 1rem; }
.crm-modal-topo h3 { margin: 0; font-size: 1rem; font-weight: 600; }
.crm-modal-topo p { margin: 0.15rem 0 0; font-size: 0.78rem; color: var(--muted-foreground); }
.crm-modal-rodape { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.1rem; }
.crm-modal-secao { margin-top: 1.1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.crm-modal-secao-titulo { font-size: 0.78rem; font-weight: 600; color: var(--muted-foreground); margin: 0 0 0.55rem; text-transform: uppercase; letter-spacing: 0.03em; }

.campo { display: flex; flex-direction: column; gap: 0.3rem; }
/* Mesma armadilha de `.crm-modal-contexto` acima (`display:flex` de autor vence
   `[hidden]`) — achado pelo portão automático de public/js/__tests__/
   hidden-vence-display.test.js ao esconder os campos Empresa/Negociação do
   modal "Criar tarefa" avulso (ver crm-funil-panel.js). */
.campo[hidden] { display: none; }

/* Linhas de telefone do modal "Criar contato" (menu "Nova oportunidade" do
   CRM) — cada linha é [tipo] + [número] + [remover], montada por JS
   (#adicionarLinhaTelefone em crm-funil-panel.js). */
.crm-contato-telefone-linha { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; }
.crm-contato-telefone-linha .zr-select { flex: 0 0 9rem; }
.crm-contato-telefone-linha .zr-input { flex: 1; min-width: 0; }

.crm-situacao-linha { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.crm-perdida-motivo { display: none; margin-top: 0.6rem; }
.crm-perdida-motivo.aberto { display: block; }

.crm-badge-ganha { background: var(--success-bg-soft); color: var(--success-strong); }


/* Painel que declara `data-largura="cheia"` usa a janela inteira. Aplicada
   pelo AlternadorDePaineis (ver o porquê no #ajustarLargura dele): o teto de
   1200px vive no PAI (`.workspace`, ou a coluna `.workspace-conteudo` com o menu lateral), então filho nenhum escapa dele por CSS
   próprio. As três telas do CRM pedem isso (Funil/Meu dia/LinkedIn, sessão
   149, pedido do Thiago) — formulário isolado (ex.: Cadastro de Clientes)
   continua com o teto, que lê melhor estreito. */
.workspace.workspace--cheia { max-width: none; }
.workspace--lateral.workspace--cheia .workspace-conteudo { max-width: none; }

.crm-config-funil-chip--inativo {
  color: var(--muted-foreground);
  background: var(--muted);
  text-decoration: line-through;
  text-decoration-color: var(--muted-foreground);
}


/* Drill-down "clique no município" (Tarefa B, sessão 125) — mesma casca de
   `.expand-clientes-wrap` (nasce fechado, `.open` revela). O badge é
   `warning`, não `destructive`: `precisao='municipio'` não é erro, é um pino
   aproximado que a tela precisa marcar para não ser lido como endereço exato. */
.bi-geo-badge-aproximado {
  display: inline-flex; font-size: 0.72rem; font-weight: 600;
  padding: 0.12rem 0.5rem; border-radius: 999px;
  background: var(--warning-bg-soft); color: var(--warning-strong);
}

/* ============================================================================
   Central de Ingestão Vexta (sessão 133) — mestre-detalhe das duas telas
   (Conexões SQL Server / Execução). Vocabulário trazido 1:1 do mockup
   aprovado (docs/specs/bi-ingestao-ui-mockup.html) — nenhum token novo, só
   composição sobre .zr-*/tokens.css, mesma convenção do bloco de BI de Vendas
   acima. `code.inline` é o único nome novo de propósito genérico (o mockup e
   o bloco de BI de Vendas cada um já tinha o seu "code dentro de texto miúdo"
   — esta é a versão que este painel usa fora de `.fine-print`).
   ============================================================================ */

.bi-ing-view { display: grid; grid-template-columns: 340px 1fr; gap: 1.25rem; min-height: 0; }
.bi-ing-view[hidden] { display: none; }
@media (max-width: 860px) { .bi-ing-view { grid-template-columns: 1fr; } }

.bi-ing-master { display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
.bi-ing-master-toolbar { display: flex; gap: 0.5rem; }
.bi-ing-master-toolbar .zr-input { flex: 1; }
.bi-ing-master-list { display: flex; flex-direction: column; gap: 0.4rem; overflow-y: auto; padding-right: 0.15rem; }

.bi-ing-item {
  display: flex; flex-direction: column; gap: 0.3rem; text-align: left; width: 100%;
  padding: 0.65rem 0.75rem; border-radius: var(--radius-md); border: 1px solid var(--border);
  background: var(--card); cursor: pointer; font: inherit; color: inherit;
}
.bi-ing-item:hover { background: var(--accent); }
.bi-ing-item.selected { border-color: var(--primary); background: color-mix(in oklch, var(--primary) 6%, var(--card)); }
.bi-ing-item-top { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.bi-ing-item-title { font-weight: 600; font-size: 0.85rem; }
.bi-ing-item-sub { font-size: 0.75rem; color: var(--muted-foreground); }
.bi-ing-item-meta { display: flex; align-items: center; gap: 0.4rem; font-size: 0.72rem; color: var(--muted-foreground); }

/* Conciliação Ranking × Venda (sessão 180): uma coleção divergente por gaveta. */
.bi-ing-reconc-col { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.5rem 0.7rem; background: var(--card); }
.bi-ing-reconc-col > summary { cursor: pointer; font-size: 0.8rem; font-weight: 500; color: var(--foreground); }

/* ── Central de Ingestão BI, sessão 157 ─────────────────────────────────────
   Abas de NÍVEL 2, em pílula. Dois níveis de aba sublinhada empilhados fazem o
   olho procurar qual dos dois está ativo; a forma diferente diz, sem ler, que
   este nível é subordinado ao de cima. O contador evita abrir uma aba vazia
   para descobrir que ela está vazia. */
.bi-ing-abas-2 { display: flex; gap: 0.4rem; flex-wrap: wrap; margin-bottom: 0.85rem; }
.bi-ing-aba-2 {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.3rem 0.7rem; border-radius: 999px; border: 1px solid var(--border);
  background: var(--card); color: var(--muted-foreground);
  font: inherit; font-size: 0.8rem; font-weight: 500; cursor: pointer;
}
.bi-ing-aba-2:hover { background: var(--accent); }
.bi-ing-aba-2.active {
  border-color: var(--primary); color: var(--primary-foreground); background: var(--primary);
}
.bi-ing-aba-2-cont {
  min-width: 1.25rem; padding: 0 0.3rem; border-radius: 999px; text-align: center;
  font-size: 0.7rem; font-weight: 600;
  background: color-mix(in oklch, currentColor 14%, transparent);
}

/* O `›` entre fornecedor e plataforma. Decorativo: vai com aria-hidden na
   marcação, para o leitor de tela não soletrar o sinal no meio do nome. */
.bi-ing-sep { color: var(--muted-foreground); font-weight: 400; margin: 0 0.15rem; }

/* 🔴 "fornecedor não cadastrado"/"plataforma não cadastrada" em LETRAS, nunca
   espaço em branco — branco se lê como "não tem". A cor de aviso é o que faz o
   olho parar nele sem que o texto precise gritar. */
.bi-ing-pendente { color: var(--warning-strong); font-style: italic; font-weight: 500; }

/* O sub-detalhe: um cartão por vínculo, DENTRO da conexão. É aqui que URL das
   fotos e empresa de origem moram — no mesmo nível em que moram no banco. */
.bi-ing-vinculo {
  display: flex; flex-direction: column; gap: 0.85rem;
  padding: 0.9rem 1rem; border: 1px solid var(--border); border-radius: var(--radius-md);
  background: color-mix(in oklch, var(--muted) 35%, var(--card));
}
.bi-ing-vinculo + .bi-ing-vinculo { margin-top: 0.75rem; }
.bi-ing-vinculo-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.bi-ing-vinculo-head h3 { font-size: 0.88rem; font-weight: 600; margin: 0; }
.bi-ing-vinculo-head-dir { margin-left: auto; display: inline-flex; align-items: center; gap: 0.5rem; font-size: 0.75rem; }

/* A linha do "Vínculo ID". O id é longo e precisa caber em tela estreita sem
   empurrar a página — regra de tela pequena do DESIGN.md. */
.bi-ing-id-linha { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.bi-ing-id-valor {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.75rem;
  padding: 0.15rem 0.4rem; border-radius: var(--radius-sm);
  background: var(--muted); color: var(--foreground);
  overflow-wrap: anywhere;
}

.bi-ing-detail { display: flex; flex-direction: column; gap: 1rem; min-width: 0; overflow-y: auto; }
.bi-ing-card { padding: 1.1rem 1.25rem; display: flex; flex-direction: column; gap: 1rem; }
/* Mesma armadilha: sem esta linha o formulário "Nova conexão" da Central de Ingestão nasce
   visível, porque `display:flex` de autor vence o `[hidden]` do navegador. */
.bi-ing-card[hidden] { display: none; }
.bi-ing-card h2 { font-size: 0.95rem; font-weight: 600; margin: 0; }
.bi-ing-card-head { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.bi-ing-card-head .hint { font-size: 0.75rem; color: var(--muted-foreground); }

.bi-ing-field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem; }
.bi-ing-field-grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; }
@media (max-width: 640px) { .bi-ing-field-grid, .bi-ing-field-grid.cols-3 { grid-template-columns: 1fr; } }
.bi-ing-field { display: flex; flex-direction: column; }

/* Valor que se LÊ, nunca se edita — hoje só a "Tecnologia", que é a aba aberta
   e não um campo.
   🔴 Nasceu de defeito: era `<input disabled>`, e o painel atualizava com
   `.textContent`, que num campo de formulário não muda nada do que se vê (o
   texto de um input vem de `value`). O rótulo ficava cravado em "SQL Server" em
   toda aba. Sendo texto de verdade, `.textContent` passa a valer — e a altura
   acompanha a do `.zr-input` ao lado para a grade não desalinhar. */
.bi-ing-leitura {
  margin: 0;
  min-height: 2.25rem;
  display: flex;
  align-items: center;
  font-weight: 600;
}

.bi-ing-kv { display: flex; flex-direction: column; gap: 0.15rem; }
.bi-ing-kv-label { font-size: 0.72rem; font-weight: 500; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.04em; }
.bi-ing-kv-value { font-size: 0.9rem; font-weight: 500; }
.bi-ing-kv-value.muted { color: var(--muted-foreground); font-weight: 400; }

.bi-ing-status-dot { width: 0.5rem; height: 0.5rem; border-radius: 999px; flex-shrink: 0; display: inline-block; }
.bi-ing-status-dot--ok { background: var(--success); }
.bi-ing-status-dot--divergente, .bi-ing-status-dot--erro { background: var(--destructive); }
.bi-ing-status-dot--pendente { background: var(--muted-foreground); }

/* Credencial editável inline — usuário+senha, um olho só que revela ENQUANTO
   PRESSIONADO (nunca um alternador). Grava no MESMO registro do cofre de
   Segredos (categoria "BI Vexta") — esta caixa é outra porta de entrada, não
   um armazenamento paralelo. Ver bi-ingestao-panel.js, ligarOlhoCredencial(). */
.bi-ing-credential-box {
  display: flex; flex-direction: column; gap: 0.65rem;
  border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.85rem 0.9rem; background: var(--muted);
}
.bi-ing-credential-box-head { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.bi-ing-credential-edit-fields { display: grid; grid-template-columns: 1fr 1fr auto; gap: 0.6rem; align-items: end; }
.bi-ing-credential-edit-fields .bi-ing-field { margin: 0; }
.bi-ing-credential-edit-fields .zr-input { background: var(--card); }
@media (max-width: 640px) { .bi-ing-credential-edit-fields { grid-template-columns: 1fr; } }
.bi-ing-eye-btn {
  flex-shrink: 0; width: 2.5rem; height: 2.5rem; border-radius: var(--radius-md);
  border: 1px solid var(--border); background: var(--card); color: var(--muted-foreground);
  cursor: pointer; font-size: 1rem; display: flex; align-items: center; justify-content: center;
  transition: background-color 0.15s, color 0.15s, border-color 0.15s; user-select: none; -webkit-user-select: none;
}
.bi-ing-eye-btn:hover { background: var(--accent); color: var(--foreground); }
.bi-ing-eye-btn.revealing { background: var(--accent); color: var(--foreground); border-color: var(--ring); }
.bi-ing-eye-btn:focus-visible { border-color: var(--ring); box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent); }

.bi-ing-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.15rem 0; }
.bi-ing-toggle-row-text { display: flex; flex-direction: column; gap: 0.1rem; }
.bi-ing-toggle-row-title { font-size: 0.85rem; font-weight: 500; }
.bi-ing-toggle-row-desc { font-size: 0.75rem; color: var(--muted-foreground); }
/* Checkbox nativo, de propósito — não um "switch" custom: o projeto já foi
   mordido por interruptor customizado que ouve o evento errado (ver
   CLAUDE.md, "Interruptor custom escuta o change do CHECKBOX"). Nativo não
   tem como ter esse bug. */
.bi-ing-toggle-row input[type="checkbox"] { width: 1.1rem; height: 1.1rem; flex-shrink: 0; }

.bi-ing-actions-row { display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* Excluir vínculo: o botão de entrada é contorno (não é a ação principal do
   cartão); o vermelho cheio fica só no "Excluir definitivamente", dentro do
   formulário que exige motivo e o nome do banco. */
.bi-ing-btn-excluir { color: var(--destructive); border-color: color-mix(in oklch, var(--destructive) 45%, var(--border)); }
.bi-ing-exclusao {
  display: flex; flex-direction: column; gap: 0.6rem;
  padding: 0.85rem 1rem; border: 1px solid color-mix(in oklch, var(--destructive) 40%, var(--border));
  border-radius: var(--radius-md); background: color-mix(in oklch, var(--destructive) 6%, var(--card));
}
.bi-ing-exclusao-titulo { margin: 0; font-size: 0.85rem; font-weight: 600; color: var(--destructive); }

code.inline { font-size: 0.78em; background: var(--muted); border-radius: var(--radius-sm); padding: 0.1rem 0.35rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.bi-geo-pendentes-lista { margin: 0; padding-left: 1.1rem; font-size: 0.82rem; display: flex; flex-direction: column; gap: 0.3rem; }

/* ═══ Central de Ingestão BI — a TRAVA dos blocos (sessão 159) ═══════════════
   Mockup: docs/specs/bi-ingestao-blocos-travados-mockup.html

   🔴 A hachura é DECORAÇÃO, nunca proteção: `pointer-events: none` e desenhada
   num `::after`. Quem impede a digitação é o `disabled` de cada campo. Proteger
   pela hachura seria proteção que o teclado atravessa — Tab chegaria no campo,
   e o `::after` ficaria por cima de um campo que aceita texto.

   ⚠️ O `inset` negativo faz a hachura invadir a folga ao redor da grade de
   campos, senão ela para na borda do último campo e o bloco parece meio
   travado. */
.bi-ing-campos { position: relative; }
.bi-ing-travado > .bi-ing-campos::after,
.bi-ing-travado .bi-ing-campos::after {
  content: "";
  position: absolute;
  inset: -0.45rem -0.55rem;
  pointer-events: none;
  border-radius: var(--radius-md);
  /* ⚠️ Preto translúcido, sem variante escura de propósito: `styles.css` é o
     shell CLARO (`color-scheme: light`, e `data-theme="dark"` é proibido em
     `public/` por teste). Uma `@media (prefers-color-scheme: dark)` aqui seria
     a primeira do arquivo — e o portão do tema a recusa, porque a string casa
     com a proibição de `color-scheme: dark`. */
  background: repeating-linear-gradient(135deg, transparent 0 7px, rgb(0 0 0 / 5%) 7px 8px);
}

/* O anel azul do bloco em edição. No cartão de vínculo ele substitui a borda
   cinza; no `.zr-card` da conexão ele se soma à borda que já existe. */
.bi-ing-editando { box-shadow: 0 0 0 2px var(--info); }

/* Campo travado: o cinza do `:disabled` já vem dos primitivos, mas `cursor`
   não — sem ele o cursor de texto convida a clicar num campo que não aceita. */
.bi-ing-campos .zr-input:disabled,
.bi-ing-campos .zr-select:disabled { cursor: not-allowed; }

.bi-ing-selo {
  display: inline-flex; align-items: center; gap: 0.25rem;
  padding: 0.1rem 0.5rem; border-radius: 999px;
  font-size: 0.72rem; font-weight: 500; white-space: nowrap;
}
.bi-ing-selo--travado { background: var(--muted); color: var(--muted-foreground); }
.bi-ing-selo--editando { background: var(--info-bg-soft); color: var(--info-strong); }

/* O canto direito do cabeçalho de um bloco: selo + "Alterar parâmetros".
   `align-items: center` local porque o cabeçalho é `baseline` — um botão
   alinhado pela linha de base do <h2> fica pendurado abaixo dele. */
.bi-ing-card-head-dir { display: inline-flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.bi-ing-card-head { align-items: center; }

/* ── G3 — a Geografia na Central de Ingestão (mockup bi-geo-clientes-mockup.html
   §2/§3, tarefa G3 do LOOP_OPERACIONAL). As duas linhas de DATA do cartão, o
   andamento em 4 passos, o placar do resultado e a lista dos cadastros a
   corrigir. Nenhum token novo: composição sobre `.zr-*` e `sdk-ui/tokens.css`.

   Os contêineres `#<id>-geo-andamento` e `#<id>-geo-corrigir` são divs SEM
   classe de propósito — o `[hidden]` do navegador os esconde sozinho, sem
   precisar do override `.classe[hidden]` que os primitivos `.zr-*` exigem. */

.bi-ing-geo {
  display: flex; flex-direction: column; gap: 0.25rem;
  padding: 0.6rem 0.75rem; border: 1px solid var(--border);
  border-radius: var(--radius-md); background: var(--muted);
}
.bi-ing-geo-linha { display: flex; align-items: baseline; justify-content: space-between; gap: 0.75rem; font-size: 0.82rem; }
.bi-ing-geo-data { color: var(--muted-foreground); font-variant-numeric: tabular-nums; text-align: right; }

/* As 4 linhas do andamento — ícone + rótulo; a cor diz o estado, e o texto
   repetido NÃO some: quem distingue é a mensagem do desfecho, não só a cor. */
.bi-ing-passos { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; }
.bi-ing-passo { display: flex; align-items: center; gap: 0.45rem; }
.bi-ing-passo--ok { color: var(--success); }
.bi-ing-passo--agora { font-weight: 500; }
.bi-ing-passo--espera { color: var(--muted-foreground); }
.bi-ing-passo--erro { color: var(--destructive); }

.bi-ing-barra { height: 0.5rem; border-radius: 999px; background: var(--muted); overflow: hidden; }
.bi-ing-barra i { display: block; height: 100%; border-radius: 999px; background: var(--primary); transition: width 0.3s ease; }

.bi-ing-placar { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.bi-ing-placar div { display: flex; flex-direction: column; }
.bi-ing-placar b { font-size: 1.3rem; font-variant-numeric: tabular-nums; line-height: 1.15; }
.bi-ing-placar span { font-size: 0.72rem; color: var(--muted-foreground); }

.bi-ing-corrigir-topo { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.bi-ing-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
/* O chip é `aria-pressed`, não aba: a lista por baixo é UMA, filtrada. */
.bi-ing-chip {
  padding: 0.25rem 0.7rem; border-radius: 999px; font-size: 0.78rem; cursor: pointer;
  border: 1px solid var(--border); background: var(--background); color: var(--foreground);
}
.bi-ing-chip:hover { background: var(--accent); }
.bi-ing-chip:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--ring); }
.bi-ing-chip--on { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }

.bi-ing-tabela-wrap { overflow-x: auto; }
.bi-ing-tabela { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
.bi-ing-tabela th, .bi-ing-tabela td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border); }
.bi-ing-tabela th { color: var(--muted-foreground); font-weight: 500; white-space: nowrap; }
.bi-ing-num { text-align: right; font-variant-numeric: tabular-nums; }

/* O que FALTA no cadastro — o mesmo vermelho do estado de erro, para a coluna
   ler como pendência e não como descrição. */
.bi-ing-falta { color: var(--destructive); white-space: nowrap; }

/* A linha de data dos endereços na aba Geografia (sem aviso: atualizados). */
.bi-geo-data-enderecos { font-size: 0.8rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }

/* ============================================================================
   Aba "Metas" do BI de Vendas (sessão 161) — mockup aprovado pelo Eder em
   17/09 (`docs/specs/bi-metas-mockup.html`), decisões em `BI_METAS.md`.

   Nenhum token novo: composição sobre `.zr-*` e `tokens.css`, mesma convenção
   dos blocos de BI acima.

   🔴 **Os nomes de token do mockup NÃO são os do produto.** Ele declara
   `--destructive-bg-soft`/`--destructive-strong` no próprio `:root`, e esses
   dois não existem em `sdk-ui/tokens.css` — lá a família é `--error-bg-soft`/
   `--error-strong`. Copiar o mockup ao pé da letra resolveria `var()` para
   string VAZIA: texto invisível sobre fundo transparente, sem nada no console.
   É a mesma armadilha da "ponte de vocabulário" do `DESIGN.md`.
   ============================================================================ */

.bi-mt-estado { min-height: 1.25rem; color: var(--muted-foreground); font-size: 0.85rem; margin-bottom: 0.5rem; }
/* 🔴 VAZIA, a região viva não reserva altura (medido na sessão 170: 20px de
   `min-height` + 8px de margem, sempre, para texto que no caminho feliz nunca
   aparece — o estado só ganha texto quando a carga FALHA).
   ⚠️ `min-height: 0`, NUNCA `display: none`: ela é `role="status"`, e leitor de
   tela monitora o elemento RENDERIZADO. Escondê-la arriscaria a mensagem de
   erro não ser anunciada justamente quando ela importa. Assim ela continua no
   fluxo, monitorada, e só deixa de custar espaço em branco.
   ⚠️ `:empty` exige ZERO nós filhos — inclusive texto. A marcação não tem
   espaço em branco dentro da div, e o painel escreve `textContent = ''`. */
.bi-mt-estado:empty { min-height: 0; margin-bottom: 0; }

.bi-mt-secao { margin-top: 1.75rem; }
.bi-mt-secao:first-child { margin-top: 0; }
/* 🔴 `:first-child` acima é DOM, não é "primeiro VISÍVEL" — e os dois divergem
   na persona representante (sessão 170, achado do Eder na tela). Para ela os
   três recolhíveis e os cartões de modalidade ficam `[hidden]`, mas seguem
   sendo filhos: o bloco do realizado é o primeiro que a pessoa VÊ e mesmo
   assim herda os 28px de margem que existem para separá-lo de um bloco acima
   que não está lá. Somado à região viva vazia, dava 72px de nada entre as abas
   e o título.
   Quem decide é a persona, num lugar só (`visibilidadePorPersona` →
   `#aplicarPersona` põe a classe); o CSS só reage. */
.bi-aba--sem-recolhiveis > #bi-geral-realizado-secao { margin-top: 0; }
.bi-mt-titulo { font-size: 1rem; font-weight: 600; margin: 0 0 0.15rem; }
.bi-mt-sub { color: var(--muted-foreground); margin: 0 0 0.9rem; font-size: 0.85rem; }
.bi-mt-nota { color: var(--muted-foreground); font-size: 0.8rem; margin-top: 0.6rem; }

/* ── Medidor em arco ────────────────────────────────────────────────────────
   Substitui o velocímetro com agulha do E-Store: agulha seria primitivo novo e
   decorativo, e o inventário do `DESIGN.md` é congelado de propósito. O número
   no centro é quem INFORMA; a cor é reforço, nunca o único canal. */
/* 🔴 `auto-fit`, e NUNCA um número fixo de colunas. Quantos medidores existem
   é decisão de CADASTRO (`metas.ordem` no E-Store), não do CSS: nasceu com 3,
   virou 4 no mesmo dia em que o degrau de 100% foi criado, e cravar `repeat(4,
   1fr)` só adiaria a quebra para o 5º. Com 3 colunas fixas o 4º medidor caía
   sozinho numa segunda linha — achado na conferência da tela, não no teste.
   Isto também dispensa a media query que existia aqui: uma regra que já sabe
   quebrar sozinha não precisa de uma segunda que possa discordar dela. */
.bi-mt-medidores {
  display: grid; gap: 0.85rem;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
}

.bi-mt-medidor { text-align: center; }
.bi-mt-medidor svg { display: block; margin: 0 auto; width: 100%; max-width: 11rem; height: auto; }
.bi-mt-medidor-valor { font-weight: 600; letter-spacing: -0.02em; }
.bi-mt-medidor-nome { font-weight: 600; margin-top: 0.2rem; font-size: 0.85rem; }
.bi-mt-medidor-alvo { color: var(--muted-foreground); font-variant-numeric: tabular-nums; font-size: 0.8rem; }
.bi-mt-medidor-falta { font-size: 0.78rem; font-variant-numeric: tabular-nums; margin-top: 0.3rem; }

.bi-mt-arco-baixa { stroke: var(--error-bg-soft); }
.bi-mt-arco-media { stroke: var(--warning-bg-soft); }
.bi-mt-arco-alta { stroke: var(--success-bg-soft); }
.bi-mt-arco--sucesso { stroke: var(--success); }
.bi-mt-arco--atencao { stroke: var(--warning); }
.bi-mt-arco--critico { stroke: var(--destructive); }
/* Sem meta: o arco fica no cinza do trilho, não numa cor de julgamento. */
.bi-mt-arco--sem-meta { stroke: var(--muted-foreground); }

.bi-mt-txt--sucesso { color: var(--success-strong); }
.bi-mt-txt--atencao { color: var(--warning-strong); }
.bi-mt-txt--critico { color: var(--error-strong); }
.bi-mt-txt--sem-meta { color: var(--muted-foreground); }

/* A etiqueta do patamar (90%/120%) ao lado do nome do CADASTRO — a tela nunca
   renomeia "Meta Super Sky" para "Super Sky 100%". */
.bi-mt-patamar {
  display: inline-block; padding: 0 0.3rem; border-radius: var(--radius-sm);
  background: var(--secondary); color: var(--secondary-foreground);
  font-size: 0.7rem; font-weight: 600; vertical-align: middle;
}
.bi-mt-fora-da-carga {
  display: inline-block; padding: 0 0.3rem; border-radius: var(--radius-sm);
  background: var(--warning-bg-soft); color: var(--warning-strong);
  font-size: 0.7rem; font-weight: 600; vertical-align: middle;
}

/* ── KPIs do realizado ────────────────────────────────────────────────────
   Os cartões são o `.zr-kpi` do design system desde a KP4 (item 9, D5) — o
   CSS próprio `bi-mt-kpi-*` saiu; o valor agora é o `zr-kpi__valor` e o
   "de R$ X" é o `zr-kpi__alvo`. A grade de CINCO colunas (`.bi-mt-kpis`) nunca
   teve uso e saiu junto: a grade padrão do primitivo é 4/2/1 e a variante
   `--cinco` fica disponível para a tela que precisar. */

/* ── Abertura de clientes: o wrapper de DUAS colunas; os cartões dentro são o
      `.zr-kpi--barra` (a largura de cada um é desta grade, não do primitivo). */
.bi-mt-abertura { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.85rem; }
@media (max-width: 720px) { .bi-mt-abertura { grid-template-columns: 1fr; } }

.bi-mt-barra {
  height: 0.6rem; border-radius: 999px; background: var(--muted);
  overflow: hidden; margin: 0.55rem 0 0.35rem;
}
.bi-mt-barra-preenchida { height: 100%; border-radius: 999px; }
.bi-mt-barra--sucesso { background: var(--success); }
.bi-mt-barra--atencao { background: var(--warning); }
.bi-mt-barra--critico { background: var(--destructive); }
.bi-mt-barra--sem-meta { background: var(--muted-foreground); }

/* ── Metas por cliente: o card à esquerda, a lista à direita ────────────── */
/* 🔴 `minmax(0, 1fr)`, nunca `1fr`: a trilha da lista contém a tabela de metas.
   `1fr` = `minmax(auto, 1fr)`, e o `auto` não encolhe abaixo do `min-content` da
   tabela — a trilha ESTICA para caber a tabela inteira e a última coluna
   ("Mais Ações") + o "Ver todos" transbordam e são cortados na borda. Só aparecia
   no portal SSO (`bi-painel.html`), cuja coluna é mais estreita. `minmax(0, 1fr)`
   constrange a trilha à largura disponível; a coluna Cliente então QUEBRA linha
   (regra abaixo) e tudo cabe SEM barra de rolagem lateral. Mesmo padrão que os
   Rankings (`.bi-rg-linha`) já usam. */
.bi-mt-grade-cliente { display: grid; grid-template-columns: 16rem minmax(0, 1fr); gap: 0.85rem; align-items: start; }
@media (max-width: 900px) { .bi-mt-grade-cliente { grid-template-columns: 1fr; } }

/* ── Tabelas ────────────────────────────────────────────────────────────── */
.bi-mt-lista { padding: 0.4rem 0.9rem; }
.bi-mt-rolagem { overflow-x: auto; }
.bi-mt-tabela { width: 100%; border-collapse: collapse; }
.bi-mt-tabela th, .bi-mt-tabela td {
  text-align: left; padding: 0.5rem 0.6rem; border-bottom: 1px solid var(--border); white-space: nowrap;
}
/* A coluna Cliente é a única que estica (nomes longos, ex. "JOLENA MANUTENCAO E
   CONFECCAO LTDA ME") — é a única que QUEBRA linha. As colunas numéricas e o botão
   "Mais Ações" seguem em linha única (nowrap), e o nome absorve a sobra: tudo cabe
   na largura do card sem barra de rolagem lateral. */
.bi-mt-tabela th:first-child, .bi-mt-tabela td:first-child { white-space: normal; overflow-wrap: break-word; }
.bi-mt-tabela th { font-weight: 600; color: var(--muted-foreground); font-size: 0.78rem; }
.bi-mt-tabela td { font-size: 0.85rem; }
.bi-mt-tabela tbody tr:hover { background: var(--accent); }
.bi-mt-tabela .bi-mt-num, .bi-mt-tabela th.bi-mt-num { text-align: right; font-variant-numeric: tabular-nums; }
.bi-mt-linha-destaque { background: color-mix(in oklch, var(--primary) 5%, transparent); }

.bi-mt-mini {
  display: inline-block; width: 5rem; height: 0.45rem; border-radius: 999px;
  background: var(--muted); overflow: hidden; vertical-align: middle;
}
.bi-mt-mini > span { display: block; height: 100%; border-radius: 999px; }

.bi-mt-rodape {
  display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
  flex-wrap: wrap; padding: 0.6rem 0 0.3rem; color: var(--muted-foreground); font-size: 0.8rem;
}

/* ═══ Representantes por meta atingida (sessão 173) ═══════════════════════
   Mockup aprovado: docs/specs/bi-metas-representantes-por-patamar-mockup.html.
   Um bloco por régua; cada linha é nome + um medidor pequeno por régua, na
   ordem da tela. `--bi-rg-colunas` vem do painel (o nº de réguas é o do
   cadastro, não 4 cravado). */
.bi-rg { --bi-rg-colunas: 4; display: flex; flex-direction: column; gap: 0.85rem; }
.bi-rg-legenda, .bi-rg-linha {
  display: grid; gap: 0.5rem; align-items: center;
  grid-template-columns: minmax(11rem, 1.1fr) repeat(var(--bi-rg-colunas), minmax(0, 1fr));
}
.bi-rg-legenda {
  padding: 0 1rem; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--muted-foreground);
}
.bi-rg-legenda > span { text-align: center; }
.bi-rg-legenda > span:first-child { text-align: left; }
/* O alvo em R$ da coluna, abaixo do nome — como o "Acompanhamento de Metas"
   mostra por medidor (Thiago, sessão 177). Texto normal, não o caixa-alta da
   legenda. */
.bi-rg-legenda-valor {
  display: block; margin-top: 0.1rem; font-weight: 600; font-size: 0.72rem;
  text-transform: none; letter-spacing: 0; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.bi-rg-rotulo-valor {
  display: block; font-weight: 600; color: var(--foreground);
  font-variant-numeric: tabular-nums;
}
.bi-rg-bloco { border: 1px solid var(--border); border-radius: var(--radius); background: var(--card); overflow: hidden; }
.bi-rg-cab {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 0.75rem;
  padding: 0.7rem 1rem; background: var(--muted); border-bottom: 1px solid var(--border);
}
.bi-rg-cab h4 { margin: 0; font-size: 0.92rem; }
.bi-rg-cab--topo { background: var(--success-bg-soft); }
.bi-rg-resumo { margin-left: auto; font-size: 0.8rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-rg-linha { padding: 0.6rem 1rem; border-top: 1px solid var(--border); }
.bi-rg-cab + .bi-rg-linha { border-top: 0; }
.bi-rg-quem { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; }
.bi-rg-nome { font-weight: 600; font-size: 0.88rem; }
.bi-rg-real { font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.bi-rg-prox { font-size: 0.74rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-rg-mini { display: flex; flex-direction: column; align-items: center; min-width: 0; }
.bi-rg-mini svg { width: 100%; max-width: 6rem; height: auto; display: block; }
.bi-rg-aro { stroke: var(--foreground); }
/* O rótulo sob o medidor só aparece no celular, onde a legenda some. */
.bi-rg-rotulo { display: none; font-size: 0.66rem; color: var(--muted-foreground); text-align: center; }
/* 🔴 Lista VERTICAL (Thiago, 23/09): um representante por linha, nunca lado a
   lado. Nome à esquerda, detalhe (falta/valor) à direita, com divisor sutil
   entre as linhas — vale para "Com meta e sem venda" e "Sem meta cadastrada". */
.bi-rg-sem-meta { display: flex; flex-direction: column; padding: 0; font-size: 0.84rem; }
.bi-rg-sem-item { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.5rem 1rem; }
.bi-rg-sem-item + .bi-rg-sem-item { border-top: 1px solid var(--border); }
.bi-rg-sem-nome { min-width: 0; }
.bi-rg-sem-meta em { font-style: normal; color: var(--muted-foreground); font-variant-numeric: tabular-nums; white-space: nowrap; }
@media (max-width: 720px) {
  .bi-rg-legenda { display: none; }
  .bi-rg-linha { grid-template-columns: repeat(var(--bi-rg-colunas), minmax(0, 1fr)); }
  .bi-rg-quem { grid-column: 1 / -1; }
  .bi-rg-rotulo { display: block; }
  .bi-rg-resumo { margin-left: 0; width: 100%; }
}

/* O prêmio em reais dentro do card do medidor (sessão 161). Separado da linha
   de "Faltam R$ X" por um filete: é outra grandeza — aquela é distância para a
   meta, esta é dinheiro. */
.bi-mt-premio {
  margin-top: 0.4rem; padding-top: 0.4rem; border-top: 1px solid var(--border);
  font-size: 0.8rem; font-variant-numeric: tabular-nums;
}
.bi-mt-premio-rotulo { color: var(--muted-foreground); }
.bi-mt-premio-pct { color: var(--muted-foreground); font-size: 0.72rem; }

/* ═══════════════════════════════════════════════════════════════════════════
   TÍTULOS A RECEBER EM ABERTO (sessão 161) — a tag, a coluna "Mais Ações" e
   a gaveta. Mockup aprovado em 19/09: `docs/specs/bi-titulos-abertos-mockup.html`.

   🔴 A família de erro chama-se `--error-*`, NUNCA `--destructive-bg-soft`
   nem `--destructive-strong`: esses dois NÃO EXISTEM em `sdk-ui/tokens.css`,
   e `var()` de token inexistente resolve para string VAZIA, sem nada no
   console — texto invisível sobre fundo transparente. O mockup da aba Metas
   errou exatamente assim e só não chegou à tela porque foi conferido no
   navegador. `--destructive` (sem sufixo) existe e é usado só na borda.
   ═══════════════════════════════════════════════════════════════════════════ */

/* A tag fica ao lado do NOME, inline — nunca `display:block` como a
   `.bi-tag-dividida`: aquela descreve a CÉLULA do município inteira, esta
   qualifica a palavra imediatamente à esquerda. */
.bi-tag-inad {
  display: inline-flex; align-items: center; gap: 0.25rem;
  margin-left: 0.4rem; padding: 0.05rem 0.4rem;
  border-radius: var(--radius-sm, 0.375rem);
  background: var(--error-bg-soft); color: var(--error-strong);
  font-size: 0.68rem; font-weight: 700; white-space: nowrap;
  border: 1px solid color-mix(in srgb, var(--destructive) 25%, transparent);
  vertical-align: middle;
}
.bi-tag-inad::before { content: "\25CF"; font-size: 0.6em; }

/* 🆕 Município e representante como TAG abaixo do nome do cliente (Thiago,
   23/09, mockup `docs/specs/bi-cliente-local-em-tag-mockup.html` aprovado).
   Liberou as colunas "Município" e "Representante" das listas de cliente, dando
   folga às colunas de número. Fica numa linha PRÓPRIA sob o nome — ao contrário
   da `.bi-tag-inad`, que qualifica a palavra à esquerda e por isso é inline. */
.bi-cliente-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; margin-top: 0.3rem; }
.bi-meta-tag {
  display: inline-flex; align-items: center; gap: 0.28rem;
  padding: 0.05rem 0.45rem; border-radius: 999px;
  background: var(--secondary); color: var(--muted-foreground);
  font-size: 0.68rem; font-weight: 500; white-space: nowrap;
  border: 1px solid var(--border);
}
/* Representante em tom um pouco mais forte que o município — os dois se leem à
   parte sem virar a mesma mancha. O ícone é decorativo (`::before`), então não
   entra no `textContent` que a busca lê. */
.bi-meta-tag--rep { color: var(--secondary-foreground); }
.bi-meta-tag::before { font-size: 0.85em; opacity: 0.7; }
.bi-meta-tag--local::before { content: "\1F4CD"; }
.bi-meta-tag--rep::before { content: "\1F464"; }

/* ── "Mais Ações" ───────────────────────────────────────────────────────── */
/* `position: relative` na casca e `absolute` no menu: o menu precisa flutuar
   SOBRE a linha seguinte. Dentro do fluxo, ele empurraria a tabela para baixo
   a cada abertura, e a linha que se queria ler sairia de onde estava. */
.bi-acoes-wrap { position: relative; display: inline-block; }
.bi-acoes-btn {
  background: transparent; border: 1px solid var(--border);
  border-radius: var(--radius-md, 0.5rem);
  padding: 0.2rem 0.5rem; font: inherit; font-size: 0.8rem;
  color: var(--foreground); cursor: pointer; white-space: nowrap;
}
.bi-acoes-btn:hover { background: var(--accent); }
.bi-acoes-menu {
  position: absolute; right: 0; top: calc(100% + 0.25rem); z-index: 20;
  min-width: 15rem; text-align: left; padding: 0.25rem;
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-md, 0.5rem);
  box-shadow: 0 8px 24px rgb(0 0 0 / 12%);
}
.bi-acoes-item {
  display: flex; align-items: center; justify-content: space-between; gap: 0.6rem;
  width: 100%; background: none; border: 0; padding: 0.45rem 0.6rem;
  font: inherit; font-size: 0.83rem; color: var(--foreground);
  border-radius: var(--radius-sm, 0.375rem); cursor: pointer; text-align: left;
}
.bi-acoes-item:hover { background: var(--accent); }
/* 🔴 `aria-disabled`, nunca o atributo `disabled`: um botão `disabled` não
   recebe foco nem é lido por leitor de tela, e aqui o item DESABILITADO
   carrega a única explicação ("nenhum") de por que não há o que listar.
   Esconder essa informação faria procurar o que não existe. */
.bi-acoes-item[aria-disabled="true"] { color: var(--muted-foreground); cursor: not-allowed; }
.bi-acoes-item[aria-disabled="true"]:hover { background: none; }
.bi-acoes-contador {
  display: inline-flex; padding: 0.05rem 0.45rem; border-radius: 999px;
  background: var(--error-bg-soft); color: var(--error-strong);
  font-size: 0.72rem; font-weight: 700;
}
.bi-acoes-nota { font-size: 0.72rem; color: var(--muted-foreground); }
/* 🆕 TP3 (item 70; o desenho do mockup): o motivo do item desligado é uma FRASE ("Cancelado com a
   NFS-e 1702 — emita uma fatura nova.") — sem a quebra, o menu ficaria largo demais e a 390 px
   estouraria. Mesma regra do `.acoes-item .acoes-nota` do mockup aprovado. */
.bi-acoes-item .bi-acoes-nota { white-space: normal; max-width: 13rem; text-align: right; }

/* ── A gaveta ───────────────────────────────────────────────────────────── */
/* Linha da própria tabela, não modal (decisão do mockup aprovado): a pergunta
   é sobre AQUELE cliente e a tabela em volta é metade da resposta. */
.bi-gaveta-linha > td { padding: 0.6rem 0.65rem; background: var(--muted); }
.bi-gaveta {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius-lg, 0.625rem); overflow: hidden;
}
.bi-gaveta-topo {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 0.75rem; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--border);
}
.bi-gaveta-topo h4 { margin: 0; font-size: 0.9rem; font-weight: 600; }
/* ⚠️ Declara tamanho e cor, não só margem: NÃO existe `.hint` global neste
   projeto (só `.injector-head .hint` e `.bi-ing-card-head .hint` — está
   registrado algumas centenas de linhas acima). Herdar dela deixaria o
   subtítulo da gaveta do tamanho do corpo, competindo com o próprio título. */
.bi-gaveta-topo .hint {
  margin: 0.1rem 0 0;
  font-size: 0.75rem;
  color: var(--muted-foreground);
}
.bi-gaveta-kpis { display: flex; gap: 1.5rem; flex-wrap: wrap; }
.bi-gaveta-kpis > div { display: flex; flex-direction: column; }
.bi-gaveta-kpi-nome { color: var(--muted-foreground); font-size: 0.72rem; }
.bi-gaveta-kpi-valor { font-size: 1rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── Quem é o devedor, na coluna que só existe em linha de GRUPO ─────────
   Duas linhas numa célula só: nome em cima, documento e praça embaixo. É o
   que distingue dois cadastros do mesmo grupo quando a razão social deles é
   idêntica — medido no piloto, é o caso real, não hipótese. */
.bi-devedor-nome { display: block; }
.bi-devedor-detalhe {
  display: block;
  font-size: 0.72rem;
  color: var(--muted-foreground);
  /* O documento é o desempate e precisa ser lido dígito a dígito. */
  font-variant-numeric: tabular-nums;
}

/* Ênfase por FAIXA de atraso (30/180 dias) — cortes de LEITURA, para o olho
   achar a pior linha primeiro numa gaveta de 20 parcelas. Não são o corte de
   inadimplência do cadastro: amarrá-los a ele faria a gaveta inteira ficar
   vermelha sempre que o corte fosse alto, e aí a cor pararia de informar. */
.atraso-forte { color: var(--error-strong); font-weight: 600; }
.atraso-medio { color: var(--warning-strong); font-weight: 600; }

/* ── O campo de dias na Central de Ingestão ─────────────────────────────── */
.bi-corte-linha { display: flex; align-items: center; gap: 0.6rem; max-width: 26rem; }
.bi-corte-linha .zr-input { width: 7rem; }
.bi-corte-efeito { color: var(--muted-foreground); font-size: 0.8rem; }
.bi-corte-efeito b { color: var(--foreground); }

#segredosConteudo { display: flex; flex-direction: column; gap: 1rem; }
#segredosConteudo[hidden] { display: none; }
.segredos-bloco { padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 0.6rem; }
.segredos-bloco[hidden] { display: none; }
.segredos-bloco h4 { margin: 0; font-size: 0.95rem; font-weight: 600; }
.segredos-linha { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: flex-end; }
.segredos-acoes { white-space: nowrap; text-align: right; }
.segredos-acoes .zr-btn + .zr-btn { margin-left: 0.3rem; }
/* .zr-btn. na frente: sem ela, o display: inline-flex do primitivo vence por ordem de
   arquivo e o Abrir aparece no desktop (visto na verificação da 186 — família do [hidden]). */
.segredos-so-pequena, .zr-btn.segredos-so-pequena { display: none; }
/* D4 do Eder: em tela pequena, nada de tabela rolando na horizontal — a linha
   mostra só provedor + chave e um "Abrir", que leva a um modal com os dados e as ações. */
@media (max-width: 640px) {
  .segredos-so-grande { display: none; }
  .segredos-so-pequena, .zr-btn.segredos-so-pequena { display: inline-flex; }
  /* Formulário: cada campo e o botão ocupam a linha inteira — botão com o nome do cliente
     não cabe ao lado do campo e seria cortado. */
  .segredos-linha > * { flex: 1 1 100%; min-width: 0; }
  .segredos-linha .zr-btn { white-space: normal; height: auto; min-height: 2.25rem; }
  .segredos-bloco .zr-table th, .segredos-bloco .zr-table td { padding-left: 0.4rem; padding-right: 0.4rem; }
  .segredos-bloco .zr-table code { overflow-wrap: anywhere; word-break: break-all; }
  .segredos-acoes { white-space: normal; }
}
/* O modal de senha abre POR CIMA do de detalhe (Revelar a partir do detalhe). */
.reaut-overlay { z-index: 60; }
/* Texto só para leitor de tela (cabeçalho da coluna de ações). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ── ORDENS DE SERVIÇO (ERP 2.1, E3 — telas 1–3; mockup docs/specs/ordens-servico-mockup.html) ──
   Tudo em tokens do DESIGN.md. As cores de situação são as do Help-Desk para o estado equivalente
   (DESIGN.md, "Mapeamento de situação"). 🔴 Toda classe daqui que declara `display` e é escondida por
   atributo tem o seu `[hidden]` logo abaixo — regra de autor com `display` vence o `[hidden]` do
   navegador (portão: hidden-vence-display.test.js). */
.os-vista { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.os-vista[hidden] { display: none; }
.os-vista > * { min-width: 0; }

/* Ordens de serviço: os cartões de indicador agora são o `.zr-kpi` do design system
   (item 9, KP3) — o CSS próprio (`os-kpi*`) saiu daqui na KP3. */

.os-barra { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.os-busca { position: relative; flex: 1 1 220px; min-width: 0; }
.os-busca > .zr-icone { position: absolute; left: 0.65rem; top: 50%; transform: translateY(-50%); color: var(--muted-foreground); pointer-events: none; }
.os-busca > .zr-input { padding-left: 2rem; width: 100%; }
.os-barra > .zr-select { width: auto; flex: 0 1 auto; }
.os-empurra { margin-left: auto; }

.os-rolagem { overflow-x: auto; }
/* 🔴 ND4: `position: relative` aqui é o par do `overflow-x`. O `.sr-only` (o "Ações" das
   tabelas) é `position: absolute`: sem um ancestral POSICIONADO dentro do rolador, o bloco
   contentor dele é a viewport e ele ESCAPA do recorte do `overflow` — a tabela larga volta a
   empurrar a PÁGINA para o lado (medido a 390 px: 670 px de largura com o catálogo de
   descrições). Com o contêiner posicionado, o `sr-only` ancora AQUI e é recortado como o resto. */
.os-rolagem { position: relative; }

#atv-status:empty { display: none; }
.atv-tabela { min-width: 1080px; }
.atv-tabela .celula-sub { color: var(--muted-foreground); font-size: 0.78rem; }
.atv-tabela .tabela-vazia { text-align: center; color: var(--muted-foreground); padding: 1rem; }
.atv-tabela .zr-badge.b-solta { background: var(--secondary); color: var(--secondary-foreground); }
.atv-tabela .zr-badge.b-agrupada { background: var(--muted); color: var(--muted-foreground); }
.atv-tabela .zr-badge.b-parada { background: var(--warning-bg-soft); color: var(--warning-strong); }
/* §15 (Telas 2 e 3 do mockup): o selo "N horários" na célula Horas — o cinza neutro do b-faixas do mockup. */
.celula-sub .zr-badge { background: var(--muted); color: var(--foreground); }
/* ── Lista de Pessoas (Clientes/Fornecedores/Colaboradores) — item 49 (20/10/2026; mockup
   `colaboradores-lista-mockup.html`, D1–D9). SEM coluna presa e SEM rolagem lateral: largura
   fixa, cada célula corta com "…" (o texto inteiro vive na dica copiável) e, abaixo de 1080 px
   de ÁREA DA LISTA (container query, não a janela), cada pessoa vira um cartão. O `position:
   sticky` do "Editar" saiu: ele cobria a coluna Termo em vez de aparecer na ponta. */
#clientesListaArea { container-type: inline-size; }
#clientesTable { table-layout: fixed; }
#clientesTable th, #clientesTable td { padding: 0.45rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#clientesTable th { font-size: 0.78rem; }
#clientesTable .num { font-variant-numeric: tabular-nums; }
#clientesTable .centro { text-align: center; }
#clientesTable .dir { text-align: right; }
#clientesTable th.col-codigo { width: 7.75rem; }
#clientesTable th.col-doc { width: 11rem; } /* o "00.000.000/0000-00" inteiro, sem reticências (D3). 11rem, não 9,5: a 9,5 cabia na fonte do Windows e cortava na DejaVu do Linux (CI da #957) — a folga é para a fonte mais larga */
#clientesTable th.col-nat { width: 2.75rem; }
#clientesTable th.col-ativo { width: 3.25rem; }
#clientesTable th.col-email { width: 9rem; }
#clientesTable th.col-vinculo { width: 5.25rem; }
#clientesTable th.col-nivel { width: 3.5rem; }
#clientesTable th.col-assin { width: 5.5rem; }
#clientesTable th.col-termo { width: 6.75rem; }
#clientesTable th.col-acoes { width: 5rem; }
#clientesTable .celula-corte { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#clientesTable .ativo-sim { display: inline-flex; width: 1.15rem; height: 1.15rem; border-radius: 0.25rem; background: var(--primary); color: var(--primary-foreground); align-items: center; justify-content: center; }
#clientesTable .ativo-sim .zr-icone { width: 0.8rem; height: 0.8rem; }
#clientesTable .ativo-nao { display: inline-flex; width: 1.15rem; height: 1.15rem; border-radius: 0.25rem; border: 1.5px solid var(--border); }
#clientesTable abbr[title] { text-decoration: none; cursor: help; }
#clientesTable .linha-inativa td { color: var(--muted-foreground); }
@container (max-width: 1080px) {
  #clientesTable, #clientesTable tbody, #clientesTable tr, #clientesTable td { display: block; width: auto; }
  #clientesTable thead { display: none; }
  #clientesTable tr { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0.75rem 0.9rem; margin-bottom: 0.65rem; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.4rem 1rem; }
  #clientesTable td { border: 0; padding: 0; white-space: normal; }
  #clientesTable td[data-rotulo]::before { content: attr(data-rotulo); display: block; font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); }
  #clientesTable td.cartao-titulo { grid-column: 1 / -1; font-size: 0.95rem; font-weight: 600; }
  #clientesTable td.cartao-titulo::before { display: none; }
  #clientesTable td.cartao-acoes { grid-column: 1 / -1; }
  #clientesTable td.cartao-acoes .zr-btn { width: 100%; justify-content: center; }
  #clientesTable .centro { text-align: left; }
  #clientesTable .celula-corte { display: inline-block; max-width: 100%; }
}
/* Grade das atividades (Lançar e Agrupar) — mockup ordens-servico-atividades-grade-mockup.html, 13/10/2026.
   O ID do tipo é o MESMO desenho da impressão da ordem (número branco em círculo preto), nos tokens da tela
   (--primary / --primary-foreground, que no tema escuro invertem sozinhos); a regra do texto é uma só
   (public/js/tipos-atendimento.js). Com 2+ dígitos o círculo se alarga o mínimo. */
.tipo-id { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; min-width: 1.15rem; height: 1.15rem; padding: 0 0.3rem; border-radius: 9999px; background: var(--primary); color: var(--primary-foreground); font-size: 0.7rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.tipos-legenda { border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--sidebar); padding: 0.55rem 0.8rem; margin: 0.75rem 0; }
.tipos-legenda-rotulo { display: block; font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted-foreground); margin-bottom: 0.3rem; }
.tipos-legenda ol { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); gap: 0.25rem 1rem; }
.tipos-legenda li { display: flex; align-items: center; gap: 0.45rem; font-size: 0.83rem; }
/* "+ Ações": Editar e Apagar um abaixo do outro, na largura do maior. */
.atv-acoes-pilha { display: flex; flex-direction: column; align-items: stretch; gap: 0.3rem; }
.atv-acoes-pilha .zr-btn { justify-content: flex-start; }

/* ── AT5 — a tela Agrupar atividades (Tela 2 do mockup) ──────────────────────────────────────
   AF4 (fusão de 27/10/2026): a tela saiu; a marcação vive na lista do Lançar (`consultoria.css`,
   `.atv-travada`), e o `#ag-voltar` (o botão "Voltar a Lançar atividades") morreu com ela. */
/* Atividade de OUTRO cliente que o da seleção: travada (uma ordem tem um cliente só). */
.ag-bloqueada { opacity: 0.55; }
.ag-bloqueada input { cursor: not-allowed; }
.os-tabela tbody tr { cursor: pointer; }
.os-tabela tbody tr:focus-visible { outline: 3px solid color-mix(in oklch, var(--ring) 50%, transparent); outline-offset: -3px; }
.os-celula-titulo { font-weight: 500; }
.os-celula-sub { font-size: 0.75rem; color: var(--muted-foreground); }
.os-mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.8rem; }
.os-num { font-variant-numeric: tabular-nums; }
.os-dir { text-align: right; }
.os-fraco { color: var(--muted-foreground); }
.os-acao-erro { margin-top: 0.7rem; display: flex; gap: 0.5rem; flex-wrap: wrap; }

/* ── FX3 (item 22 de TAREFAS_PRO_LOOP; mockup ordens-servico-acoes-lista-mockup.html, telas 1/2/4,
   17/10/2026) — a coluna Ações da lista de ordens e o modo CARTÃO do estreito.
   🔴 `DESIGN.md`, "Tela pequena" (17/10/2026): tela de LISTAGEM nunca tem barra de rolagem — nem a da
   página, nem a da tabela. Quando a área não comporta a tabela, a linha vira CARTÃO com os mesmos
   campos (o rótulo de cada um) e as Ações embaixo. Quem decide é a largura da ÁREA da lista
   (`container-type: inline-size`), não a da janela: vale com o menu lateral aberto ou recolhido. */
#os-lista-conteudo { container-type: inline-size; }
.os-tabela th:last-child, .os-tabela td:last-child { width: 1%; }
.os-acoes-celula { text-align: right; white-space: nowrap; }
.os-acoes-linha { display: inline-flex; flex-wrap: wrap; gap: 0.4rem; justify-content: flex-end; }
@container (max-width: 900px) {
  #os-lista-conteudo .os-tabela thead { display: none; }
  #os-lista-conteudo .os-tabela, #os-lista-conteudo .os-tabela tbody { display: block; }
  #os-lista-conteudo .os-tabela tbody tr {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0.4rem 1rem;
    padding: 0.75rem 0.85rem; border-bottom: 1px solid var(--border);
  }
  #os-lista-conteudo .os-tabela tbody tr:last-child { border-bottom: 0; }
  #os-lista-conteudo .os-tabela tbody td { display: block; padding: 0; border: 0; text-align: left; white-space: normal; }
  #os-lista-conteudo .os-tabela tbody td::before {
    content: attr(data-rotulo); display: block; font-size: 0.68rem; font-weight: 600;
    color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.1rem;
  }
  /* As Ações são a última linha do cartão, na largura toda. `::before` some: o grupo de botões se
     explica sozinho (e o rótulo "AÇÕES" repetido em cada cartão seria ruído). */
  #os-lista-conteudo .os-tabela tbody td.os-acoes-celula { grid-column: 1 / -1; }
  #os-lista-conteudo .os-tabela tbody td.os-acoes-celula::before { content: none; }
  #os-lista-conteudo .os-acoes-linha { justify-content: flex-start; }
  /* O cartão não rola: o rolador da tabela deixaria a barra escondida ali dentro. `overflow` inteiro —
     `overflow-x: visible` sozinho vira `auto` quando o outro eixo não é `visible`. */
  #os-lista-conteudo .zr-table-wrap, #os-lista-conteudo .os-rolagem { overflow: visible; }
}

.os-trilho--limite > i { background: var(--warning); }
.os-trilho--cartao { margin: 0.6rem 0 0.2rem; }

.os-sit--aberta { background: var(--info-bg-soft); color: var(--info-strong); }
.os-sit--execucao { background: var(--warning-bg-soft); color: var(--warning-strong); }
.os-sit--aguardando { background: var(--muted); color: var(--foreground); }
.os-sit--afaturar { background: var(--success-bg-soft); color: var(--success-strong); }
.os-sit--faturada { background: transparent; border-color: var(--border); color: var(--foreground); }
.os-sit--cancelada { background: transparent; border-color: var(--destructive); color: var(--error-strong); }

.os-passo { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; color: var(--muted-foreground); padding: 0.35rem 0.75rem; border: 1px solid var(--border); background: var(--background); }
.os-passo:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
.os-passo:last-child { border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.os-passo + .os-passo { border-left: 0; }
.os-passo--feito { color: var(--foreground); }
.os-passo--atual { background: var(--primary); color: var(--primary-foreground); border-color: var(--primary); }
.os-coluna { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.os-coluna > * { min-width: 0; }
.os-par--sub { font-size: 0.78rem; color: var(--muted-foreground); }
.os-par--exc span { color: var(--warning-strong); }
.os-acoes-col { display: flex; flex-direction: column; gap: 0.15rem; align-items: stretch; }
.os-acoes-col .zr-btn { justify-content: center; }
.os-b--nao { background: var(--muted); color: var(--muted-foreground); }
.os-b--fora { background: var(--warning-bg-soft); color: var(--warning-strong); }

.os-secao-cab { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.os-secao-cab h4 { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0; font-size: 0.875rem; font-weight: 600; }
.os-secao-cab .zr-help { margin: 0; flex: 1 1 260px; }

.os-duracao { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 0.83rem; display: inline-flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
/* §15 (mockup, Tela 1): o selo "N horários" ao lado da duração — o cinza neutro do b-faixas do mockup. */
.os-duracao .zr-badge { background: var(--muted); color: var(--foreground); }
.os-linha-grade { display: grid; grid-template-columns: 9.5rem repeat(3, minmax(5rem, 6.5rem)) minmax(0, 1fr); gap: 0.6rem 0.7rem; align-items: end; }
.os-linha-grade > * { min-width: 0; }
.os-linha-grade .os-toda { grid-column: 1 / -1; }
.os-linha-grade textarea { resize: vertical; }
/* A linha da ATIVIDADE (Lançar atividades): 12 colunas, para a 2ª linha — Consultor, Centro de custo e
   Tipo de atendimento — dividir a largura em três partes iguais, em vez de herdar as colunas estreitas
   do Início/Fim/Intervalo. §15 (vários horários, mockup `ordens-servico-atividades-multi-horarios-mockup.html`):
   Início/Fim/Intervalo moram num bloco só (`.os-horarios` — 🔴 NUNCA `.os-faixa`: esse nome já é a faixa do resumo no detalhe da ordem), que cresce para baixo a cada horário; por isso a grade
   alinha pelo TOPO. 1ª linha: Dia · Horários · Cliente; 2ª: Consultor · Centro · Tipo (filhos 1–3 e 4–6). */
.os-linha-grade--atividade { align-items: start; }
@media (min-width: 861px) {
  .os-linha-grade--atividade { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .os-linha-grade--atividade > :nth-child(1) { grid-column: span 3; }
  .os-linha-grade--atividade > :nth-child(2) { grid-column: span 6; }
  .os-linha-grade--atividade > :nth-child(3) { grid-column: span 3; }
  .os-linha-grade--atividade > :nth-child(4), .os-linha-grade--atividade > :nth-child(5), .os-linha-grade--atividade > :nth-child(6) { grid-column: span 4; }
}
@media (min-width: 1360px) {
  .os-linha-grade--atividade > :nth-child(1) { grid-column: span 2; }
  .os-linha-grade--atividade > :nth-child(3) { grid-column: span 4; }
}
/* O bloco dos horários: o cabeçalho (Início · Fim · Intervalo/Duração) uma vez, e uma linha de três colunas por horário. */
.os-horarios-cab, .os-horario { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.6rem 0.7rem; align-items: center; }
.os-horarios-cab .zr-label { margin: 0; }
/* O horário que o SERVIDOR apontou (V5): vermelho = mesmo cliente (bloqueia); amarelo = outro cliente (pede confirmação). */
.os-horario--mesmo-cliente .zr-input { border-color: var(--error-strong); background: var(--error-bg-soft); }
.os-horario--outro-cliente .zr-input { border-color: var(--warning-strong); background: var(--warning-bg-soft); }
.atv-dinheiro { display: flex; align-items: center; gap: 0.35rem; }
.atv-dinheiro > span { color: var(--muted-foreground); font-size: 0.85rem; }
.atv-dinheiro .zr-input { text-align: right; font-variant-numeric: tabular-nums; }
.atv-valor-estado--ajustado { background: var(--warning-bg-soft); border-color: color-mix(in srgb, var(--warning) 35%, transparent); color: var(--warning-strong); }
.atv-valor-estado--ok { background: var(--success-bg-soft); border-color: color-mix(in srgb, var(--success) 35%, transparent); color: var(--success-strong); }
.atv-dinheiro--p { flex-wrap: wrap; }
.atv-dinheiro--p .zr-input { max-width: 8rem; }
@media (max-width: 860px) {
  .os-linha-grade { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .os-linha-grade .os-larga, .os-linha-grade .os-toda { grid-column: 1 / -1; }
}

.os-edit-acoes { display: flex; flex-direction: column; gap: 0.3rem; }
.os-edit-acoes .zr-btn { justify-content: center; }

/* AT3 (M0-B, aprovado em 16/10/2026): o bloco "Anexos desta atividade" dentro da LINHA da atividade —
   escolha → chips → (na edição, a lista do gravado antes da escolha) → erro → ajuda. */
.os-bloco-anexos { border-top: 1px solid var(--border); margin-top: 0.9rem; padding-top: 0.8rem; }
.os-bloco-anexos h5 { display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.5rem; font-size: 0.85rem; font-weight: 600; }
.os-bloco-anexos .os-anexos { margin-bottom: 0.5rem; }
.os-bloco-anexos .os-chips { margin-top: 0.5rem; }
.os-bloco-anexos .zr-alert, .os-bloco-anexos .zr-help { margin: 0.5rem 0 0; }
.os-bloco-anexos .zr-help { margin-top: 0.35rem; }

/* Tela 2 — nova ordem (modal próprio). */
.os-modal-fundo { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: flex-start; justify-content: center; overflow-y: auto; padding: 1.5rem 1rem; background: color-mix(in oklch, var(--foreground) 35%, transparent); }
.os-modal-fundo[hidden] { display: none; }
/* TI-L4 (TELA_INICIAL_LOOP.md): a aba Conexão do modal da carteira é um `.os-modal-corpo`
   escondido por JS — sem o override, o `display: flex` vence o `[hidden]` e a aba fica visível. */
.os-modal-corpo[hidden] { display: none; }
/* AJ6 (mockup aprovado): o RODAPÉ fica FIXO — o corpo rola por dentro e Salvar/Cancelar
   continuam à vista, nunca abaixo da dobra do modal. */
.os-modal { width: 100%; max-width: 760px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card); box-shadow: 0 10px 30px rgb(0 0 0 / 10%); display: flex; flex-direction: column; max-height: calc(100dvh - 3rem); }
.os-modal-cab { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; padding: 1rem 1.15rem 0; flex: none; }
.os-modal-cab h3 { margin: 0; font-size: 1rem; }
/* TI-L4: o envoltório das abas da carteira — SEM display de propósito, para o `hidden` valer
   (quem pinta é o .zr-tabs do sdk-ui, dentro). */
.cc-carteira-abas { padding: 0.3rem 1.15rem 0; }
.os-modal-corpo { padding: 1rem 1.15rem; display: flex; flex-direction: column; gap: 0.9rem; overflow-y: auto; }
.os-modal-rodape { display: flex; justify-content: flex-end; gap: 0.5rem; padding: 0.85rem 1.15rem; border-top: 1px solid var(--border); flex-wrap: wrap; flex: none; }
/* O display:flex acima vence o [hidden] do navegador: o modal de 2 passos (cópia) esconde um rodapé por passo. */
.os-modal-rodape[hidden] { display: none; }
/* 🆕 VM2 (item 83; mockup `voltar-ao-mergulho-mockup.html` §4.2/§7): o rodapé da PERGUNTA do Esc —
   "Continuar editando" à esquerda, as outras duas à direita; a 640 px ou menos os três empilham em
   largura total, com "Salvar e sair" EM CIMA (o mais seguro fica mais perto do polegar). */
.os-modal-rodape--pergunta .pergunta-continuar { margin-right: auto; }
@media (max-width: 640px) {
  .os-modal-rodape--pergunta { flex-direction: column-reverse; align-items: stretch; }
  .os-modal-rodape--pergunta .pergunta-continuar { margin-right: 0; }
  .os-modal-rodape--pergunta .zr-btn { width: 100%; justify-content: center; }
}
.os-grade { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.9rem 1rem; }
.os-grade > * { min-width: 0; }
.os-inteiro { grid-column: 1 / -1; }
.os-fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }

/* Telas 4–5 — concluir, a faturar e registrar faturamento (E4). */
.os-modal--estreito { max-width: 540px; }
/* Item 37 (mockup nfse-imprimir-ao-autorizar): o modal da folha da NFS-e — a folha da página
   nfse-impressao.html no quadro, que ocupa o corpo inteiro; em tela de celular, a tela toda. */
.os-modal--folha { max-width: 980px; height: calc(100dvh - 3rem); }
.nf-folha-cab h3 { display: flex; align-items: center; gap: 0.45rem; }
.nf-folha-cab .zr-help { margin: 0.25rem 0 0; }
.nf-folha-ok { display: inline-flex; color: var(--success-strong); }
.os-modal-corpo.nf-folha-corpo { flex: 1; padding: 0; gap: 0; background: var(--muted); }
.nf-folha-corpo .zr-alert { margin: 1rem 1.15rem; }
.nf-folha-quadro { flex: 1; width: 100%; min-height: 60vh; border: 0; background: var(--card); }
.nf-folha-quadro[hidden] { display: none; }
@media (max-width: 640px) {
  #nf-folha-overlay { padding: 0; }
  .os-modal--folha { max-width: none; height: 100dvh; max-height: 100dvh; border-radius: 0; }
  .os-modal--folha .os-modal-rodape { flex-direction: column-reverse; align-items: stretch; }
}
.os-link:focus-visible { outline: 3px solid color-mix(in oklch, var(--ring) 50%, transparent); outline-offset: 2px; }
/* 🆕 VM1 (item 83; mockup `voltar-ao-mergulho-mockup.html` §2/§7): a cápsula da TECLA ao lado do
   texto do "Voltar" ("Voltar a Contas a receber [Esc]"). É utilitária, não primitivo `.zr-*`: o
   desenho é o do mockup, com os tokens do produto. */
.kbd { display: inline-block; font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.7rem; line-height: 1; padding: 0.18rem 0.38rem; border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 0.3rem; background: var(--background); color: var(--muted-foreground); }
.os-link { background: none; border: 0; padding: 0; color: var(--primary); text-decoration: underline; cursor: pointer; }

/* Tela pequena — decidido no mockup: a lista rola dentro da tabela, e o apontamento de horas
   funciona inteiro numa coluna (é o que quem executa faz fora da mesa). A grade dos cartões
   (`os-kpis`) saiu na KP3: quem colapsa agora é o `.zr-kpi-grade` do design system. */
@media (max-width: 600px) {
  /* O apontamento funciona inteiro numa coluna (decisão do mockup: é o que quem executa faz fora da mesa). */
  .os-apontar { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .os-apontar .os-larga, .os-apontar .os-toda, .os-apontar .zr-btn { grid-column: 1 / -1; }
  .os-grade { grid-template-columns: minmax(0, 1fr); }
  .os-empurra { margin-left: 0; }
}


.os-tp-track { display: inline-flex; width: 40px; height: 22px; border-radius: 999px; background: var(--muted); border: 1px solid var(--muted-foreground); position: relative; transition: background 0.2s, border-color 0.2s; flex-shrink: 0; }
.os-tp-track--on { background: color-mix(in oklch, var(--primary) 20%, transparent); border-color: var(--primary); }
.os-tp-rotulo { font-size: 0.7rem; font-weight: 600; color: var(--muted-foreground); min-width: 52px; }
.os-tp-rotulo--on { color: var(--primary); }

/* ═══ FATURAMENTO (Fase 1 — FT5 de FATURAMENTO_ITENS.md; mockup M0) ═════════
   A lista de faturas, a fatura (abas + aba Parcelas) e os cadastros. Reusa as
   utilidades `os-*` de tabela/rolagem/modal (o vocabulário do portal) e define
   só o que é próprio. Tudo em tokens do DESIGN.md. */
.fat-vista { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
/* 🔴 Mesma armadilha do `.os-vista`: o `display` de AUTOR vence o `[hidden]` do
   navegador — sem esta linha, a vista "escondida" continuaria na tela. */
.fat-vista[hidden] { display: none; }
.fat-vista > * { min-width: 0; }
/* Contas a receber e Recebimentos por conta: os cartões de indicador agora são o `.zr-kpi`
   do design system (item 9, KP2) — o CSS próprio saiu daqui na KP2. */

.cb-mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.8rem; }
.cb-faixa-ambiente { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; padding: 0.5rem 0.8rem; margin-bottom: 1rem;
  background: var(--warning-bg-soft); border: 1px solid color-mix(in srgb, var(--warning) 35%, transparent);
  border-radius: var(--radius-md); color: var(--warning-strong); font-size: 0.8rem; font-weight: 500; }
.cb-faixa-ambiente--producao { background: var(--success-bg-soft); border-color: color-mix(in srgb, var(--success) 35%, transparent); color: var(--success-strong); }
/* CB9: o "Confirmo que este é o destinatário" do modal de e-mail — o portão do envio. */
.cb-confirma { display: flex; gap: 0.6rem; align-items: flex-start; font-size: 0.85rem; }
.cb-confirma input { margin-top: 0.25rem; flex: none; }
/* CB8 — a lista e o detalhe. */
.cb-filtro { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; color: var(--muted-foreground); }
.cb-historico { list-style: none; margin: 0; padding: 0; font-size: 0.8rem; }
.cb-historico li { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: 0.5rem; padding: 0.35rem 0; border-bottom: 1px dashed var(--border); }
.cb-historico li:last-child { border-bottom: 0; }
.cb-qr { width: 132px; height: 132px; border: 1px dashed var(--border); border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; padding: 0.4rem; }
.cb-qr img { width: 100%; height: 100%; object-fit: contain; }
.cb-copia-cola { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.8rem; word-break: break-all; }

.cc-mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.8rem; }
.cc-fraco { color: var(--muted-foreground); }
.cc-seletor { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.cc-seletor .zr-input { flex: 1 1 220px; min-width: 0; }
#cc-pes-box { margin-top: 0.6rem; }
#cc-pes-box .zr-select { width: 100%; margin-top: 0.4rem; }
.cc-checks { display: flex; flex-direction: column; gap: 0.45rem; font-size: 0.85rem; }
.cc-check { display: flex; align-items: flex-start; gap: 0.5rem; }
.cc-check input { margin-top: 0.2rem; flex: none; }
.cc-obrig { color: var(--destructive); }

/* Os modais do Financeiro que pedem comprovante — a carteira (R5), o fechar e o reabrir período
   (CC4) — usam `z-index: 55` pelo MESMO motivo do `.colab-modal` e do `.cert-modal`: o
   `.reaut-overlay` (60) fica POR CIMA enquanto a senha não sai. (O `1000` do `.os-modal-fundo`
   cobriria o da senha — armadilha já documentada junto do `.colab-modal`.) */
#cc-carteira-overlay, #cc-fechar-overlay, #cc-reabrir-overlay { z-index: 55; }

/* O `--n` é o nível do recuo; declarado aqui com o padrão 0 para o portão da ponte de tokens
   (`tema-shell.test.js`: `var()` de token que ninguém declara resolve para string vazia, calado).
   O HTML de cada linha o sobrescreve no `style`. */
.arv-no { --n: 0; display: inline-flex; align-items: center; gap: 0.35rem; padding-left: calc(var(--n) * 1.4rem); min-width: 0; }
.arv-no .zr-icone { flex: none; color: var(--muted-foreground); }
.arv-caret { width: 1.6rem; min-width: 1.6rem; padding: 0; justify-content: center; }
.arv-caret .zr-icone { transition: transform 0.15s; }
.arv-caret[aria-expanded="false"] .zr-icone { transform: rotate(-90deg); }
.arv-sintetica td { background: var(--sidebar); }
.arv-inativa td { color: var(--muted-foreground); }
.arv-obrig { color: var(--destructive); }

#mp-conteudo { padding: 0 1rem 1rem; }
#mp-liquido { margin-top: 1rem; }

/* PC6 — a aba "Revisão sugerida" (PC-M2): o resumo, a barra decididas/total, os filtros, os
   grupos recolhíveis e o antes/depois do diálogo do mover. */
#rp-view-revisao { padding: 0 1rem; }
.rp-grupo { margin-top: 0.8rem; }
.rp-grupo > summary { display: flex; align-items: center; gap: 0.4rem; cursor: pointer; }
.rp-grupo > summary .zr-icone { color: var(--muted-foreground); }

.fat-detalhe { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 1rem; align-items: start; }
.fat-coluna { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.fat-coluna > * { min-width: 0; }
.fat-card { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card); padding: 1rem; }
.fat-card h4 { margin: 0 0 0.6rem; font-size: 0.875rem; font-weight: 600; display: flex; align-items: center; gap: 0.4rem; }
.fat-par { display: flex; justify-content: space-between; gap: 1rem; padding: 0.3rem 0; border-bottom: 1px dashed var(--border); font-size: 0.85rem; }
.fat-par:last-of-type { border-bottom: 0; }
.fat-par span:last-child { font-variant-numeric: tabular-nums; text-align: right; }
.fat-par--total { border-bottom: 0; border-top: 1px solid var(--border); font-weight: 600; }
.fat-historico { list-style: none; margin: 0; padding: 0; font-size: 0.8rem; }
.fat-historico li { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr); gap: 0.5rem; padding: 0.35rem 0; border-bottom: 1px dashed var(--border); }
.fat-historico li:last-child { border-bottom: 0; }
.fat-parcelas-cab { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.75rem; font-size: 0.85rem; }
.fat-pc-acoes { display: flex; justify-content: flex-start; margin-top: 0.75rem; }
/* Em tela estreita a tabela rola dentro de si (.os-rolagem); os campos não podem encolher até caber só um dígito. */
.zr-table [data-fat-pc-venc] { min-width: 9.5rem; }
.zr-table [data-fat-pc-valor] { min-width: 6.5rem; text-align: right; }
.fat-soma--erro { background: color-mix(in oklch, var(--destructive) 12%, transparent); color: var(--destructive); }
.fat-cad-form { display: flex; flex-direction: column; gap: 0.4rem; max-width: 640px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--card); padding: 1rem; }
.fat-cad-form h3 { margin: 0 0 0.4rem; font-size: 0.95rem; }
.fat-cad-acoes { display: flex; gap: 0.5rem; margin-top: 0.5rem; flex-wrap: wrap; }
.fat-total-previsto { display: flex; justify-content: space-between; gap: 1rem; padding: 0.6rem 0.8rem; background: var(--muted); border-radius: var(--radius-md); font-variant-numeric: tabular-nums; }
/* ND4 — a prévia "Como sai na NFS-e" (mockup NFD-M1 v3): o que o SERVIDOR montou, só leitura. */
.nfd-previa { border: 1px dashed var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.8rem; background: var(--muted); display: flex; flex-direction: column; gap: 0.25rem; }
.nfd-previa p { margin: 0; font-size: 0.8rem; color: var(--muted-foreground); }
.nfd-previa .nfd-previa-valor { font-size: 0.9rem; color: var(--foreground); overflow-wrap: anywhere; }
.nfd-previa .nfd-previa-linha { display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
.nfd-previa .nfd-previa-rotulo { flex: none; min-width: 8rem; }
@media (max-width: 880px) {
  .fat-detalhe { grid-template-columns: minmax(0, 1fr); }
  .fat-parcelas-cab { grid-template-columns: minmax(0, 1fr); }
}

/* ═══ A ABA "ORDENS PARA FATURAR" (FO5 de FATURAS_ORDENS_LOOP.md; mockup aprovado em 13/10) ═══
   Só classes `fo-` com tokens; o que já existia (`.os-rolagem`, `.os-busca`, `.os-link`,
   `.os-num`, `.ag-bloqueada`, `.tipo-id`, `.tipos-legenda`, `.fat-barra-selecao`, `.zr-empty`)
   é REUSADO, nunca recriado. */
.fo-cab { display: flex; gap: 0.75rem; align-items: flex-start; flex-wrap: wrap; margin-bottom: 0.5rem; }
.fo-cab > div:first-child { flex: 1 1 320px; min-width: 0; }
.fo-cab h3 { margin: 0 0 0.2rem; font-size: 1.05rem; }
.fo-cab .zr-help { margin: 0; }
/* Faturas › Ordens para faturar: o cartão do total é o `.zr-kpi--cabecalho` do design
   system (item 9, KP3) — o CSS próprio (`fo-kpi*`) saiu daqui na KP3. */
.fo-filtros { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; margin: 0.4rem 0 0.75rem; }
.fo-filtros .fo-grupo { min-width: 0; }
.fo-grupo--cliente { flex: 1 1 14rem; }
.fo-grupo--data { flex: 0 1 9.5rem; }
.fo-grupo--responsavel { flex: 0 1 13.5rem; }

.fn-campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 0.75rem; }
.fn-soma--ok { background: var(--success-bg-soft); color: var(--success-strong); }
.fn-soma--erro { background: var(--warning-bg-soft); color: var(--warning-strong); }
.fn-opcao-regra { display: flex; gap: 0.5rem; align-items: flex-start; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.75rem; cursor: pointer; }
.fn-opcao-regra input { margin-top: 0.2rem; accent-color: var(--primary); }
.fn-opcao-regra span { display: flex; flex-direction: column; gap: 0.1rem; font-size: 0.875rem; }
.fn-opcao-regra .os-celula-sub { color: var(--muted-foreground); font-size: 0.75rem; }

/* ═══ Nível 3 da Ficha do Cliente — composição do pedido (208) ═════════════
   Classes `bi-f3-*`, do mockup aprovado `bi-ficha-nivel3-apresentacao2-mockup.html`. Só tokens do
   `tokens.css` (as cores das fatias são `--chart-*`). O contêiner some por atributo: `#bi-ficha-n3` declara
   `display` de autor? Não — mas o override fica, como em `#bi-ficha-n2`, para o portão do `[hidden]`. */
#bi-ficha-n3[hidden] { display: none; }
.bi-ficha-pedido {
  background: none; border: 0; margin: 0; padding: 0.1rem 0; font: inherit; font-weight: 600;
  color: var(--primary); cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
  font-variant-numeric: tabular-nums; border-radius: var(--radius-sm);
}
.bi-ficha-pedido:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
#bi-ficha-n3-corpo { display: flex; flex-direction: column; gap: 0.9rem; }
.bi-f3-trilha { display: flex; align-items: center; gap: 0.4rem; font-size: 0.82rem; flex-wrap: wrap; }
.bi-f3-trilha button {
  background: none; border: 0; padding: 0; font: inherit; color: var(--info-strong, var(--primary));
  cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
}
.bi-f3-trilha button:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; border-radius: var(--radius-sm); }
.bi-f3-trilha .atual { font-weight: 600; }
.bi-f3-trilha .sep { color: var(--muted-foreground); }
.bi-f3-bloco { padding: 0.9rem 1rem; display: flex; flex-direction: column; gap: 0.65rem; }
.bi-f3-bloco h3 { margin: 0; font-size: 0.95rem; font-weight: 600; }
.bi-f3-bloco h4 {
  margin: 0; font-size: 0.78rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.03em; color: var(--muted-foreground);
}
.bi-f3-cabeca { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.bi-f3-cabeca .apoio { font-size: 0.8rem; color: var(--muted-foreground); }
.bi-f3-botoes { display: flex; gap: 0.4rem; }
.bi-f3-numeros { display: flex; gap: 1.6rem; flex-wrap: wrap; }
.bi-f3-numero .rotulo { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted-foreground); }
.bi-f3-numero .valor { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.bi-f3-colunas { display: flex; align-items: flex-end; gap: 0.6rem; padding-top: 0.2rem; overflow-x: auto; }
.bi-f3-coluna { flex: 1 1 0; min-width: 4.2rem; display: flex; flex-direction: column; align-items: center; gap: 0.3rem; }
.bi-f3-coluna-pct { font-size: 0.78rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.bi-f3-coluna-valor { font-size: 0.68rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-f3-coluna-trilho { width: 100%; height: 9rem; display: flex; align-items: flex-end; background: var(--muted); border-radius: var(--radius-sm); }
.bi-f3-coluna-barra { display: block; width: 100%; border-radius: var(--radius-sm) var(--radius-sm) 0 0; min-height: 2px; }
.bi-f3-coluna-nome { font-size: 0.72rem; text-align: center; overflow-wrap: anywhere; line-height: 1.25; }
.bi-f3-tres { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.9rem; }
.bi-f3-pizza-corpo { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.bi-f3-pizza { width: 6.5rem; height: 6.5rem; border-radius: 50%; flex-shrink: 0; border: 1px solid var(--border); }
.bi-f3-legenda { display: flex; flex-direction: column; gap: 0.3rem; flex: 1 1 8rem; min-width: 0; }
.bi-f3-item { display: flex; align-items: center; gap: 0.45rem; font-size: 0.78rem; }
.bi-f3-dot { width: 0.6rem; height: 0.6rem; border-radius: 999px; flex-shrink: 0; }
.bi-f3-rotulo { flex: 1; overflow-wrap: anywhere; }
.bi-f3-valor { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--muted-foreground); }
.bi-f3-rank { display: flex; flex-direction: column; gap: 0.4rem; }
.bi-f3-rank-item { display: grid; grid-template-columns: 11rem minmax(0, 1fr) 4.2rem 3.4rem; gap: 0.6rem; align-items: center; font-size: 0.8rem; }
.bi-f3-rank-nome { overflow-wrap: anywhere; }
.bi-f3-rank-trilho { height: 1rem; background: var(--muted); border-radius: var(--radius-sm); overflow: hidden; }
.bi-f3-rank-barra { display: block; height: 100%; background: var(--chart-2); border-radius: var(--radius-sm); min-width: 2px; }
.bi-f3-rank-barra.lider { background: var(--primary); }
.bi-f3-rank-num { text-align: right; font-variant-numeric: tabular-nums; }
.bi-f3-rank-num.fraco { color: var(--muted-foreground); }
.bi-f3-grade { display: flex; align-items: flex-end; gap: 0.35rem; overflow-x: auto; padding-bottom: 0.2rem; }
.bi-f3-grade-col { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; min-width: 2.1rem; }
.bi-f3-grade-trilho { width: 100%; height: 5rem; display: flex; align-items: flex-end; background: var(--muted); border-radius: var(--radius-sm); overflow: hidden; }
.bi-f3-grade-barra { display: block; width: 100%; background: var(--chart-2); border-radius: var(--radius-sm) var(--radius-sm) 0 0; min-height: 2px; }
.bi-f3-grade-barra.pico { background: var(--primary); }
.bi-f3-grade-rotulo { font-size: 0.72rem; font-variant-numeric: tabular-nums; }
.bi-f3-grade-apoio { font-size: 0.66rem; color: var(--muted-foreground); font-variant-numeric: tabular-nums; }
.bi-f3-fora { display: flex; gap: 0.4rem; flex-wrap: wrap; align-items: center; font-size: 0.78rem; color: var(--muted-foreground); }
/* Tela estreita (a decisão explícita que o DESIGN.md pede): três pizzas viram coluna única e o ranking de cor
   perde a coluna fixa de nome. A tabela de modelos NÃO se reorganiza — rola dentro do `.zr-table-wrap`. */
@media (max-width: 860px) {
  .bi-f3-tres { grid-template-columns: 1fr; }
  .bi-f3-rank-item { grid-template-columns: 7.5rem minmax(0, 1fr) 3.6rem 3rem; }
}
#os-centro-sugerido { max-width: 26rem; }

.paineis-card { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 1rem; background: var(--card); display: flex; flex-direction: column; gap: 0.75rem; min-width: 0; }
.paineis-card h3 { margin: 0; font-size: 0.95rem; font-weight: 600; }
/* O interruptor: o checkbox guarda o estado (e o foco de teclado) e fica invisível; o trilho e o
   polegar desenhados é que se veem — mesmo desenho do mockup aprovado. */
/* DESIGN.md, piso de qualidade: coluna de FLEX que contém tabela declara `min-width: 0` no
   filho — sem isto o `overflow: auto` do `.zr-table-wrap` não tem efeito e a PÁGINA inteira
   rola de lado a 390 px (medido: 17 px de sobra antes desta linha). */
.paineis-card > .zr-table-wrap { min-width: 0; }

.crm-hist-filtro { display: flex; align-items: center; gap: 0.5rem; font-size: 0.85rem; color: var(--muted-foreground); }
.crm-hist-filtro .zr-select { min-width: 11rem; }
.crm-anotacao-form[hidden] { display: none; }
.crm-anotacao-form { padding: 0 1rem 0.75rem; display: grid; gap: 0.5rem; }
.crm-anotacao-form .zr-textarea { min-height: 5rem; }
.crm-hist-item--anotacao { border-left-color: var(--primary); }
.crm-hist-item--tarefa_criada, .crm-hist-item--tarefa_concluida { border-left-color: var(--success-strong); }
.crm-hist-item--alteracao { border-left-color: var(--warning-strong); }
.crm-tarefa { display: flex; align-items: flex-start; gap: 0.75rem; flex-wrap: wrap; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.75rem; }
.crm-tarefa--atrasada { border-color: var(--error-strong); }
.crm-tarefa--concluida { opacity: 0.75; }

/* ═══ CONTAS A RECEBER — filtros no padrão do Vexta, boleto na linha, seleção e impressão ═════
   Item 8 (docs/specs/contas-receber-filtros-boletos-mockup.html, aprovado em 17/10/2026). A ordem
   da tela é a D19: conferência → cartões → filtros (com a moldura `.zr-moldura-ia`) → lista.
   🔴 A lista NÃO rola de lado: ela mede o espaço que TEM (container query) e abaixo de 1000 px
   cada título vira um cartão com os mesmos campos (D17). O rótulo de cada campo sai do
   `data-rotulo` que o painel escreve a partir do cabeçalho. */
.cr-fraco { color: var(--muted-foreground); }

.cr-conferencia { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem 0.75rem; flex-wrap: wrap; padding: 0.45rem 0.75rem; margin-bottom: 0.9rem; border: 1px solid var(--border); border-radius: var(--radius-md); background: var(--sidebar, var(--muted)); font-size: 0.8rem; }
.cr-conferencia .cr-fraco { display: inline-flex; align-items: center; gap: 0.35rem; }

.cr-filtros { border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 0.75rem 0.9rem; margin-bottom: 0.9rem; background: var(--background); }
.cr-filtros-linha { display: flex; gap: 0.75rem 1rem; align-items: flex-end; flex-wrap: wrap; }
/* A quebra que põe o grupo de caixas (F59) numa linha própria — em conteúdo largo o painel
   recolhido cobriria os botões da linha de baixo. Altura zero: só reorganiza as linhas. */
.cr-quebra { flex-basis: 100%; height: 0; }
/* 🆕 VM7 (item 90; Eder, 27/10/2026): a faixa do Contas a PAGAR cabe numa linha — o modificador,
   que só a marcação do a pagar usa, dispensa a quebra acima e deixa a busca crescer; a última
   coluna empilha "Mais filtros" e "Limpar filtros" pelo `cr-botoes--pilha` (o mesmo das faturas).
   As outras telas da faixa (a receber, faturas, notas, ordens) NÃO mudam — a pergunta "o a receber
   acompanha?" é do Eder (registrada na §8 do VOLTAR_AO_MERGULHO_LOOP.md). Em 640 px ou menos o
   `@media` abaixo já empilha tudo, como nas demais telas. */
.cr-filtros--uma-linha .cr-quebra { display: none; }
.cr-filtros-mais { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0.75rem 1rem; margin-top: 0.85rem; padding-top: 0.85rem; border-top: 1px dashed var(--border); }
/* 🔴 O `display: grid` acima vence o `[hidden]` do navegador (regra de autor > regra do user
   agent): sem este override, o "Mais filtros" nasceria aberto e o recolher não esconderia nada. */
.cr-filtros-mais[hidden] { display: none; }
.cr-campo { display: flex; flex-direction: column; min-width: 0; flex: 0 1 170px; }
.cr-campo--par { flex: 0 1 300px; }
/* Item 59 (F59): o campo do filtro de caixas recolhido — a largura do mockup (13,5rem) fica no
   lugar do campo, e o primitivo `.zr-filtro-caixas` só cuida do desenho de dentro. */
.cr-campo--caixas { flex: 0 1 220px; }
.cr-campo--largo { grid-column: 1 / -1; }
.cr-rotulo { display: block; font-size: 0.875rem; font-weight: 500; margin: 0 0 0.3rem; }
.cr-par-datas { display: flex; align-items: center; gap: 0.4rem; min-width: 0; }
.cr-par-datas .zr-input { flex: 1 1 0; min-width: 0; }
.cr-busca { flex: 1 1 240px; min-width: 0; }
.cr-multi { border: 0; margin: 0; padding: 0; min-width: 0; flex: 0 1 230px; }
.cr-multi legend { padding: 0; font-size: 0.8rem; color: var(--muted-foreground); }
.cr-multi legend .cr-rotulo { display: inline; margin: 0; color: var(--foreground); }
.cr-multi legend .os-link { font-size: 0.8rem; color: var(--muted-foreground); }
.cr-multi-caixas { display: flex; flex-direction: column; gap: 0.15rem; max-height: 6.2rem; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.35rem 0.55rem; margin-top: 0.3rem; }
.cr-multi-caixas .cr-check { font-size: 0.82rem; }
.cr-multi-caixas--linha { flex-direction: row; flex-wrap: wrap; gap: 0.25rem 1.1rem; max-height: none; }
.cr-check { display: flex; gap: 0.5rem; align-items: flex-start; font-size: 0.875rem; }
.cr-botoes { display: flex; gap: 0.6rem; align-items: center; flex-wrap: wrap; margin-left: auto; }
/* FN5 (D6 do mockup `faturamento-abas-filtros`): os botões da faixa de FILTRO empilhados à
   direita e em largura total — "Mais filtros" e "Limpar filtros" são contorno, nunca ação. */
.cr-botoes--pilha { flex-direction: column; align-items: stretch; }
.cr-botoes--pilha > .zr-btn { width: 100%; }
@media (max-width: 640px) {
  .cr-filtros-linha > * { flex: 1 1 100%; }
  .cr-botoes { margin-left: 0; }
}

.cr-titulos { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
/* Z2: o espaçamento lateral desceu de 0.55 para 0.3rem e a "Conta · carteira" de 9 para 8rem quando entrou a coluna
   "Mais Ações": com a DejaVu do Linux do CI a tabela de 13 colunas passava 78 px da área a 1440 px (medido com a
   fonte do CI forçada; folga depois: 26 px). Coluna nova nesta lista? Meça de novo do mesmo jeito. */
.cr-titulos th { text-align: left; font-weight: 500; color: var(--muted-foreground); font-size: 0.8rem; padding: 0.55rem 0.3rem; border-bottom: 1px solid var(--border); white-space: nowrap; }
.cr-titulos td { padding: 0.6rem 0.3rem; border-bottom: 1px solid var(--border); vertical-align: top; white-space: nowrap; }
.cr-titulos tbody tr:last-child td { border-bottom: 0; }
.cr-titulos tbody tr:hover td { background: var(--muted); }
.cr-titulos .os-dir { text-align: right; }
.cr-titulos .cr-centro { text-align: center; }
/* D16: a largura de "JEANS VALE VERDE LTDA" (154 px, medido) + o espaçamento da célula; nome
   mais comprido termina em "..." e a dica (D18) traz o inteiro. */
.cr-col-cliente { width: calc(154px + 0.6rem); max-width: calc(154px + 0.6rem); overflow: hidden; text-overflow: ellipsis; }
.cr-col-conta { width: 8rem; max-width: 8rem; }
/* Z2: o "Mais Ações" da linha, sempre a última célula (o `.fat-col-acoes` das Faturas). */
.cr-col-acoes { white-space: nowrap; }
/* O menu é `fixed` (a rolagem da lista recortaria o `absolute` do `.bi-acoes-menu`); o JS o posiciona. */
.cr-acoes-menu { position: fixed; z-index: 40; max-width: min(24rem, calc(100vw - 1rem)); max-height: min(70vh, 24rem); overflow-y: auto; }
.cr-col-conta > * { overflow: hidden; text-overflow: ellipsis; }
/* "Sem boleto emitido"/"Sem conta definida" é um <span> em linha: o corte acima não vale para ele,
   e com fonte mais larga (a DejaVu do Linux do CI) o texto vazava 6 px da lista a 1440 e 1920 px.
   Ele quebra em duas linhas — curto, sem dica, nada fica escondido. */
.cr-titulos td.cr-col-conta > .os-fraco { display: block; white-space: normal; }
.cr-col-sel { width: 2.75rem; text-align: center; padding-inline: 0.5rem; }
.cr-col-sel input { width: 1rem; height: 1rem; margin: 0.15rem 0 0; cursor: pointer; accent-color: var(--primary); }
.cr-titulos tr.cr-marcada td { background: color-mix(in oklch, var(--info-bg-soft) 70%, var(--background)); }
@container (max-width: 1000px) {
  .cr-titulos thead { display: none; }
  .cr-titulos, .cr-titulos tbody { display: block; }
  .cr-titulos tr { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0.35rem 1rem; padding: 0.75rem 0.85rem; border-bottom: 1px solid var(--border); }
  .cr-titulos tr:last-child { border-bottom: 0; }
  .cr-titulos td { display: block; padding: 0; border: 0; text-align: left; white-space: normal; }
  .cr-titulos td::before { content: attr(data-rotulo); display: block; font-size: 0.68rem; font-weight: 600; color: var(--muted-foreground); text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.1rem; }
  .cr-titulos td.cr-col-sel { grid-column: 1 / -1; }
  .cr-titulos td.cr-col-sel::before { content: none; }
  .cr-titulos .cr-col-cliente, .cr-titulos .cr-col-conta { width: auto; max-width: none; white-space: normal; }
  .cr-titulos td.os-dir, .cr-titulos td.cr-centro { text-align: left; }
}

/* D18 — a dica do texto cortado, copiável (o componente é o `dica-copiavel.js`). */
.cr-dica-alvo { cursor: help; border-radius: var(--radius-sm); }
.cr-dica-alvo:focus-visible { outline: 3px solid color-mix(in oklch, var(--ring) 50%, transparent); outline-offset: -2px; }
.cr-dica { position: fixed; z-index: 50; max-width: min(28rem, calc(100vw - 2rem)); display: flex; align-items: flex-start; gap: 0.5rem; padding: 0.45rem 0.5rem 0.45rem 0.7rem; background: var(--foreground); color: var(--background); border-radius: var(--radius-md); font-size: 0.8rem; line-height: 1.4; box-shadow: 0 4px 14px rgb(0 0 0 / 18%); white-space: normal; }
.cr-dica[hidden] { display: none; }
.cr-dica-texto { overflow-wrap: anywhere; }
/* Rótulos e a segunda linha da dica (item 49: razão social em cima, nome fantasia embaixo). */
.cr-dica-rotulo { display: block; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; opacity: 0.7; }
.cr-dica-rotulo--depois { margin-top: 0.3rem; }
.cr-dica-copiar { flex: none; border: 0; background: color-mix(in oklch, var(--background) 16%, transparent); color: inherit; border-radius: var(--radius-sm); width: 1.75rem; height: 1.75rem; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.cr-dica-copiar:hover { background: color-mix(in oklch, var(--background) 28%, transparent); }
.cr-dica-copiar:focus-visible { outline: 2px solid var(--background); outline-offset: 1px; }
.cr-dica-status { flex: none; align-self: center; font-weight: 600; font-size: 0.75rem; }
.cr-dica-status:empty { display: none; }


/* ═══ KI2 (ATIVIDADES_VALOR_INDICADORES_LOOP.md; mockup atividades-kpis-feriados-mockup.html,
   aprovado em 17/10/2026) — os indicadores das atividades, a faixa de filtros (indicadores →
   filtros → lista, D15) e a tela Gestão › Feriados. 🔴 Nenhuma regra aqui decide número: o
   servidor manda os minutos, os centavos e as horas úteis; estas classes só desenham. ═══ */


.atv-ind-matriz--5col { grid-template-columns: minmax(8.5rem, 0.9fr) repeat(5, minmax(0, 1fr)); }
.atv-ind-matriz--3col { grid-template-columns: minmax(8.5rem, 0.9fr) repeat(3, minmax(0, 1fr)); }
@media (max-width: 1100px) {
  .atv-ind-matriz--5col, .atv-ind-matriz--3col { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .atv-ind-periodo { grid-column: 1 / -1; padding-top: 0.4rem; }
}
@media (max-width: 640px) {
  .atv-ind-matriz--5col, .atv-ind-matriz--3col { grid-template-columns: minmax(0, 1fr); }
}

/* LA2 (item 25): o rótulo de horas sai do FLUXO e ganha a própria faixa (1rem) no rodapé da
   coluna. A barra de 100% (a vazia, cheia) termina no TOPO do trilho — somada ao rótulo, ela
   vazava por cima da frase do gráfico. `position: relative` é do rótulo absoluto; a faixa é o
   `padding-bottom`, que a barra desconta da própria altura. */
.atv-g-dia { --h: 0%; position: relative; flex: 1 1 0; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: stretch; padding: 0 0 1rem; border: 0; background: transparent; cursor: pointer; font: inherit; color: inherit; }
/* LA2 (item 25): a linha da jornada usa a MESMA escala das barras — a fração `--j` (0 a 1) vale
   sobre a área de plotagem, que é o trilho MENOS a faixa do rótulo (1rem). */
.atv-g-jornada { --j: 0; position: absolute; left: 0; right: 0; bottom: calc(1rem + (100% - 1rem) * var(--j, 0)); border-top: 1px dashed var(--muted-foreground); pointer-events: none; }

.atv-g-ordem-horas { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; font-size: 0.82rem; margin-left: auto; }
.atv-g-botoes { display: flex; gap: 0.5rem; flex-wrap: wrap; }
/* LA3 (item 27/D1–D6) — a tabela de cada grupo: Horário numa linha, Horas, a marca Faturar, a
   atividade quebrando sem reticências e a coluna das ações. D12: sem recuo nas pontas, para o
   Editar e o cadeado terminarem na MESMA borda direita dos botões do cliente. */
.atv-hor { width: 100%; border-collapse: collapse; margin-top: 0.45rem; font-size: 0.8rem; }
.atv-hor th { text-align: left; font-size: 0.7rem; font-weight: 600; color: var(--muted-foreground); padding: 0.2rem 0.5rem; border-bottom: 1px solid var(--border); white-space: nowrap; }
.atv-hor td { padding: 0.35rem 0.5rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.atv-hor tr:last-child td { border-bottom: 0; }
.atv-hor td.atv-mesma { border-bottom-color: transparent; }
.atv-c-fat { text-align: center; width: 1%; }
.atv-c-txt { width: 100%; overflow-wrap: anywhere; }
.atv-c-acao { width: 1%; white-space: nowrap; text-align: right; vertical-align: middle; }
.atv-hor th:first-child, .atv-hor td:first-child { padding-left: 0; }
.atv-hor th:last-child, .atv-hor td:last-child { padding-right: 0; }
.atv-flag--sim { background: var(--primary); border-color: var(--primary); color: var(--primary-foreground); }
.atv-flag--sim .zr-icone { width: 0.75rem; height: 0.75rem; }
@media (max-width: 640px) {
  /* D6 — tela estreita: um cartão por horário; os botões do cliente dividem a largura. */
  .atv-hor thead { display: none; }
  .atv-hor, .atv-hor tbody, .atv-hor td { display: block; width: 100%; }
  .atv-hor tr { display: grid; grid-template-columns: auto auto 1fr; gap: 0.25rem 0.6rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); }
  .atv-hor tr:last-child { border-bottom: 0; }
  .atv-hor td { border: 0; padding: 0; width: auto; }
  .atv-hor .atv-c-fat { text-align: right; }
  .atv-hor .atv-c-txt, .atv-hor .atv-c-acao { grid-column: 1 / -1; text-align: left; }
  .atv-g-ordem-horas { width: 100%; margin-left: 0; }
  .atv-g-botoes { width: 100%; }
  .atv-g-botoes .zr-btn { flex: 1 1 0; }
}

.atv-f-datas { display: flex; align-items: center; gap: 0.35rem; }
.atv-f-datas .zr-input { min-width: 0; }
/* Item 30/D1–D3 (LA7; mockup atividades-filtros-botoes-mockup.html): só a faixa de Lançar
   atividades. A última coluna é uma PILHA — "Mais filtros" em cima, "Limpar filtros" embaixo,
   mesma largura, à direita; os dois seguem em contorno (D5: são filtros, não ações). Para não
   espremer a Situação, a pilha não encolhe. */
.atv-f-botoes { display: flex; flex-direction: column; align-items: stretch; gap: 0.4rem; margin-left: auto; flex: 0 0 auto; }
.atv-f-botoes .zr-btn { width: 100%; }
.atv-f-tipos-caixas { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 0.2rem 1rem; width: 100%; }
.atv-f-tipos-caixas .zr-checkbox { font-size: 0.83rem; }

.atv-fer-ano .zr-select { width: auto; }
.atv-fer-tabela { width: 100%; }
.atv-fer-tabela td { vertical-align: middle; }
.atv-fer-tabela .atv-fer-acoes { white-space: nowrap; text-align: right; }
.atv-fer-tabela .atv-fer-acoes .zr-btn + .zr-btn { margin-left: 0.3rem; }
/* A regra do DESIGN de 17/10: listagem não rola de lado — na largura curta a tabela vira CARDS,
   cada campo com o rótulo do `data-rotulo`. O contêiner é quem decide (container query). */
@container (max-width: 700px) {
  .atv-fer-tabela thead { display: none; }
  .atv-fer-tabela, .atv-fer-tabela tbody, .atv-fer-tabela tr, .atv-fer-tabela td { display: block; width: 100%; }
  .atv-fer-tabela tr { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.75rem; margin-bottom: 0.5rem; background: var(--card); }
  .atv-fer-tabela td { display: flex; justify-content: space-between; gap: 0.75rem; padding: 0.15rem 0; border: 0; text-align: left; }
  .atv-fer-tabela td::before { content: attr(data-rotulo); font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted-foreground); }
  .atv-fer-tabela .atv-fer-acoes { justify-content: flex-end; }
  .atv-fer-tabela .atv-fer-acoes::before { content: none; }
}

.crm-parcela-linha { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.crm-parcela-linha .crm-parcela-data { flex: 0 0 9.5rem; min-width: 0; }
.crm-parcela-linha span { min-width: 6rem; color: var(--muted-foreground); }
.crm-parcela-linha .zr-input { flex: 1 1 auto; min-width: 0; }


/* ============================================================================
   Contrato — as SEIS posições (D4/D5 do mockup da minuta, 26/10/2026) e a
   janela "Gerar minuta de contrato" (CM8). Nenhum token novo: as mesmas
   famílias soft/strong do design system; "Encerrado" é só contorno.
   ============================================================================ */
.zr-badge.p-minuta { background: var(--muted); color: var(--muted-foreground); }
.zr-badge.p-enviado { background: var(--info-bg-soft); color: var(--info-strong); }
.zr-badge.p-pendente { background: var(--warning-bg-soft); color: var(--warning-strong); }
.zr-badge.p-ativo { background: var(--success-bg-soft); color: var(--success-strong); }
.zr-badge.p-cancelado { background: var(--error-bg-soft); color: var(--error-strong); }
.zr-badge.p-encerrado { background: transparent; color: var(--muted-foreground); box-shadow: inset 0 0 0 1px var(--border); }

.crm-modal--minuta { max-width: 36rem; }
.crm-minuta-status { padding: 0.75rem 0; color: var(--muted-foreground); font-size: 0.85rem; }
.crm-minuta-recolhe { border: 1px solid var(--border); border-radius: var(--radius-md); padding: 0.6rem 0.75rem; margin-top: 0.75rem; }
.crm-minuta-recolhe summary { cursor: pointer; font-weight: 600; display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.crm-minuta-recolhe summary small { font-weight: 400; color: var(--muted-foreground); }
.crm-minuta-recolhe .campos { margin-top: 0.6rem; }
.crm-minuta-dica { font-size: 0.75rem; color: var(--muted-foreground); }

/* CM9: a coluna "Mais Ações" (a ÚLTIMA da lista de contratos) — botão + o menu da linha. */
.ct-mais-cel { position: relative; }
.ct-mais-menu {
  position: absolute; right: 0.9rem; top: calc(100% - 0.2rem); z-index: 30;
  min-width: 10rem; padding: 0.25rem; background: var(--card); color: var(--card-foreground);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
