/* ==========================================================================
   components.css — Kit de componentes da Torre, em CSS vanilla.
   Um bloco por item do DESIGN.md §6, na ordem do inventário.

   REGRA DURA, e é dela que sai o valor da fundação: componente só usa
   `var(--tc-*)`. Zero hex, zero primitiva `--g-*`, zero z-index numérico.
   Quem confere isso são os critérios 1 e 2 do §12.6 — dois greps.

   O QUE ESTÁ AQUI é só o subconjunto que as telas JÁ CONSTRUÍDAS exigem. O §6
   tem 33 itens; escrever os 33 seria criar CSS sem consumidor, que é o oposto do
   princípio de simplicidade (ARQUITETURA.md §2.1) e envelhece errado — cada
   componente nasce junto da tela que o pede.

   Onda 0, com o LAUNCHER:
     §6.1  header do app + §11 marca e lockup
     §6.7  botão
     §6.10 estado vazio
     §6.11 estado de erro / dado desatualizado
     §6.12 link
     §6.16 card de módulo
     §6.17 menu lateral
     §6.32 avatar
     §11.1 assinatura de rodapé

   Onda 1, com a tela de SEPARAÇÃO E ROTAS:
     §6.3  tabela densa — e o padrão TABELA→CARDS do §4.1–4.2, que a decisão de
           13/08/2026 adiou de propósito até existir a primeira consumidora
     §6.5  badge de status (etapa)
     §6.8  campo de formulário (input e select)
     §6.9  barra de filtros + drawer de filtros em `compact`
     §6.14 checkbox
     §6.11 faixa de dado desatualizado (o banner que faltava)
     seção, barra de ação e `.tc-page` — o esqueleto que o shell pressupõe

   Nenhuma tela LEGADA carrega este arquivo: /pedidos, /itens, /configuracoes,
   /auditoria e /dashboard seguem no header.js e nos tokens próprios. A migração
   delas é da onda de UX/IX (§7).
   ========================================================================== */

/* ── Base ────────────────────────────────────────────────────────────────────
   O mínimo para o launcher se apoiar. Não é reset global do produto: as telas
   legadas têm o próprio, e não carregam este arquivo. */

/* `border-box` em tudo, e não só dentro de `.tc-app`.
   A primeira versão limitava o reset a `.tc-app *`, mas o drawer e o scrim são
   IRMÃOS do `.tc-app` (precisam ficar fora dele para o scrim cobrir a tela
   inteira) — e ficavam de fora do reset. O item do menu tem `width: 100%` mais
   `padding: 0 16px` e uma borda de 2px: em `content-box` isso soma 34px além da
   largura do drawer, e era essa a barra de rolagem horizontal que aparecia no
   menu. Um kit que assume `border-box` precisa aplicá-lo onde ele desenha.
   Só as páginas tokenizadas carregam este arquivo, então o reset global aqui
   não alcança nenhuma tela legada. */
*, *::before, *::after { box-sizing: border-box; }

/* O atributo `hidden` precisa VENCER o `display` dos componentes.
   `[hidden] { display: none }` é regra do USER-AGENT, e qualquer `display` de
   folha de autor a derrota — não por especificidade, mas por origem: autor vence
   UA sempre. Como quase todo componente daqui declara `display: flex` ou
   `inline-flex`, o atributo virava decoração: a faixa de dado desatualizado
   aparecia vazia, o "limpar filtros" nascia visível sem filtro ativo, e — o pior
   — o item **Auditoria** do menu aparecia para quem não tem a capacidade, furando
   a regra "sem permissão não aparece" (§6.16), e o badge de ambiente pintava uma
   pílula âmbar vazia em produção.

   Foi assim que o launcher subiu na Onda 0; o defeito só ficou visível quando a
   segunda tela passou a esconder blocos inteiros. É o segundo e último
   `!important` do kit, e ele existe porque a alternativa seria repetir
   `&[hidden] { display: none }` em cada componente — ou seja, um lugar a mais
   para esquecer, em cada componente novo, para sempre. */
[hidden] { display: none !important; }

/* Foco visível, uma vez, para todo controle (§6, estados obrigatórios).
   `:focus-visible` e não `:focus`: o anel aparece na navegação por teclado e
   não a cada clique de mouse — o operador que clica a tela inteira não fica
   com um anel perseguindo o cursor. */
.tc-app :focus-visible {
  outline: 2px solid var(--tc-border-focus);
  outline-offset: 1px;
  border-radius: var(--tc-radius-control);
}

/* Ícone (§8). Os atributos de traço vivem no <symbol> do sprite; aqui fica o
   que é de layout e de cor. 16px é o padrão; 20 e 24 são as outras duas
   medidas permitidas — nada entre elas. */
.tc-icon {
  width: 16px; height: 16px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  vertical-align: -0.125em;
}
.tc-icon-20 { width: 20px; height: 20px; }
.tc-icon-24 { width: 24px; height: 24px; }

/* Camada de foto (§2.6). A foto é desenhada CHEIA e quem controla a
   intensidade é só o véu — uma atenuação, um lugar para ajustar. Empilhar
   opacidade na foto E no véu dava ~0,7% de foto efetiva: invisível.
   As linhas e cards ficam opacos por cima; a foto aparece nas calhas e margens,
   nunca sob o dado. */
.tc-photo-bg::before {
  content: ""; position: fixed; inset: 0; z-index: var(--tc-z-photo);
  background: var(--tc-photo-image) center / cover no-repeat;
  filter: blur(var(--tc-scrim-photo-blur)) saturate(var(--tc-scrim-photo-sat));
  /* O blur puxa pixels de fora da viewport; sem a folga, as bordas clareiam. */
  transform: scale(1.06);
}
.tc-photo-bg::after {
  content: ""; position: fixed; inset: 0; z-index: var(--tc-z-veil);
  background: var(--tc-scrim-photo-veil);
}
.tc-app { position: relative; z-index: var(--tc-z-app); }

/* Skeleton (§2.9): pulso de 1.2s, opacidade 0.4↔0.7. Nunca causa layout shift —
   o bloco tem o tamanho do conteúdo que vai substituí-lo. */
@keyframes tc-pulse { 0%, 100% { opacity: .4 } 50% { opacity: .7 } }
.tc-skeleton {
  background: var(--tc-surface-hover);
  border-radius: var(--tc-radius-control);
  animation: tc-pulse 1.2s var(--tc-ease-inout) infinite;
}
@keyframes tc-spin { to { transform: rotate(360deg) } }

/* Troca de tema/ambiente NÃO anima (§2.9). Sem isto, mudar `data-theme` põe
   todo elemento com `transition` de cor para animar ao mesmo tempo — dois cards
   passam despercebidos, uma tabela de 200 linhas engasga. Quem liga e desliga é
   o JS, por um frame: marca `data-switching` no <html>, troca o tema, e tira a
   marca no frame seguinte.
   `!important` porque precisa vencer a transição declarada em cada componente,
   e este é o único lugar do kit onde ele aparece. */
[data-switching] *,
[data-switching] *::before,
[data-switching] *::after { transition: none !important; }

/* ── §6.1 Header do app ──────────────────────────────────────────────────────
   Fixo, `--tc-surface-header`, altura `--tc-header-h`, escuro nos DOIS temas —
   já era assim e é bom traço de identidade; virou decisão declarada (§2.1).
   Nada mais entra no header sem sair outra coisa: é a faixa mais cara do
   produto. Por isso a assinatura da matriz (§11.1) NÃO entra aqui. */
.tc-appbar {
  position: fixed; top: 0; left: 0; right: 0;
  height: var(--tc-header-h);
  z-index: var(--tc-z-header);
  background: var(--tc-surface-header);
  color: var(--tc-text-on-header);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--tc-space-3);
  padding: 0 var(--tc-space-5);
}
.tc-appbar-right { display: flex; align-items: center; gap: var(--tc-space-3); }

/* O corpo da página, abaixo do header fixo. É só a compensação da faixa — o
   padding e o arranjo interno pertencem a cada tela, porque o launcher respira
   e uma tela de tabela não. Nasceu com o shell compartilhado (Onda 1): as duas
   linhas eram idênticas em toda tela nova e viravam cópia por página. */
.tc-page {
  min-height: calc(100vh - var(--tc-header-h));
  margin-top: var(--tc-header-h);
}
/* Com barra de telas (§6.33), quem compensa o header fixo é a BARRA — ela vem
   antes da página no fluxo. Somar as duas compensações abriria uma faixa vazia
   de 56px sob o header, e é o tipo de defeito que só aparece no módulo que
   ganhou a segunda tela. O atributo é posto no <html> pelo shell antes de o
   <main> ser interpretado, então a página nunca é pintada com a margem errada. */
[data-screens] .tc-page {
  margin-top: 0;
  min-height: calc(100vh - var(--tc-header-h) - var(--tc-touch-min));
}

/* Botão de ícone do header (☰). O alvo cresce sem mudar o desenho (§4.1). */
.tc-icon-btn {
  position: relative;
  width: var(--tc-control-h); height: var(--tc-control-h);
  display: grid; place-items: center;
  background: transparent; border: 0; border-radius: var(--tc-radius-control);
  color: inherit; cursor: pointer;
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-icon-btn:hover { background: var(--tc-surface-hover); }
.tc-icon-btn::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: var(--tc-touch-min); height: var(--tc-touch-min);
  transform: translate(-50%, -50%);
}

/* Badge de ambiente (HML/DEV). Cor sozinha não pode ser o único sinal de
   ambiente (§2) — a tinta tinge as superfícies, este badge diz o nome. */
.tc-env-badge {
  padding: 0 var(--tc-space-2);
  height: var(--tc-control-h-sm);
  display: inline-flex; align-items: center;
  border-radius: var(--tc-radius-pill);
  border: 1px solid var(--tc-state-warning-border);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}

/* ── §11 Marca: símbolo e lockup ─────────────────────────────────────────────
   O arranjo padrão é LADO A LADO, a mesma estrutura da matriz. O wordmark é
   monocromático — o verde é do símbolo, não do texto: com o símbolo já verde,
   verde no texto divide a leitura do nome em dois e enfraquece o símbolo.

   AJUSTE ÓPTICO, e ele vale em qualquer lockup lateral. `align-items: center`
   centraliza a CAIXA DE LINHA, não as maiúsculas, e essa caixa carrega o
   descendente que um wordmark em caixa alta nunca usa: sem correção a marca
   pende ~6–8% abaixo do eixo das capitais, visível a olho. São duas regras,
   sempre juntas: `line-height: 1` no wordmark (em type.css) e um
   `translateY(-0,1 × corpo)` em PX no <svg> — nunca em `em`, que dentro do
   <svg> resolve contra a fonte herdada do contêiner e não contra o corpo do
   wordmark. */
.tc-lockup { display: inline-flex; align-items: center; text-decoration: none; }
.tc-lockup-mark { flex: none; }

/* Header: semente 26px + 9px + nome da tela em caps 13px. Medida única,
   idêntica em todos os módulos.
   O `min-height` é alvo de toque, não respiro: o lockup tem 26px de altura e é
   clicável (volta ao launcher), então precisa dos 44px do §4.1 — que cabem
   dentro dos 56px do header sem mudar o desenho.

   O nome da tela usa `.tc-screen-name` (--tc-font-ui), e não a Visby do
   wordmark: decisão de 14/08/2026 registrada no type.css e no DESIGN.md §3. A
   medida (13px caps, tracking .08em) é a mesma, então o ajuste óptico do
   símbolo — o translateY em px logo abaixo — continua valendo sem recalcular. */
.tc-lockup-header { min-height: var(--tc-touch-min); }
.tc-lockup-header .tc-lockup-mark { width: 26px; height: 26px; transform: translateY(-1.3px); }
.tc-lockup-header .tc-screen-name { margin-left: 9px; color: var(--tc-text-on-header); }

/* Corpo do launcher: L1, símbolo 1,3× o corpo do wordmark. O wordmark do
   produto NÃO se repete na mesma tela — por isso o header mostra só semente +
   nome da tela, e o lockup completo vive aqui. */
.tc-lockup-body .tc-lockup-mark { width: 29px; height: 29px; transform: translateY(-2.2px); }
.tc-lockup-body .tc-wordmark {
  margin-left: var(--tc-space-2);
  font-size: 22px;
  color: var(--tc-text-primary);
}

/* ── §6.3 Tabela densa ───────────────────────────────────────────────────────
   Linha de 44px (`--tc-row-h`): duas linhas de texto — principal 13/600 e meta
   12/400 —, header de coluna em `--tc-type-label` sticky, números em mono
   tabular à direita, hover e linha selecionada por token.

   Nasceu na Onda 1, com a tela de Separação: até aqui o kit não tinha tabela
   porque nenhuma tela tokenizada tinha uma. É o consumidor que faz o componente
   nascer (ARQUITETURA.md §2.1) — foi exatamente esse o motivo de a decisão de
   13/08/2026 adiar o padrão tabela→cards até haver quem o usasse. */
.tc-table-wrap { overflow-x: auto; }
.tc-table {
  width: 100%;
  border-collapse: collapse;
  /* O piso de largura é o que faz a rolagem horizontal acontecer DENTRO do
     contêiner em vez de na página. Em `compact` a tabela vira cards e este
     piso é zerado — lá não existe rolagem lateral nenhuma. */
  min-width: 720px;
}
.tc-table thead th {
  position: sticky; top: var(--tc-header-h);
  z-index: var(--tc-z-sticky);
  background: var(--tc-surface-page);
  color: var(--tc-text-muted);
  text-align: left;
  white-space: nowrap;
  padding: var(--tc-space-2) var(--tc-space-3);
  border-bottom: 1px solid var(--tc-border-default);
}
/* DENTRO do wrap o offset da faixa fixa é ERRADO, e o erro é silencioso.
   `overflow-x: auto` torna o `.tc-table-wrap` um contêiner de rolagem, e a
   partir daí o `sticky` resolve o `top` contra ELE, não contra a viewport. Com
   `top: var(--tc-header-h)` o cabeçalho ficava cravado 56px para DENTRO do
   corpo da tabela — por cima das primeiras linhas —, e ali permanecia: medido
   em 26/08/2026, o deslocamento era 56px em toda posição de rolagem, enquanto
   a mesma tabela fora do wrap pinava corretamente em 56 (isto é, o sticky de
   viewport nunca funcionou na Separação, que tem as quatro tabelas no wrap).

   Por que `0` e não outro valor: dentro de um contêiner de rolagem o cabeçalho
   deve grudar no topo DESSE contêiner. Como o wrap rola só na horizontal, o
   efeito visível é o cabeçalho parado no topo da tabela — sem sobreposição e
   sem prometer um pino que não existe. Se algum dia um wrap ganhar rolagem
   vertical própria (`max-height`), esta mesma regra passa a grudar de verdade.

   A regra base acima NÃO é zerada de propósito: fora do wrap ela está certa e
   pina sob a faixa fixa. Zerar as duas faria o cabeçalho de uma tabela sem
   wrap grudar em `0`, atrás do header do app — um cabeçalho invisível, que é
   defeito pior do que um cabeçalho parado. */
.tc-table-wrap .tc-table thead th { top: 0; }
.tc-table tbody td {
  height: var(--tc-row-h);
  padding: 0 var(--tc-space-3);
  border-bottom: 1px solid var(--tc-border-subtle);
  vertical-align: middle;
  color: var(--tc-text-primary);
}
.tc-table tbody tr { background: var(--tc-surface-raised); }
.tc-table tbody tr:hover { background: var(--tc-surface-hover); }
/* `data-selected` e não `aria-selected`: `aria-selected` só é válido dentro de
   um `role="grid"`/`listbox`, e esta é uma tabela comum com checkbox — quem
   anuncia a seleção ao leitor de tela é o próprio checkbox, que já tem estado.
   ARIA errada é pior que ARIA nenhuma: ela mente para quem depende dela. */
.tc-table tbody tr[data-selected="1"] { background: var(--tc-surface-selected); }
/* Números, datas e IDs à direita, em mono tabular — alinhamento vem do tabular,
   largura vem do CSS (§3: nunca dimensionar coluna em `ch`). */
.tc-table .tc-col-num { text-align: right; }
.tc-table th.tc-col-num { text-align: right; }
.tc-cell-strong { color: var(--tc-text-primary); }
.tc-cell-meta   { color: var(--tc-text-secondary); }
/* Célula de duas linhas: é o que justifica os 44px do §4 (18 + 16 + 8). */
.tc-cell-2l { display: flex; flex-direction: column; justify-content: center; min-width: 0; }
.tc-cell-2l > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Prioridade de coluna (§4.2), declarada por atributo e sem uma linha de JS. */
@media (max-width: 1119px) { .tc-table [data-col-priority="3"] { display: none; } }
@media (max-width: 719px)  { .tc-table [data-col-priority="2"] { display: none; } }

/* ── §4.1–4.2 Tabela → cards em `compact` ────────────────────────────────────
   O padrão mobile do DESIGN.md, materializado pela primeira vez. Abaixo de
   720px a tabela deixa de ser tabela: cada linha vira um card e cada célula
   vira uma linha rótulo→valor, com o rótulo saindo do `data-rotulo` da própria
   célula. Sem JS, sem segunda marcação e sem duplicar conteúdo — o mesmo HTML
   serve as duas leituras, que é o que impede as duas de divergirem.

   Célula com `data-destaque` não recebe rótulo: é o título do card (o PV), e
   repetir "PV" ao lado do número seria ruído no lugar mais nobre do card. */
@media (max-width: 719px) {
  .tc-table-cards { min-width: 0; }
  .tc-table-cards thead { display: none; }
  .tc-table-cards,
  .tc-table-cards tbody,
  .tc-table-cards tr,
  .tc-table-cards td { display: block; width: 100%; }
  .tc-table-cards tbody tr {
    border: 1px solid var(--tc-border-subtle);
    border-radius: var(--tc-radius-surface);
    padding: var(--tc-space-3);
    margin-bottom: var(--tc-space-3);
  }
  .tc-table-cards tbody td {
    height: auto;
    min-height: var(--tc-touch-min);
    padding: var(--tc-space-1) 0;
    border-bottom: 0;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--tc-space-3);
  }
  .tc-table-cards tbody td::before {
    content: attr(data-rotulo);
    flex: none;
    color: var(--tc-text-muted);
    font: 600 11px/14px var(--tc-font-ui);
    letter-spacing: .06em;
    text-transform: uppercase;
  }
  .tc-table-cards tbody td[data-destaque]::before { content: none; }
  .tc-table-cards tbody td[data-destaque] {
    border-bottom: 1px solid var(--tc-border-subtle);
    padding-bottom: var(--tc-space-2);
    margin-bottom: var(--tc-space-1);
  }
  .tc-table-cards .tc-cell-2l > * { white-space: normal; }
}

/* ── §6.5 Badge de status (etapa) ────────────────────────────────────────────
   Pill outline, caps 11/600, SEMPRE neutro: etapa do fluxo é etapa, não
   julgamento — "PEÇAS" deixa de ser vermelho. Não é interativo e não tem hover.
   Severidade mora em badges próprios: URGENTE sólido (o único preenchido em
   contexto denso, para preservar saliência) e ⚠ âmbar para atenção. */
.tc-badge {
  display: inline-flex; align-items: center; gap: var(--tc-space-1);
  height: var(--tc-control-h-sm);
  padding: 0 var(--tc-space-2);
  border-radius: var(--tc-radius-pill);
  border: 1px solid var(--tc-border-default);
  color: var(--tc-text-secondary);
  white-space: nowrap;
}
.tc-badge-urgent {
  border-color: transparent;
  background: var(--tc-state-urgent);
  color: var(--tc-text-on-accent);
}
.tc-badge-warning {
  border-color: var(--tc-state-warning-border);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}
.tc-badge-progress {
  border-color: var(--tc-state-progress-border);
  background: var(--tc-state-progress-bg);
  color: var(--tc-state-progress);
}

/* ── §6.7 Botão ──────────────────────────────────────────────────────────────
   Altura `--tc-control-h`, raio de controle, tipo control 600, padding-x 16.
   Máximo UM primário por região. */
.tc-btn {
  height: var(--tc-control-h);
  padding: 0 var(--tc-space-4);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--tc-space-2);
  border: 1px solid transparent;
  border-radius: var(--tc-radius-control);
  font: 600 14px/20px var(--tc-font-ui);
  cursor: pointer;
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out),
              border-color var(--tc-dur-instant) var(--tc-ease-out),
              color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-btn-primary {
  background: var(--tc-action-primary);
  color: var(--tc-text-on-accent);
}
.tc-btn-primary:hover  { background: var(--tc-action-primary-hover); }
.tc-btn-primary:active { background: var(--tc-action-primary-active); }

.tc-btn-secondary {
  background: transparent;
  border-color: var(--tc-border-default);
  color: var(--tc-text-primary);
}
.tc-btn-secondary:hover {
  border-color: var(--tc-border-strong);
  background: var(--tc-surface-hover);
}

/* Ghost é ação terciária em barra densa: padding curto de propósito, senão ele
   ocupa a largura de um primário sem ter o peso de um. */
.tc-btn-ghost { background: transparent; color: var(--tc-action-primary);
                padding: 0 var(--tc-space-2); }
.tc-btn-ghost:hover { color: var(--tc-action-primary-hover); }

/* Botão pequeno: mesma anatomia na altura de badge/pill (--tc-control-h-sm).
   Para ação que mora DENTRO de um card, onde um botão de 36px domina a caixa. */
.tc-btn-sm {
  height: var(--tc-control-h-sm);
  padding: 0 var(--tc-space-3);
  font: 600 12px/16px var(--tc-font-ui);
}

.tc-btn-danger {
  background: var(--tc-action-danger);
  color: var(--tc-text-on-accent);
}

.tc-btn:disabled { opacity: .45; cursor: not-allowed; }

/* Carregando: o spinner SUBSTITUI o rótulo com a largura travada, para o botão
   não encolher e a barra inteira não se reorganizar no meio da ação. */
.tc-btn[data-loading="1"] { color: transparent; position: relative; pointer-events: none; }
.tc-btn[data-loading="1"]::after {
  content: ""; position: absolute; width: 14px; height: 14px;
  border: 2px solid currentColor; border-top-color: transparent;
  border-radius: var(--tc-radius-pill);
  color: var(--tc-text-on-accent);
  animation: tc-spin 700ms linear infinite;
}

/* ── §6.8 Campo de formulário (input / select) ───────────────────────────────
   Altura `--tc-control-h`, fundo inset, borda default, raio de controle. O
   rótulo vive ACIMA em `--tc-type-label` — placeholder é exemplo, nunca rótulo.
   Nasceu com a barra de filtros da Onda 1; só input e select por enquanto,
   porque textarea e campo de data não têm consumidor tokenizado ainda. */
.tc-field { display: flex; flex-direction: column; gap: var(--tc-space-1); min-width: 0; }
.tc-field-label { color: var(--tc-text-muted); }
.tc-input, .tc-select {
  height: var(--tc-control-h);
  padding: 0 var(--tc-space-3);
  background: var(--tc-surface-inset);
  border: 1px solid var(--tc-border-default);
  border-radius: var(--tc-radius-control);
  color: var(--tc-text-primary);
  font: 400 14px/20px var(--tc-font-ui);
  transition: border-color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-input::placeholder { color: var(--tc-text-muted); }
.tc-input:hover, .tc-select:hover { border-color: var(--tc-border-strong); }
.tc-select { cursor: pointer; }

/* ── §6.9 Barra de filtros ───────────────────────────────────────────────────
   Busca + selects + filtros rápidos, todos a `--tc-control-h`, gap 12, numa
   linha. À direita, SEMPRE o resumo do resultado, e um "limpar filtros" ghost
   só quando houver filtro ativo.

   Em `compact` a barra inteira migra para um drawer (§4.2): a linha não cabe em
   390px e quebrá-la em quatro linhas comeria a tela antes do primeiro dado. O
   nó do DOM é o MESMO nos dois casos — quem o move é o JS da tela, porque CSS
   não move DOM e duplicar os controles duplicaria o estado deles. */
.tc-filters { display: flex; align-items: flex-end; gap: var(--tc-space-3); flex-wrap: wrap; }
.tc-filters-resumo { margin-left: auto; color: var(--tc-text-secondary); }
/* Botão que abre o drawer de filtros — só existe em ponteiro estreito. */
.tc-filters-btn { display: none; }
@media (max-width: 719px) {
  .tc-filters-btn { display: inline-flex; }
  /* Dentro do drawer os controles empilham e ocupam a largura toda. */
  .tc-drawer .tc-filters { flex-direction: column; align-items: stretch; }
  .tc-drawer .tc-filters-resumo { margin-left: 0; }
}

/* Drawer ancorado à ESQUERDA. Mesma mecânica do menu (§6.17) — abre por botão,
   fecha por Esc, clique no scrim e X —, do lado oposto de propósito: o menu
   mora à direita, e filtro chegando pelo mesmo lado faria os dois lerem como o
   mesmo painel. */
.tc-drawer-left {
  right: auto; left: 0;
  transform: translateX(-100%);
  border-left: 0;
  border-right: 1px solid var(--tc-border-subtle);
}
.tc-drawer-left[data-open="1"] { transform: translateX(0); }

/* ── §6.14 Checkbox ──────────────────────────────────────────────────────────
   16px, marcado = cor de ação, rótulo clicável, alvo mínimo de 24px (44 em
   ponteiro grosso, pelo `min-height` do rótulo). */
.tc-check {
  display: inline-flex; align-items: center; gap: var(--tc-space-2);
  min-height: var(--tc-control-h);
  color: var(--tc-text-primary);
  cursor: pointer;
  user-select: none;
}
.tc-check input[type="checkbox"] {
  width: 16px; height: 16px;
  flex: none;
  margin: 0;
  accent-color: var(--tc-action-primary);
  cursor: pointer;
}
/* Filtro rápido ativo (§6.9): fundo sutil de ação + borda de ação. */
.tc-check-chip {
  height: var(--tc-control-h);
  padding: 0 var(--tc-space-3);
  border: 1px solid var(--tc-border-default);
  border-radius: var(--tc-radius-control);
  background: var(--tc-surface-inset);
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out),
              border-color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-check-chip:hover { border-color: var(--tc-border-strong); }
/* O estado ligado é um ATRIBUTO no rótulo, marcado pelo JS da tela, e não um
   `:has(input:checked)`: o `:has` ainda não é seguro no tablet antigo do
   armazém, e um filtro que não mostra que está ligado é pior que um filtro a
   mais — é a mesma razão pela qual o anel de foco do card de módulo usa
   `:focus-within`. */
.tc-check-chip[data-on="1"] {
  background: var(--tc-action-primary-subtle);
  border-color: var(--tc-action-primary);
  color: var(--tc-action-primary);
}

/* ── §6.10 Estado vazio ──────────────────────────────────────────────────────
   Ícone (ou a foto da marca esmaecida — aqui a foto é bem-vinda), frase curta
   em `--tc-text-secondary` e a ação sugerida como botão secundário. Nunca uma
   área em branco sem explicação. */
.tc-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--tc-space-4);
  padding: var(--tc-space-8) var(--tc-space-6);
  max-width: 460px; margin: 0 auto;
}
.tc-empty-mark { width: 56px; height: 56px; opacity: .5; color: var(--tc-text-muted); }
/* Estado vazio que é ERRO (falha de carga, sem acesso) usa a marca em `danger`.
   É atributo e não estilo inline porque nasceu em três lugares — launcher,
   shell e tela de separação — e cor de estado não se repete solta. */
.tc-empty-mark[data-tone="danger"] { color: var(--tc-state-danger); }
.tc-empty-title { color: var(--tc-text-primary); }
.tc-empty-text  { color: var(--tc-text-secondary); text-wrap: pretty; }

/* ── §6.11 Estado de erro / dado desatualizado ───────────────────────────────
   Erro de carga fica NO LUGAR do conteúdo: ícone danger, mensagem de uma linha,
   "Tentar novamente" secundário. Nunca console silencioso.
   Dado desatualizado é o oposto de esconder: o número continua visível,
   esmaecido, com a idade do dado ao lado — tela mentindo é pior que tela
   apagada. */
.tc-error {
  display: flex; align-items: center; gap: var(--tc-space-2);
  color: var(--tc-state-danger);
}
.tc-error-actions { margin-left: auto; }

.tc-stale-value { opacity: .55; }
.tc-stale-badge {
  display: inline-flex; align-items: center; gap: var(--tc-space-1);
  height: var(--tc-control-h-sm);
  padding: 0 var(--tc-space-2);
  border-radius: var(--tc-radius-pill);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}

/* Faixa de dado desatualizado / erro persistente, no topo do conteúdo. O
   threshold é POR MÓDULO (§6.11) — o WMS sincroniza 5×/dia, então "3h atrás"
   ali é normal e na home de pedidos seria alarme. Quem decide o tom é a tela;
   o componente só sabe desenhar `warning` e `danger`.
   O conteúdo permanece visível por baixo: dado velho legível vale mais que tela
   quebrada, desde que a idade esteja escrita. */
.tc-banner {
  display: flex; align-items: center; gap: var(--tc-space-2);
  padding: var(--tc-space-2) var(--tc-space-3);
  border: 1px solid var(--tc-state-warning-border);
  border-radius: var(--tc-radius-control);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}
.tc-banner[data-tone="danger"] {
  border-color: var(--tc-state-danger-border);
  background: var(--tc-state-danger-bg);
  color: var(--tc-state-danger);
}
.tc-banner-acoes { margin-left: auto; }

/* Seção de conteúdo: título + metadado de contexto na mesma linha. O
   qualificador é irmão inline em peso 400 e nunca quebra em três linhas (§3). */
.tc-section { display: flex; flex-direction: column; gap: var(--tc-space-3); min-width: 0; }
/* `min-width: 0` nos FILHOS, e não é detalhe: item de flex nasce com
   `min-width: auto`, ou seja, ele se RECUSA a encolher abaixo do conteúdo. O
   efeito é que um bloco largo lá dentro — uma tabela em `.tc-table-wrap`, um
   mapa, um gráfico — empurra a seção, a seção empurra a página, e a rolagem
   horizontal que deveria acontecer DENTRO da caixa acontece na página inteira
   (o que o §12.6 proíbe em qualquer largura).
   Medido em 27/08/2026 com o mapa de locações: a 375px o documento ia a 606px.
   Uma linha aqui devolve a rolagem para dentro do contêiner que a declarou. */
.tc-section > * { min-width: 0; }
.tc-section-head {
  display: flex; align-items: baseline; gap: var(--tc-space-3); flex-wrap: wrap;
}
.tc-section-title { color: var(--tc-text-primary); }
.tc-section-meta  { color: var(--tc-text-secondary); }

/* Barra de ação de seleção. Gruda no rodapé da viewport porque no tablet a
   lista de PVs é mais alta que a tela: sem isso, escolher o último PV exigiria
   rolar de volta ao topo para achar o botão. Vive na faixa `sticky` do §2.8. */
.tc-actionbar {
  position: sticky; bottom: 0;
  z-index: var(--tc-z-sticky);
  display: flex; align-items: center; gap: var(--tc-space-3); flex-wrap: wrap;
  padding: var(--tc-space-3);
  background: var(--tc-surface-raised);
  border: 1px solid var(--tc-border-default);
  border-radius: var(--tc-radius-surface);
}
.tc-actionbar-acoes { margin-left: auto; display: flex; gap: var(--tc-space-2); }
@media (max-width: 719px) {
  .tc-actionbar-acoes { margin-left: 0; width: 100%; }
  .tc-actionbar-acoes .tc-btn-primary { flex: 1; }
}

/* ── §6.33 Navegação de módulo (barra de telas) ──────────────────────────────
   Nasceu na `1.01.03`, com a SEGUNDA tela do módulo de Separação — e não antes,
   de propósito: módulo de uma tela não ganha navegação de módulo, e um
   componente sem consumidor envelhece errado (ARQUITETURA.md §2.1).

   Ela é KERNEL: quem a monta é o `shell.js`, a partir do catálogo de
   `modulos.js`. Uma tela nova de um módulo existente aparece aqui com ZERO
   linha de código na tela — pelo mesmo caminho que faz o card do launcher
   nascer sozinho.

   Três níveis, e só três (§8.6): `launcher → módulo → tela`. Esta barra é o
   terceiro. Sub-recorte dentro de uma tela é filtro ou aba de conteúdo, nunca
   uma entrada aqui.

   O `margin-top` compensa o header fixo, e por isso a página perde o dela
   quando a barra existe (`[data-screens] .tc-page`) — as duas compensações
   somadas abririam uma faixa vazia de 56px sob o header. O atributo é posto no
   <html> pelo shell ANTES de o <main> ser interpretado, então não há salto. */
.tc-screens {
  margin-top: var(--tc-header-h);
  height: var(--tc-touch-min);
  display: flex; align-items: stretch; gap: var(--tc-space-1);
  padding: 0 var(--tc-space-6);
  /* FIXA logo abaixo do header (1.02.06). Ela é navegação E, desde que ganhou o
     slot, também o seletor de depósito — rolar até o fim de uma tabela de duas
     mil linhas e ter que voltar ao topo para trocar de galpão é o tipo de
     fricção que a pessoa paga dezenas de vezes por dia.
     `sticky` e não `fixed`: ela continua no fluxo, então a página não precisa
     de compensação extra e nada se desloca quando a barra não existe. */
  /* `--tc-z-sticky` e nao `--tc-z-app`: com a mesma camada do conteudo, quem
     vem depois no DOM ganha, e a barra ficava ATRAS dos cards da pagina. O
     token existe exatamente para o que gruda no topo. */
  position: sticky; top: var(--tc-header-h); z-index: var(--tc-z-sticky);
  background: var(--tc-surface-page);
  /* A barra em si não rola: quem rola é a fileira de ABAS. O seletor do slot
     precisa ficar sempre visível, e ele sairia de vista junto com a última aba
     se os dois dividissem a mesma rolagem. */
  overflow: visible;
}
@media (max-width: 719px) { .tc-screens { padding: 0 var(--tc-space-4); } }

/* A fileira de abas — é ela que rola quando não cabe. Estouro rola na
   horizontal; NUNCA duas linhas. A máscara nas pontas avisa que há mais: sem
   ela, a última tela some sem sinal.
   `transparent`/`black` aqui não são cor de tema — numa máscara eles são canal
   alfa (0 e 1), e é por isso que não violam a regra de zero hex do §12.6. */
.tc-screens-abas {
  display: flex; align-items: stretch; gap: var(--tc-space-1);
  flex: 1 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  -webkit-mask-image: linear-gradient(to right, transparent 0, black 12px,
                                      black calc(100% - 12px), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, black 12px,
                                      black calc(100% - 12px), transparent 100%);
}
.tc-screens-abas::-webkit-scrollbar { display: none; }

/* O SLOT: encostado à direita, sem encolher. Ele carrega um controle — hoje o
   seletor de depósito —, e controle que encolhe vira alvo pequeno demais. */
.tc-screens-slot {
  display: flex; align-items: center; gap: var(--tc-space-2);
  flex: 0 0 auto; padding-left: var(--tc-space-3);
}
.tc-screens-slot:empty { display: none; }

.tc-screens-item {
  display: inline-flex; align-items: center; gap: var(--tc-space-2);
  padding: 0 var(--tc-space-3);
  flex: none;
  white-space: nowrap;
  text-decoration: none;
  color: var(--tc-text-secondary);
  /* Indicador de 2px na tela ativa. A borda existe em TODAS as telas, só
     transparente nas inativas: sem isso, ativar uma tela mudaria a altura do
     item e a barra inteira pularia 2px. */
  border-bottom: 2px solid transparent;
  transition: color var(--tc-dur-instant) var(--tc-ease-out),
              background-color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-screens-item:hover { color: var(--tc-text-primary); background: var(--tc-surface-hover); }
.tc-screens-item[aria-current="page"] {
  color: var(--tc-text-primary);
  border-bottom-color: var(--tc-action-primary);
}

/* Badge numérico SÓ quando exige ação (§6.33). Volume não vira badge: "23
   rotas em andamento" é informação da tela, não pendência. */
.tc-screens-badge {
  display: inline-flex; align-items: center;
  height: 18px; padding: 0 var(--tc-space-2);
  border-radius: var(--tc-radius-pill);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}
.tc-screens-badge[data-tone="danger"] {
  background: var(--tc-state-danger-bg);
  color: var(--tc-state-danger);
}

/* ── §6.6 Card ───────────────────────────────────────────────────────────────
   O card genérico do inventário — distinto do `.tc-module-card`, que é o card do
   LAUNCHER e carrega o comportamento dele (hover de destino, contadores,
   estados de resumo). Nasceu com o Panorama, a primeira tela tokenizada que
   agrupa indicadores em caixas.

   Título em `--tc-type-label`, rodapé de contexto (período, fonte do dado) em
   `--tc-type-meta`: é o rodapé que faz um número ser auditável, e é por isso que
   ele é parte do componente e não um enfeite opcional. */
.tc-card {
  display: flex; flex-direction: column; gap: var(--tc-space-3);
  padding: var(--tc-space-4);
  background: var(--tc-surface-raised);
  border: 1px solid var(--tc-border-subtle);
  border-radius: var(--tc-radius-surface);
}
/* `flex-wrap: nowrap` e um título que ENCOLHE, e não um cabeçalho que quebra.
   Com wrap, um título longo empurrava o tooltip para uma segunda linha — e o
   card ficava com o "?" solto embaixo do nome, desalinhado dos vizinhos na
   mesma grade. Título com `min-width: 0` corta com reticências; o "?" e o
   metadado são `flex: none` porque são pequenos e não podem sumir. */
.tc-card-head {
  display: flex; align-items: center; gap: var(--tc-space-2); flex-wrap: nowrap;
}
.tc-card-title {
  color: var(--tc-text-primary);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tc-card-meta  { color: var(--tc-text-muted); margin-left: auto; flex: none; }
.tc-card-head > .tc-tip, .tc-card-head > span:last-child:not(.tc-card-meta) { flex: none; }
.tc-card-foot  { color: var(--tc-text-muted); text-wrap: pretty; }

/* KPI dentro de um card (§6.6): valor no nível de KPI, colorido PELO ESTADO que
   representa — nunca por decoração. Zero é neutro, e essa é a regra que faz o
   olho varrer só o que importa. */
.tc-kpi { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tc-kpi-value { color: var(--tc-text-primary); }
.tc-kpi-label { color: var(--tc-text-muted); }
.tc-kpi-meta  { color: var(--tc-text-secondary); }
.tc-kpi-value[data-tone="success"]  { color: var(--tc-state-success); }
.tc-kpi-value[data-tone="progress"] { color: var(--tc-state-progress); }
.tc-kpi-value[data-tone="warning"]  { color: var(--tc-state-warning); }
.tc-kpi-value[data-tone="danger"]   { color: var(--tc-state-danger); }
.tc-kpi-value[data-tone="neutral"],
.tc-kpi-value[data-tone="zero"]     { color: var(--tc-state-neutral); }

/* ── §6.20 Abas de conteúdo ──────────────────────────────────────────────────
   Sub-recorte DENTRO de uma tela — nunca um quarto nível de navegação (§8.6).
   A barra de telas (§6.33) troca de TELA; isto troca o RECORTE da mesma tela, e
   a diferença visual precisa acompanhar: aba é pill, tela é sublinhado.

   Nasceu na `1.01.03` com as visões **PV** e **OP** do Panorama: são dois
   objetos de trabalho na mesma pergunta ("como está o trabalho de atender?"),
   não duas telas. */
.tc-tabs {
  display: inline-flex; gap: var(--tc-space-1);
  padding: 3px;
  background: var(--tc-surface-inset);
  border-radius: var(--tc-radius-pill);
}
.tc-tab {
  display: inline-flex; align-items: center; gap: var(--tc-space-2);
  height: var(--tc-control-h);
  padding: 0 var(--tc-space-4);
  border: 0; border-radius: var(--tc-radius-pill);
  background: transparent; color: var(--tc-text-secondary);
  font: inherit; cursor: pointer; white-space: nowrap;
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out),
              color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-tab:hover { color: var(--tc-text-primary); }
.tc-tab[aria-selected="true"] {
  background: var(--tc-surface-raised);
  color: var(--tc-text-primary);
}
/* Aba de recorte que ainda não existe: ela APARECE e diz que está vazia, em vez
   de sumir. Sumir esconderia que o produto tem um segundo eixo previsto. */
.tc-tab[disabled] { color: var(--tc-text-disabled); cursor: default; }

/* ── §6.19 Tooltip ───────────────────────────────────────────────────────────
   Obrigatório em card de UM número só: um número sem a pergunta que ele responde
   é um número que cada pessoa interpreta de um jeito. Ele carrega também a BASE
   do percentual — é o que torna a taxa verificável em vez de crível.

   CSS puro, sem JS: `title` nativo demora ~1s e não quebra linha. Em ponteiro
   grosso ele não aparece (não há hover), e por isso o texto do tooltip NUNCA
   carrega informação que só exista nele — ele explica, não informa. */
.tc-tip { position: relative; display: inline-flex; }
.tc-tip-alvo {
  position: relative;
  width: 16px; height: 16px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--tc-radius-pill);
  border: 1px solid var(--tc-border-default);
  color: var(--tc-text-muted);
  font: 600 10px/1 var(--tc-font-ui);
  cursor: help;
}
/* O alvo de toque cresce sem mudar o desenho (§4.1), como no `.tc-icon-btn`: o
   glifo continua com 16px, a área clicável vai a 44. Um "?" de 16px é
   inalcançável no dedo, e o tooltip é a única forma de ver a base do número. */
.tc-tip-alvo::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: var(--tc-touch-min); height: var(--tc-touch-min);
  transform: translate(-50%, -50%);
}
.tc-tip-texto {
  /* Ancorado à DIREITA por padrão, e não à esquerda. O alvo do tooltip vive no
     fim do cabeçalho do card (`margin-left: auto`), então abrir para a direita
     joga o balão para fora da tela — e a 375px, onde todo card ocupa a largura
     inteira, isso vale para TODOS eles, não só para os da coluna da direita.
     O `max-width` com `100vw` é o teto que nenhuma âncora consegue furar. */
  position: absolute; top: calc(100% + 6px); right: 0; left: auto;
  z-index: var(--tc-z-tooltip);
  width: max-content; max-width: min(280px, calc(100vw - var(--tc-space-8)));
  padding: var(--tc-space-3);
  background: var(--tc-surface-overlay);
  border: 1px solid var(--tc-border-default);
  border-radius: var(--tc-radius-control);
  color: var(--tc-text-secondary);
  text-wrap: pretty;
  opacity: 0; visibility: hidden;
  transition: opacity var(--tc-dur-fast) var(--tc-ease-out),
              visibility var(--tc-dur-fast);
}
.tc-tip:hover .tc-tip-texto,
.tc-tip:focus-within .tc-tip-texto { opacity: 1; visibility: visible; }
/* Alvo colado na borda ESQUERDA (o caso raro) inverte a âncora. */
.tc-tip[data-lado="inicio"] .tc-tip-texto { left: 0; right: auto; }

/* ── §6.29 Modal de expansão ─────────────────────────────────────────────────
   Foco numa lista que veio de um indicador. Ele existe porque algumas listas são
   RESPOSTA de um número e não um passo seguinte da leitura: a lista do saldo
   cruzado é para levar ao galpão, não para continuar lendo a página.

   Fecha com Esc e com o ✕ — dois caminhos para a MESMA saída são aceitáveis
   aqui, ao contrário do painel lateral, porque um deles é teclado. */
.tc-modal-scrim {
  position: fixed; inset: 0; z-index: var(--tc-z-modal);
  background: var(--tc-scrim-overlay);
  opacity: 0; visibility: hidden;
  transition: opacity var(--tc-dur-base) var(--tc-ease-out), visibility var(--tc-dur-base);
}
.tc-modal-scrim[data-open="1"] { opacity: 1; visibility: visible; }
.tc-modal {
  position: fixed; z-index: calc(var(--tc-z-modal) + 1);
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: min(1040px, calc(100vw - var(--tc-space-6) * 2));
  max-height: calc(100vh - var(--tc-space-8));
  display: none; flex-direction: column;
  background: var(--tc-surface-overlay);
  border: 1px solid var(--tc-border-default);
  border-radius: var(--tc-radius-surface);
  overflow: hidden;
}
.tc-modal[data-open="1"] { display: flex; }
.tc-modal-head {
  display: flex; align-items: center; gap: var(--tc-space-3);
  padding: var(--tc-space-4);
  border-bottom: 1px solid var(--tc-border-subtle);
}
.tc-modal-head .tc-icon-btn { margin-left: auto; }
.tc-modal-body { overflow: auto; padding: var(--tc-space-4); }
@media (max-width: 719px) {
  /* No celular ele ocupa a tela inteira: um modal centralizado de 90% da
     viewport com 10% de scrim é só uma tela pior. */
  .tc-modal {
    top: 0; left: 0; transform: none;
    width: 100vw; max-height: 100vh; height: 100vh;
    border: 0; border-radius: 0;
  }
}

/* ── §6.27 Gauge — MEIA ROSCA ────────────────────────────────────────────────
   Um semicírculo, não um anel inteiro. A metade de baixo de uma rosca completa
   é espaço que não codifica nada, e o vazio no meio é onde o número mora —
   quem lê o painel lê o número e usa o arco como contexto, não o contrário.

   O desenho é SVG no DOM (não canvas): canvas não lê custom property, e a ponte
   do §9 existe justamente por isso. Com um semicírculo e poucos segmentos, o DOM
   resolve — e herda tema e ambiente de graça.

   A cor vem de quem chama, por `data-tom`: aqui é sempre TOKEN DE ESTADO, porque
   o que a meia rosca mede nesta tela é julgamento (acerto, conformidade), e não
   série. */
.tc-rosca { display: block; overflow: visible; }
.tc-rosca-trilha { stroke: var(--tc-surface-inset); }
.tc-rosca-seg { transition: stroke-dasharray var(--tc-dur-base) var(--tc-ease-out); }
/* A AGULHA (variante acelerômetro). Ela não acrescenta informação ao arco — diz
   a mesma coisa —, e é justamente por isso que ela ajuda: num painel lido de
   passagem, a posição de um ponteiro é reconhecida antes de qualquer número.
   O arco continua sendo quem carrega a precisão; a agulha carrega o relance. */
.tc-rosca-agulha { stroke: var(--tc-text-secondary); stroke-linecap: round; }
.tc-rosca-eixo   { fill: var(--tc-surface-page); stroke: var(--tc-text-secondary); }
.tc-rosca-seg[data-tom="success"]  { stroke: var(--tc-state-success); }
.tc-rosca-seg[data-tom="progress"] { stroke: var(--tc-state-progress); }
.tc-rosca-seg[data-tom="warning"]  { stroke: var(--tc-state-warning); }
.tc-rosca-seg[data-tom="danger"]   { stroke: var(--tc-state-danger); }
.tc-rosca-seg[data-tom="neutral"]  { stroke: var(--tc-state-neutral); }
.tc-rosca-seg[data-tom="acao"]     { stroke: var(--tc-action-primary); }
/* O rótulo vive DENTRO do arco, e é o elemento mais forte do componente. */
.tc-rosca-cx { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.tc-rosca-valor { color: var(--tc-text-primary); }
.tc-rosca-rotulo { color: var(--tc-text-muted); text-align: center; }

/* ── §6.12 Link ──────────────────────────────────────────────────────────────
   Sem sublinhado no repouso, sublinhado no hover. Visitado não muda: isto é
   ferramenta, não site. */
.tc-link {
  color: var(--tc-action-primary);
  text-decoration: none;
  cursor: pointer;
}
.tc-link:hover { color: var(--tc-action-primary-hover); text-decoration: underline; }

/* ── §6.16 Card de módulo (launcher) ─────────────────────────────────────────
   O ponto de entrada da plataforma. É o card que justifica um launcher existir
   em vez de um menu: nome, ícone e 1–3 CONTADORES VIVOS.

   GRADE. O §6.16 fixa a contagem de colunas por breakpoint (2 tablet, 3 desk,
   4 wide) e o mockup pede que dois cards não virem metades esticadas da tela.
   As duas coisas convivem: contagem fixa por breakpoint MAIS teto de 366px por
   card, com `justify-content: start` para a fileira preencher da esquerda.
   Em `compact` a grade é de uma coluna — o §6.16 não nomeia esse caso, e em
   390px não há outro.

   MÓDULO SEM PERMISSÃO NÃO APARECE: nada de card desabilitado com cadeado. A
   grade se recompõe com o que sobra, e rota acessada direto sem permissão cai
   no estado de erro, nunca em redirecionamento silencioso. */
.tc-modules {
  display: grid;
  grid-template-columns: repeat(1, minmax(0, 366px));
  justify-content: start;
  gap: var(--tc-space-4);
}
@media (min-width: 720px)  { .tc-modules { grid-template-columns: repeat(2, minmax(0, 366px)); } }
@media (min-width: 1120px) { .tc-modules { grid-template-columns: repeat(3, minmax(0, 366px)); } }
@media (min-width: 1920px) { .tc-modules { grid-template-columns: repeat(4, minmax(0, 366px)); } }

/* O card NÃO é um <a>. Ele contém um: o nome do módulo é o link, e um
   `::after` esticado faz a caixa inteira clicar.
   O motivo é estrutural, não estético — o estado "indicador não carregou" tem
   um botão "Tentar novamente" dentro do card, e conteúdo interativo dentro de
   <a> é HTML inválido: o parser tira o botão de lá e desmonta o card. Com o
   link esticado, o card inteiro continua clicável, o botão fica por cima (ele
   vem depois no DOM, e elemento posicionado posterior pinta em cima — sem
   precisar de z-index), e o leitor de tela ouve um link só com o nome do
   módulo. */
.tc-module-card {
  position: relative;
  display: flex; flex-direction: column; gap: var(--tc-space-3);
  padding: var(--tc-space-4);
  background: var(--tc-surface-raised);
  border: 1px solid var(--tc-border-subtle);
  border-radius: var(--tc-radius-surface);
  cursor: pointer;
  text-align: left;
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out),
              border-color var(--tc-dur-instant) var(--tc-ease-out);
}
/* Hover muda fundo e borda e MAIS NADA: sem translação, sem sombra. É
   ferramenta — card que salta a cada passagem de mouse cansa em uso diário. */
.tc-module-card:hover {
  background: var(--tc-surface-hover);
  border-color: var(--tc-border-strong);
}
.tc-module-card:active { border-color: var(--tc-action-primary); }
/* O anel de foco cerca o CARD, não a linha de texto do link: `:focus-within`
   e não `:has(:focus-visible)` porque o `:has` ainda não é seguro no tablet
   antigo do armazém, e um anel que não aparece é pior que um anel a mais. */
.tc-module-card:focus-within {
  outline: 2px solid var(--tc-border-focus);
  outline-offset: 1px;
}

.tc-module-head { display: flex; align-items: flex-start; gap: var(--tc-space-3); }
.tc-module-icon { margin-top: 1px; color: var(--tc-text-secondary); }
.tc-module-name { color: var(--tc-text-primary); text-decoration: none; display: block; }
.tc-module-name::after { content: ""; position: absolute; inset: 0;
                         border-radius: inherit; }
.tc-module-desc { color: var(--tc-text-muted); text-wrap: pretty; }
.tc-module-rule { height: 1px; background: var(--tc-border-subtle); }

.tc-module-counters { display: flex; gap: var(--tc-space-6); min-height: 38px; }
.tc-counter { display: flex; flex-direction: column; gap: 1px; }
/* O §6.16 pede o KPI "reduzido a 20px" — é o único lugar do sistema onde o
   nível de KPI aparece menor, e por isso está escrito aqui e não em type.css:
   é característica do card, não um oitavo tamanho da escala. */
.tc-counter-value { font-size: 20px; line-height: 24px; color: var(--tc-text-primary); }
.tc-counter-label { color: var(--tc-text-muted); }
/* Contador que exige ação usa a cor do estado; os demais ficam em texto
   primário. Zero é neutro — o olho precisa varrer só o que importa. */
.tc-counter-value[data-tone="danger"]  { color: var(--tc-state-danger); }
.tc-counter-value[data-tone="warning"] { color: var(--tc-state-warning); }
.tc-counter-value[data-tone="zero"]    { color: var(--tc-state-neutral); }

/* Carregando: o NOME já está legível e só o número está em skeleton. O
   contrário — card inteiro cinza — esconde a única informação que já se tem. */
.tc-counter-skeleton-value { width: 56px; height: 20px; }
.tc-counter-skeleton-label { width: 48px; height: 10px; border-radius: 3px; }

/* Indisponível: badge âmbar. O módulo continua clicável — indicador que não
   carregou não é módulo fora do ar. */
.tc-module-badge {
  display: inline-flex; align-items: center; gap: var(--tc-space-1);
  height: var(--tc-control-h-sm);
  padding: 0 var(--tc-space-2);
  border-radius: var(--tc-radius-pill);
  background: var(--tc-state-warning-bg);
  color: var(--tc-state-warning);
}

/* ── §6.17 Menu lateral (☰) ──────────────────────────────────────────────────
   Drawer à direita, 320px, sobre o scrim. Abre pelo ☰, fecha por Esc, clique no
   scrim e X. Em `compact` ocupa 100% da largura.
   Ícones monocromáticos herdando currentColor — emoji não entra. */
.tc-scrim {
  position: fixed; inset: 0;
  z-index: var(--tc-z-drawer);
  background: var(--tc-scrim-overlay);
  opacity: 0; visibility: hidden;
  transition: opacity var(--tc-dur-base) var(--tc-ease-out),
              visibility var(--tc-dur-base);
}
.tc-scrim[data-open="1"] { opacity: 1; visibility: visible; }

.tc-drawer {
  position: fixed; top: 0; right: 0; bottom: 0;
  width: 320px; max-width: 100%;
  /* +1 sobre o scrim: mesmo overlay, mesma faixa de camada (§2.8). */
  z-index: calc(var(--tc-z-drawer) + 1);
  background: var(--tc-surface-overlay);
  border-left: 1px solid var(--tc-border-subtle);
  display: flex; flex-direction: column;
  transform: translateX(100%);
  visibility: hidden;
  transition: transform var(--tc-dur-base) var(--tc-ease-out),
              visibility var(--tc-dur-base);
}
.tc-drawer[data-open="1"] { transform: translateX(0); visibility: visible; }
@media (max-width: 719px) { .tc-drawer { width: 100%; border-left: 0; } }

/* Topo: avatar 40px à esquerda da saudação; cargo na linha de baixo, quando
   existir — sem "—", sem "Cargo não informado" (§6.32). */
.tc-drawer-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--tc-space-3);
  padding: var(--tc-space-5) var(--tc-space-4);
  border-bottom: 1px solid var(--tc-border-subtle);
}
/* `overflow-x: hidden` junto do `auto` vertical: sem ele, um rótulo longo de
   módulo reintroduz a barra horizontal que o `border-box` acabou de matar. */
.tc-drawer-body { flex: 1; overflow-y: auto; overflow-x: hidden; padding: var(--tc-space-2) 0; }
/* Cargo/perfil sob a saudação — exibido só quando existir (§6.32). */
.tc-drawer-role { color: var(--tc-text-muted); }
.tc-drawer-group-title {
  padding: var(--tc-space-3) var(--tc-space-4) var(--tc-space-1);
  color: var(--tc-text-muted);
}
.tc-drawer-rule {
  height: 1px; background: var(--tc-border-subtle);
  margin: var(--tc-space-2) var(--tc-space-4);
}

.tc-drawer-item {
  width: 100%;
  min-height: var(--tc-touch-min);
  display: flex; align-items: center; gap: var(--tc-space-2);
  padding: 0 var(--tc-space-4);
  background: transparent;
  border: 0; border-left: 2px solid transparent;
  color: var(--tc-text-primary);
  text-align: left; text-decoration: none;
  cursor: pointer;
  transition: background-color var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-drawer-item:hover { background: var(--tc-surface-hover); }
.tc-drawer-item .tc-icon { color: var(--tc-text-secondary); }
/* `min-width: 0` é o que permite o truncamento: sem ele, um item de flex não
   encolhe abaixo do próprio conteúdo e o rótulo longo empurra a largura. */
.tc-drawer-item-label { flex: 1; min-width: 0;
                        overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tc-drawer-item-meta  { color: var(--tc-text-muted); }
/* Rota atual: fundo sutil de ação, texto de ação e barra de 2px à esquerda. */
.tc-drawer-item[aria-current="page"] {
  background: var(--tc-action-primary-subtle);
  border-left-color: var(--tc-action-primary);
  color: var(--tc-action-primary);
}
.tc-drawer-item[aria-current="page"] .tc-icon { color: var(--tc-action-primary); }
/* Em execução (item de ação como "Sincronizar"): rótulo muda, spinner à
   direita, item inerte até terminar. */
.tc-drawer-item[data-busy="1"] { pointer-events: none; color: var(--tc-text-muted); }

.tc-drawer-foot {
  border-top: 1px solid var(--tc-border-subtle);
  padding: var(--tc-space-3) var(--tc-space-4);
  display: flex; flex-direction: column; gap: var(--tc-space-2);
}
.tc-drawer-foot-line {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--tc-space-2);
  color: var(--tc-text-muted);
}

/* ── §6.32 Avatar ────────────────────────────────────────────────────────────
   Iniciais NÃO são fallback de exceção: são o caso mais comum — parte dos
   usuários entra por login local, sem e-mail e sem foto. Os dois estados têm o
   mesmo peso visual.
   Fundo neutro SEMPRE: nada de cor por hash do nome, que colidiria com a
   semântica de estado do §5 e transformaria pessoas em status. */
.tc-avatar {
  flex: none;
  display: grid; place-items: center;
  overflow: hidden;
  border-radius: var(--tc-radius-pill);
  border: 1px solid var(--tc-border-subtle);
  background: var(--tc-surface-hover);
  color: var(--tc-text-secondary);
  font-family: var(--tc-font-ui); font-weight: 600;
  text-transform: uppercase;
  user-select: none;
}
.tc-avatar img { width: 100%; height: 100%; object-fit: cover; }
.tc-avatar-24 { width: 24px; height: 24px; font-size: 10px; line-height: 1; }
.tc-avatar-32 { width: 32px; height: 32px; font-size: 12px; line-height: 1; }
.tc-avatar-40 { width: 40px; height: 40px; font-size: 14px; line-height: 1; }
/* Carregando: círculo em skeleton. Nunca mostra iniciais antes de saber se há
   foto — trocar iniciais por foto meio segundo depois é pior que esperar. */
.tc-avatar[data-loading="1"] {
  color: transparent; border-color: transparent;
  animation: tc-pulse 1.2s var(--tc-ease-inout) infinite;
}

/* Bloco identidade (avatar + nome + cargo). Avatar nunca é o único
   identificador: sempre com nome ao lado, exceto no header em `compact`, onde
   só o círculo cabe — e aí ele carrega `aria-label`. */
.tc-identity { display: flex; align-items: center; gap: var(--tc-space-3); min-width: 0; }
.tc-identity-lines { display: flex; flex-direction: column; min-width: 0; }
.tc-identity-name,
.tc-identity-role {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tc-identity-name { color: var(--tc-text-on-header); }
.tc-identity-role { color: var(--tc-text-secondary); }
@media (max-width: 719px) { .tc-identity .tc-identity-lines { display: none; } }

/* ── §11.1 Assinatura de propriedade ─────────────────────────────────────────
   "Um sistema" + o logo da matriz a 15px, centralizada na base da viewport no
   login e no launcher (na faixa inferior do menu ☰ nas demais telas).
   Discreta, nunca competindo com o produto — e NUNCA no header do app: duas
   marcas na mesma barra competem.

   Duas imagens e não uma: o logo da matriz tem versão para fundo escuro e
   versão para fundo claro, e CSS não troca `src`. Trocar a visibilidade das
   duas é o caminho que já existe no produto. */
.tc-signature {
  display: flex; align-items: center; justify-content: center;
  gap: var(--tc-space-2);
  color: var(--tc-text-muted);
}
.tc-signature img { height: 15px; width: auto; opacity: .75; }
.tc-signature .tc-sig-on-light { display: none; }
[data-theme="light"] .tc-signature .tc-sig-on-dark  { display: none; }
[data-theme="light"] .tc-signature .tc-sig-on-light { display: block; }

/* ── Papel ───────────────────────────────────────────────────────────────────
   O par do bloco @media print do tokens.css: lá os tokens ficam neutros, aqui
   somem o cromo e a camada de foto. NÃO é o print.css do §6.22 (cabeçalho de
   folha, repetição de thead, caixas de anotação) — esse nasce com o módulo de
   rotas de separação, que é quem existe para ser impresso.

   A foto precisa sair explicitamente: os pseudo-elementos são `position: fixed`,
   e elemento fixo imprime na primeira página, cobrindo o conteúdo. */
@media print {
  .tc-photo-bg::before,
  .tc-photo-bg::after { display: none; }
  .tc-appbar, .tc-drawer, .tc-scrim, .tc-icon-btn { display: none; }
  .tc-module-card { border: 1px solid var(--tc-border-default); break-inside: avoid; }
  /* Estado sem depender de cor: a maioria das impressoras do armazém é
     monocromática, então o contador em `danger` ganha marca tipográfica. */
  .tc-counter-value[data-tone="danger"]::before  { content: "* "; }
  .tc-counter-value[data-tone="warning"]::before { content: "! "; }
}

/* ══ CHAVE DE FRENTE (§6.36, `1.02.05`) ═══════════════════════════════════
   Dois botões no header — ícone + rótulo —, um por frente do módulo. Hoje é o
   Estudo de Estoque: Pós-Vendas e Indústria.

   ── POR QUE ELA NÃO É UMA SEGUNDA BARRA DE ABAS ─────────────────────────
   Porque barra dentro de barra faz a pessoa perder a noção de qual das duas
   está lendo, e a hierarquia entre elas só existe para quem já sabe que
   existem duas. A chave muda de CATEGORIA: escolher a frente deixa de parecer
   "mais uma aba" e passa a parecer o que é — trocar de trabalho.

   ── ONDE ELA FICA, e por quê ────────────────────────────────────────────
   Ao lado do nome do módulo, e não à direita junto da identidade: é uma
   propriedade DO QUE SE ESTÁ VENDO, não do usuário. À direita ela leria como
   preferência pessoal, que é o oposto do que é.

   ── E ELA NÃO CARREGA A DIFERENÇA SOZINHA ───────────────────────────────
   O estado ativo aqui resolve "onde eu clico". Quem resolve "em qual frente eu
   estou" para quem já rola a página são a FOTO DE FUNDO, a LINHA DE ACENTO sob
   as abas e o nome (`ESTUDO DE ESTOQUE · INDÚSTRIA`). Quatro sinais, e nenhum
   deles sozinho.

   Nenhum é cor de FUNDO, e isso é a regra: fundo já significa AMBIENTE nesta
   Torre, e a primeira versão disto tingia o header — confundia produção com
   indústria, que é o pior erro possível dos dois. */
/* As cores saem do HEADER, e não das superfícies do tema. O header é escuro nos
   dois temas, então `--tc-surface-inset` (quase branco no tema claro) faria um
   bloco branco no meio da faixa escura, e `--tc-text-muted` (escuro no claro)
   deixaria a frente inativa ilegível. `color-mix` sobre `--tc-text-on-header`
   resolve os dois: ele já é o token de "o que se escreve nesta faixa". */
.tc-fronts {
  display: flex; align-items: center; gap: 2px;
  margin-left: var(--tc-space-4);
  padding: 3px;
  border-radius: var(--tc-radius-control);
  background: color-mix(in oklab, var(--tc-text-on-header) 7%, transparent);
  border: 1px solid color-mix(in oklab, var(--tc-text-on-header) 12%, transparent);
  flex: 0 0 auto;
}
.tc-fronts-item {
  display: inline-flex; align-items: center; gap: var(--tc-space-2);
  /* 32px de altura dentro do header de 56px. O alvo de 44px do §4.1 é atendido
     pela ALTURA DO HEADER, que é toda clicável na vertical — a pill é a marca
     visual, não a área de toque. */
  height: 32px; padding: 0 var(--tc-space-3);
  border-radius: calc(var(--tc-radius-control) - 2px);
  /* 46%: o inativo tem que ser LEGÍVEL e claramente secundário. Acima disso ele
     compete com o ativo, e a chave passa a mostrar dois estados iguais. */
  color: color-mix(in oklab, var(--tc-text-on-header) 46%, transparent);
  text-decoration: none;
  transition: color var(--tc-dur-instant) var(--tc-ease-out),
              background var(--tc-dur-instant) var(--tc-ease-out);
}
.tc-fronts-item:hover { color: color-mix(in oklab, var(--tc-text-on-header) 85%, transparent); }
.tc-fronts-item[aria-current="true"] {
  background: color-mix(in oklab, var(--tc-front-accent-hdr) 16%, transparent);
  /* O acento é o DA FRENTE, e não o de ação: aqui a cor não diz "clicável", diz
     QUAL frente. Usar o azul de ação faria as duas ficarem azuis quando ativas,
     e a chave perderia metade do que ela existe para dizer. O fallback mantém a
     regra viva em módulo que não declare frentes. */
  color: var(--tc-front-accent-hdr, var(--tc-action-primary));
}

/* ── O RÓTULO some antes do ícone ─────────────────────────────────────────
   Abaixo de 900px o header disputa espaço com o nome do módulo e a identidade.
   O ícone sozinho continua dizendo qual frente está ativa — é para isso que as
   duas silhuetas são opostas (§8) —, e o `title` guarda o nome para quem
   precisar confirmar. Some o rótulo, nunca o ícone. */
@media (max-width: 899px) {
  .tc-fronts-label { display: none; }
  .tc-fronts-item { padding: 0 var(--tc-space-2); }
  .tc-fronts { margin-left: var(--tc-space-2); }
}

/* A chave gruda no nome do módulo e empurra o resto para a direita: com
   `space-between` e três filhos, sem isto ela cairia no CENTRO do header, longe
   do nome que ela qualifica. */
.tc-fronts { margin-right: auto; }

/* A LINHA DE ACENTO sob a barra de telas — o sinal permanente da frente.
   Ela é uma linha, e não um fundo, e essa distinção é a decisão inteira:
   cor de FUNDO nesta Torre já significa AMBIENTE (dev/homologação/produção), e
   tingir o header para dizer a frente destruiria um sinal que hoje é confiável.
   Acento e fundo não se confundem nem à distância. */
:root[data-frente] .tc-screens {
  /* A linha CONTÍNUA e fraca é o sinal permanente da frente: ela atravessa a
     largura inteira e está sempre lá, independente de qual aba está aberta. */
  border-bottom: 1px solid color-mix(in oklab, var(--tc-front-accent) 42%, transparent);
}
/* A aba ATIVA marca na MESMA cor, mais forte. Mesma cor de propósito: as duas
   linhas leem como uma coisa só — uma régua da frente, com um trecho grosso
   onde você está — em vez de dois sinais competindo. E a cor é a da frente, e
   não a de ação, pelo mesmo motivo da chave: aqui ela não diz "clicável". */
:root[data-frente] .tc-screens-item[aria-current="page"] {
  border-bottom-color: var(--tc-front-accent);
}

/* A FRENTE no nome do header. Ela some no telefone porque truncar
   "ESTUDO DE ESTOQUE · PÓS-VENDAS" cortaria justamente a metade nova; lá quem
   diz a frente são o ícone ativo da chave e a foto de fundo. */
.tc-screen-front { color: var(--tc-front-accent-hdr, var(--tc-action-primary)); }
@media (max-width: 719px) { .tc-screen-front { display: none; } }

/* ── O seletor dentro do SLOT da barra de telas (1.02.06) ─────────────────
   Compacto porque divide a faixa de 44px com as abas, e a barra não pode
   crescer: ela é fixa e cada pixel dela é pixel que a tabela perde. */
.tc-screens-slot-rotulo { color: var(--tc-text-muted); white-space: nowrap; }
.tc-select-compacto {
  height: 30px; padding: 0 var(--tc-space-6) 0 var(--tc-space-2);
  max-width: 260px;
}
/* No telefone o rótulo sai e fica só o campo: "Depósito" já está escrito dentro
   de cada opção, e a palavra repetida custa metade da largura útil da barra. */
@media (max-width: 719px) {
  .tc-screens-slot-rotulo { display: none; }
  .tc-select-compacto { max-width: 148px; }
}
