/* ObraCusto V1.00.000
   Base visual inicial. Sem dependências externas. */

:root{
  /* ============ SETA DE EXPANDIR/RECOLHER — PADRÃO ÚNICO ============
     Todo elemento HIERÁRQUICO expansível do OrçaMeta (etapa da EAP, grupo da
     navegação, e qualquer estrutura futura em árvore) usa estes dois glifos, e
     só estes: fechado > · aberto ∨.

     ⚠️ Definidos aqui, e não em cada componente, porque o defeito que isto
     corrige foi exatamente a divergência: `+`/`−` na árvore de EAP e `›`
     rotacionado na sidebar — a mesma função com dois desenhos, em duas telas.
     Componente novo referencia estas variáveis; nunca escolhe um caractere
     próprio.

     Painel de filtros e popover NÃO entram: não são hierarquia, são
     mostrar/ocultar de um bloco só. Misturá-los aqui faria a seta significar
     duas coisas diferentes. */
  /* Chevron VAZADO, escolha do usuario (V1.00.431): traco fino, sem o peso
     do triangulo cheio que estava aqui na V1.00.430.

     Nenhum dos dois tem variante emoji — renderizam sempre como texto,
     que era o unico risco tipografico do par anterior (o 25B6). */
  --oc-seta-fechado:"\003E";  /* > GREATER-THAN SIGN */
  --oc-seta-aberto:"\2228";   /* ∨ LOGICAL OR */

  --bg:#f5f6f8;
  --surface:#ffffff;
  --border:#e2e5ea;
  --text:#1f2430;
  --text-muted:#667085;
  --primary:#1d5fd6;
  --primary-dark:#154aa8;
  --sidebar-bg:#151a2b;
  --sidebar-text:#c9cfe0;
  --sidebar-text-active:#ffffff;
  --radius:10px;
  --header-btn-height:36px;  /* Altura global padrão para botões do cabeçalho */

  /* ============ ALTURA DOS CONTROLES DA TOOLBAR DA TABELA ============
     Um token para os TRÊS controles da barra da listagem — o seletor "Exibir N
     por página", o botão "Colunas" e o botão "Compactar".

     ⚠️ Ele existe porque os três chegavam à altura por caminhos diferentes: o
     seletor e o "Compactar" declaravam `height:32px` cada um por conta própria,
     e o "Colunas" não declarava nada — herdava o `min-height:36px` de `.oc-btn`
     e saía 4px mais alto que os vizinhos, na mesma linha. Três controles lado a
     lado, três origens de altura, e nenhuma delas errada isoladamente.

     Com um token, a altura vira uma decisão só: mudá-la move os três juntos, e
     um controle novo na barra pede a variável em vez de escolher um número. */
  --oc-toolbar-controle-h:32px;

  /* V1.00.154 — Espaçamento vertical do cabeçalho (fonte de verdade única).
     A altura do header é determinada SOMENTE pelo conteúdo (título +
     subtítulo + botões) mais estes dois valores pequenos e uniformes.
     Nunca usar height/min-height fixos nem margin-bottom exagerado. */
  --header-pad-top:20px;     /* respiro acima do título */
  --header-pad-bottom:16px;  /* respiro abaixo do título */
  --header-gap-panel:8px;    /* distância do header até o primeiro painel */

  /* V1.00.305 — cores de ERRO no tema, e não escritas em cada tela.
     Estavam repetidas em hex dentro do style= de várias páginas; qualquer
     ajuste exigia caçar todas, e uma esquecida destoava sem ninguem notar. */
  --oc-erro-texto:#8c2f26;
  --oc-erro-fundo:#fdf1f0;
  --oc-erro-borda:#e6b5b0;
}

*{box-sizing:border-box;}

/* V1.00.048 — html/body nunca rolam (altura travada em 100%); a página
   inteira nunca é o container de rolagem no desktop/tablet. A ÚNICA
   barra vertical do app fica em .oc-content-panel (ver .oc-layout/
   .oc-sidebar/.oc-main/.oc-topbar/.oc-content-panel logo abaixo) —
   .oc-sidebar e .oc-topbar ficam fixos por construção (flex, sem
   position:fixed/sticky, sem números mágicos de altura). */
html,body{
  margin:0;
  padding:0;
  height:100%;
  overflow:hidden;
  background:var(--bg);
  color:var(--text);
  font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
  font-size:15px;
  line-height:1.45;
}

a{color:var(--primary);text-decoration:none;}
a:hover{text-decoration:underline;}

/* Link discreto para navegação à origem técnica (Curva ABC — Código e
   Descrição inteira clicáveis pra mesma Composição/Insumo). Cor de texto
   normal (não a cor de link padrão), pra não deixar uma tabela cheia de
   frases longas em azul — só fica visualmente "link" no hover/foco,
   cursor:pointer sinaliza que é clicável o tempo todo. Foco visível pra
   navegação por teclado (V1.00.044). */
.oc-link-origem{
  color:inherit;
  text-decoration:none;
  cursor:pointer;
}
.oc-link-origem:hover{
  color:var(--primary);
  text-decoration:underline;
}
.oc-link-origem:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
  border-radius:2px;
}
.oc-link-origem-desc{
  display:inline;
}

.oc-layout{
  display:flex;
  height:100vh; /* era min-height — altura FIXA trava a rolagem da página inteira aqui, nunca cresce com o conteúdo */
  overflow:hidden;
}

/* Sidebar (recolhível — ver .oc-sidebar-collapsed / .oc-sidebar-mobile-open
   mais abaixo). Largura e transição vivem aqui para que o conteúdo
   principal (.oc-main, flex:1) reajuste sozinho, sem JS recalculando nada.
   height:100% + overflow-y:auto (V1.00.048): preenche a altura fixa de
   .oc-layout e nunca acompanha a rolagem do conteúdo — ganha barra
   própria só se o menu algum dia crescer além da tela (hoje não cresce). */
.oc-sidebar{
  /* Token do recuo horizontal: o flyout do modo "Manter fechado" precisa
     saber exatamente quanto o item esta afastado da borda da barra para se
     ancorar do lado de fora dela. Uma definicao so, usada nos dois lugares. */
  --oc-sidebar-pad-x:14px;
  width:240px;
  flex-shrink:0;
  height:100%;
  background:var(--sidebar-bg);
  color:var(--sidebar-text);
  padding:20px var(--oc-sidebar-pad-x);
  display:flex;
  flex-direction:column;
  gap:18px;
  transition:width .2s ease;
  overflow-x:hidden;
  overflow-y:auto;
}

.oc-brand{
  display:flex;
  align-items:baseline;
  gap:8px;
  padding:0 6px;
  min-width:0;
}

.oc-brand strong,
.oc-brand span{
  white-space:nowrap;
  overflow:hidden;
}

.oc-sidebar-toggle{
  margin-left:auto;
  flex-shrink:0;
  width:26px;
  height:26px;
  border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.06);
  color:var(--sidebar-text);
  border-radius:7px;
  cursor:pointer;
  font-size:13px;
  line-height:1;
  display:flex;
  align-items:center;
  justify-content:center;
}

.oc-sidebar-toggle:hover{
  border-color:rgba(255,255,255,.4);
  color:#fff;
}

.oc-sidebar-toggle:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

/* ============ BOTAO DE MODO DA BARRA (V1.00.412) ============
   O controle de modo REUSA `.oc-sidebar-toggle` — mesma caixa, mesma cor,
   mesmo foco do botao que existia antes. O que muda a cada clique e o
   simbolo dentro dele, escrito por assets/js/sidebar.js.

   Nao ha CSS proprio: um botao que cicla nao precisa de estado visual extra
   alem do simbolo. A tentativa anterior desta versao era um menu <details>
   com tres opcoes, e as ~70 linhas de CSS dele foram REMOVIDAS junto com o
   markup — CSS orfao vira a proxima regra que ninguem sabe se pode apagar. */

.oc-sidebar-hamburger{
  display:none;
  width:var(--header-btn-height);
  height:var(--header-btn-height);  /* V1.00.096 — Altura global do cabeçalho */
  border:1px solid var(--border);
  background:var(--surface);
  border-radius:8px;
  cursor:pointer;
  font-size:16px;
  flex-shrink:0;
}

.oc-sidebar-hamburger:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

.oc-sidebar-overlay{
  display:none;
}

.oc-brand strong{
  color:var(--sidebar-text-active);
  font-size:18px;
  letter-spacing:.3px;
}

.oc-brand span{
  font-size:11px;
  color:var(--sidebar-text);
  opacity:.7;
}

.oc-nav{
  display:flex;
  flex-direction:column;
  gap:2px;
}

.oc-nav-label{
  font-size:11px;
  text-transform:uppercase;
  letter-spacing:.08em;
  color:var(--sidebar-text);
  opacity:.55;
  padding:10px 10px 4px;
}

.oc-nav a{
  display:flex;
  align-items:center;
  gap:10px;
  padding:9px 10px;
  border-radius:8px;
  color:var(--sidebar-text);
  font-size:14px;
}

.oc-nav a:hover{
  background:rgba(255,255,255,.06);
  text-decoration:none;
}

.oc-nav a.active{
  background:var(--primary);
  color:#fff;
}

.oc-nav-icon{
  width:18px;
  text-align:center;
  opacity:.9;
  flex-shrink:0;
}

.oc-nav-text{
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}

.oc-sidebar-footer{
  margin-top:auto;
  font-size:11px;
  color:var(--sidebar-text);
  opacity:.5;
  padding:0 10px;
  white-space:nowrap;
  overflow:hidden;
}

/* Desktop/notebook recolhido: só ícones, com tooltip no hover/foco por
   teclado (item 5/14 da tarefa de sidebar) — cobre tanto mouse quanto
   navegação via Tab, ao contrário do atributo title nativo. */
body.oc-sidebar-collapsed .oc-sidebar{
  width:64px;
}

/* ⚠️ `.oc-nav-text` ESCOPADO À SIDEBAR.



body.oc-sidebar-collapsed .oc-sidebar-toggle{
  margin-left:0;
}

body.oc-sidebar-collapsed .oc-nav a{
  position:relative;
  justify-content:center;
}

/* ============ NOME DA PÁGINA AO APONTAR O ÍCONE (V1.00.441) ============
   Com a barra estreita o rótulo está com `max-width:0`, então o ícone é tudo
   que se vê. Apontar para ele mostra o nome ao lado.

   ⚠️ A DICA VIVE NO <body>, NUNCA DENTRO DA BARRA. A primeira versão era um
   `::after` `position:absolute` no próprio item, e NUNCA apareceu: a barra tem
   `overflow-x:hidden` e `backdrop-filter` — logo ela é o bloco contentor E o
   recorte de qualquer descendente posicionado, `position:fixed` inclusive.
   É a mesma descoberta que obrigou o flyout do modo "Manter fechado" a liberar
   `overflow` enquanto aberto (§109). Medido antes de trocar, no navegador: com
   o ponteiro sobre o ícone, nada era pintado na tela.

   Fora da barra não há o que recortar, e nenhuma regra de overflow da sidebar
   precisa ser afrouxada para a dica existir. O preço é o mesmo do flyout —
   `top`/`left` são da viewport, então quem os escreve é assets/js/sidebar.js. */
.oc-nav-tooltip{
  position:fixed;
  z-index:70;            /* acima da sidebar (65), que já está acima do topbar (60) */
  background:var(--text);
  color:#fff;
  padding:5px 10px;
  border-radius:6px;
  font-size:12.5px;
  line-height:1.35;
  white-space:nowrap;
  box-shadow:0 4px 14px rgba(0,0,0,.22);
  pointer-events:none;   /* nunca rouba o hover do próprio ícone */
  /* ⚠️ NASCE INVISÍVEL, ATÉ TER COORDENADAS — mesma disciplina do flyout:
     `visibility` (não `display`) para o JS conseguir medir a altura antes de
     centralizar a dica na linha do ícone. */
  visibility:hidden;
  opacity:0;
  transition:opacity .12s ease;
}

.oc-nav-tooltip[data-visivel="1"]{
  visibility:visible;
  opacity:1;
}

/* Main content — V1.00.048: vira coluna flex (Topbar fixo + Content
   Panel rolável), nunca mais um bloco único que a página inteira rola
   junto. min-height:0 é essencial aqui (item 13 do pedido de rolagem):
   sem isso, o filho .oc-content-panel força .oc-main/.oc-layout/body a
   crescer em vez de ganhar sua própria barra de rolagem. */
.oc-main{
  flex:1;
  min-width:0;
  height:100%;
  min-height:0;
  display:flex;
  flex-direction:column;
  overflow-x:hidden;  /* Impede scroll horizontal do documento inteiro */
}

/* Header da página — fica de fora do .oc-content-panel de propósito,
   então nunca rola (item 6 do pedido: cabeçalho fixo, calculado pela
   própria cadeia flex, nunca um número mágico de altura).
   Título SEMPRE primeiro/esquerda, ações+hambúrguer SEMPRE por último/
   direita (V1.00.073 — padronização de header) — order fixa no HTML
   gerado por ocPageStart(), nunca duplicada por página.
   container-type:inline-size: torna este elemento uma CONTAINER QUERY,
   não uma media query de viewport — o motivo é o mesmo já usado nos
   filtros (V1.00.072): a largura REAL disponível aqui muda com a
   sidebar aberta/recolhida, não só com o tamanho da janela, e não dá
   pra simplesmente deixar os botões quebrarem linha (pedido explícito:
   nunca várias linhas no header) — então quem precisa encolher primeiro
   é o TEXTO dos botões/o subtítulo, nunca o layout quebrando. Seguro
   como container aqui porque a largura de .oc-topbar vem do pai
   (.oc-main, bloco), nunca do conteúdo dos próprios filhos flex — sem
   dependência circular de tamanho. */
/* ================================================================== *
 * .oc-topbar — FONTE DE VERDADE ÚNICA (V1.00.154)                    *
 * ------------------------------------------------------------------ *
 * Este é o ÚNICO bloco que define o componente por completo:          *
 * display, direção, alinhamento, gap, padding, margin, sticky,        *
 * background, z-index e host de container query.                      *
 *                                                                     *
 * Media/container queries abaixo alteram SOMENTE o mínimo necessário  *
 * (um padding, um display) — nunca redefinem o componente inteiro.    *
 *                                                                     *
 * ALTURA: determinada exclusivamente pelo conteúdo (título +          *
 * subtítulo quando visível + botões) + os dois paddings do :root.     *
 * NUNCA usar height/min-height fixos, margin-top:auto,                *
 * justify-content vertical ou margin-bottom exagerado.                *
 *                                                                     *
 * REGRESSÃO CORRIGIDA (V1.00.153 → V1.00.154): havia `flex:1 1 auto`  *
 * aqui, herdado de quando .oc-content-panel era CSS Grid (onde flex   *
 * no filho é inerte). Quando o painel virou `flex-direction:column`,  *
 * esse `flex-grow:1` passou a esticar o cabeçalho verticalmente para  *
 * ocupar TODA a altura livre — 412px medidos em vez de ~72px. É a     *
 * causa do enorme vazio em Contas Bancárias/Planejamento/Medições.    *
 * `flex:0 0 auto` trava a altura no conteúdo.                         *
 * ================================================================== */
.oc-topbar{
  /* Layout horizontal — título à esquerda, ações à direita, SEMPRE 1 linha */
  display:flex;
  flex-direction:row;
  align-items:center;      /* centralização vertical automática (Parte 12) */
  flex-wrap:nowrap;        /* CRÍTICO: nunca quebra para segunda linha */
  gap:16px;

  /* Altura = conteúdo. flex-grow:0 impede o esticamento vertical. */
  flex:0 0 auto;

  /* Espaçamento vertical pequeno e uniforme, vindo dos tokens globais */
  padding:var(--header-pad-top) 0 var(--header-pad-bottom);
  margin:0 0 var(--header-gap-panel);

  /* Largura: herdada do pai; pode encolher sem estourar */
  width:auto;
  max-width:none;
  min-width:0;
  box-sizing:border-box;

  /* Host da container query — a largura real aqui muda com a sidebar
     recolhida/aberta, não só com o tamanho da janela. */
  container-type:inline-size;
  container-name:oc-topbar;

  /* Permanece visível ao rolar o conteúdo */
  position:sticky;
  top:0;
  z-index:60;            /* acima do conteúdo (0), abaixo da sidebar (80) */
  background:var(--bg);  /* fundo opaco para não revelar o conteúdo por baixo */
}

/* Bloco de texto: encolhe PRIMEIRO, liberando espaço para os botões.
   É o que garante que as ações nunca desçam de linha. */
.oc-title{
  display:flex;
  flex-direction:column;
  justify-content:center;
  flex:1 1 auto;
  min-width:0;
}

/* Container das ações + hambúrguer. Não cresce, não quebra. */
.oc-topbar-actions{
  display:flex;
  align-items:center;
  justify-content:flex-end;
  gap:8px;
  flex-wrap:nowrap;
  flex:0 0 auto;      /* largura fixa pelo conteúdo — nunca é comprimido */
  margin-left:auto;   /* encosta na extrema direita */
  white-space:nowrap;
}

/* Prioridade visual (item 15/19 do pedido): 1º título, 2º ações, 3º
   hambúrguer — nesta ordem, o SUBTÍTULO é o primeiro a sumir quando o
   espaço aperta (menor prioridade), bem antes do texto dos botões. */

/* ------------------------------------------------------------------ *
 * MÁQUINA DE ESTADOS RESPONSIVA (Parte 8) — ESTRATÉGIA ÚNICA: CSS.    *
 * ------------------------------------------------------------------ *
 * Estado A (>1000px de container): título + subtítulo + ícone e texto *
 * Estado B (≤1000px):              título + botões com ícone e texto  *
 *                                  subtítulo OCULTO                   *
 * Estado C (≤820px):               título + botões ICON-ONLY          *
 *                                  subtítulo OCULTO                   *
 *                                                                     *
 * Não existe JS decidindo estes estados — os scripts de header eram   *
 * apenas console.log de diagnóstico e foram removidos na V1.00.154.   *
 * Logo, não há duplicação de responsabilidade CSS×JS (Parte 9).       *
 *                                                                     *
 * Sem `!important` (Parte 10): a especificidade destes seletores       *
 * (0,2,0) já vence a das regras-base (0,1,0), então o estado normal    *
 * volta sozinho quando a query deixa de casar.                        *
 * ------------------------------------------------------------------ */

/* Estado B — o subtítulo é o primeiro a sair (menor prioridade visual).
   Ocultado por completo, nunca truncado com ellipsis. */
@container oc-topbar (max-width:1000px){
  .oc-topbar .oc-title p{
    display:none;
  }
}

/* Estado C — todos os botões viram icon-only, quadrados (36×36px).
   Só a LARGURA muda; a altura continua sendo --header-btn-height
   em todos os estados (Parte 11). O hambúrguer não é afetado. */
@container oc-topbar (max-width:820px){
  .oc-topbar-actions .oc-btn-label{
    display:none;
  }
  .oc-topbar-actions .oc-btn{
    width:var(--header-btn-height);
    min-width:var(--header-btn-height);
    max-width:var(--header-btn-height);
    padding:0;
    gap:0;
  }
  .oc-topbar-actions .oc-btn-icon{
    margin:0;
    line-height:1;
  }
}


/* Grupo de ações dentro do topbar */
.oc-page-actions{
  display:flex;
  align-items:center;
  gap:8px;
  flex-wrap:nowrap;
  flex:0 1 auto;  /* Não cresce, pode encolher, tamanho natural */
  min-width:0;    /* Permite que conteúdo se comprima */
  justify-content:flex-end;  /* Alinha botões à direita */
}

.oc-page-actions .oc-btn{
  height:var(--header-btn-height);  /* V1.00.096 — Altura global do cabeçalho */
  flex:0 1 auto;  /* Não cresce, pode encolher, tamanho natural */
  min-width:0;    /* Permite que conteúdo se comprima */
  white-space:nowrap;  /* Evita quebra de linha do texto */
}

/* ------------------------------------------------------------------ *
 * MENU [⋮] DE AÇÕES SECUNDÁRIAS DO CABEÇALHO (§9 a §16)               *
 * ------------------------------------------------------------------ *
 * Mesmo `<details class="oc-menu">` das linhas de tabela — só o       *
 * POSICIONAMENTO é diferente, e por um motivo concreto: `.oc-topbar`  *
 * é `container-type:inline-size`, o que a torna bloco de contenção    *
 * de descendentes `position:fixed`. O painel flutuante do script      *
 * apareceria deslocado. Aqui não há `overflow` recortando nada, então *
 * `absolute` ancorado ao próprio `<details>` é a solução simples e a  *
 * correta (ver `data-menu-ancorado` em assets/menu-flutuante.js).     *
 * ------------------------------------------------------------------ */
.oc-topbar-menu{
  position:relative;
  flex:0 0 auto;
}

/* O botão é quadrado, da MESMA altura dos demais botões do cabeçalho — não um
   ícone solto ao lado deles. A largura fixa iguala a do estado icon-only, para
   que ele não mude de tamanho quando os vizinhos perdem o texto. */
.oc-topbar-menu > summary.oc-btn{
  height:var(--header-btn-height);
  width:var(--header-btn-height);
  min-width:var(--header-btn-height);
  padding:0;
  gap:0;
  /* Área clicável inteira, texto centralizado: o glifo `⋮` é estreito e, sem
     isto, só a coluna de pixels do próprio caractere responderia ao clique. */
  display:inline-flex;
  align-items:center;
  justify-content:center;
  font-size:18px;
  line-height:1;
  list-style:none;
}

.oc-topbar-menu > summary.oc-btn::-webkit-details-marker{ display:none; }

.oc-topbar-menu > summary.oc-btn:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

/* Abre ABAIXO e alinhado pela borda DIREITA do botão — o botão vive no canto
   direito da tela, e alinhar pela esquerda jogaria o painel para fora. */
.oc-topbar-menu > .oc-menu-painel{
  position:absolute;
  top:calc(100% + 6px);
  right:0;
  left:auto;
  /* O componente base já traz `margin-top:6px` para o caso em-fluxo; aqui o
     afastamento vem do `top`, e somar os dois abriria 12px. */
  margin-top:0;
  z-index:var(--oc-z-menu-contexto, 1000);
  box-shadow:0 8px 24px rgba(0,0,0,.18);
  /* Viewport baixo: rola o menu em vez de estourar a tela. */
  max-height:min(60vh, 420px);
  overflow-y:auto;
}

/* ⚠️ Em telas estreitas o painel não pode nascer da borda direita e crescer
   para fora: `min-width:200px` do componente base, somado a um rótulo longo
   ("Exportar Rateios"), passaria da largura da janela em celulares pequenos.
   O `max-width` prende o painel dentro da viewport com folga para as margens
   do conteúdo. */
@media (max-width: 480px){
  .oc-topbar-menu > .oc-menu-painel{
    max-width:calc(100vw - 32px);
  }
}

/* ------------------------------------------------------------------ *
 * Grupo de ações do cabeçalho (componente GLOBAL reutilizável)        *
 * Faixa de largura total logo abaixo do título, mas com o GRUPO       *
 * compacto e alinhado à DIREITA (canto superior direito do painel).   *
 * Os N botões têm a MESMA largura (fixa, comporta "Excluir não        *
 * utilizados/as") — não 25% do painel. Ordem lógica preservada pela   *
 * ordem do HTML (sem flex-order). Altura/padding/ícone vêm do .oc-btn. *
 * Responsivo: desktop 1 linha à direita · tablet 2 por linha · celular *
 * 1 por linha (100%).                                                  *
 * ------------------------------------------------------------------ */
.oc-action-grid{
  display:flex;
  flex-wrap:wrap;
  justify-content:flex-end;   /* grupo compacto encostado à direita */
  align-items:center;
  gap:8px;                     /* mesmo gap do design system */
  width:100%;                  /* a faixa ocupa a largura; o GRUPO fica à direita */
  margin:0 0 20px;             /* respiro antes do painel de filtros */
}
.oc-action-grid > .oc-btn{
  flex:0 0 var(--oc-action-btn-w);  /* largura IGUAL e fixa para todos os botões */
  min-height:var(--header-btn-height);  /* mesma altura do cabeçalho */
  min-width:0;
  white-space:nowrap;          /* não quebra em 2 linhas nem corta texto (§3/§6) */
}
:root{ --oc-action-btn-w:13.75rem; }  /* ~220px — comporta "Excluir não utilizados/as" */
/* Tablet / telas médias: 2 por linha, ainda à direita. */
@media (max-width: 900px){
  .oc-action-grid > .oc-btn{ flex-basis:calc(50% - 4px); }
}
/* Celular: 1 por linha, 100% da largura. */
@media (max-width: 560px){
  .oc-action-grid > .oc-btn{ flex-basis:100%; }
}

/* Garante que botões no topbar também se comportam corretamente.
   Altura global padronizada (36px) em todos os breakpoints. */
.oc-topbar-actions .oc-btn{
  height:var(--header-btn-height);  /* V1.00.096 — Altura fixa do cabeçalho */
  flex:0 1 auto;  /* Não cresce, pode encolher */
  min-width:0;
  white-space:nowrap;  /* Texto nunca quebra linha */
  padding:6px 16px;  /* Vertical+horizontal — conteúdo centralizado dentro de altura fixa */
}

/* Único container que rola verticalmente no app inteiro (item 1/2/11 do
   pedido de rolagem) — sempre o mesmo elemento, reutilizado por TODA
   página via ocPageStart()/ocPageEnd() (includes/layout.php), nunca CSS
   duplicado por página. overflow-x:hidden aqui — rolagem horizontal de
   tabelas continua sendo responsabilidade do .oc-table-wrap de cada
   tabela, nunca do painel inteiro. */
.oc-content-panel{
  /* V1.00.153 — Refatoração Arquitetural do Header
     .oc-content-panel volta a ser simplesmente o container do conteúdo,
     em fluxo vertical puro. Grid removido — não é responsabilidade deste
     container montar o layout do cabeçalho.

     Estrutura:
     Vertical (flex-direction:column):
     1. .oc-topbar (cabeçalho com titulo + ações, layout horizontal interno)
     2. .oc-flash (mensagens, opcional)
     3. .oc-panel (filtro, cadastro, demais painéis — fluxo normal)

     O layout horizontal do cabeçalho é responsabilidade de .oc-topbar
     (display:flex, align-items:center), nunca de .oc-content-panel. */
  flex:1;
  min-width:0;
  min-height:0;
  overflow-y:auto;
  padding:0 32px 60px;

  /* Fluxo VERTICAL — voltou ao padrão */
  display:flex;
  flex-direction:column;
  gap:0;  /* Sem gap — espaçamento é responsabilidade de cada elemento */
}

/* V1.00.154 — A segunda definição completa de .oc-topbar que existia aqui
   foi REMOVIDA. O componente é definido uma única vez, no bloco marcado
   "FONTE DE VERDADE ÚNICA" mais acima neste arquivo. Era essa duplicata
   que reintroduzia `padding:28px 0 0` + `margin-bottom:22px`. */

/* V1.00.153 — .oc-action-grid é LEGADO (mantido para compatibilidade futura)
   Não deve mais ser usado para ações principais do header.
   As ações principais agora fazem parte de .oc-topbar > .oc-topbar-actions > .oc-page-actions.

   Se ainda for necessário em contextos secundários (fora do header), preserva flexbox. */
.oc-action-grid {
  margin:0;
  display:flex;
  align-items:center;
  flex-wrap:nowrap;
  gap:8px;
  min-width:0;
  white-space:nowrap;
  /* Grid-column/row removidos — era responsabilidade do container antigo (V1.00.152) */
}

.oc-flash {
  /* Fluxo vertical puro (sem grid) */
  margin:0 0 16px;
}

/* V1.00.154 — As redefinições de .oc-title, .oc-topbar-actions e
   .oc-page-actions que existiam aqui foram REMOVIDAS: eram cópias
   integrais dos blocos já declarados acima (fonte de verdade única). */

.oc-pagination {
  /* V1.00.153 — Paginação (embaixo das tabelas) — NOVO: classe desambígua
     Antes: .oc-page-actions (confundia com ações do header)
     Agora: .oc-pagination (nunca afetada por lógica de header)

     Botões numerados, nunca sofrem transformação icon-only do header. */
  display:flex;
  align-items:center;
  gap:8px;
  flex-wrap:wrap;  /* Pode quebrar linha se necessário */
}

/* V1.00.183 — Paginação compacta (Insumos e Composições compartilham).
   Antes a listagem imprimia UM link por página: com 528 páginas, a
   paginação virava um bloco de várias linhas ocupando meia tela. Agora
   são no máximo 7 números + reticências, e estes ajustes cuidam só do
   que a nova estrutura acrescentou. */

.oc-pagination-rotulo {
  /* "Pág." antes dos números (§25) — rótulo, não botão. */
  font-size:13px;
  color:var(--oc-text-muted, #64748b);
  margin-right:2px;
}

.oc-pagination-gap {
  /* Reticências: mesma altura dos botões para a linha não "pular",
     mas sem borda nem fundo — não é clicável (§11). */
  display:inline-flex;
  align-items:center;
  min-height:28px;
  padding:0 2px;
  color:var(--oc-text-muted, #64748b);
  user-select:none;
}

.oc-pagination-navs {
  /* Dois grupos: « ‹ antes dos números, › » depois. O gap lateral é
     simétrico para que os dois blocos fiquem visualmente equilibrados. */
  display:inline-flex;
  align-items:center;
  gap:4px;
}

.oc-pagination-navs:first-child { margin-right:4px; }
.oc-pagination-navs:last-child  { margin-left:4px; }

.oc-pagination-nav {
  /* Largura fixa para os quatro controles: sem isto, "«" e "‹" têm
     larguras diferentes e o grupo dança ao trocar de página. */
  min-width:32px;
  justify-content:center;
}

.oc-pagination-nav[aria-disabled="true"] {
  /* §19 — desabilitado é <span>, não link. O cursor e o contraste
     reduzido comunicam isso sem depender só da cor. */
  opacity:.45;
  cursor:default;
  pointer-events:none;
}

@media (max-width: 480px) {
  /* §43 — em tela estreita o bloco numérico pode quebrar, mas os quatro
     controles de navegação continuam acessíveis e juntos. */
  .oc-pagination { gap:6px; }
  .oc-pagination-navs { margin-left:0; }
}

.oc-title h1{
  margin:0 0 2px;
  font-size:22px;
  white-space:nowrap;
  min-width:0;

  /* V1.00.154 — Degradação graciosa em telas muito estreitas (≤360px).
     Abaixo desse ponto não há largura física para "Contas Bancárias" +
     3 botões icon-only + hambúrguer. Antes o texto era CORTADO no meio
     do glifo; agora termina em reticências. O título nunca quebra linha,
     nunca empurra os botões e nunca gera rolagem horizontal. */
  overflow:hidden;
  text-overflow:ellipsis;
}

.oc-title p{
  margin:0;
  color:var(--text-muted);
  font-size:13.5px;
  white-space:nowrap;
  flex-shrink:0;
  /* Removido: overflow:hidden e text-overflow:ellipsis
     Subtítulo é visível completo em 1 linha, OU oculto completamente (ver @container abaixo).
     Nunca truncado com ellipsis. */
}

.oc-badge{
  display:inline-block;
  background:#eef2ff;
  color:var(--primary-dark);
  font-size:12px;
  font-weight:600;
  padding:4px 10px;
  border-radius:999px;
  border:1px solid #d7e0ff;
}

.oc-badge-muted{
  background:#f1f2f4;
  color:var(--text-muted);
  border-color:var(--border);
}

/* Cards */
.oc-cards{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
  gap:16px;
  margin:18px 0 28px;
}

.oc-card{
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  padding:18px;
  display:block;
  transition:border-color .15s,transform .15s;
}

.oc-card:hover{
  border-color:var(--primary);
  text-decoration:none;
  transform:translateY(-1px);
}

.oc-card-icon{
  font-size:22px;
  margin-bottom:8px;
}

.oc-card h3{
  margin:0 0 4px;
  font-size:15px;
  color:var(--text);
}

.oc-card p{
  margin:0;
  font-size:12.5px;
  color:var(--text-muted);
}

/* Dashboard (index.php) --------------------------------------------- */

.oc-card-kpi{
  display:flex;
  flex-direction:column;
  gap:6px;
}

.oc-card-kpi .oc-card-numero{
  font-size:30px;
  font-weight:700;
  color:var(--text);
  line-height:1;
}

.oc-card-kpi .oc-card-rotulo{
  font-size:13px;
  color:var(--text-muted);
  font-weight:600;
}

.oc-empresa-atual{
  display:inline-flex;
  align-items:center;
  gap:8px;
  font-size:13px;
  color:var(--text-muted);
  margin:-6px 0 18px;
}

.oc-empresa-atual strong{
  color:var(--text);
}

.oc-dashboard-secao{
  display:flex;
  align-items:center;
  justify-content:space-between;
  margin-bottom:10px;
}

.oc-dashboard-secao h2{
  margin:0;
}

.oc-acoes-rapidas{
  display:flex;
  flex-wrap:wrap;
  gap:10px;
}

.oc-cadastros-tecnicos{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(200px,1fr));
  gap:2px 18px;
}

.oc-cadastro-tecnico{
  display:flex;
  align-items:center;
  gap:10px;
  padding:10px 4px;
  border-bottom:1px solid var(--border);
  color:var(--text);
}

.oc-cadastro-tecnico:hover{
  color:var(--primary);
  text-decoration:none;
}

.oc-cadastro-tecnico .oc-card-icon{
  font-size:18px;
  margin:0;
  flex-shrink:0;
}

.oc-cadastro-tecnico-texto{
  display:flex;
  flex-direction:column;
  min-width:0;
}

.oc-cadastro-tecnico-texto strong{
  font-size:13.5px;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

.oc-cadastro-tecnico-texto span{
  font-size:12px;
  color:var(--text-muted);
}

.oc-fluxo{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  gap:6px;
}

.oc-fluxo-passo{
  padding:8px 14px;
  border-radius:999px;
  font-size:12.5px;
  font-weight:600;
  border:1px solid var(--border);
  color:var(--text-muted);
  white-space:nowrap;
}

.oc-fluxo-passo-pronto{
  border-color:var(--primary);
  color:var(--primary);
  background:#f0f5ff;
}

a.oc-fluxo-passo-pronto{
  cursor:pointer;
}

.oc-fluxo-seta{
  color:var(--text-muted);
  font-size:14px;
}

.oc-estado-vazio{
  text-align:center;
  padding:28px 16px;
  color:var(--text-muted);
}

.oc-estado-vazio p{
  margin:0 0 14px;
}

/* Panels / sections */
.oc-panel{
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  padding:20px;
  margin-bottom:20px;
}

.oc-panel h2{
  margin:0 0 4px;
  font-size:16px;
}

.oc-panel .oc-panel-hint{
  margin:0 0 16px;
  font-size:13px;
  color:var(--text-muted);
}

/* Tables */
.oc-table-wrap{
  overflow-x:auto;
}

/* Pré-visualizações longas (ex.: exclusão em lote) — rolagem vertical
   dentro do próprio painel, sem paginação, mantendo o cabeçalho da tabela
   visível no topo do contêiner. */
.oc-table-wrap-scroll{
  max-height:420px;
  overflow-y:auto;
}

/* Árvore da Estrutura Analítica (EAP) do Orçamento — indentação por nível
   via variável CSS, reduzida no mobile (item 12 do pedido de UX da EAP:
   "não usar indentação excessiva no celular"). A hierarquia continua
   perceptível pelo Código + espaço reduzido, sem estourar a largura. */
.oc-eap-cell{
  --oc-eap-unidade:22px;
}
.oc-eap-indent{
  display:inline-block;
  width:calc(var(--oc-eap-nivel, 0) * var(--oc-eap-unidade));
}
@media (max-width:600px){
  .oc-eap-cell{
    --oc-eap-unidade:10px;
  }
}

/* Ação "Excluir" da EAP — <details> nativo (abre com clique/tap, nunca
   depende de hover, funciona igual em desktop/tablet/mobile). O painel
   fica NO FLUXO normal (não position:absolute) de propósito: a tabela
   fica dentro de um .oc-table-wrap com overflow-x:auto, e um overlay
   absoluto seria recortado/ficaria fora da área visível ao rolar a
   tabela horizontalmente no mobile — em vez disso, a linha simplesmente
   cresce em altura quando o painel abre, sem risco de ficar invisível. */
.oc-eap-excluir > summary{
  cursor:pointer;
  list-style:none;
}
.oc-eap-excluir > summary::-webkit-details-marker{
  display:none;
}
.oc-eap-excluir-painel{
  margin-top:8px;
  min-width:220px;
  max-width:360px;
  padding:10px 12px;
  background:var(--bg);
  border:1px solid var(--border);
  border-radius:8px;
}
.oc-eap-realocar-form{
  display:flex;
  flex-direction:column;
  gap:8px;
}
.oc-eap-realocar-form select{
  width:100%;
  min-width:0;
}

/* Edição inline de Quantidade (itens-orcamento.php, V1.00.069) — mesmo
   padrão em-fluxo do .oc-eap-excluir acima (NUNCA position:absolute, que
   seria cortado pelo overflow-x:auto de .oc-table-wrap ao rolar
   horizontalmente no mobile). */
.oc-inline-edit > summary{
  cursor:pointer;
  list-style:none;
}
.oc-inline-edit > summary::-webkit-details-marker{
  display:none;
}
.oc-inline-edit-painel{
  margin-top:6px;
}
.oc-inline-edit-form{
  display:flex;
  align-items:center;
  gap:6px;
  flex-wrap:wrap;
}
.oc-inline-edit-form input[type="text"]{
  width:110px;
  min-width:0;
}

/* Menu [...] consolidado por Serviço (itens-orcamento.php, V1.00.069,
   item 16 do pedido) — mesmo padrão em-fluxo já usado por .oc-eap-excluir
   (NUNCA position:absolute, cortado pelo overflow-x:auto de
   .oc-table-wrap). Usado tanto na árvore de EAP (dentro de
   .oc-eap-c-acoes) quanto na lista achatada (dentro de <td>). */
.oc-menu > summary{
  cursor:pointer;
  list-style:none;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  min-width:32px;
}
.oc-menu > summary::-webkit-details-marker{
  display:none;
}
.oc-menu-painel{
  margin-top:6px;
  min-width:200px;
  max-width:280px;
  padding:4px;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:8px;
  display:flex;
  flex-direction:column;
  gap:1px;
}
.oc-menu-item,
.oc-menu-item-form{
  margin:0;
}
a.oc-menu-item,
button.oc-menu-item{
  /* V1.00.485 — flex (era `block`) porque o item passou a ter ÍCONE + rótulo:
     é o que mantém os dois na mesma linha de base, com um respiro só entre
     eles, e a altura igual em todos os itens — com ou sem ícone. `gap` em vez
     de margem no ícone: item sem ícone não ganha recuo fantasma. */
  display:flex;
  align-items:center;
  gap:8px;
  width:100%;
  text-align:left;
  padding:7px 10px;
  border-radius:6px;
  border:none;
  background:none;
  color:var(--text);
  text-decoration:none;
  font:inherit;
  font-size:13px;
  cursor:pointer;
}
/* Wrapper do ícone: caixa de 16×16 com o desenho centrado dentro dela. A ALTURA
   explícita é o que tira o alinhamento da mão da linha de texto — sem ela o
   <span> herda `font-size:14px`/`line-height`, e o ícone passa a flutuar em
   relação ao rótulo conforme a fonte. Largura fixa mantém os rótulos alinhados
   entre si; `:not(:empty)` deixa o item SEM ícone sem recuo fantasma.
   Nada de margin-top/translateY: o centro vem do flex, aqui e no item. */
.oc-menu-item > .oc-btn-icon:not(:empty){
  display:inline-flex;
  align-items:center;
  justify-content:center;
  flex:0 0 auto;
  width:16px;
  height:16px;
  line-height:0;
}
/* No painel o rótulo é o conteúdo, nunca escondido: `.oc-btn-label` some por
   container query na topbar (botão vira só-ícone), e essa regra não pode
   atravessar para cá — um menu de ações sem texto seria ilegível. */
.oc-menu-item > .oc-btn-label{
  display:inline !important;
  white-space:nowrap;
}
a.oc-menu-item:hover,
button.oc-menu-item:hover{
  background:var(--bg);
}
.oc-menu-item-perigo{
  color:#b3261e;
}

/* Ação bloqueada (ex.: Excluir de um orçamento com planejamento vinculado).
   Ela continua LISTADA de propósito: some da lista, e o usuário procura por
   ela achando que errou o caminho. O `title` do item diz o porquê. */
button.oc-menu-item[disabled],
button.oc-menu-item[aria-disabled="true"]{
  opacity:.5;
}
button.oc-menu-item[disabled]{
  cursor:not-allowed;
}
button.oc-menu-item[disabled]:hover{
  background:transparent;
}
.oc-menu-submenu > summary{
  list-style:none;
  cursor:pointer;
}
.oc-menu-submenu > summary::-webkit-details-marker{
  display:none;
}
.oc-menu-mover-form{
  display:flex;
  flex-direction:column;
  gap:8px;
  padding:6px 10px 8px;
}
.oc-menu-mover-form select{
  width:100%;
  min-width:0;
}

/* Gatilho do modal de Centro de Custo (itens-orcamento.php, V1.00.069) —
   <button> em vez de <a> (não há navegação), reaproveita a aparência de
   .oc-link-origem (cor de texto normal, sublinha no hover) por cima do
   reset dos estilos padrão de <button>. */
.oc-cc-trigger{
  background:none;
  border:none;
  padding:0;
  margin:0;
  font:inherit;
  /* ⚠️ `inherit`, nunca `left`. Este <button> é inline-block, então em nome
     CURTO ele encolhe até o texto e a borda direita coincide com a da coluna
     — parecia alinhado à direita. Em nome que QUEBRA (TAPUMES E PROTEÇÕES,
     INSTALAÇÕES PROVISÓRIAS) a caixa passa a ocupar os 150px inteiros da
     coluna, e aí o alinhamento interno do próprio botão é que manda: as duas
     linhas voltavam para a esquerda. Medido: botão de 96px encostando em
     819px contra botão de 150px começando em 669px, na mesma coluna.

     Por isso `.oc-eap-c-cc{text-align:right}` parecia não fazer efeito — ele
     fazia, e o botão desfazia. Herdar deixa o CONTÊINER decidir o eixo, que é
     onde a decisão pertence: à direita na árvore de EAP, à esquerda em
     qualquer lista que use este gatilho numa célula alinhada à esquerda. */
  text-align:inherit;
}

/* Trigger de edição de Quantidade (itens-orcamento.php, V1.00.076) —
   mesmo padrão de .oc-cc-trigger: remove aparência nativa de <button>,
   reutiliza .oc-link-origem (cor normal, sublinha no hover, foco visível).
   appearance:none remove qualquer estilo default de browser para <button>. */
.oc-qtd-trigger{
  appearance:none;
  -webkit-appearance:none;
  -moz-appearance:none;
  background:none !important;
  border:none !important;
  padding:0 !important;
  margin:0 !important;
  font:inherit;
  text-align:left;
  box-shadow:none !important;
  outline:none;
}

/* Barra de ações em lote (itens-orcamento.php, V1.00.069, item 7/8 do
   pedido) — fixa no rodapé da viewport (nunca dentro de .oc-content-panel,
   que é o único elemento que rola no app, CLAUDE.md §78 — precisa
   continuar visível independente do scroll), some quando não há seleção
   (controlado via o atributo `hidden`, nunca display:none direto no
   estilo — mais simples de alternar por JS). */
.oc-barra-lote{
  position:fixed;
  left:50%;
  bottom:20px;
  transform:translateX(-50%);
  z-index:40;
  display:flex;
  align-items:center;
  gap:14px;
  padding:10px 16px;
  background:var(--sidebar-bg);
  color:var(--sidebar-text-active);
  border-radius:999px;
  box-shadow:0 4px 20px rgba(0,0,0,.25);
  max-width:calc(100vw - 32px);
}
.oc-barra-lote[hidden]{
  display:none;
}
.oc-barra-lote-acoes{
  display:flex;
  gap:8px;
  flex-wrap:wrap;
}
.oc-barra-lote .oc-btn{
  background:rgba(255,255,255,.1);
  border-color:rgba(255,255,255,.2);
  color:var(--sidebar-text-active);
}
.oc-barra-lote .oc-btn:hover{
  background:rgba(255,255,255,.18);
}
/* ---- A ação destrutiva da barra continua parecendo destrutiva ----
   ⚠️ A regra acima pinta TODO `.oc-btn` da barra de branco translúcido, para
   funcionar sobre o fundo escuro. Ela tem a mesma especificidade (0,2,0) que
   `.oc-barra-lote .oc-btn-danger` — então o que decide é a ORDEM, e por isso
   esta regra vem DEPOIS. Sem ela, "Excluir" saía visualmente idêntico a "Mover
   EAP" e a "Desmarcar"; medido no navegador antes desta correção.

   As cores são as mesmas de `.oc-btn-danger` — o token destrutivo do sistema,
   o mesmo do "Excluir não utilizadas" — e não um vermelho pensado só para a
   barra. Sobre o fundo escuro o chip claro destaca-se sozinho; o contraste do
   texto vermelho sobre o rosa é o já validado no resto do sistema. */
.oc-barra-lote .oc-btn-danger{
  background:#fdf4f3;
  border-color:#f0d3d1;
  color:#b3261e;
}
.oc-barra-lote .oc-btn-danger:hover{
  background:#fff;
  border-color:#b3261e;
}

/* Modal de Centro de Custo — <dialog> nativo (V1.00.069), sem biblioteca
   nova. Fica no "top layer" do navegador — nunca é cortado pelo
   overflow-x:auto de .oc-table-wrap, diferente do padrão em-fluxo usado
   por "Mover"/edição de Quantidade (que precisam ficar dentro da linha da
   tabela). ::backdrop é o overlay nativo por trás do <dialog>. */
.oc-modal{
  width:min(480px, calc(100vw - 32px));
  max-height:min(600px, calc(100vh - 64px));
  border:1px solid var(--border);
  border-radius:12px;
  padding:20px;
  color:var(--text);
  background:var(--surface);
}
.oc-modal::backdrop{
  background:rgba(0,0,0,.4);
}
.oc-modal .oc-cc-lista{
  max-height:220px;
}

/* Modal "Editar Composição" (V1.00.077) — mesmo look do .oc-modal, porém
   MAIOR: mais conteúdo (cabeçalho + itens + adicionar). Cabeçalho fixo,
   corpo com scroll interno; o body da página nunca rola com o modal aberto
   (<dialog> nativo já trava o scroll de fundo). Itens 8/9/29 do pedido. */
.oc-modal--composicao{
  width:min(1100px, calc(100vw - 48px));
  max-width:90vw;
  max-height:90vh;
  padding:0;              /* cabeçalho/corpo controlam o próprio padding */
  overflow:hidden;
}
/* display:flex SÓ quando aberto — um <dialog> nativo depende de display:none
   no estado fechado; aplicar flex incondicionalmente mantinha o modal visível
   mesmo após close() (o modal "não fechava"). O UA cuida do display:none
   quando não há [open]. */
.oc-modal--composicao[open]{
  display:flex;
  flex-direction:column;
}
.oc-modal-composicao-cabecalho{
  position:sticky; top:0;
  display:flex; align-items:flex-start; justify-content:space-between; gap:16px;
  padding:18px 20px;
  border-bottom:1px solid var(--border);
  background:var(--surface);
  z-index:2;
}
.oc-modal-composicao-titulo-wrap{ min-width:0; }
.oc-modal-composicao-cabecalho h2{ margin:0; font-size:1.15rem; }
.oc-modal-composicao-contexto{
  margin-top:4px; font-size:.82rem; color:var(--text-muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:60ch;
}
.oc-modal-composicao-cab-acoes{ display:flex; align-items:center; gap:8px; flex-shrink:0; }
.oc-modal-composicao-fechar{
  border:none; background:none; cursor:pointer; color:var(--text-muted);
  font-size:26px; line-height:1; width:32px; height:32px;
  display:flex; align-items:center; justify-content:center; border-radius:8px;
}
.oc-modal-composicao-fechar:hover{ background:rgba(0,0,0,.06); color:var(--text); }
.oc-modal-composicao-corpo{
  overflow-y:auto; -webkit-overflow-scrolling:touch;
  padding:20px; flex:1 1 auto;
}
.oc-modal-composicao-corpo .oc-panel{ margin-top:0; }
.oc-modal-composicao-corpo .oc-panel + .oc-panel{ margin-top:16px; }

/* Tablet/Mobile: modal quase cheio / fullscreen (drawer), sem esmagar a
   tabela larga de itens (item 29). */
@media (max-width:768px){
  .oc-modal--composicao{
    width:100vw; max-width:100vw; height:100vh; max-height:100vh;
    border-radius:0; border:none;
  }
}

/* Fórmula de BDI configurável (V1.00.077) */
.oc-bdi-formula-config{
  margin:12px 0 4px; padding:12px 14px;
  border:1px solid var(--border); border-radius:10px; background:var(--surface);
}
.oc-modal--formula{ width:min(680px, calc(100vw - 48px)); max-width:92vw; }
.oc-modal-formula-cab{
  display:flex; align-items:flex-start; justify-content:space-between; gap:16px;
  margin-bottom:6px;
}
.oc-bdi-formula-tokens{
  display:flex; flex-wrap:wrap; align-items:center; gap:6px; margin:8px 0;
}
.oc-bdi-tok-label{ font-size:.82rem; color:var(--text-muted); margin-right:2px; }
.oc-formula-token{ min-width:34px; }
.oc-bdi-formula-previa{
  margin-top:12px; padding:10px 12px; border-radius:8px;
  background:rgba(0,0,0,.04); font-size:.95rem;
}
.oc-bdi-formula-atual{ word-break:break-word; }
@media (max-width:768px){
  .oc-modal--formula{ width:100vw; max-width:100vw; border-radius:0; border:none; }
}

/* Visualização matemática da Fórmula de BDI (V1.00.077) — gerada por
   parser+AST no JS (assets/js/bdi-formula-render.js), sem biblioteca
   externa. Usa var()/currentColor → respeita o dark mode automaticamente. */
.oc-math-titulo{ font-size:.82rem; color:var(--text-muted); margin:12px 0 4px; }
.oc-math{
  overflow-x:auto;               /* rolagem horizontal só se não couber (mobile) */
  padding:14px 12px; border:1px solid var(--border); border-radius:8px;
  background:var(--surface); text-align:center; color:var(--text);
}
.oc-math-expr{
  display:inline-flex; align-items:center; gap:6px;
  font-size:1.05rem; line-height:1.25; white-space:nowrap;
}
.oc-math-bdi{ font-weight:600; margin-right:4px; }
.oc-math-row{ display:inline-flex; align-items:center; gap:4px; }
.oc-math-group{ display:inline-flex; align-items:center; }
.oc-math-op{ margin:0 3px; opacity:.85; }
.oc-math-paren{ opacity:.7; }
.oc-math-var{ font-style:italic; font-weight:600; }
.oc-math-num-lit{ }
/* Fração: numerador sobre denominador com barra (border-top = currentColor). */
.oc-math-frac{ display:inline-flex; flex-direction:column; align-items:stretch; text-align:center; vertical-align:middle; margin:0 6px; }
.oc-math-num{ display:inline-flex; align-items:center; justify-content:center; padding:0 12px 5px; }
.oc-math-den{ display:inline-flex; align-items:center; justify-content:center; padding:5px 12px 0; }
.oc-math-bar{ width:100%; border-top:1.5px solid currentColor; }
.oc-math-msg{ color:var(--text-muted); font-size:.92rem; }
.oc-math--erro .oc-math-msg{ color:#b3261e; }

/* "Valores atuais" — grid compacto, data-driven, atualizado ao vivo. */
.oc-bdi-valores-grid{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(180px, 1fr));
  gap:6px 14px; margin-top:4px;
}
.oc-bdi-valor-item{ display:flex; align-items:baseline; gap:6px; font-size:.9rem; }
.oc-bdi-valor-cod{ font-weight:700; font-style:italic; min-width:2.5em; }
.oc-bdi-valor-desc{ color:var(--text-muted); flex:1 1 auto; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.oc-bdi-valor-num{ font-variant-numeric:tabular-nums; font-weight:600; }
.oc-formula-token-val{ font-weight:400; opacity:.75; font-size:.85em; margin-left:2px; }

/* Árvore de EAP + Serviços aninhados (itens-orcamento.php) — cada nó da
   EAP é um <details> nativo, aninhado DE VERDADE no DOM dentro do
   <details> do pai (não uma lista plana só indentada por CSS): recolher
   um nó já esconde nativamente tudo dentro dele (filhos + Serviços), sem
   nenhum JavaScript de layout (reforçado via JS no toggle por segurança,
   ver itens-orcamento.php). Serviços NUNCA são <details> — nunca ganham
   marcador +/-.

   V1.00.040 — os códigos NÃO são mais indentados por nível. Toda linha
   (nó de EAP, linha vazia, cabeçalho ou Serviço — ".oc-eap-linha") usa o
   MESMO grid de colunas de largura fixa, definido uma única vez como
   custom properties em .oc-eap-arvore e herdado por qualquer descendente
   (CSS custom properties atravessam o aninhamento normalmente, então uma
   ".oc-eap-linha" 5 níveis abaixo tem exatamente o mesmo
   grid-template-columns que uma no nível 0) — por isso Código/Descrição/
   demais colunas ficam sempre na mesma posição horizontal, qualquer que
   seja a profundidade de aninhamento no DOM. A hierarquia continua 100%
   real (orcamento_eap.parent_id / <details> aninhado, inalterados) — só
   deixou de ser expressa por recuo horizontal; a diferença visual entre
   nó de EAP e Serviço agora vem do estilo (marcador +/- + tag colorida +
   fundo discreto vs. texto simples sem marcador), nunca do recuo. */
.oc-eap-arvore{
  /* Comporta [caixa de selecao][seta] na linha de ETAPA e so [caixa] na de
     Servico. Alargar esta coluna e o que permite a etapa ter os DOIS
     controles sem a arvore ganhar uma 10a coluna (§70/§85) — e os codigos
     seguem alinhados entre etapa e Servico, que e o ponto da §70. */
  --oc-eap-c-marcador:48px;
  --oc-eap-c-codigo:132px;
  --oc-eap-c-cc:150px; /* nome do Centro de Custo, não só o código (V1.00.050) — pode quebrar linha em nomes longos, mesmo tratamento já dado à Descrição */
  --oc-eap-c-unidade:56px;
  --oc-eap-c-qtd:96px; /* link discreto, 4 casas decimais (V1.00.046 — Quantidade abre editar-item-orcamento.php) */
  --oc-eap-c-preco:104px;
  --oc-eap-c-valor:112px;
  --oc-eap-c-acoes:64px;
  display:flex;
  flex-direction:column;
  gap:2px;
  min-width:1012px; /* rolagem horizontal controlada pelo .oc-table-wrap ao redor, nunca overflow da página inteira */
}
@media (max-width:600px){
  .oc-eap-arvore{
    --oc-eap-c-codigo:104px;
    --oc-eap-c-cc:110px;
    --oc-eap-c-qtd:84px;
    min-width:760px;
  }
}
/* V1.00.058 — evita o "flash" de restaurar o estado expandido/recolhido
   salvo (assets/js/eap-state.js): a árvore nasce OCULTA
   (visibility:hidden, nunca display:none — precisa continuar contando
   layout/altura pro scroll ser calculado corretamente enquanto ainda
   oculta) e só é revelada pelo JS depois de aplicar o estado + o scroll.
   Rede de segurança 100% CSS: se o JS nunca rodar (bloqueado, erro,
   arquivo não carregou), a animação com delay revela sozinha depois de
   2s, no estado padrão do servidor — nunca fica invisível pra sempre.
   <noscript> (itens-orcamento.php) cobre o caso de JS desabilitado de
   propósito, revelando na hora. */
.oc-eap-arvore[data-eap-ready="false"]{
  visibility:hidden;
  animation:oc-eap-revelar-fallback 0s 2s forwards;
}
@keyframes oc-eap-revelar-fallback{
  to{ visibility:visible; }
}
/* V1.00.055 — a coluna Preço Unitário/Valor Total é sempre uma SÓ (grid
   fixo de 9 colunas, nunca 10): quando o filtro por Tipo de Insumo está
   ativo, ela passa a mostrar "PU/Valor no Tipo" NO MESMO lugar, nunca uma
   coluna adicional ao lado — nunca as duas versões simultaneamente
   (itens-orcamento.php escolhe o conteúdo, o grid nunca muda). */
.oc-eap-linha{
  display:grid;
  grid-template-columns:
    var(--oc-eap-c-marcador) var(--oc-eap-c-codigo) minmax(180px,1fr)
    var(--oc-eap-c-cc) var(--oc-eap-c-unidade) var(--oc-eap-c-qtd)
    var(--oc-eap-c-preco) var(--oc-eap-c-valor) var(--oc-eap-c-acoes);
  column-gap:10px;
  align-items:start; /* código/marcador ficam ancorados no topo mesmo quando a Descrição quebra em várias linhas */
  padding:6px 4px;
}
.oc-eap-linha-cabecalho{
  font-size:12px;
  font-weight:600;
  color:var(--text-muted);
  text-transform:uppercase;
  letter-spacing:.02em;
  padding-bottom:8px;
  margin-bottom:2px;
  border-bottom:1px solid var(--border);
}
.oc-eap-no-item > summary.oc-eap-linha{
  cursor:pointer;
  list-style:none;
  background:var(--bg);
  border-radius:6px;
}
.oc-eap-no-item > summary::-webkit-details-marker{
  display:none;
}
.oc-eap-linha-vazia{
  color:var(--text-muted);
}
.oc-eap-linha-servico{
  border-bottom:1px dashed var(--border);
}

/* ---- Serviço com cálculo incompleto (preço faltando em algum componente) ----
   Deixou de ser erro de validação (não bloqueia mais o fechamento da versão),
   então o destaque na listagem virou o canal principal do aviso: quem orça
   precisa enxergar a linha antes de decidir seguir com ela.

   ⚠️ As três cores são EXATAMENTE as de `.oc-btn-danger` (o botão "Excluir não
   utilizadas") — mesmo vermelho de texto, mesmo rosa de fundo, mesma borda.
   Repetir os valores aqui já é a segunda cópia; uma terceira variação de
   vermelho no sistema é o que esta nota existe para impedir.

   O fundo vai na LINHA e não nas células: `.oc-eap-linha` é o grid inteiro, e
   pintar célula a célula deixaria faixas brancas nos gaps entre colunas. */
.oc-eap-linha-servico.oc-eap-linha-incompleta{
  background:#fdf4f3;
  border-left:3px solid #b3261e;
  border-bottom-color:#f0d3d1;
}
.oc-eap-linha-servico.oc-eap-linha-incompleta .oc-eap-c-codigo,
.oc-eap-linha-servico.oc-eap-linha-incompleta .oc-eap-c-desc{
  color:#b3261e;
}
/* O link da origem herda o vermelho da linha em vez do cinza padrão — sem isto
   o código e a descrição, que SÃO links, ficariam fora do destaque. */
.oc-eap-linha-servico.oc-eap-linha-incompleta .oc-link-origem{
  color:#b3261e;
}
.oc-eap-filhos{
  margin:0;
  padding:0;
  border:0;
}
.oc-eap-c-codigo{
  font-weight:500;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.oc-eap-linha-servico .oc-eap-c-codigo{
  font-family:ui-monospace,SFMono-Regular,Consolas,"Liberation Mono",monospace;
  font-size:12.5px;
  font-weight:400;
}
.oc-eap-c-desc{
  min-width:0;
}
.oc-eap-c-cc,
.oc-eap-c-unidade,
.oc-eap-c-qtd,
.oc-eap-c-preco,
.oc-eap-c-valor{
  text-align:right;
  font-variant-numeric:tabular-nums;
}
.oc-eap-c-valor{
  font-weight:600;
}
.oc-eap-c-marcador{
  display:flex;
  align-items:center;
  gap:4px;
  /* ⚠️ CENTRADO NA LINHA, nao ancorado no topo.
     A linha usa `align-items:start` para o CODIGO ficar alinhado ao topo
     quando a Descricao quebra em varias linhas — util para o codigo, que
     e texto e le junto com a primeira linha da descricao. A caixa de
     selecao e a seta nao sao texto: presas no topo de uma linha de tres
     alturas, ficam soltas, longe do centro visual do que representam.
     `align-self` sobrepoe o `align-items` do grid so nesta celula. */
  align-self:center;
}
/* Linha de ETAPA: caixa + seta. A caixa vem primeiro, a seta depois —
   {controle} {seta} {nome}. */
.oc-eap-marcador-wrap{
  display:flex;
  align-items:center;
  gap:4px;
}
/* ⚠️ A caixa da etapa NAO pode carregar o cursor de expandir que o
   <summary> aplica a linha inteira: ela tem funcao propria e nao abre nada. */
.oc-etapa-selecionar{
  cursor:pointer;
  margin:0;
  flex:0 0 auto;
}

/* ============ CAIXAS DE SELECAO A 150% ============
   O padrao do navegador (~13px) e pequeno demais para um alvo que se clica
   dezenas de vezes seguidas ao montar um lote. 1,5x = ~20px.

   ⚠️ `width`/`height`, nunca `transform:scale()`. O scale desenha maior mas
   NAO aumenta a caixa de acerto do clique nem reserva espaco no layout — o
   controle pareceria maior e continuaria dificil de acertar, e ainda
   sobreporia o vizinho. Com width/height o alvo cresce de verdade.

   `accent-color` mantem a cor de marcado igual a do resto do sistema. */
.oc-item-selecionar,
.oc-etapa-selecionar,
#oc-selecionar-todos-lista{
  width:20px;
  height:20px;
  accent-color:var(--primary);
}
.oc-eap-c-marcador input[type="checkbox"]{
  margin:0;
}

/* Marcador +/- customizado (item 1/4/8 do pedido original da árvore) —
   nunca o triângulo padrão do navegador, nunca usado como "adicionar"
   (isso continua em botões separados, "+ Adicionar Item"/"Gerenciar
   Estrutura Analítica"). Puro CSS, sincronizado automaticamente com o
   estado nativo aberto/fechado do <details> — não precisa de JS pra
   alternar o símbolo. */
/* ============ MARCADOR DE ETAPA EXPANSÍVEL ============
   Glifos definidos em `:root` (--oc-seta-fechado / --oc-seta-aberto), UM lugar
   só para todo elemento hierárquico do sistema. Antes cada estrutura escolhia
   o seu: `+`/`−` aqui, `›` rotacionado na sidebar — a mesma função com dois
   desenhos, em duas telas. Trocar o padrão agora é editar duas linhas. */
.oc-eap-marcador{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  width:18px;
  height:18px;
  flex:0 0 auto;
  /* O chevron vazado nao tem o peso do triangulo cheio: volta ao tamanho de
     texto normal, senao fica fino demais para enxergar. */
  font-size:13px;
  line-height:1;
  color:var(--text-muted);
}
.oc-eap-marcador::before{
  content:var(--oc-seta-fechado);
}
.oc-eap-no-item[open] > summary .oc-eap-marcador::before{
  content:var(--oc-seta-aberto);
}

/* Tabela compacta de Serviços dentro de um nó — mesmas regras de
   overflow-x:auto/scroll do .oc-table normal, só com padding reduzido. */
.oc-table-sm th,
.oc-table-sm td{
  padding:6px 8px;
  font-size:13px;
}

table.oc-table{
  width:100%;
  border-collapse:collapse;
  font-size:13.5px;
}

/* ============ DENSIDADE DA LINHA ============
   Era `10px 12px`. Medido em composicoes.php (1280px): a linha tinha 54px de
   altura para exibir um texto de 18px — os outros 36px eram padding (20px) e,
   sobretudo, o botão de ação de 32px, que é quem realmente ditava a altura.
   Dez registros consumiam 569px de tela.

   `7px 10px` devolve ~8px por linha sem apertar: sobra folga acima e abaixo do
   texto, e as badges (`.oc-tag`, `.oc-status`, que têm padding próprio de 2px)
   continuam sem encostar na borda. Reduzir mais faria a tabela parecer uma
   planilha; menos não resolveria o vazio que motivou a mudança. */
/* ⚠️ TOKEN, e não um número solto. A barra de cima ("90 registro(s)
   encontrado(s)") precisa começar exatamente onde começa a primeira
   coluna. Enquanto o recuo da célula for um literal aqui e outro lá, os
   dois divergem no primeiro ajuste — e foi o que aconteceu: o contador
   ficava 10px à esquerda do "CÓDIGO". Com o token, a barra herda o
   MESMO valor, inclusive quando o modo compacto o reduz. */
:root{ --oc-tabela-padding-x:10px; }

table.oc-table th,
table.oc-table td{
  text-align:left;
  padding:7px var(--oc-tabela-padding-x);
  border-bottom:1px solid var(--border);
  white-space:nowrap;
  /* ⚠️ EXPLÍCITO, e não por acaso. O valor inicial de `vertical-align` é
     `baseline`: com uma descrição de três linhas ao lado, as células curtas
     grudavam na PRIMEIRA linha do texto e o registro parecia desalinhado,
     mesmo com todas as células na altura certa da linha. `middle` centra o
     conteúdo curto na altura do registro, que é o que a leitura de uma tabela
     espera.

     A Descrição é a exceção logo abaixo: texto longo centrado verticalmente
     afasta a primeira linha do topo e desencontra do rótulo da coluna. */
  vertical-align:middle;
}

/* Colunas de texto longo ancoram no topo. `data-tm-key` cobre as listagens
   principais; `.oc-col-topo` serve às tabelas sem gerenciador de colunas. */
table.oc-table td[data-tm-key="descricao"],
table.oc-table td.oc-col-topo{
  vertical-align:top;
}

table.oc-table th{
  color:var(--text-muted);
  font-weight:600;
  font-size:12px;
  text-transform:uppercase;
  letter-spacing:.04em;
}

table.oc-table tbody tr:hover{
  background:#fafbfd;
}

/* Célula monetária contábil (reutilizável): bloco compacto "R$ 22.400,00"
   alinhado à direita da coluna. inline-grid de 2 colunas com column-gap
   pequeno (não space-between, que espalhava R$ e valor às bordas). O
   wrapper é um span (não o próprio <td>) para o <td> continuar sendo
   table-cell (largura/resize/oc-tm-hidden intactos); a <td> alinha à
   direita para o bloco encostar na direita e os números terminarem na
   mesma posição. Sem espaços/&nbsp; manuais. */
/* ⚠️ O prefixo `table.` NÃO é decoração: `table.oc-table td` (0,1,2) declara
   `text-align:left` e vencia `td.oc-money-cell` (0,1,1). O alinhamento à
   direita que o comentário acima descreve estava declarado e inerte — em TODAS
   as listagens, não só numa. Medido em explodir-composicao.php: `text-align`
   computado da célula de valor = `left`.
   O `.oc-money` é inline-grid, então R$ e número continuavam colados um ao
   outro; o que se perdia era a coluna de números terminar na mesma posição —
   exatamente o que torna uma coluna de dinheiro conferível de relance. */
table.oc-table td.oc-money-cell{
  text-align:right;
}

/* Célula numérica não-monetária (coeficiente, quantidade, percentual).
   Mesma ideia da monetária, sem o bloco de moeda: à direita e com dígitos de
   largura fixa, para as casas decimais empilharem verticalmente. Opt-in por
   classe — nenhuma coluna passa a ser numérica sozinha. */
table.oc-table td.oc-num-cell{
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* Cabeçalho de coluna numérica acompanha os valores. Sem isto o rótulo fica na
   esquerda e a coluna de números na direita — foi o que a tela de explosão
   mostrava com "Coeficiente acumulado" a 184px do próprio número. */
table.oc-table th.oc-num-col{
  text-align:right;
}

/* Coluna de TEXTO alinhada à direita (utilitária, opt-in por classe).
   Distinta de `.oc-num-cell`: aqui não entra `tabular-nums` — isso é para
   dígito, e forçá-lo em texto só desalinharia as letras.

   ⚠️ O prefixo `table.oc-table` não é decoração, é o que faz a regra valer:
   `table.oc-table th, table.oc-table td` declara `text-align:left` com
   especificidade (0,1,2), e uma utilitária solta `.oc-text-right` (0,1,0)
   perderia — ficaria escrita no arquivo e inerte na tela, exatamente o que já
   aconteceu com `.oc-money-cell` (ver o bloco acima).

   Vale para <th> e <td> juntos de propósito: alinhar só o cabeçalho, ou só as
   células, produz duas bordas direitas diferentes na mesma coluna. */
table.oc-table th.oc-text-right,
table.oc-table td.oc-text-right{
  text-align:right;
}

/* O conteúdo interno herda o eixo da célula. Sem isto, um wrapper com
   alinhamento próprio (é o caso de `.oc-cc-trigger`, um <button>) continua
   mandando no texto quando a caixa dele ocupa a largura toda da coluna —
   o alinhamento parece funcionar em texto curto e falhar no que quebra. */
table.oc-table th.oc-text-right > *,
table.oc-table td.oc-text-right > *{
  text-align:inherit;
}

/* ============================================================
   COLUNAS CENTRALIZADAS POR SEMÂNTICA  (padrão global)
   ============================================================
   Código, Categoria, Unidade, Status e Ações são centralizados — cabeçalho E
   células, sempre no mesmo eixo. Descrição, Nome, Cliente, Obra e o resto do
   texto continuam à esquerda, que é o padrão da tabela e não precisa de regra.

   ⚠️ O GANCHO É `data-tm-key`, NÃO A POSIÇÃO DA COLUNA.
   Cada listagem já marca `<th>` e `<td>` com a chave semântica da coluna — o
   mesmo atributo que o gerenciador de colunas usa para esconder e reordenar.
   Usá-lo aqui resolve de uma vez três coisas que um `:nth-child` não resolve:

     · a posição varia entre tabelas (Código é a 1ª em Unidades, a 2ª em
       Medições) e um seletor por índice acertaria numa e erraria na outra;
     · o usuário pode ESCONDER colunas pelo botão "Colunas", e os índices
       mudam debaixo do CSS;
     · o alinhamento passa a ser propriedade da coluna, não do lugar dela.

   Nenhuma página precisou de markup novo: o atributo já existia em todas as
   listagens principais. Para as poucas tabelas que não o têm, existe a classe
   `.oc-col-centro` logo abaixo — mesmo efeito, declarada à mão.

   ⚠️ O prefixo `table.oc-table` não é decoração. A regra base
   `table.oc-table th, table.oc-table td` declara `text-align:left` com
   especificidade (0,1,2); um seletor de atributo solto (0,1,0) perderia e
   ficaria escrito no arquivo sem efeito na tela — foi exatamente o que já
   aconteceu com `.oc-money-cell` (ver o bloco acima). */
table.oc-table th[data-tm-key="codigo"],
table.oc-table td[data-tm-key="codigo"],
table.oc-table th[data-tm-key="categoria"],
table.oc-table td[data-tm-key="categoria"],
table.oc-table th[data-tm-key="unidade"],
table.oc-table td[data-tm-key="unidade"],
table.oc-table th[data-tm-key="tipo"],
table.oc-table td[data-tm-key="tipo"],
table.oc-table th[data-tm-key="uf"],
table.oc-table td[data-tm-key="uf"],
table.oc-table th[data-tm-key="status"],
table.oc-table td[data-tm-key="status"],
table.oc-table th[data-tm-key="acoes"],
table.oc-table td[data-tm-key="acoes"],
table.oc-table th.oc-col-centro,
table.oc-table td.oc-col-centro{
  text-align:center;
}

/* O conteúdo interno herda o eixo da célula.
   Sem isto, um wrapper de bloco dentro da célula (um <div>, um <form> de ação)
   mantém o alinhamento próprio e o texto continua à esquerda dentro de uma
   célula centralizada — o alinhamento parece funcionar em valor curto e falhar
   no que ocupa a largura toda. Mesmo raciocínio da regra de `.oc-text-right`.

   ⚠️ Isto é o que mantém o §23 de pé: o cabeçalho de coluna ordenável é um
   `<a>Código ▲</a>`, e texto + seta permanecem centralizados COMO CONJUNTO. */
table.oc-table th[data-tm-key="codigo"] > *,
table.oc-table td[data-tm-key="codigo"] > *,
table.oc-table th[data-tm-key="categoria"] > *,
table.oc-table td[data-tm-key="categoria"] > *,
table.oc-table th[data-tm-key="unidade"] > *,
table.oc-table td[data-tm-key="unidade"] > *,
table.oc-table th[data-tm-key="tipo"] > *,
table.oc-table td[data-tm-key="tipo"] > *,
table.oc-table th[data-tm-key="uf"] > *,
table.oc-table td[data-tm-key="uf"] > *,
table.oc-table th[data-tm-key="status"] > *,
table.oc-table td[data-tm-key="status"] > *,
table.oc-table th[data-tm-key="acoes"] > *,
table.oc-table th.oc-col-centro > *,
table.oc-table td.oc-col-centro > *{
  text-align:inherit;
}

/* ============ OPT-OUT: VOLTAR UMA COLUNA PARA A ESQUERDA ============
   Escape hatch para uma coluna que, apesar do nome, não deve ser centralizada.

   ⚠️ O caso que motivou a classe foi o Código de Centros de Custo, uma ÁRVORE:
   cada linha recebe `padding-left: nivel * 20px`, e é o recuo que mostra o que
   é filho de quê. **A pedido, aquela coluna passou a ser centralizada como as
   demais** — o recuo continua existindo (o `padding-left` está no `<span>`, que
   é inline e centraliza junto com ele), mas o deslocamento visual entre níveis
   cai pela metade: metade do recuo é absorvida pela centralização.

   A classe fica disponível: uma coluna de código com hierarquia mais profunda,
   onde a metade do recuo não bastar para distinguir os níveis, resolve com ela
   sem tocar na regra global.

   A regra vem DEPOIS da centralização e com a mesma especificidade (0,2,2),
   então quem declara a classe volta para a esquerda. Vale para `<th>` e `<td>`
   juntos: alinhar só um dos dois tira o cabeçalho do eixo das células, que é
   justamente o defeito que esta tarefa veio corrigir. */
table.oc-table th.oc-col-esquerda,
table.oc-table td.oc-col-esquerda{
  text-align:left;
}
.oc-money{
  display:inline-grid;
  grid-template-columns:auto auto;
  align-items:baseline;
  column-gap:6px;
  white-space:nowrap;
  text-align:right;
}
.oc-money-symbol{
  color:var(--text-muted);
}
.oc-money-value{
  text-align:right;
  font-variant-numeric:tabular-nums;
}

.oc-tag{
  display:inline-block;
  font-size:11px;
  font-weight:600;
  padding:2px 8px;
  border-radius:999px;
  background:#f1f2f4;
  color:var(--text-muted);
}

.oc-tag-primary{
  background:#eef2ff;
  color:var(--primary-dark);
}

.oc-note{
  font-size:12.5px;
  color:var(--text-muted);
  margin-top:12px;
}

.oc-checklist{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:6px 24px;
  margin:0;
  padding:0;
  list-style:none;
  font-size:13.5px;
}

.oc-checklist li{
  display:flex;
  align-items:center;
  gap:8px;
}

.oc-check-ok{color:#1a8f4c;font-weight:700;}
.oc-check-pending{color:#b4b9c4;font-weight:700;}

/* Botões — CENTRALIZADOS GLOBALMENTE */
.oc-btn{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:6px;
  padding:9px 16px;
  min-height:36px;
  border-radius:8px;
  border:1px solid var(--border);
  background:var(--surface);
  color:var(--text);
  font-family:inherit;  /* <button> e <a> com .oc-btn tipografam idêntico (não herda por padrão em <button>) */
  font-size:13.5px;
  font-weight:600;
  cursor:pointer;
  text-decoration:none;
  text-align:center;
}

.oc-btn:hover{
  border-color:var(--primary);
  text-decoration:none;
}

.oc-btn:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

/* Ícone + label reutilizável (V1.00.073 — padronização de botões) —
   ocBtnConteudo() (includes/layout.php) gera os dois spans dentro de
   QUALQUER .oc-btn existente; nunca um componente de botão novo/paralelo.
   Ícone e label são centralizados dentro do flex container pai (.oc-btn). */
.oc-btn-icon{
  display:inline-flex;
  align-items:center;
  justify-content:center;
  flex-shrink:0;
  font-size:14px;
  line-height:1;
  color:inherit;  /* Herda cor do botão pai — garante branco em primary, etc. */
}
/* Ícone SVG (Importar/Exportar, V1.00.485): o traço é `currentColor`, então a
   cor vem do item — hover, foco e botão primário sem regra própria. `block`
   remove o espaço de linha de base que o <svg> inline carregaria, e é o que
   mantém o desenho centrado na mesma altura dos ícones de glifo ao lado. */
.oc-icone-svg{
  display:block;
  width:16px;
  height:16px;
}

.oc-btn-label{
  white-space:nowrap;
  display:flex;
  align-items:center;
  justify-content:center;
}

.oc-btn-primary{
  background:var(--primary);
  border-color:var(--primary);
  color:#fff;
}

.oc-btn-primary:hover{
  background:var(--primary-dark);
  border-color:var(--primary-dark);
}

.oc-btn-danger{
  color:#b3261e;
  border-color:#f0d3d1;
  background:#fdf4f3;
}

.oc-btn-danger:hover{
  border-color:#b3261e;
}

/* Botão Cancelar — reutiliza visual de atenção/secundário */
.oc-btn-cancel{
  color:#b3261e;
  border-color:#f0d3d1;
  background:#fdf4f3;
}

.oc-btn-cancel:hover{
  border-color:#b3261e;
}

.oc-btn-sm{
  padding:5px 10px;
  font-size:12.5px;
}

/* Ação só com ícone — botão quadrado usado nas colunas "Ações" (Editar/
   Inativar/Ativar/Excluir/Editar Fator). Reutiliza .oc-btn/.oc-btn-icon
   (herança de cor, hover, focus-visible) — nunca um componente novo. O ícone
   é centralizado pelo flex do .oc-btn; o nome acessível vem do aria-label do
   botão e o desenho é aria-hidden. Padrão único, compartilhado entre páginas. */
.oc-btn-icon-only{
  width:32px;
  min-width:32px;
  height:32px;
  min-height:32px;
  padding:0;
  gap:0;
}
.oc-btn-icon-only .oc-btn-icon{
  font-size:15px;
}

/* Flash messages */
.oc-flash{
  padding:11px 14px;
  border-radius:8px;
  font-size:13.5px;
  margin-bottom:18px;
  border:1px solid transparent;
}

.oc-flash-success{
  background:#eafaf0;
  color:#166534;
  border-color:#bfe8cf;
}

.oc-flash-error{
  background:#fdf2f2;
  color:#a12622;
  border-color:#f4c6c3;
}

/* Aviso — usado pelo alerta de fim do período de avaliação. Âmbar, não
   vermelho: é um lembrete com prazo, não um erro. */
.oc-flash-warning{
  background:#fdf6e3;
  color:#8a5a00;
  border-color:#f0d9a0;
}
.oc-flash-warning a{
  color:inherit;
  font-weight:600;
}

/* Formulários */
.oc-form{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(220px,1fr));
  gap:16px 18px;
  max-width:760px;
}

.oc-field{
  display:flex;
  flex-direction:column;
  gap:6px;
  min-width:0; /* item de grid — sem isso, um filho largo (ex.: select
                  com option de texto longo) pode empurrar a largura do
                  próprio grid, não só do campo. */
}

.oc-field-full{
  grid-column:1 / -1;
}

/* Grade de detalhes (telas visualizar-*.php) */
.oc-detail-grid{
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
  gap:14px 24px;
  margin:0;
}

.oc-detail-grid dt{
  font-size:12.5px;
  font-weight:600;
  color:var(--text-muted);
  margin-bottom:2px;
}

.oc-detail-grid dd{
  margin:0;
  font-size:14px;
  color:var(--text);
}

.oc-field label{
  font-size:12.5px;
  font-weight:600;
  color:var(--text-muted);
}

.oc-field label:has(input[type="checkbox"]){
  display:flex;
  align-items:center;
  gap:8px;
  font-size:13.5px;
  font-weight:500;
  color:var(--text);
  cursor:pointer;
}

.oc-field label:has(input[type="checkbox"]) input{
  width:auto;
}

.oc-radio-group{
  display:flex;
  gap:16px;
  align-items:center;
  padding-top:9px;
}

.oc-radio-group label{
  display:flex;
  align-items:center;
  gap:6px;
  font-size:13.5px;
  font-weight:500;
  color:var(--text);
  cursor:pointer;
}

.oc-radio-group input{
  width:auto;
}

/* Lista pesquisável de Centro de Custo (editar-centro-custo-item.php,
   V1.00.056) — evita <select> gigante quando há muitos Centros; filtro por
   código/descrição feito em JS puro (data-busca), nenhuma biblioteca nova. */
.oc-cc-lista{
  max-height:260px;
  overflow-y:auto;
  border:1px solid var(--border);
  border-radius:8px;
  padding:4px;
  margin-top:10px;
  display:flex;
  flex-direction:column;
  gap:2px;
}
.oc-cc-opcao{
  display:flex;
  align-items:center;
  gap:8px;
  padding:8px 10px;
  border-radius:6px;
  cursor:pointer;
  font-size:13.5px;
}
.oc-cc-opcao:hover{
  background:var(--bg);
}
.oc-cc-opcao input{
  width:auto;
  flex-shrink:0;
}

/* ============ APARÊNCIA DOS CAMPOS — UMA DEFINIÇÃO SÓ ============
   ⚠️ `.oc-filtros-linha-busca input` (o Buscar de Composições) entra AQUI,
   e não numa regra própria. Aquele campo vive num wrapper de LAYOUT
   diferente (rótulo ao lado, não acima), então ficava fora de `.oc-field`
   e renderizava com o estilo PADRÃO DO NAVEGADOR: borda `2px inset`,
   cantos retos, Arial 13.33px, preto puro — visivelmente diferente do
   mesmo campo em Insumos.

   Copiar as propriedades para uma regra nova resolveria a tela de hoje e
   criaria duas fontes da mesma verdade: o próximo ajuste de borda/raio/
   foco valeria só numa metade do sistema. A LARGURA continua fora daqui,
   no wrapper de cada página (ver `.oc-filtros-linha-busca input`). */
.oc-field input,
.oc-field select,
.oc-field textarea,
.oc-filtros-linha-busca input,
.oc-filtros-linha-busca select{
  /* width:100%+min-width:0 evita que um <select> com uma option de texto
     muito longo (ex.: descrição de Insumo/Composição) empurre a largura
     do campo além do container — sem isso, o item de grid/flex assume a
     largura intrínseca do conteúdo e pode alargar a página inteira. O
     <select> fechado trunca a option comprida (não quebra o layout); o
     texto completo continua acessível ao abrir o dropdown. */
  width:100%;
  min-width:0;
  padding:9px 11px;
  border-radius:8px;
  border:1px solid var(--border);
  font-size:14px;
  font-family:inherit;
  background:#fff;
  color:var(--text);
}

.oc-field input:focus,
.oc-field select:focus,
.oc-field textarea:focus,
.oc-filtros-linha-busca input:focus,
.oc-filtros-linha-busca select:focus{
  outline:none;
  border-color:var(--primary);
  box-shadow:0 0 0 3px rgba(29,95,214,.12);
}

.oc-field input:disabled,
.oc-filtros-linha-busca input:disabled,
.oc-filtros-linha-busca select:disabled{
  background:var(--bg);
  color:var(--text-muted);
  cursor:not-allowed;
}

.oc-field-error{
  font-size:12px;
  color:#b3261e;
}

.oc-form-actions{
  grid-column:1 / -1;
  display:flex;
  gap:10px;
  margin-top:4px;
}

/* Status */
.oc-status{
  display:inline-block;
  font-size:11px;
  font-weight:700;
  padding:2px 9px;
  border-radius:999px;
}

.oc-status-ativo{
  background:#eafaf0;
  color:#166534;
}

.oc-status-inativo{
  background:#f1f2f4;
  color:var(--text-muted);
}

/* ============ A CÉLULA DE AÇÕES VOLTA A SER UMA CÉLULA ============
   ⚠️ ESTA REGRA DECLARAVA `display:flex` NO PRÓPRIO `<td>`, e era a causa das
   linhas "quebradas".

   Um `<td>` com `display:flex` deixa de ser `table-cell`: sai do algoritmo de
   linha da tabela e vira uma caixa independente. Três consequências, e todas
   apareciam na tela:

     · a célula NÃO estica até a altura da linha. Com uma descrição de três
       linhas ao lado, os botões ficavam na altura deles e "flutuavam" entre um
       registro e outro;
     · `vertical-align` vira inerte — não havia como centralizá-los;
     · o `border-bottom` é desenhado na altura da CAIXA, não da linha, então as
       divisórias horizontais deixavam de coincidir de ponta a ponta.

   O flex estava ali por um motivo real: `.oc-btn` é `display:flex` (bloco), e
   numa célula de verdade blocos EMPILHAM verticalmente. A correção não é tirar
   o flex e deixar os botões empilhados — é tirá-lo do `<td>` e pôr os BOTÕES em
   `inline-flex`, o que os deixa lado a lado sem custar a semântica da tabela.

   O espaçamento entre eles deixa de vir de `gap` (que exige contêiner flex) e
   passa a ser margem ENTRE irmãos — `* + *`, então não sobra margem nas pontas
   e a centralização segue exata com qualquer quantidade de botões.

   ⚠️ O seletor é `td.oc-actions-cell`, sem prefixo de tabela: a classe também é
   usada em `partials/titulos-lista-view.php`, que tem tabela própria
   (`.oc-tit-tab`). Prender a correção a `table.oc-table` deixaria aquela tela
   com o defeito. */
td.oc-actions-cell{
  display:table-cell;
  vertical-align:middle;
  text-align:center;
  white-space:nowrap;
}

/* A regra base `table.oc-table th, table.oc-table td` declara `text-align:left`
   com especificidade (0,1,2) e venceria o `center` acima (0,1,1). */
table.oc-table td.oc-actions-cell{
  text-align:center;
}

td.oc-actions-cell .oc-btn{
  display:inline-flex;
  vertical-align:middle;
}

/* O <form> de uma ação é inline-level para os botões ficarem lado a lado na
   célula. Isto vinha de `style="display:inline;"` emitido por `ocAcaoForm()`;
   passou a ser esta classe porque o estilo inline vence a folha — inclusive o
   `display:none` do modo compacto, que precisa esconder a ação original quando
   ela já está no menu [⋮] da linha. Regra na folha, um estado pode sobrepor. */
.oc-acao-form{
  display:inline;
}

td.oc-actions-cell > form,
td.oc-actions-cell > span{
  vertical-align:middle;
}

td.oc-actions-cell > * + *{
  margin-left:6px;
}

/* Fora de uma célula de tabela a classe segue sendo um grupo flex — é o caso de
   barras de ação que não são `<td>`. */
.oc-actions-cell:not(td){
  display:flex;
  justify-content:center;
  align-items:center;
  gap:6px;
  white-space:nowrap;
}

/* ============ AÇÕES NÃO ROUBA LARGURA DA DESCRIÇÃO ============
   Os botões de ação são o que determinava a altura da linha: 32px de botão
   contra 18px de texto. Dentro de uma LINHA de tabela eles podem ser menores —
   continuam com alvo de clique confortável (28px, acima dos 24px mínimos de
   toque em ponteiro fino) e param de inflar a linha inteira.

   ⚠️ Escopo deliberado em `td.oc-actions-cell`: `.oc-btn-icon-only` é global e
   vale também para cabeçalhos, árvore de EAP e barras de ferramentas, onde o
   botão é elemento principal e 32px é o tamanho certo. Encolher a classe
   inteira para resolver a densidade de UMA tabela mexeria em telas que não têm
   esse problema. */
table.oc-table td.oc-actions-cell .oc-btn-icon-only{
  width:28px;
  min-width:28px;
  height:28px;
  min-height:28px;
}

/* Campo numérico com sufixo de unidade (ex.: Área [ 125,00 ] m²). Componente
   global reutilizado por editar-orcamento.php e editar-composicao.php. */
.oc-input-unit{ display:flex; align-items:stretch; }
.oc-input-unit input{ flex:1 1 auto; border-top-right-radius:0; border-bottom-right-radius:0; }
.oc-input-unit-suffix{ display:flex; align-items:center; padding:0 10px; border:1px solid var(--border); border-left:0; border-top-right-radius:6px; border-bottom-right-radius:6px; background:var(--surface-muted,var(--surface)); color:var(--text-muted); font-size:13px; }

.oc-empty{
  padding:30px 10px;
  text-align:center;
  color:var(--text-muted);
  font-size:13.5px;
}

/* Estado vazio com orientação e ação (includes/estado_vazio.php).
   Substitui o "Nenhum registro encontrado." solto, que deixava o usuário
   novo sem saber o próximo passo. */
.oc-empty-bloco{
  max-width:460px;
  margin:0 auto;
  padding:14px 0 6px;
}
.oc-empty-titulo{
  margin:0 0 6px;
  font-size:15px;
  font-weight:600;
  color:var(--text);
}
.oc-empty-texto{
  margin:0 0 18px;
  font-size:13.5px;
  color:var(--text-muted);
  line-height:1.5;
}
.oc-empty-acoes{
  display:flex;
  gap:10px;
  justify-content:center;
  flex-wrap:wrap;
}
@media (max-width:520px){
  .oc-empty-acoes{ flex-direction:column; }
  .oc-empty-acoes .oc-btn{ width:100%; }
}

/* Filtros recolhíveis — <details>/<summary> nativos (mesma técnica já
   usada em toda a árvore de EAP, itens-orcamento.php/visualizar-composicao.php):
   zero JS necessário pro toggle em si (teclado, clique, toque, estado
   acessível já vêm de graça do navegador), progressive enhancement
   automático (sem JS, o atributo `open` do HTML já decide o estado
   inicial), e recolhido nunca deixa espaço vazio (o navegador remove o
   conteúdo do fluxo de verdade, não só visualmente — nunca precisa de
   CSS pra "zerar altura"). assets/js/filtros.js só adiciona persistência
   OPCIONAL da preferência (item 9.10 do pedido) — puramente visual,
   nunca necessária pro componente funcionar. */
.oc-filtros summary{
  cursor:pointer;
  list-style:none;
  display:flex;
  align-items:center;
  gap:8px;
  padding:4px 6px;
  margin:-4px -6px;
  border-radius:8px;
  transition:background-color .15s;
}

.oc-filtros summary::-webkit-details-marker{
  display:none;
}

.oc-filtros summary:hover{
  background:var(--bg);
}

.oc-filtros summary:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

.oc-filtros summary::before{
  content:"▸";
  color:var(--text-muted);
  font-size:12px;
  transition:transform .15s;
  flex-shrink:0;
}

.oc-filtros[open] summary::before{
  transform:rotate(90deg);
}

.oc-filtros summary h2{
  margin:0;
  font-size:16px;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}

/* Indicador de filtros ativos no cabeçalho (item 9.7) — visível mesmo
   com o painel recolhido, porque fica dentro do <summary> (sempre
   visível) nunca dentro do conteúdo recolhível. Mesma linguagem visual
   de .oc-chip (achado no design system já existente), nunca uma cor
   nova. Calculado a partir de count($qsFiltro) — já filtrado nas
   páginas (exclui "Todos"/vazio/valor padrão), nenhuma lógica nova de
   backend precisou ser criada. */
.oc-filtros-badge{
  display:inline-flex;
  align-items:center;
  flex-shrink:0;
  padding:2px 8px;
  border-radius:999px;
  background:#eef2ff;
  color:var(--primary-dark);
  border:1px solid #d7e0ff;
  font-size:11.5px;
  font-weight:700;
  line-height:1.5;
}

/* Área do formulário de filtro — Flexbox com flex-wrap (V1.00.075),
   substituindo o CSS Grid de colunas fixas da V1.00.074.
   Causa raiz do defeito anterior (Grid): a decisão de quebra de linha
   do Grid é por ENCAIXE EXATO DE COLUNAS INTEIRAS — cada faixa de
   Container Query fixava um número de colunas (4/8/12/16) e cada campo
   um grid-column:span N também fixo. Isso é quantizado: em qualquer
   largura onde sobrassem, por exemplo, 2 de 16 colunas depois de
   Código+Símbolo+Descrição+Status, e o grupo Filtrar/Limpar precisasse
   de 5, o Grid não faz encaixe parcial — ele joga o span inteiro pra
   próxima linha, mesmo sobrando espaço em pixels reais na linha
   anterior. Isso gerava exatamente os dois defeitos reportados: um
   "buraco" no meio de uma linha cheia e Filtrar/Limpar sozinhos numa
   linha curta abaixo, com spans calibrados como "pior caso" genérico
   (pensados pra páginas com muito mais campos, ex. Composições) que
   nunca encaixavam certo na combinação real de 4 campos de Unidades.
   Flexbox com flex-wrap não sofre desse problema: a especificação
   decide quebra de linha usando o tamanho HIPOTÉTICO de cada item
   (flex-basis, em pixels contínuos) ANTES de qualquer flex-grow ser
   resolvido — o crescimento só é distribuído DEPOIS que o navegador já
   decidiu quais itens cabem em cada linha, então dar flex-grow ao campo
   de busca não desloca as ações da linha (ao contrário do que a
   quantização de colunas do Grid causava). Sem "múltiplo de coluna" pra
   bater, Filtrar/Limpar entra em qualquer linha que tenha os ~200px
   reais que precisa, e só quebra quando genuinamente não cabe mais
   nada — que é exatamente a regra do item 6 do pedido.
   Campos curtos (--sm) NUNCA recebem flex-grow (item 9: não esticar
   Código/Símbolo só pra preencher espaço à toa) — só o campo de busca
   cresce (com teto de max-width, pra não virar enorme em ultrawide).
   Ações (.oc-form-actions) tem flex:0 1 auto — largura de conteúdo,
   nunca vira bloco/coluna própria só por sobrar espaço (item 11).
   UM ÚNICO Container Query pro modo mobile (não mais 3 faixas) —
   simplificação deliberada (item 37 do pedido: "não adicione mais
   breakpoints sem entender o problema", preferir comportamento
   intrínseco): acima de 640px de largura REAL do painel, o fluxo é
   inteiramente intrínseco (nenhuma outra faixa necessária); abaixo
   disso, força coluna única — sem essa faixa, flex-wrap puro ainda
   deixaria 2 campos curtos lado a lado até ~300px de container, o que
   o pedido proíbe explicitamente no celular (item 15). Escopado SÓ
   dentro de .oc-filtros — nunca redefine .oc-form global (todo
   formulário de cadastro/edição do projeto, layout diferente por
   design). */
.oc-filtros{
  container-type:inline-size;
  container-name:oc-filtros;
}

.oc-filtros .oc-form{
  display:flex;
  flex-wrap:wrap;
  align-items:end;
  gap:14px 16px;
  width:100%;
  min-width:0;
  max-width:none;
  margin-top:14px;
}

/* Campo médio genérico (selects/textos comuns sem modificador de
   tamanho, ex.: Unidade/Insumo/Tipo em Composições) — cresce moderado
   se sobrar espaço na própria linha, com teto pra não dominar a linha
   sozinho. */
.oc-filtros .oc-field{
  flex:1 1 200px;
  max-width:340px;
  min-width:0;
}

/* Campo curto — Código, Símbolo, Status, UF, Categoria, valores
   mín/máx. flex-grow:0 de propósito (item 9 do pedido): nunca estica
   além do tamanho natural só porque sobra espaço — esse espaço deve ir
   pro campo de busca ou ficar como respiro no fim da linha. */
.oc-filtros .oc-field--sm{
  flex:0 1 130px;
}

/* Campo de busca/descrição — o único com prioridade real de
   crescimento (grow:2), com teto de largura pra não virar um campo
   gigante em monitor ultrawide. */
.oc-filtros .oc-field--busca{
  flex:2 1 240px;
  max-width:480px;
}

/* Filtrar/Limpar — largura de CONTEÚDO (flex:0 1 auto), nunca cresce
   nem vira bloco/coluna própria. Continua um wrapper único (item 20:
   nunca botões soltos em regiões diferentes) e flui pra qualquer linha
   com espaço real, exatamente como qualquer outro campo do grupo. */
.oc-filtros .oc-form-actions{
  flex:0 1 auto;
  display:flex;
  gap:10px;
  margin:0;
}

/* ============================================================
   Por que NÃO há regra específica de #oc-filtro-insumos aqui
   ============================================================
   Existia neste ponto um bloco `@container oc-filtros (max-width:1500px)` que
   dava `grid-column: span N !important` aos campos de Insumos, tentando forçar
   um layout de 2 linhas só para aquela página. Dois problemas:

     1. INERTE — `.oc-filtros .oc-form` é `display:flex`. `grid-column` não tem
        efeito nenhum num item de flex, nem com `!important`. O layout que a
        página mostrava nunca veio dessas regras;
     2. FRÁGIL — endereçava `.oc-field:nth-of-type(2..5)`, e os campos Base e
        Data-base só são renderizados quando existe competência cadastrada. Num
        cadastro sem histórico os índices andavam e as regras caíam em campos
        diferentes.

   Insumos e Composições usam o MESMO vocabulário: `--busca` para o campo de
   texto, `--sm` para os selects curtos, `.oc-field` puro para os médios
   (Tipo/Unidade). Quem distribui é o flex-wrap do componente, igual nas duas.
   Uma exceção por página é justamente o "terceiro padrão" que não deve existir. */

/* Mobile — abaixo de 640px de largura REAL do painel (Container Query,
   nunca viewport — mesma faixa já usada em .oc-topbar, V1.00.073),
   força coluna única: todo campo e o grupo de ações passam a 100% de
   largura (itens 13/14/15 do pedido), nunca duas colunas espremidas. */
@container oc-filtros (max-width:640px){
  .oc-filtros .oc-form{
    flex-direction:column;
    align-items:stretch;
  }
  .oc-filtros .oc-field,
  .oc-filtros .oc-field--sm,
  .oc-filtros .oc-field--busca{
    flex:1 1 auto;
    width:100%;
    max-width:none;
  }
  .oc-filtros .oc-form-actions{
    flex-direction:row;  /* MUDADO: buttons lado a lado, não empilhados */
    width:100%;
    gap:10px;
  }
  .oc-filtros .oc-form-actions .oc-btn{
    flex:1 1 0;  /* Distribui 50/50 */
    width:auto;
  }

  /* Insumos não precisa de exceção aqui: desde a V1.00.231 ele usa
     `.oc-filtros-grade`, que já vira uma coluna só até 600px. A regra que
     existia neste ponto forçava `display:flex !important` e brigaria com a
     grade entre 601 e 640px. */
}

@media (prefers-reduced-motion: reduce){
  .oc-filtros summary,
  .oc-filtros summary::before{
    transition:none;
  }
}

/* Unidades de Medida — filtros com layout responsivo preciso
   Desktop: [ Pesquisar 40% ] [ Status 20% ] [ Filtrar 20% + Limpar 20% ]
   Mobile:  [ Pesquisar 50% ] [ Status 50% ]
            [ Filtrar 50%   ] [ Limpar 50%   ] */

.oc-filtros #oc-filtro-unidades .oc-form {
  display: flex;
  flex-wrap: nowrap; /* Força 1 linha no desktop, não quebra */
  align-items: center;
  gap: 16px;
  width: 100%;
  min-width: 0;
  max-width: none;
  margin-top: 14px;
}

/* Usa @media query com precedência igual/maior para mobile */
@media (max-width: 768px) {
  .oc-filtros #oc-filtro-unidades .oc-form {
    flex-direction: row !important;
    flex-wrap: wrap !important;
  }
}

#oc-filtro-unidades .oc-field {
  max-width: none;
  min-width: 0;
}

/* Desktop: Pesquisar 40%, Status 20%, Ações 40% (flex ratios 2:1:2 = 40:20:40) */
#oc-filtro-unidades .oc-field--busca {
  flex: 2 0 0; /* 40% via grow ratio */
  min-width: 0;
}

/* ⚠️ `#oc-filtro-unidades .oc-field--sm{flex:1 0 0}` foi removida (V1.00.383):
   dava 20% da linha ao Status e, por ser seletor de ID, vencia qualquer
   classe compartilhada. O Status agora usa `.oc-field--largura-acao` e fica
   do tamanho de um botão; o que ele deixa de ocupar vai para o Pesquisar. */

/* ⚠️ AS AÇÕES DE UNIDADES NÃO TÊM MAIS REGRA PRÓPRIA (V1.00.380).

   Existiam aqui `#oc-filtro-unidades .oc-form-actions{flex:2 0 0}` e
   `#oc-filtro-unidades .oc-form-actions .oc-btn{flex:1 0 0}`, que davam às
   ações 40% da linha e esticavam os dois botões para preencher a faixa: em
   1440px saíam com 240px cada, contra 143px em Composições. Como o seletor
   usa ID, nenhuma classe compartilhada conseguia padronizar por cima — a
   especificidade de ID vence qualquer combinação de classes.

   O tamanho do botão passou a vir de `.oc-filtros-acoes-linha`, a mesma
   definição usada pela coluna de Composições/Insumos. O espaço que as ações
   deixam de esticar volta para Pesquisar e Status, que continuam com as
   proporções declaradas logo acima. */

/* Composições — layout responsivo preciso
   Desktop (>1000px):     1 linha: [Buscar 35%][Unidade 15%][Filtrar 25%][Limpar 25%]
   Tablet (640-1000px):   2 linhas: [Buscar 50%][Unidade 50%]
                                    [Filtrar 50%][Limpar 50%]
   Mobile (<640px):       4 linhas: [Buscar 100%]
                                    [Unidade 100%]
                                    [Filtrar 100%]
                                    [Limpar 100%] */

/* ============================================================================
   COMPOSIÇÕES — LAYOUT RESPONSIVO CSS GRID PURO
   ============================================================================
   Implementação única e definitiva sem fallback, sem !important desnecessário.

   Estrutura HTML:
   1. .oc-field--busca (input Buscar)
   2. .oc-field (select Unidade)
   3. .oc-form-actions (wrapper com botões)
      3a. .oc-btn (Filtrar)
      3b. .oc-btn (Limpar)

   Desktop (>838px):           Buscar 25% | Unidade 25% | Filtrar 25% | Limpar 25% [1 linha × 4 colunas]
   Tablet/Telas menores (≤838px): Buscar 50% | Unidade 50% [linha 1]
                                   Filtrar 50% | Limpar 50% [linha 2]
   ============================================================================ */

/* ---- A GRADE DE FILTROS (componente) ----
   Usada por Composições e Insumos via a classe `oc-filtros-grade` no <form>.
   Opt-in de propósito: as demais listagens seguem no flex-wrap da regra base,
   e trocar todas de uma vez estaria fora do que foi pedido.

   Cada campo ocupa 100%% da sua célula, então quem distribui é a grade — não
   o flex-basis de cada `.oc-field`. É isso que dá a linha equilibrada. */

.oc-form.oc-filtros-grade {
  display: grid;
  box-sizing: border-box;

  /* Remove herança de Flexbox da regra base .oc-filtros .oc-form */
  flex-wrap: initial;
  flex-direction: initial;
  align-items: initial;
}

/* Neutralizar max-width e flex-basis herdados */
.oc-form.oc-filtros-grade .oc-field--busca,
.oc-form.oc-filtros-grade .oc-field {
  max-width: none;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Inputs e selects ocupam 100% da célula com altura padronizada */
.oc-form.oc-filtros-grade input,
.oc-form.oc-filtros-grade select {
  width: 100%;
  box-sizing: border-box;
  min-height: 36px;
  padding: 9px 11px;
}

/* Ações (wrapper dos botões) */
.oc-form.oc-filtros-grade .oc-form-actions {
  display: grid;
  gap: 10px;
  margin: 0;
  box-sizing: border-box;
  align-items: end;
}

/* Botões: mesma altura dos inputs/selects */
.oc-form.oc-filtros-grade .oc-form-actions .oc-btn {
  width: 100%;
  box-sizing: border-box;
  /* Remover height: 100% para não expandir além da altura natural */
  /* Usar as mesmas propriedades que os inputs para altura consistente */
  padding: 9px 11px;
  min-height: 36px;
  font-size: 14px;
}

/* ---- DESKTOP (>838px): 4 colunas iguais — 25% cada ---- */
@container oc-filtros (min-width: 839px) {
  .oc-form.oc-filtros-grade {
    grid-template-columns: 1fr 1fr 1fr 1fr;
    gap: 16px;
  }

  .oc-form.oc-filtros-grade .oc-form-actions {
    grid-column: -3 / -1;
    grid-template-columns: 1fr 1fr;
  }

  /* Quando os campos deixam SÓ UMA coluna livre na última linha.
     O bloco de ações e sempre o ultimo filho, entao seu indice = nº de campos + 1;
     em 4n os campos sao 4n-1, ou seja resto 3 e apenas uma coluna sobrando.
     Sem isto as acoes nao caberiam (querem 2 colunas) e desceriam para uma
     linha propria — e o que acontecia em Insumos, que tem 7 campos.
     A contagem vem do que foi REALMENTE renderizado: Base e Data-base so
     existem havendo competencia cadastrada, e a regra acompanha sozinha. */
  .oc-form.oc-filtros-grade > .oc-form-actions:nth-child(4n) {
    grid-column: -2 / -1;
  }
}

/* ⚠️ A variante `.oc-filtros-grade--5` (duas linhas de cinco) existia SÓ para
   insumos.php e foi removida na V1.00.369: aquela página passou a usar o
   painel `.oc-filtros-acoes-lado`, o mesmo de composicoes.php. Nenhuma
   outra página a usava — mantê-la seria CSS morto pedindo para divergir. */

/* ==========================================================================
   FILTROS COM COLUNA DE AÇÕES À DIREITA  (`.oc-filtros-acoes-lado`)
   ==========================================================================
   Outro arranjo para o mesmo painel de filtros, para telas com muitos campos
   e três ações:

     ┌───────────────────────────────────────────────┬──────────┐
     │ Buscar [.................................]    │ Filtrar  │
     │                                               │ Ocultar  │
     │ [Base][Data-base][UF][Status][Base de Preço]  │ Limpar   │
     └───────────────────────────────────────────────┴──────────┘

   A grade de 4/5 colunas (`.oc-filtros-grade`) distribui TUDO — campos e
   botões — numa mesma malha, e o bloco de ações acaba encaixado onde sobrou
   espaço. Aqui as ações têm coluna própria: a busca ganha a linha inteira e os
   demais filtros dividem a largura restante por igual.

   Componente, não estilo de página: Composições é o primeiro uso, e qualquer
   listagem pode pedir o mesmo arranjo trocando a classe do <form>. */
.oc-form.oc-filtros-acoes-lado {
  display:grid;
  grid-template-columns:minmax(0, 1fr) auto;
  align-items:stretch;
  gap:16px;
  box-sizing:border-box;

  /* Neutraliza a herança de flex da regra base `.oc-filtros .oc-form`. */
  flex-wrap:initial;
  flex-direction:initial;
  max-width:none;
  width:100%;
}

/* Campos e selects com a MESMA altura dos botões ao lado — é o que faz
   "Filtrar alinhado com Buscar" e "Limpar alinhado com os filtros" valer de
   fato, e não por aproximação. */
.oc-form.oc-filtros-acoes-lado input,
.oc-form.oc-filtros-acoes-lado select {
  width:100%;
  box-sizing:border-box;
  min-height:36px;
  padding:9px 11px;
}

/* ⚠️ `space-between` NOS DOIS LADOS, senão o alinhamento só vale num deles.
   A coluna de ações é esticada pela grade e distribui Filtrar/Ocultar/Limpar
   pela altura inteira. Sem o mesmo tratamento aqui, a coluna dos campos era
   esticada mas mantinha suas duas linhas coladas no topo: sobrava um vão
   embaixo e a base dos filtros ficava 14px acima da base de "Limpar".

   Com os dois lados distribuindo, tanto faz qual coluna é mais alta — os
   extremos se encontram por construção, não por um número calibrado. */
.oc-filtros-campos {
  display:flex;
  flex-direction:column;
  justify-content:space-between;
  /* Vão entre a linha do Buscar e a linha dos filtros. Menor que o `gap:16px`
     usado DENTRO de cada linha de propósito: entre as duas linhas já existe a
     separação visual dos rótulos ("Base", "UF"…), que ocupam ~18px logo
     abaixo do Buscar. Com 16px aqui o painel ficava com uma faixa vazia
     grande no meio; a linha inteira — filtros E o botão Limpar, que continua
     ancorado na base dela — sobe 8px. */
  gap:8px;
  min-width:0;
}

/* ---- Linha de cima: rótulo à esquerda, campo ocupando o resto ---- */
.oc-filtros-linha-busca {
  display:flex;
  align-items:center;
  gap:12px;
  min-width:0;
}
.oc-filtros-linha-busca label {
  margin:0;
  white-space:nowrap;
  flex:0 0 auto;
}
/* SÓ LARGURA. A aparência (borda, raio, tipografia, foco) vem do seletor
   compartilhado dos campos — nunca redeclarada aqui, senão o campo volta a
   divergir do resto do sistema na primeira mudança de tema.

   O Buscar ocupa todo o espaço que sobra até a coluna de ações — nunca uma
   largura fixa copiada de outra página. */
.oc-filtros-linha-busca input {
  flex:1 1 auto;
  min-width:0;
}
/* Um filtro que ACOMPANHA o Buscar na linha de cima (o Cliente, em Obras).
   Entra com o mesmo formato do Buscar — rótulo ao lado, não acima — para a
   linha continuar com a altura de UM controle: um `.oc-field` empilhado mede
   ~65px e esticaria a linha, desalinhando o "Filtrar", que a coluna de ações
   ancora no topo.

   LARGURA = UMA COLUNA DA GRADE DE BAIXO. A conta reproduz a do
   `grid-template-columns` da outra linha a partir da MESMA contagem de campos
   (`--oc-filtros-colunas`, emitida por `ocFiltroPainelLinhaBuscaAbrir($n)`):
   descontados os gaps entre as N colunas, divide o que sobra por N. Assim o
   acompanhante tem exatamente o tamanho do último filtro da linha de baixo —
   e continua tendo quando um campo entrar ou sair da grade, o que um valor
   fixo em pixels deixaria de acompanhar.

   `flex:0 0` (e não `1 1`) mantém o Buscar como o campo mais largo: o
   acompanhante não disputa a sobra da linha com o campo principal. */
.oc-filtros-linha-busca select {
  flex:0 0 calc(
    (100% - (var(--oc-filtros-colunas, 5) - 1) * 16px) / var(--oc-filtros-colunas, 5)
  );
  min-width:0;
}

/* ---- Linha de baixo: os filtros da página, todos da mesma largura ----
   `--oc-filtros-colunas` é a CONTAGEM REAL de campos, calculada em PHP por
   quem monta a linha (`ocFiltroPainelLinhaCampos()`). Campos condicionais —
   Base e Data-base só existem havendo competência cadastrada — mudam a
   contagem, e um `repeat(5, …)` fixo deixaria células vazias à direita.

   ⚠️ Contar em PHP, e não por `:has()` no CSS. As regras `:has()` que isto
   substitui tinham especificidade (0,2,1) contra (0,1,0) das regras de
   tablet/celular abaixo: venciam TAMBÉM dentro dos breakpoints e devolviam
   colunas espremidas onde o layout já é de 2 ou 1. Uma custom property não
   participa da cascata de seletores — as regras responsivas continuam
   sobrescrevendo `grid-template-columns` normalmente.

   `minmax(0, 1fr)` (e não `1fr`) é o que impede um <select> de opção longa
   de estourar a própria coluna: sem o mínimo zerado, a trilha nunca encolhe
   abaixo do conteúdo. */
.oc-filtros-linha-campos {
  display:grid;
  grid-template-columns:repeat(var(--oc-filtros-colunas, 5), minmax(0, 1fr));
  gap:16px;
  min-width:0;
}
.oc-filtros-linha-campos .oc-field {
  display:flex;
  flex-direction:column;
  gap:6px;
  max-width:none;
  width:100%;
  min-width:0;
  box-sizing:border-box;
}

/* ---- Coluna das ações ----
   `space-between` distribui os três: Filtrar encosta no topo (linha da busca),
   Limpar na base (linha dos filtros) e Ocultar fica entre eles. O `gap` é um
   PISO — em telas onde a coluna esquerda é baixa, ele impede que os três
   botões fiquem colados um no outro. */
/* ⚠️ A COLUNA 2 PRECISA SER RECLAMADA EXPLICITAMENTE.
   `.oc-form-actions` declara `grid-column:1 / -1` (o layout de formulário de
   cadastro, onde as ações ocupam a linha inteira embaixo). Herdado aqui, esse
   `1 / -1` fazia o bloco de ações abranger TODAS as colunas — logo, não cabia
   na segunda: caía para uma linha própria, à esquerda, e a coluna da direita
   colapsava para 0px. Era esse o defeito: os três botões empilhados embaixo
   dos filtros em vez de ao lado deles.

   `grid-row:1 / -1` faz o bloco ocupar a altura inteira das duas linhas da
   esquerda (Buscar em cima, os cinco filtros embaixo) — é isso que dá ao
   `justify-content:space-between` a altura necessária para Filtrar alinhar
   com Buscar e Limpar alinhar com a linha dos filtros. */
.oc-form.oc-filtros-acoes-lado > .oc-form-actions.oc-filtros-acoes-coluna {
  grid-column:2;
  grid-row:1 / -1;
}

.oc-form-actions.oc-filtros-acoes-coluna {
  /* ⚠️ GRADE, NÃO FLEX COM `space-between` — e a diferença tem uma causa.
     O arranjo antigo empurrava o primeiro botão para o TOPO da coluna. Isso
     alinhava com o campo enquanto a primeira faixa era a linha de busca de
     rótulo LATERAL, que media 39px: o campo começava no topo da faixa.

     Com o rótulo acima (padrão global de filtros), a faixa passou a medir
     ~65px e o campo desceu ~26px dentro dela — o "Filtrar" continuava no topo
     e saía do eixo do controle que ele acompanha. Sem erro, sem aviso: só
     desalinhado.

     Como grade de linhas IGUAIS com `align-items:end`, cada botão encosta no
     rodapé da sua faixa, que é exatamente onde o campo daquela linha termina.
     Vale para uma linha ou três, e deixa de depender da altura do rótulo. */
  display:grid;
  grid-auto-rows:1fr;
  align-items:end;
  justify-items:stretch;
  gap:16px;
  margin:0;
  /* Largura fixa da coluna: os botões têm de sair do mesmo tamanho, e
     deixá-la depender do texto faria "Filtrar" e "Ocultar" divergirem. */
  flex:0 0 auto;
  width:9.5rem;
}

/* ============ MEDIDAS DOS BOTÕES DE FILTRO — UMA DEFINIÇÃO SÓ ============
   Vale para o painel em COLUNA (Composições/Insumos) e para o painel EM LINHA
   (Unidades e demais listagens simples). Só o arranjo difere; o botão tem o
   mesmo tamanho nos dois, que é o que faz as telas parecerem o mesmo produto.

   ⚠️ Repetir os valores numa regra própria para cada painel resolveria a tela
   de hoje e criaria duas fontes da mesma verdade — o próximo ajuste de altura
   ou de fonte valeria só numa metade. */
.oc-form-actions.oc-filtros-acoes-coluna .oc-btn,
.oc-form-actions.oc-filtros-acoes-linha .oc-btn {
  box-sizing:border-box;
  width:var(--oc-largura-acao);
  min-height:36px;
  padding:9px 11px;
  font-size:14px;
  /* ⚠️ `line-height` EXPLÍCITO — é o que iguala os botões entre si.
     "Filtrar"/"Ocultar" são <button> e computam `normal` (folha do
     navegador); "Limpar" é <a> e herda o `1.45` da página. Resultado:
     40,3px contra 39px — um degrau visível. Uma razão (não um px fixo)
     mantém a igualdade se a fonte mudar. */
  line-height:1.35;
  justify-content:center;
  flex:0 0 auto;
}

/* Na coluna, a largura vem do container (`width:9.5rem` acima), então o botão
   preenche 100% e nunca depende do tamanho do próprio texto. */
.oc-form-actions.oc-filtros-acoes-coluna .oc-btn {
  width:100%;
}

/* Largura de uma ação de filtro. Um token só: o botão a usa, e um campo que
   precise ficar do MESMO tamanho do botão a reusa (`.oc-field--largura-acao`).
   Repetir o valor nos dois lugares faria os dois divergirem no primeiro
   ajuste. */
/* ---- Altura única dos controles DENTRO do painel de filtros ----
   ⚠️ Um `<select>` nativo mede ~2px a mais que um `<input>` com a MESMA
   borda, raio, padding e tipografia — medido: 41px contra 39px. A diferença
   vem da altura intrínseca da linha de opção do controle, não de regra
   nenhuma do sistema, então igualar borda e padding (que já estão iguais)
   nunca resolveria.

   Onde cada controle ocupa a sua própria célula ninguém percebe. Lado a lado
   — o Cliente ao lado do Buscar, o UF ao lado da Cidade — as bordas deixam de
   se alinhar e o campo parece ter outro estilo, que foi exatamente o relato.

   Fixar a altura vale para o PAINEL DE FILTROS inteiro, e não só para o campo
   que motivou o ajuste: uma regra que consertasse apenas o Cliente deixaria a
   mesma diferença viva na linha de baixo e em todas as outras telas de filtro.
   Formulários de cadastro/edição ficam fora — nada muda fora de `.oc-filtros`. */
:root {
  --oc-altura-campo:39px;
}
.oc-filtros .oc-form input[type="text"],
.oc-filtros .oc-form select {
  height:var(--oc-altura-campo);
}

:root {
  --oc-largura-acao:9.5rem;
}

/* Campo com a MESMA largura de um botão de ação. Não cresce nem encolhe: é
   isso que faz o espaço que sobra ir todo para o campo de busca ao lado.

   ⚠️ Só `flex`, sem `max-width`. Numa linha, o `flex-basis` já fixa a largura;
   numa COLUNA (o painel no celular) o basis passa a valer para a ALTURA e a
   largura fica livre — que é o desejado, o campo ocupa a linha inteira. Um
   `max-width` aqui prenderia o campo em 9,5rem também no celular. */
.oc-filtros .oc-form .oc-field--largura-acao {
  flex:0 0 var(--oc-largura-acao);
  min-width:0;
}

/* ============ PAINEL DE FILTROS EM LINHA ============
   Busca que cresce + campos curtos do tamanho de um botão + ações à direita:

     [ Pesquisar .......................... ][ Status ][ Filtrar ][ Limpar ]

   ⚠️ Tudo em CSS. Unidades chegou nesse mesmo desenho por um <script> que
   escreve `style.flex` a cada `resize` — estilo inline, que vence qualquer
   folha e já custou duas correções (V1.00.381/382). Uma tela nova NÃO deve
   copiar aquele script; usa estas classes. */
.oc-filtros .oc-form.oc-filtros-linha {
  align-items:end;
}

/* A busca absorve toda a sobra. `max-width:none` porque o `.oc-field--busca`
   genérico tem teto de 480px — ali ele existe para a busca não virar uma
   faixa gigante num painel de muitos campos; aqui a sobra é justamente o
   que se quer entregar a ela. */
.oc-filtros .oc-form.oc-filtros-linha .oc-field--busca {
  flex:1 1 240px;
  max-width:none;
}

/* ⚠️ NO CELULAR O PAINEL VIRA COLUNA — E `flex-basis` MUDA DE EIXO.
   Num container `column`, o eixo principal é o VERTICAL: `flex:1 1 240px`
   do campo de busca vira 240px de ALTURA, e `flex:0 0 9.5rem` do campo
   curto vira um bloco de 143px de altura. Medido antes do reset: o painel
   de Tipos de Insumos com 606px de altura e vãos enormes entre os campos.

   Aqui a altura volta a ser a do conteúdo; a largura já é 100% pela coluna. */
@container oc-filtros (max-width: 600px) {
  .oc-filtros .oc-form.oc-filtros-linha .oc-field--busca,
  .oc-filtros .oc-form.oc-filtros-linha .oc-field--largura-acao {
    flex:0 0 auto;
  }
}

/* ==========================================================================
   PAINEL DE FILTROS — GRADES POR LINHA  (`.oc-filtros-linhas`)
   ==========================================================================
   Cada LINHA tem a sua própria grade, com as proporções declaradas em `fr`.

   ⚠️ POR QUE NÃO UMA GRADE ÚNICA, como em `.oc-filtros-grade-fluida`.
   Aquele arranjo divide o painel inteiro em N colunas e cada campo ocupa um
   número inteiro delas. Funciona enquanto as frações pedidas têm um
   denominador comum pequeno. As de Insumos não têm: a linha de cima quer
   30/23/23/23 e a de baixo 30/17,5/17,5/17,5/17,5 — o menor denominador comum
   é 120 colunas, e 119 gaps de 16px consomem 1.904px, mais que a largura
   disponível. A grade única simplesmente não expressa estas proporções.

   Com uma grade POR LINHA, cada uma declara exatamente o que precisa, e o `fr`
   faz a conta certa: o navegador desconta os gaps primeiro e reparte o que
   sobra na proporção pedida. Dois gaps ou seis, as proporções não mudam (§5/§6)
   e nada transborda.

       desktop   linha 1  30fr 23fr 23fr 23fr
                 linha 2  30fr 17.5fr 17.5fr 17.5fr 17.5fr
       tablet    linha 1  30fr 23fr 23fr 23fr
                 linha 2  50fr 25fr 25fr   (as ações caem para a 3ª linha)
       celular   tudo numa coluna só

   O formulário é a pilha das linhas; o `gap` vertical é o mesmo horizontal, de
   modo que o espaçamento é uniforme em todas as direções. */
.oc-filtros .oc-form.oc-filtros-linhas {
  display:grid;
  grid-template-columns:minmax(0, 1fr);
  gap:16px;

  /* Neutraliza a herança de flex de `.oc-filtros .oc-form`. */
  flex-wrap:initial;
  flex-direction:initial;
  max-width:none;
  width:100%;
}

.oc-filtros-linhas .oc-filtros-linha {
  display:grid;
  gap:16px;
  /* Os rótulos ficam ACIMA dos campos e os botões não têm rótulo: alinhar pelo
     fim põe a base de todos os controles na mesma linha (§8). */
  align-items:end;
  min-width:0;
}

/* Quem dá a largura é a coluna, não o conteúdo. Os tetos genéricos
   (`.oc-filtros .oc-field` tem 340px, `--busca` tem 480px) existem para o
   painel em flex e aqui impediriam a proporção pedida. */
.oc-filtros-linhas .oc-filtros-linha > .oc-field {
  width:auto;
  max-width:none;
  min-width:0;
}

/* ⚠️ `display:contents` no wrapper das ações: Filtrar e Limpar viram itens da
   grade DA LINHA. Enquanto ele for uma caixa, os dois dividem uma célula e o
   `gap` é contado duas vezes — os botões saem mais estreitos que o campo ao
   lado, e o §15 pede que Filtrar tenha exatamente a largura de Status. */
.oc-filtros-linhas .oc-form-actions {
  display:contents;
}

.oc-filtros-linhas .oc-form-actions > .oc-btn {
  width:100%;
  box-sizing:border-box;
  min-width:0;
}

/* ---- Linha 1: Pesquisar + os campos de contexto de preço ----
   ⚠️ A CONTAGEM É DINÂMICA (§10). Base e Data-base só existem havendo
   competência cadastrada. Com um template fixo de quatro colunas, um catálogo
   sem histórico deixaria duas células VAZIAS à direita — o buraco que o §10
   proíbe. `data-oc-campos` traz quantos campos a linha realmente tem, e
   Pesquisar mantém os seus 30% enquanto os demais repartem os 70% restantes
   por igual, sejam três, dois ou um. */
.oc-filtros-linhas .oc-filtros-linha-a[data-oc-campos="4"] { grid-template-columns:30fr 23fr 23fr 23fr; }
.oc-filtros-linhas .oc-filtros-linha-a[data-oc-campos="3"] { grid-template-columns:30fr 35fr 35fr; }
.oc-filtros-linhas .oc-filtros-linha-a[data-oc-campos="2"] { grid-template-columns:30fr 70fr; }
.oc-filtros-linhas .oc-filtros-linha-a[data-oc-campos="1"] { grid-template-columns:minmax(0, 1fr); }

/* ---- Linha 2: Tipo + Status + Base de Preço + as duas ações ---- */

/* ---- Linha de N colunas IGUAIS ----
   O arranjo mais comum depois da linha de busca: um punhado de campos, ou os
   botões, repartindo a faixa por igual. `data-oc-campos` traz a contagem real,
   pelo mesmo motivo da linha de busca — campos condicionais mudam esse número,
   e um template fixo deixaria célula vazia. */
.oc-filtros-linhas .oc-filtros-linha-iguais[data-oc-campos="2"] { grid-template-columns:repeat(2, minmax(0, 1fr)); }
.oc-filtros-linhas .oc-filtros-linha-iguais[data-oc-campos="3"] { grid-template-columns:repeat(3, minmax(0, 1fr)); }
.oc-filtros-linhas .oc-filtros-linha-iguais[data-oc-campos="4"] { grid-template-columns:repeat(4, minmax(0, 1fr)); }
.oc-filtros-linhas .oc-filtros-linha-iguais[data-oc-campos="5"] { grid-template-columns:repeat(5, minmax(0, 1fr)); }
.oc-filtros-linhas .oc-filtros-linha-b {
  grid-template-columns:30fr 17.5fr 17.5fr 17.5fr 17.5fr;
}

/* ---- Proporções nomeadas pela PROPORÇÃO, não pela página ----
   Obras estreou as duas abaixo; qualquer tela que peça a mesma divisão
   reusa a mesma classe, em vez de nascer um `#obras .oc-field{...}`. */
.oc-filtros-linhas .oc-filtros-linha--30-20-20-20 {
  grid-template-columns:30fr 20fr 20fr 20fr;
}
/* Clientes e Fornecedores: Pesquisar 50%, Tipo 25%, UF 25%. */
.oc-filtros-linhas .oc-filtros-linha--50-25-25 {
  grid-template-columns:2fr 1fr 1fr;
}

/* ---- §9: input, select e botão com a MESMA altura ----
   `.oc-btn` tem min-height:36px e o campo de filtro fecha em ~37px pelo
   padding — 1px de desencontro, visível quando os dois estão lado a lado na
   mesma linha da grade. Um token só resolve para os dois. */
.oc-filtros-linhas {
  --oc-filtro-controle-h:38px;
}
.oc-filtros-linhas .oc-filtros-linha > .oc-field > input:not([type="checkbox"]):not([type="radio"]),
.oc-filtros-linhas .oc-filtros-linha > .oc-field > select,
.oc-filtros-linhas .oc-form-actions > .oc-btn {
  height:var(--oc-filtro-controle-h);
  min-height:var(--oc-filtro-controle-h);
}

/* ---- Tablet: as linhas viram FAIXAS (`--tablet-faixas`) ----
   Opt-in, porque muda o agrupamento — as telas que já usam
   `.oc-filtros-linhas` continuam com o tablet que sempre tiveram.

   ⚠️ Por que dissolver. A faixa do meio de Obras junta Cidade e UF (linha 1
   do desktop) com Status (linha 2). Enquanto as linhas forem caixas, nenhum
   `grid-template-columns` reagrupa itens de dois pais diferentes. Com
   `display:contents` os nove controles viram itens da mesma grade de 6
   colunas e o `span` de cada um desenha as faixas: 3+3, 2+2+2, 3+3, 3+3.
   Seis é o menor número que expressa metades E terços sem fração. */
@container oc-filtros (min-width: 601px) and (max-width: 900px) {
  .oc-filtros .oc-form.oc-filtros-linhas--tablet-faixas {
    grid-template-columns:repeat(6, minmax(0, 1fr));
    align-items:end;
  }
  .oc-filtros-linhas--tablet-faixas .oc-filtros-linha {
    display:contents;
  }
  .oc-filtros-linhas--tablet-faixas .oc-filtros-linha > .oc-field,
  .oc-filtros-linhas--tablet-faixas .oc-form-actions > .oc-btn {
    grid-column:span 3;      /* metade da faixa */
  }
  .oc-filtros-linhas--tablet-faixas .oc-filtros-linha > .oc-field.oc-t-terco {
    grid-column:span 2;      /* um terço */
  }

  /* ---- Ordem DENTRO das faixas do tablet ----
     ⚠️ A ordem do HTML é a do DESKTOP: linha 2 começa em Responsável, e o
     Status vem logo depois dele. No tablet o Status sobe para a faixa de
     Cidade+UF, ou seja, precisa aparecer ANTES do Responsável. Como as linhas
     estão dissolvidas, os nove controles são itens da mesma grade e `order`
     resolve isso sem duplicar markup nem mexer no HTML do desktop.

     Três grupos bastam (dentro de um grupo vale a ordem do HTML):
       0 (padrão) Pesquisar · Cliente · Cidade · UF · Status
       2          Responsável
       3          Registro · Filtrar · Limpar
     O que dá as faixas 3+3, 2+2+2, 3+3 e 3+3.

     Escopado ao 601–900: no celular a ordem pedida é a do HTML, e no desktop
     as linhas voltam a ser caixas, onde `order` não teria efeito nenhum. */
  .oc-filtros-linhas--tablet-faixas .oc-t-ordem-2 { order:2; }
  .oc-filtros-linhas--tablet-faixas .oc-t-ordem-3 { order:3; }
  .oc-filtros-linhas--tablet-faixas .oc-form-actions.oc-t-ordem-3 > .oc-btn { order:3; }
}

/* ---- Tablet ----
   Linha 1 mantém as mesmas proporções. Linha 2 passa a 50/25/25 e as ações
   caem para uma terceira faixa: Filtrar ocupa a primeira coluna (50%) e
   Limpar as duas seguintes (25+25 = 50%), o que dá a metade pedida a cada um
   sem um template extra só para eles.

   Container query, não media query: a largura útil do painel muda com a
   sidebar recolhida ou expandida — a mesma janela de 1280px produz painéis
   diferentes. Os cortes (601–900 e 600) são os que este arquivo já usa; com
   sidebar e respiros descontados, correspondem a ~1024px e ~768px de janela. */
@container oc-filtros (min-width: 601px) and (max-width: 900px) {
  /* ⚠️ `:not(--tablet-faixas)` — quem pediu faixas dissolve as linhas neste
     mesmo intervalo, e o `span 2` do último botão venceria (por
     especificidade) o `span 3` das faixas, deixando o Limpar mais estreito
     que o Filtrar. As telas sem a classe seguem exatamente como estavam. */
  .oc-filtros-linhas:not(.oc-filtros-linhas--tablet-faixas) .oc-filtros-linha-b {
    grid-template-columns:50fr 25fr 25fr;
  }
  .oc-filtros-linhas:not(.oc-filtros-linhas--tablet-faixas) .oc-filtros-linha-b .oc-form-actions > .oc-btn:last-child {
    grid-column:span 2;
  }
}

/* ---- Celular: uma coluna, cada controle em 100% ----
   ⚠️ As LINHAS deixam de existir como caixas. Sem isto, cada linha seria uma
   grade de uma coluna própria e a ordem ficaria presa dentro dela — e a
   sequência pedida para o celular ATRAVESSA as linhas (Tipo, que está na linha
   2, vem antes de UF, que está na 1). Dissolvendo as linhas, os nove controles
   viram itens da mesma coluna e as classes `oc-ordem-m*` podem reordená-los. */
@container oc-filtros (max-width: 600px) {
  .oc-filtros-linhas .oc-filtros-linha {
    display:contents;
  }
  .oc-filtros .oc-form.oc-filtros-linhas {
    grid-template-columns:minmax(0, 1fr);
  }
  .oc-filtros-linhas .oc-form-actions > .oc-btn:last-child {
    grid-column:auto;
  }
}

/* ==========================================================================
   PAINEL DE FILTROS — GRADE FLUIDA  (`.oc-filtros-grade-fluida`)
   ==========================================================================
   Um painel onde CADA campo declara quanto ocupa, por faixa de largura. As
   telas de filtro do sistema pedem proporções diferentes entre si (40/20/20/20
   numa, 40/15/15/15/15 noutra, 33/33/33 numa terceira) e proporções diferentes
   por breakpoint. Uma classe por arranjo multiplicaria o CSS; aqui o arranjo é
   DECLARADO NA PÁGINA, em unidades da grade.

   A grade tem 20 colunas no desktop e 12 no tablet. Não é número mágico: é o
   menor denominador que expressa todas as frações pedidas sem arredondar.

       desktop (20)   40% = 8   20% = 4   15% = 3
       tablet  (12)   50% = 6   33% = 4   25% = 3

   ⚠️ `fr`, NUNCA `%`. Uma largura de 40% somada aos gaps estoura o container;
   `fr` reparte o que SOBRA depois dos gaps, então a proporção se mantém e o
   painel nunca transborda, com um gap ou com cinco.

   Como uma página usa:

       <div class="oc-field oc-c-d8 oc-c-t6">   Pesquisar: 40% desktop, 50% tablet
       <div class="oc-field oc-c-d4 oc-c-t3">   Status:    20% desktop, 25% tablet

   Sem classe, o item ocupa a linha inteira — que é o padrão certo para um campo
   novo que ninguém dimensionou ainda: aparece inteiro e óbvio, em vez de
   espremido num canto. */
.oc-filtros .oc-form.oc-filtros-grade-fluida {
  display:grid;
  grid-template-columns:repeat(20, minmax(0, 1fr));
  gap:16px;
  /* Os rótulos ficam ACIMA dos campos e os botões não têm rótulo: alinhar pelo
     fim é o que põe a base de todos os controles na mesma linha. */
  align-items:end;

  /* Neutraliza a herança de flex de `.oc-filtros .oc-form`. */
  flex-wrap:initial;
  flex-direction:initial;
  max-width:none;
  width:100%;
}

/* ⚠️ `display:contents` NO WRAPPER DAS AÇÕES.
   Filtrar e Limpar vivem dentro de um `.oc-form-actions`. Enquanto ele for uma
   caixa, os dois dividem UMA célula e o `gap` é contado duas vezes — os botões
   saem alguns pixels mais estreitos que o campo ao lado, o que é visível quando
   a especificação pede que Filtrar tenha exatamente a largura de Status.
   Dissolvendo a caixa, os dois viram itens da MESMA grade, com um sistema de
   gaps só.

   As variáveis de span são herdadas do wrapper pelos botões, então declarar
   `oc-c-d3` no wrapper dimensiona os dois de uma vez. */
.oc-filtros .oc-form.oc-filtros-grade-fluida .oc-form-actions {
  display:contents;
}

.oc-filtros .oc-form.oc-filtros-grade-fluida > .oc-field,
.oc-filtros .oc-form.oc-filtros-grade-fluida .oc-form-actions > .oc-btn {
  grid-column:span var(--oc-col-d, 20);
  min-width:0;
  /* Os tetos genéricos (`.oc-filtros .oc-field` tem 340px, `--busca` tem 480px)
     existem para o painel em flex, onde o campo não deve virar uma faixa
     gigante. Aqui quem dá a largura é a coluna. */
  width:auto;
  max-width:none;
}

/* Botão preenche a célula: sem isto ele fica do tamanho do texto. Altura,
   padding, fonte e `line-height` continuam vindo de `.oc-filtros-acoes-linha`
   / `--coluna`, que as páginas mantêm na marcação — é o que iguala a altura dos
   botões à dos campos sem duplicar os valores numa segunda fonte da verdade. */
.oc-filtros .oc-form.oc-filtros-grade-fluida .oc-form-actions > .oc-btn {
  width:100%;
  box-sizing:border-box;
}

/* ---- Spans: desktop (grade de 20) ---- */
.oc-filtros-grade-fluida .oc-c-d3 { --oc-col-d:3; }
.oc-filtros-grade-fluida .oc-c-d4 { --oc-col-d:4; }
.oc-filtros-grade-fluida .oc-c-d5 { --oc-col-d:5; }
.oc-filtros-grade-fluida .oc-c-d8 { --oc-col-d:8; }

/* ---- Tablet: a grade passa a 12 colunas e vale o span `t` ----
   Container query, não media query: a largura útil do painel muda com a sidebar
   recolhida ou expandida, e a mesma janela de 1280px produz painéis diferentes.
   Os cortes (601–900 e 600) são os que este arquivo já usa em toda parte —
   introduzir 768/1024 criaria um terceiro conjunto brigando com os existentes.
   Descontadas sidebar e respiros, eles correspondem a ~768px e ~1024px de
   janela, que é o comportamento pedido. */
@container oc-filtros (min-width: 601px) and (max-width: 900px) {
  .oc-filtros .oc-form.oc-filtros-grade-fluida {
    grid-template-columns:repeat(12, minmax(0, 1fr));
  }
  .oc-filtros .oc-form.oc-filtros-grade-fluida > .oc-field,
  .oc-filtros .oc-form.oc-filtros-grade-fluida .oc-form-actions > .oc-btn {
    grid-column:span var(--oc-col-t, 12);
  }
  .oc-filtros-grade-fluida .oc-c-t3 { --oc-col-t:3; }
  .oc-filtros-grade-fluida .oc-c-t4 { --oc-col-t:4; }
  .oc-filtros-grade-fluida .oc-c-t6 { --oc-col-t:6; }
}

/* ---- Celular: uma coluna, cada controle em 100%, na ordem do markup ---- */
@container oc-filtros (max-width: 600px) {
  .oc-filtros .oc-form.oc-filtros-grade-fluida {
    grid-template-columns:minmax(0, 1fr);
  }
  .oc-filtros .oc-form.oc-filtros-grade-fluida > .oc-field,
  .oc-filtros .oc-form.oc-filtros-grade-fluida .oc-form-actions > .oc-btn {
    grid-column:span 1;
  }
}

/* ---- Ordem no celular (`oc-ordem-m*`) ----
   ⚠️ USE COM PARCIMÔNIA, E SÓ QUANDO A SEQUÊNCIA PEDIDA MUDAR ENTRE FAIXAS.

   Insumos é o caso: no desktop e no tablet os campos vêm agrupados por linha
   (Pesquisar/Base/Data-base/UF em cima; Tipo/Status/Base de Preço embaixo),
   mas a sequência pedida para o celular ATRAVESSA essas linhas — Tipo, que
   está na linha 2, vem antes de UF, que está na 1, e Base de Preço vem antes
   de Status. Nenhuma ordem de markup satisfaz as três faixas ao mesmo tempo.

   A marcação segue as LINHAS (que servem desktop e tablet sem uma única
   declaração de `order`) e só o celular reordena.

   ⚠️ O CUSTO É REAL e está aqui declarado: `order` muda a ordem VISUAL, não a
   do documento. No celular, quem navega por Tab visita UF antes de Tipo, e
   Status antes de Base de Preço, embora veja o contrário. São dois pares
   adjacentes num painel de sete controles — pequeno, mas é uma divergência de
   verdade. Se um dia a sequência puder ser a mesma nas três faixas, some com
   estas classes e deixe o markup mandar.

   Item sem classe fica em `order:0` e vem antes de qualquer um numerado; entre
   iguais, vale a ordem do markup. É o que permite numerar só os que se movem.

   Nos botões o `order` vai no BOTÃO, não no wrapper: com `display:contents` o
   wrapper não gera caixa e não é item da grade — `order` nele seria inerte. */
@container oc-filtros (max-width: 600px) {
  .oc-filtros-linhas .oc-ordem-m4,
  .oc-filtros-grade-fluida .oc-ordem-m4 { order:4; }
  .oc-filtros-linhas .oc-ordem-m5,
  .oc-filtros-grade-fluida .oc-ordem-m5 { order:5; }
  .oc-filtros-linhas .oc-ordem-m6,
  .oc-filtros-grade-fluida .oc-ordem-m6 { order:6; }
  .oc-filtros-linhas .oc-ordem-m7,
  .oc-filtros-grade-fluida .oc-ordem-m7 { order:7; }
  .oc-filtros-linhas .oc-form-actions.oc-ordem-m8 > .oc-btn,
  .oc-filtros-grade-fluida .oc-form-actions.oc-ordem-m8 > .oc-btn { order:8; }
}


/* ============================================================
   PAINEL DE FILTROS PROPORCIONAL  (`.oc-filtros-proporcional`)
   ============================================================
   Distribuição por PROPORÇÃO, não por largura de conteúdo:

     desktop  [ Pesquisar 30% ][ Status 17,5 ][ Origem 17,5 ][ Filtrar 17,5 ][ Limpar 17,5 ]
     tablet   [ Pesquisar 50% ][ Status 25% ][ Origem 25% ]
              [ Filtrar 50% ][ Limpar 50% ]
     celular  cada controle em 100%, um abaixo do outro

   Classe OPT-IN. Hoje so `tipos-insumos.php` a usa; as demais telas seguem em
   `.oc-filtros-linha` (busca elastica + campos do tamanho de um botao) e nao
   foram tocadas. Ela nasce reutilizavel para quando o padrao for estendido.

   ⚠️ BREAKPOINTS SAO DO CONTAINER, NAO DA JANELA — e isso e convencao do
   projeto, nao preferencia. A largura util do painel muda com a sidebar
   recolhida/expandida, entao uma media query de viewport erraria: a mesma
   janela de 1280px da paineis de larguras diferentes conforme a barra. Os
   cortes reusados sao os que o arquivo ja usa (600px e 900px), que
   correspondem, com sidebar e respiros descontados, a ~768px e ~1024px de
   janela. Introduzir 768/1024 aqui criaria um terceiro conjunto de cortes
   brigando com os dois que ja existem. */
.oc-filtros .oc-form.oc-filtros-proporcional {
  display:grid;
  grid-template-columns:30fr 17.5fr 17.5fr 17.5fr 17.5fr;
  gap:16px;
  /* Labels ficam ACIMA dos campos e os botoes nao tem label: alinhar pelo fim
     e o que poe a base de todos na mesma linha (§10). */
  align-items:end;

  /* Neutraliza a heranca de flex de `.oc-filtros .oc-form`. */
  flex-wrap:initial;
  flex-direction:initial;
}

/* ⚠️ `display:contents` NO WRAPPER DAS ACOES — e o ponto todo do arranjo.
   Filtrar e Limpar vivem dentro de um `.oc-form-actions`. Se ele continuasse
   sendo uma caixa, a grade teria QUATRO colunas e os dois botoes dividiriam a
   ultima: com o `gap` contado duas vezes (uma na grade, outra dentro do
   wrapper), cada botao sairia 8px mais estreito que o Status ao lado — perto
   do certo, e errado. Sem a caixa, os dois viram itens da MESMA grade, e as
   cinco fracoes repartem exatamente o mesmo espaco, com um unico sistema de
   gaps (§8: nada de somar 100% + gaps e estourar a largura). */
.oc-filtros .oc-form.oc-filtros-proporcional .oc-form-actions {
  display:contents;
}

/* Quem da a largura e a coluna, nao o conteudo. Os tetos genericos
   (`.oc-filtros .oc-field` tem 340px, `.oc-field--busca` tem 480px) existem
   para o painel de MUITOS campos, onde a busca nao deve virar uma faixa
   gigante; aqui eles impediriam a proporcao pedida. */
.oc-filtros .oc-form.oc-filtros-proporcional .oc-field {
  width:auto;
  max-width:none;
  min-width:0;
}

/* Botao ocupa a coluna inteira (§11) — sem isto ele fica do tamanho do texto.
   Altura, padding, fonte e `line-height` continuam vindo de
   `.oc-filtros-acoes-linha .oc-btn`, que a pagina mantem na marcacao: e o que
   garante a mesma altura visual entre campos e botoes (§9) sem duplicar os
   valores numa segunda fonte da verdade. */
.oc-filtros .oc-form.oc-filtros-proporcional .oc-form-actions .oc-btn {
  width:100%;
}

/* ---- Tablet: 4 colunas — 50/25/25 em cima, 50/50 embaixo ----
   Quatro colunas e nao tres: com tres nao ha como dar metade da linha a cada
   botao. Pesquisar ocupa duas (50%), Status e Origem uma cada (25%), e cada
   botao ocupa duas na linha seguinte. */
@container oc-filtros (min-width: 601px) and (max-width: 900px) {
  .oc-filtros .oc-form.oc-filtros-proporcional {
    grid-template-columns:repeat(4, minmax(0, 1fr));
  }
  .oc-filtros .oc-form.oc-filtros-proporcional .oc-field--busca,
  .oc-filtros .oc-form.oc-filtros-proporcional .oc-form-actions .oc-btn {
    grid-column:span 2;
  }
}

/* ---- Celular: uma coluna, tudo em 100%, na ordem do markup ---- */
@container oc-filtros (max-width: 600px) {
  .oc-filtros .oc-form.oc-filtros-proporcional {
    grid-template-columns:minmax(0, 1fr);
  }
  .oc-filtros .oc-form.oc-filtros-proporcional .oc-field--busca,
  .oc-filtros .oc-form.oc-filtros-proporcional .oc-form-actions .oc-btn {
    grid-column:auto;
  }
}

/* ---- Painel de filtros EM LINHA: ações não esticam ----
   Sem isto, `.oc-filtros .oc-form .oc-form-actions .oc-btn` herda `flex:1 0 0`
   e os dois botões se esticam para ocupar a faixa inteira que sobrou — em
   1440px davam 240px cada, contra 143px em Composições. */
.oc-form-actions.oc-filtros-acoes-linha {
  flex:0 0 auto;
  gap:8px;
}

/* ---- Tablet: a coluna de ações fica enquanto houver espaço ----
   Os filtros passam a duas colunas antes de as ações descerem: empurrar os
   botões para baixo cedo demais desperdiça uma faixa inteira de altura numa
   tela que já é curta. */
@container oc-filtros (max-width: 900px) {
  .oc-filtros-linha-campos {
    grid-template-columns:repeat(2, minmax(0, 1fr));
  }
  /* O acompanhante do Buscar segue a grade também aqui. Sem esta linha ele
     continuaria valendo 1/5 da largura enquanto a linha de baixo já passou a
     ter 2 colunas — medido: 91px contra 252px, visivelmente menor que o campo
     que ele deve espelhar. A conta é a mesma, com a contagem desta faixa. */
  .oc-filtros-linha-busca select {
    flex:0 0 calc((100% - 16px) / 2);
  }
}

/* ---- Mobile: uma coluna só, ações abaixo ---- */
@container oc-filtros (max-width: 600px) {
  .oc-form.oc-filtros-acoes-lado {
    grid-template-columns:minmax(0, 1fr);
  }
  .oc-filtros-linha-busca {
    flex-direction:column;
    align-items:stretch;
    gap:6px;
  }
  /* ⚠️ `flex-basis` PASSA A SER ALTURA quando o container vira coluna.
     A largura calculada do acompanhante (o Cliente em Obras, a Obra em
     Orçamentos) viraria a ALTURA do select no celular — o mesmo tropeço já
     documentado no painel de Tipos de Insumos. `auto` devolve a altura
     natural; a largura cheia vem do `align-items:stretch` logo acima. */
  .oc-filtros-linha-busca select {
    flex:0 0 auto;
  }
  .oc-filtros-linha-campos {
    grid-template-columns:minmax(0, 1fr);
  }
  /* Sem coluna à direita não há o que distribuir verticalmente: os três
     botões voltam a ser uma pilha de largura cheia.

     ⚠️ `grid-column:auto` é OBRIGATÓRIO aqui. A regra que coloca as ações na
     coluna 2 (no desktop) continuaria valendo neste ponto e criaria uma
     SEGUNDA coluna implícita — medido em 375px: os botões espremidos em 86px
     ao lado dos campos, em vez da pilha de largura cheia que esta regra
     desenha. Anular a colocação é o que devolve o layout de uma coluna. */
  .oc-form.oc-filtros-acoes-lado > .oc-form-actions.oc-filtros-acoes-coluna {
    grid-column:auto;
    grid-row:auto;
  }
  .oc-form-actions.oc-filtros-acoes-coluna {
    width:100%;
    justify-content:flex-start;
    gap:10px;
  }
}

/* ---- TABLET (601px até 838px): 2 linhas × 2 colunas iguais ---- */
@container oc-filtros (min-width: 601px) and (max-width: 838px) {
  .oc-form.oc-filtros-grade {
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }

  .oc-form.oc-filtros-grade .oc-form-actions {
    grid-column: 1 / -1;
    grid-template-columns: 1fr 1fr;
  }
}

/* ---- MOBILE (até 600px): 1 coluna — Buscar | Unidade | Filtrar | Limpar ---- */
@container oc-filtros (max-width: 600px) {
  .oc-form.oc-filtros-grade {
    grid-template-columns: 1fr;
    gap: 16px;
  }

  .oc-form.oc-filtros-grade .oc-form-actions {
    grid-column: 1 / -1;
    grid-template-columns: 1fr;
  }
}

/* Chips de filtros ativos */
.oc-chips{
  display:flex;
  flex-wrap:wrap;
  gap:8px;
  margin-top:14px;
}

.oc-chip{
  display:inline-flex;
  align-items:center;
  gap:6px;
  padding:5px 10px;
  border-radius:999px;
  background:#eef2ff;
  color:var(--primary-dark);
  border:1px solid #d7e0ff;
  font-size:12.5px;
  font-weight:600;
}

.oc-chip:hover{
  background:#e0e7ff;
  text-decoration:none;
}

.oc-chip-limpar{
  background:var(--bg);
  color:var(--text-muted);
  border-color:var(--border);
}

/* Tablet/mobile — menu vira drawer oculto por padrão, sobre o conteúdo,
   com overlay (item 7/8 da tarefa de sidebar). Não desloca o conteúdo
   principal (posição fixa, não reflui o layout de coluna). */
@media (max-width:820px){
  .oc-sidebar-hamburger{display:flex;align-items:center;justify-content:center;}
  .oc-sidebar-toggle{display:none;}

  /* Drawer ancorado na DIREITA (V1.00.074 — antes abria pela esquerda):
     right:0/left:auto + translateX(100%) (fora da viewport à direita
     quando fechado) em vez de left:0/translateX(-100%). O hambúrguer já
     fica à direita do cabeçalho (V1.00.073) — o menu agora abre do MESMO
     lado de quem o aciona. box-shadow espelhado (offset X negativo: a
     sombra cai à ESQUERDA do painel, não mais à direita). Nenhuma
     lógica de assets/js/sidebar.js muda — só alterna a classe
     `oc-sidebar-mobile-open`, sem left/right/transform hardcoded no JS. */
  .oc-sidebar{
    position:fixed;
    top:0;
    right:0;
    left:auto;
    bottom:0;
    z-index:80;
    width:min(82vw,300px);
    transform:translateX(100%);
    transition:transform .2s ease;
    box-shadow:-2px 0 24px rgba(0,0,0,.25);
  }

  /* No mobile a preferência "recolhido" do desktop não se aplica — o
     drawer usa sempre a largura/rótulos completos quando aberto. */
  body.oc-sidebar-collapsed .oc-sidebar{
    width:min(82vw,300px);
  }
  body.oc-sidebar-collapsed .oc-brand strong,
  body.oc-sidebar-collapsed .oc-brand span,
  body.oc-sidebar-collapsed .oc-sidebar .oc-nav-text,
  body.oc-sidebar-collapsed .oc-sidebar-footer{
    display:initial;
  }
  body.oc-sidebar-collapsed .oc-nav a{justify-content:flex-start;}

  body.oc-sidebar-mobile-open .oc-sidebar{
    transform:translateX(0);
  }

  .oc-sidebar-overlay{
    display:block;
    position:fixed;
    inset:0;
    background:rgba(15,18,28,.45);
    z-index:70;
    opacity:0;
    pointer-events:none;
    transition:opacity .2s ease;
  }

  body.oc-sidebar-mobile-open .oc-sidebar-overlay{
    opacity:1;
    pointer-events:auto;
  }

  /* body já é sempre overflow:hidden (V1.00.048, regra global) — quem
     precisa travar quando o drawer abre agora é o .oc-content-panel (o
     container que realmente rola), pro conteúdo de trás não rolar por
     baixo do drawer/overlay. */
  body.oc-sidebar-mobile-open .oc-content-panel{
    overflow:hidden;
  }

  /* V1.00.154 — Altera SOMENTE o mínimo necessário (Parte 4).
     display/direção/alinhamento/wrap já vêm da fonte de verdade única e
     não precisam ser repetidos aqui — as declarações `!important` que
     existiam neste bloco eram consequência da arquitetura duplicada. */
  .oc-topbar{
    padding-top:14px;
    padding-bottom:12px;
  }
  .oc-content-panel{padding:0 16px 40px;}
  .oc-form{max-width:100%;grid-template-columns:1fr;}
  .oc-form-actions{flex-wrap:wrap;}
}

/* V1.00.154 — A "garantia final" com `flex-wrap:nowrap !important` que
   existia aqui foi REMOVIDA (Parte 10). Ela era necessária apenas porque
   três blocos diferentes disputavam a definição de .oc-topbar. Agora
   `flex-wrap:nowrap` é declarado uma única vez, na fonte de verdade, e
   nenhuma media/container query o sobrescreve — o !important virou
   redundante e só atrapalhava a reversão de estado. */

/* ============================================================
   Filtros de Insumos: sem sistema próprio
   ============================================================
   Havia aqui um grid EXCLUSIVO de `#oc-filtro-insumos`: `repeat(10, 1fr)` com
   spans por `nth-of-type`. Composições tinha OUTRO, de 4 colunas. Dois sistemas
   para o mesmo componente — e o de Insumos ainda dependia da posição dos campos,
   que muda quando Base/Data-base não são renderizados (só aparecem havendo
   competência cadastrada).

   Agora as duas páginas usam a mesma grade, declarada em `.oc-filtros-grade`
   logo abaixo. Manter dois sistemas era exatamente o "terceiro padrão" que não
   deve existir. */
.oc-auth-wrap{
  width:100%;
  padding:24px;
  display:flex;
  justify-content:center;
}
.oc-auth-card{
  width:100%;
  max-width:380px;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  padding:32px 28px;
  box-shadow:0 4px 24px rgba(15,18,28,.06);
}
.oc-auth-brand{
  display:flex;
  align-items:baseline;
  gap:8px;
  justify-content:center;
  margin-bottom:22px;
}
.oc-auth-brand strong{font-size:20px;color:var(--text);}
.oc-auth-brand span{font-size:11px;color:var(--text-muted);}
.oc-auth-titulo{
  font-size:16px;
  font-weight:700;
  color:var(--text);
  text-align:center;
  margin:0 0 22px;
}
.oc-auth-google-btn{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  width:100%;
  padding:11px 16px;
  border-radius:8px;
  border:1px solid var(--border);
  background:var(--surface);
  color:var(--text);
  font-size:14px;
  font-weight:600;
  cursor:pointer;
  text-decoration:none;
}
.oc-auth-google-btn:hover{
  border-color:var(--primary);
  box-shadow:0 1px 4px rgba(15,18,28,.08);
}
.oc-auth-google-btn img{width:18px;height:18px;flex-shrink:0;}
.oc-auth-hint{
  margin-top:16px;
  font-size:12px;
  color:var(--text-muted);
  text-align:center;
  line-height:1.5;
}
.oc-auth-block{
  border-top:1px solid var(--border);
}
.oc-auth-user{
  display:flex;
  align-items:center;
  gap:8px;
  padding:10px 14px;
}
.oc-org-switch{
  padding:10px 14px 0;
}
.oc-org-switch-form{margin:0;padding:0;}
.oc-org-switch-label{
  display:block;
  font-size:10.5px;
  font-weight:600;
  text-transform:uppercase;
  letter-spacing:.03em;
  color:var(--sidebar-text);
  margin-bottom:3px;
}
.oc-org-switch-fixo{
  font-size:12.5px;
  font-weight:600;
  text-transform:none;
  letter-spacing:normal;
  color:var(--sidebar-text-active);
  margin-bottom:0;
}
.oc-org-switch-select{
  width:100%;
  font-size:12px;
  padding:5px 6px;
  border-radius:6px;
  border:1px solid rgba(255,255,255,.18);
  background:rgba(255,255,255,.06);
  color:var(--sidebar-text-active);
  cursor:pointer;
}
.oc-auth-user-avatar{
  width:28px;
  height:28px;
  border-radius:50%;
  object-fit:cover;
  background:var(--border);
  flex-shrink:0;
}
.oc-auth-user-info{
  min-width:0;
  flex:1;
  line-height:1.25;
}
.oc-auth-user-nome{
  font-size:12.5px;
  font-weight:600;
  color:var(--sidebar-text-active);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.oc-auth-user-email{
  font-size:11px;
  color:var(--sidebar-text);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.oc-auth-user-sair-form{margin:0;padding:0;flex-shrink:0;}
.oc-auth-user-sair{
  font-size:11px;
  color:var(--sidebar-text);
  text-decoration:none;
  flex-shrink:0;
  background:none;
  border:none;
  padding:0;
  font-family:inherit;
  cursor:pointer;
}
.oc-auth-user-sair:hover{color:var(--sidebar-text-active);}

/* V1.00.076 — Seção de Fórmula de BDI (editar-bdi-modelo.php) */
.oc-bdi-formula{
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  padding:20px;
  margin-bottom:20px;
}

.oc-section-title{
  margin:0 0 12px;
  font-size:14px;
  font-weight:600;
  color:var(--text);
  text-transform:uppercase;
  letter-spacing:0.5px;
}

.oc-formula-canonical{
  background:rgba(29, 95, 214, 0.05);
  border-left:3px solid var(--primary);
  padding:12px 16px;
  border-radius:4px;
  margin-bottom:16px;
  overflow-x:auto;
}

.oc-formula-notation{
  margin:0;
  font-family:Menlo, Monaco, 'Courier New', monospace;
  font-size:13px;
  line-height:1.6;
  color:var(--text);
  word-break:break-word;
  white-space:normal;
}

.oc-formula-bracket{
  color:var(--primary);
  font-weight:600;
}

.oc-formula-details{
  margin-top:12px;
}

.oc-formula-summary{
  cursor:pointer;
  user-select:none;
  padding:8px 12px;
  margin:-8px -12px 8px;
  border-radius:4px;
  font-size:13px;
  font-weight:500;
  color:var(--primary);
  transition:background 0.2s ease;
}

.oc-formula-summary:hover{
  background:rgba(29, 95, 214, 0.1);
}

.oc-formula-substitution{
  padding:12px 0;
}

.oc-formula-label{
  margin:0 0 12px;
  font-size:13px;
  font-weight:500;
  color:var(--text);
}

.oc-formula-table{
  width:100%;
  border-collapse:collapse;
  font-size:13px;
  margin-bottom:12px;
}

.oc-formula-table td{
  padding:8px 12px;
  border-bottom:1px solid var(--border);
  text-align:left;
}

.oc-formula-table td:first-child{
  width:70%;
  color:var(--text);
}

.oc-formula-table td:last-child{
  text-align:right;
  font-weight:500;
  color:var(--primary);
}

.oc-formula-numerador{
  background:rgba(76, 175, 80, 0.08);
  font-weight:600;
  color:var(--text);
}

.oc-formula-numerador td{
  border-bottom:2px solid rgba(76, 175, 80, 0.3);
}

.oc-formula-divisor{
  background:rgba(244, 67, 54, 0.08);
  font-weight:600;
  color:var(--text);
  margin-top:8px;
}

.oc-formula-divisor td{
  border-bottom:2px solid rgba(244, 67, 54, 0.3);
}

.oc-formula-value{
  font-family:Menlo, Monaco, 'Courier New', monospace;
  font-weight:600;
}

/* V1.00.077 — Fração Matemática de BDI (editar-bdi-modelo.php) */
.oc-bdi-formula-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 24px;
  margin-bottom: 20px;
}

.oc-bdi-formula-wrapper {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  margin-bottom: 24px;
  flex-wrap: wrap;
}

.oc-bdi-label {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}

.oc-bdi-minus {
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
}

.oc-bdi-fraction {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  font-family: Georgia, 'Times New Roman', serif;
}

.oc-bdi-numerator,
.oc-bdi-denominator {
  padding: 12px 16px;
  font-size: 16px;
  line-height: 1.6;
  text-align: center;
  color: var(--text);
}

.oc-bdi-numerator {
  padding-bottom: 8px;
}

.oc-bdi-denominator {
  padding-top: 8px;
}

.oc-bdi-bar {
  width: 100%;
  height: 2px;
  background: var(--text);
  min-width: 120px;
  margin: 0 8px;
}

var {
  font-style: italic;
  font-family: Georgia, 'Times New Roman', serif;
  color: var(--primary);
}

/* Legenda dos Componentes */
.oc-bdi-legend {
  margin-bottom: 20px;
  padding: 16px;
  background: rgba(29, 95, 214, 0.04);
  border-radius: 8px;
}

.oc-bdi-legend-title {
  margin: 0 0 12px;
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.oc-bdi-legend-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
}

.oc-bdi-legend-item {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.oc-bdi-legend-var {
  font-weight: 600;
  font-style: italic;
  color: var(--primary);
  min-width: 36px;
  font-size: 14px;
}

.oc-bdi-legend-desc {
  font-size: 13px;
  color: var(--text-muted);
  flex: 1;
}

/* Valores Atuais */
.oc-bdi-values-details {
  margin-top: 16px;
}

.oc-bdi-values-summary {
  cursor: pointer;
  user-select: none;
  padding: 8px 12px;
  margin: -8px -12px 8px;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 500;
  color: var(--primary);
  transition: background 0.2s ease;
}

.oc-bdi-values-summary:hover {
  background: rgba(29, 95, 214, 0.1);
}

.oc-bdi-values-table {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  padding: 12px 0;
}

.oc-bdi-values-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  background: var(--bg);
  border-radius: 4px;
  font-size: 13px;
}

.oc-bdi-values-label {
  font-weight: 600;
  font-style: italic;
  color: var(--primary);
}

.oc-bdi-values-number {
  font-family: Menlo, Monaco, 'Courier New', monospace;
  font-weight: 600;
  color: var(--text);
}

/* Responsividade Mobile */
@media (max-width: 640px) {
  .oc-bdi-formula-wrapper {
    flex-direction: column;
    gap: 8px;
  }

  .oc-bdi-fraction {
    width: 100%;
    min-width: 200px;
    max-width: 100%;
  }

  .oc-bdi-numerator,
  .oc-bdi-denominator {
    font-size: 14px;
    padding: 8px 12px;
  }

  .oc-bdi-bar {
    min-width: 80px;
  }

  .oc-bdi-legend-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .oc-bdi-values-table {
    grid-template-columns: 1fr;
  }

  .oc-bdi-values-row {
    padding: 10px;
  }
}

/* V1.00.078 — Drag-and-Drop de Itens (ETAPA 1) */
.oc-item-drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  cursor: grab;
  color: var(--text-muted);
  user-select: none;
  font-size: 14px;
  font-weight: 600;
  margin-right: 8px;
  transition: color 0.2s ease;
}

.oc-item-drag-handle:hover {
  color: var(--primary);
}

.oc-item-drag-handle:active {
  cursor: grabbing;
}

tr[data-item-id].oc-dragging {
  opacity: 0.5;
  background: rgba(29, 95, 214, 0.1);
}

.oc-drop-indicator {
  height: 2px;
  background: var(--primary);
  margin: 2px 0;
  transition: background 0.1s ease;
}

.oc-drop-indicator-eap {
  height: 3px;
  background: #4caf50; /* Verde — drop válido em EAP */
  margin: 4px 0;
  box-shadow: 0 0 4px rgba(76, 175, 80, 0.5);
}

[data-is-eap-node="true"][data-drop-position="dentro"] {
  background: rgba(76, 175, 80, 0.1) !important;
  border-left: 3px solid #4caf50;
}

/* V1.00.078 — Modal de Movimento (ETAPA 6 — fallback mobile/teclado) */
.oc-modal-movimento {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}

.oc-modal-movimento-conteudo {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  width: min(480px, calc(100vw - 32px));
  max-height: min(600px, calc(100vh - 64px));
  overflow-y: auto;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}

.oc-modal-movimento-cabecalho {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 12px;
}

.oc-modal-movimento-cabecalho h2 {
  margin: 0;
  font-size: 16px;
  color: var(--text);
}

.oc-modal-movimento-fechar {
  background: none;
  border: none;
  font-size: 28px;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.oc-modal-movimento-fechar:hover {
  color: var(--text);
}

.oc-modal-movimento-opcoes {
  margin-bottom: 16px;
}

.oc-movimento-grupo {
  margin-bottom: 16px;
}

.oc-movimento-grupo label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.oc-movimento-grupo select {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  background: #fff;
  color: var(--text);
  box-sizing: border-box;
}

.oc-movimento-grupo select:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(29, 95, 214, 0.12);
}

.oc-btn-menu {
  display: block;
  width: 100%;
  padding: 9px 11px;
  margin-bottom: 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  text-align: left;
  transition: background 0.15s, border-color 0.15s;
}

.oc-btn-menu:hover {
  background: var(--bg);
  border-color: var(--primary);
}

.oc-btn-menu:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.oc-modal-movimento-acoes {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

.oc-modal-movimento-acoes .oc-btn {
  flex: 1;
  margin: 0;
}

/* ============================================================
   Ação bloqueada que AINDA responde ao clique
   ------------------------------------------------------------
   `disabled` nativo impede o evento de clique, então um botão
   desabilitado não consegue explicar por que está bloqueado — o
   usuário clica e nada acontece.

   `aria-disabled="true"` comunica o estado ao leitor de tela e
   mantém o botão focável e clicável, para que a página possa
   mostrar o motivo. A regra continua no backend; isto é só UX.
   ============================================================ */
.oc-btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  filter: grayscale(100%);
}
.oc-btn[aria-disabled="true"]:hover {
  /* Sem realce de ação destrutiva: o botão não vai executar nada. */
  background: inherit;
  border-color: inherit;
}

/* ============================================================
   Modal do RATEIO por Centro de Custo
   ------------------------------------------------------------
   A rolagem horizontal NÃO vinha da largura do modal. Vinha de
   duas coisas dentro dele:

   1. `<select>` tem largura intrínseca igual à da sua opção mais
      longa. Um centro chamado "001.03.04 - INSTALAÇÕES ELÉTRICAS,
      LÓGICAS E SISTEMAS COMPLEMENTARES" empurrava a coluna, a
      coluna empurrava a tabela, e o `.oc-table-wrap` (que tem
      overflow-x:auto global) respondia com a barra.
   2. `table-layout` automático deixa o conteúdo decidir a largura
      das colunas — exatamente o que não se quer aqui.

   A correção ataca as duas: `table-layout:fixed` faz as colunas
   obedecerem às larguras declaradas, e `min-width:0` nos campos
   permite que encolham abaixo do conteúdo. Sem isso, aumentar o
   modal só adiaria o problema para o próximo nome comprido.
   ============================================================ */
.oc-modal--rateio{
  width:min(1180px, calc(100vw - 40px));
  max-width:calc(100vw - 40px);
  max-height:calc(100vh - 96px);
  overflow-y:auto;          /* vertical é aceitável e esperado */
}

/* Sem barra horizontal aqui: as colunas passam a caber de fato,
   então o wrap não precisa mais rolar. */
.oc-modal--rateio .oc-table-wrap{
  overflow-x:visible;
}

/* ---- Distribuição das colunas ----------------------------------
   São 5 colunas (há também Atividade/Serviço). Centro de Custo
   recebe a maior fatia; % e Ação ficam compactos. */
.oc-modal--rateio .oc-rateio-tabela{
  width:100%;
  min-width:0;
  table-layout:fixed;       /* a causa nº 2 */
}
.oc-modal--rateio .oc-rateio-tabela th:nth-child(1),
.oc-modal--rateio .oc-rateio-tabela td:nth-child(1){ width:34%; }
.oc-modal--rateio .oc-rateio-tabela th:nth-child(2),
.oc-modal--rateio .oc-rateio-tabela td:nth-child(2){ width:28%; }
.oc-modal--rateio .oc-rateio-tabela th:nth-child(3),
.oc-modal--rateio .oc-rateio-tabela td:nth-child(3){ width:20%; }
.oc-modal--rateio .oc-rateio-tabela th:nth-child(4),
.oc-modal--rateio .oc-rateio-tabela td:nth-child(4){ width:12%; }
.oc-modal--rateio .oc-rateio-tabela th:nth-child(5),
.oc-modal--rateio .oc-rateio-tabela td:nth-child(5){ width:6%; text-align:center; }

/* ---- Campos que podem encolher ---------------------------------
   `min-width:0` é o que quebra a largura intrínseca do <select>
   (causa nº 1). O ellipsis mantém legível o que não coube. */
.oc-modal--rateio .oc-rateio-tabela select,
.oc-modal--rateio .oc-rateio-tabela input{
  width:100%;
  min-width:0;
  max-width:100%;
  box-sizing:border-box;
}
.oc-modal--rateio .oc-rateio-tabela select{
  text-overflow:ellipsis;
}
/* Valores financeiros alinhados à direita, como no resto do sistema. */
.oc-modal--rateio .oc-rateio-tabela td:nth-child(3) input,
.oc-modal--rateio .oc-rateio-tabela td:nth-child(4) input{
  text-align:right;
}

/* ---- Resumo e rodapé ------------------------------------------- */
.oc-modal--rateio .oc-rateio-resumo{
  display:flex;
  flex-wrap:wrap;
  gap:8px 20px;
  align-items:baseline;
}
.oc-modal--rateio .oc-modal-acoes{
  display:flex;
  justify-content:flex-end;
  gap:8px;
  margin-top:12px;
}

/* ---- Mobile: cada rateio vira um bloco -------------------------
   Abaixo de 640px (breakpoint já usado no projeto) cinco colunas
   não cabem de forma legível em nenhuma largura. Em vez de rolar
   lateralmente, cada linha vira um cartão vertical rotulado. */
@media (max-width:640px){
  .oc-modal--rateio{
    width:calc(100vw - 20px);
    max-width:calc(100vw - 20px);
  }
  .oc-modal--rateio .oc-rateio-tabela,
  .oc-modal--rateio .oc-rateio-tabela tbody,
  .oc-modal--rateio .oc-rateio-tabela tr,
  .oc-modal--rateio .oc-rateio-tabela td{
    display:block;
    width:100% !important;
  }
  .oc-modal--rateio .oc-rateio-tabela thead{
    /* Cabeçalho some: cada célula passa a se rotular sozinha. */
    position:absolute;
    width:1px;
    height:1px;
    overflow:hidden;
    clip:rect(0 0 0 0);
    white-space:nowrap;
  }
  .oc-modal--rateio .oc-rateio-tabela tr{
    border:1px solid var(--border);
    border-radius:8px;
    padding:10px;
    margin-bottom:10px;
  }
  .oc-modal--rateio .oc-rateio-tabela td{
    border:0;
    padding:4px 0;
  }
  /* O rótulo vem do atributo data-rot da própria célula. */
  .oc-modal--rateio .oc-rateio-tabela td[data-rot]::before{
    content:attr(data-rot);
    display:block;
    font-size:12px;
    color:var(--muted);
    margin-bottom:2px;
  }
  .oc-modal--rateio .oc-rateio-tabela td:nth-child(5){
    text-align:right;
  }
  .oc-modal--rateio .oc-modal-acoes{
    flex-direction:column-reverse;
  }
  .oc-modal--rateio .oc-modal-acoes .oc-btn{
    width:100%;
  }
}

/* Texto só para leitor de tela — usado em cabeçalhos de coluna que são
   visualmente óbvios (ícone de ação) mas precisam de nome acessível. */
.oc-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;
}

/* ===================== BARRA DE PROGRESSO DE IMPORTAÇÃO =====================
   Existe no HTML desde o carregamento, apenas oculta: criá-la só na resposta
   seria tarde, porque a resposta é justamente o que demora. A largura vem do
   progresso REAL vindo do backend — a transição só suaviza o salto entre lotes,
   nunca simula avanço. */
.oc-progress { margin: 16px 0 4px; }
.oc-progress[hidden] { display: none; }
.oc-progress-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-bottom: 8px; font-size: 14px;
}
.oc-progress-head strong { font-variant-numeric: tabular-nums; }
.oc-progress-track {
  height: 12px; width: 100%; background: var(--oc-border, #e5e7eb);
  border-radius: 999px; overflow: hidden;
}
.oc-progress-bar {
  height: 100%; width: 0; border-radius: 999px;
  background: var(--oc-primary, #2563eb);
  transition: width .2s ease;
}
.oc-progress-detail {
  margin-top: 8px; font-size: 13px; color: var(--oc-muted, #6b7280);
  font-variant-numeric: tabular-nums;
}
.oc-progress--erro .oc-progress-bar { background: var(--oc-danger, #dc2626); }
@media (prefers-reduced-motion: reduce) { .oc-progress-bar { transition: none; } }

/* ============ MENU [⋯] FLUTUANTE, SEM RECORTE ============
   O painel era em fluxo dentro de `.oc-table-wrap`, que tem `overflow-x:auto`
   para a rolagem horizontal da grade. Um filho de container com overflow
   recortado é cortado SEMPRE — z-index não resolve, porque o recorte acontece
   antes do empilhamento.
   A saída é tirar o painel do container: o JS o move para o <body> ao abrir e
   posiciona por `getBoundingClientRect()` do botão. `position:fixed` deixa as
   coordenadas relativas ao viewport, que é exatamente o que precisamos medir
   para decidir entre abrir para baixo ou para cima.
   O overflow da tabela NÃO foi alterado — a rolagem horizontal continua. */
.oc-menu-painel.oc-menu-flutuante{
  position:fixed;
  margin-top:0;
  z-index:1000;
  box-shadow:0 8px 24px rgba(0,0,0,.18);
  /* Só o MENU rola quando não cabe nem acima nem abaixo (viewport baixo);
     nenhuma opção fica escondida. */
  overflow-y:auto;
}

/* Popover de Centros de Custo da Curva ABC ("Múltiplos", V1.00.200).
   =================== POR QUE ISTO EXISTE ===================
   O painel antes era `.oc-eap-excluir-painel`: um bloco EM FLUXO dentro do
   <td>. Abrir empurrava a linha inteira para baixo — a linha do C_087535
   virava um bloco gigante com área vazia e a grade desalinhava.
   A correção NÃO é z-index: é tirar o painel do fluxo. Reusa o mecanismo
   flutuante já existente (`.oc-menu-painel.oc-menu-flutuante`, position:fixed
   posicionado por assets/menu-flutuante.js), só trocando as medidas — o menu
   [⋯] é uma lista estreita de ações, aqui é uma tabela Centro × Valor. */
.oc-ccpop-painel{
  /* §7 — largura que comporta "008.02.01 — Cimentício" + "R$ 123.456,78"
     sem truncar o valor. */
  min-width:320px;
  max-width:min(420px, calc(100vw - 24px));
  padding:10px 12px;
  /* Sobrescreve o flex-column do menu de ações: aqui o conteúdo é tabela. */
  display:block;
  gap:0;
  /* §24 — muitos Centros rolam DENTRO do popover; a tabela principal não se
     mexe. O JS mede depois deste teto, então o flip continua correto. */
  max-height:min(400px, calc(100vh - 32px));
}
.oc-ccpop-titulo{
  margin:0 0 6px;
  font-size:.85rem;
  font-weight:600;
  color:var(--muted);
}
/* §25/§26 — largura fixa para o valor (nunca corta) e o resto para o Centro,
   que trunca com reticências. O CÓDIGO vem primeiro justamente para sobreviver
   à truncagem; o rótulo completo fica no title. */
.oc-ccpop-painel table{ table-layout:fixed; width:100%; }
.oc-ccpop-painel th:last-child,
.oc-ccpop-painel td:last-child{
  width:118px;
  text-align:right;
  white-space:nowrap;
}
.oc-ccpop-rotulo{
  display:block;
  overflow:hidden;
  text-overflow:ellipsis;
  white-space:nowrap;
}
.oc-ccpop-painel tfoot td{
  font-weight:600;
  border-top:1px solid var(--border);
}
/* §14 — indicador de estado sem biblioteca nova nem emoji (glifo textual,
   como já feito no botão de adição). */
.oc-ccpop-chevron{ margin-left:5px; font-size:.8em; opacity:.75; }
.oc-ccpop-chevron::after{ content:'\25BE'; }
details[open] > summary .oc-ccpop-chevron::after{ content:'\25B4'; }
/* Separador entre grupos de ações do menu [⋯] (§50). */
.oc-menu-sep{
  height:1px;
  margin:4px 2px;
  background:var(--border);
}

/* ============ SERVIÇO INATIVO DO ORÇAMENTO ============
   Continua legível e continua mostrando quantidade/preço/valor: o usuário
   precisa ver O QUE está fora da conta. Só o tom muda — nada de riscar o texto
   nem de esconder a linha, que confundiria "desativado" com "excluído". */
/* ============ O ESMAECIDO É DO CONTEÚDO, NUNCA DA LINHA INTEIRA ============
   A primeira versão era `.oc-item-inativo { opacity: .55 }` na LINHA. Dois
   estragos, não um:

   1. `opacity` é herdada por composição: o menu [⋯], que vive dentro da
      linha, era pintado a 55% junto com ela — a tabela aparecia ATRAVÉS das
      opções, e tudo parecia desabilitado.
   2. `opacity < 1` cria stacking context próprio. Nenhum `z-index` no menu
      escapa dele — por isso subir z-index não resolveria.

   Agora o esmaecido atinge só as CÉLULAS DE DADOS. A célula de ações e o
   menu ficam de fora por seletor, não por sobrescrita — não há opacity
   nenhuma para anular depois. */
.oc-item-inativo > td:not(.oc-actions-cell),
.oc-item-inativo > span:not(.oc-eap-c-acoes),
.oc-item-inativo > .oc-eap-c-descricao {
  opacity: .55;
  color: var(--muted, #5b6270);
}

/* O botão [⋯] continua legível numa linha inativa: a linha pode estar
   apagada, a porta de saída dela não. */
.oc-item-inativo .oc-actions-cell,
.oc-item-inativo .oc-eap-c-acoes,
.oc-item-inativo details.oc-menu,
.oc-item-inativo details.oc-menu * {
  opacity: 1;
}

/* O painel já define `background: var(--surface)` lá em cima — não repito o
   valor aqui para não criar duas fontes da mesma cor. Só reafirmo a opacidade,
   que é o que a linha inativa contaminava (§12). */
.oc-menu-painel { opacity: 1; }


/* ============ GRUPOS EXPANSÍVEIS DA SIDEBAR ============
   <details>/<summary> nativo: abre/fecha, responde a Enter e Espaço e entra na
   ordem de Tab sem uma linha de JavaScript — o mesmo padrão que o projeto já
   usa nos filtros e no menu [⋯]. */
.oc-nav-grupo > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  list-style: none;          /* esconde o triângulo padrão do navegador */
  user-select: none;
}
.oc-nav-grupo > summary::-webkit-details-marker { display: none; }

/* A linha inteira é clicável, não só o chevron. */
.oc-nav-grupo > summary { width: 100%; }

.oc-nav-chevron {
  margin-left: auto;
  flex: 0 0 auto;
  /* Sem `transition: transform`: a seta nao gira mais, o glifo TROCA. Uma
     transicao aqui nao teria o que animar e so deixaria codigo morto. */
}
/* Mesmo par de glifos da árvore de EAP (`:root`) — a mesma função nunca tem
   dois desenhos. Antes era `›` girado 90°: além de ser um dos indicadores que
   o padrão proíbe, a rotação deixava o traço na diagonal durante a transição,
   sem nunca coincidir com um triângulo. */
.oc-nav-chevron::before { content: var(--oc-seta-fechado); }
.oc-nav-grupo[open] .oc-nav-chevron::before { content: var(--oc-seta-aberto); }

/* Filhos recuados o suficiente para ler como hierarquia, sem desperdiçar
   largura numa sidebar estreita. */
.oc-nav-filhos { display: flex; flex-direction: column; }
.oc-nav a.oc-nav-filho { padding-left: 30px; font-size: .95em; }

/* O grupo que contém a página atual fica marcado mesmo depois de fechado —
   sem isso, fechá-lo apagaria a única pista de onde o usuário está. */
.oc-nav-grupo--ativo > summary { font-weight: 600; }

/* ---- Sidebar recolhida: só ícones ---- */
body.oc-sidebar-collapsed .oc-nav-chevron { display: none; }
body.oc-sidebar-collapsed .oc-nav-grupo > summary { justify-content: center; }
/* Recolhida, os filhos ficariam ilegíveis (texto escondido, recuo sem sentido).
   O grupo continua acessível: clicar nele abre, e o tooltip do ícone diz qual é. */
body.oc-sidebar-collapsed .oc-nav a.oc-nav-filho { padding-left: 0; justify-content: center; }

/* ============ [hidden] GANHA DE QUALQUER `display` DE AUTOR ============
   ⚠️ O comentário desta seção prometia uma regra global, mas a regra em si não
   existia — só as duas exceções por componente logo abaixo. Achado ao medir o
   botão "Ver problemas" do Resumo: `hidden = true` no DOM e 122×36 px na tela
   ao mesmo tempo, porque `.oc-btn` declara `display:flex` e qualquer `display`
   de autor vence o `[hidden]` da folha do user-agent.

   O efeito era pior que cosmético: o link "Ver problemas" aparecia ANTES de a
   validação responder, dizendo que havia problemas quando ainda não se sabia.

   `!important` aqui é a exceção certa — `[hidden]` é uma afirmação do HTML
   sobre o elemento não estar na página, e nenhuma regra de apresentação
   deveria poder contradizê-la. */
[hidden] {
  display: none !important;
}

/* As regras por componente abaixo ficaram redundantes com a global acima.
   Mantidas porque documentam onde o problema ja mordeu. */


/* Mesma armadilha, agora nos campos de formulario: `.oc-field` declara
   `display:flex`, que vence o `[hidden]` da folha do user-agent. Foi o que
   fez os campos exclusivos da Verba continuarem visiveis com a origem
   Insumo selecionada (medido no navegador: hidden=true e offsetParent
   diferente de null ao mesmo tempo). Regra generica de proposito — qualquer
   tela que esconda um campo por atributo depende disto. */
.oc-field[hidden],
.oc-panel-hint[hidden] {
  display: none;
}













/* ============================================================
   Seletor de Origem (includes/origem_picker.php)
   Compartilhado por novo-item-orcamento.php e editar-composicao.php.
   ============================================================ */

/* O grupo de radios é um <fieldset> por semântica (§37): o leitor de tela
   precisa anunciar "Origem" antes de cada opção, e só <legend> faz isso. O
   fieldset nativo traz borda, margem lateral e padding próprios — que não têm
   nada a ver com o resto do formulário. Aqui ele volta a se comportar como
   qualquer .oc-field. */
.oc-fieldset-campo{
  border:0;
  margin:0;
  padding:0;
  min-width:0; /* fieldset ignora min-width:auto de item de grid em vários
                  navegadores; sem isto um conteúdo largo estoura a coluna. */
}

.oc-fieldset-campo > legend{
  padding:0;
  font-size:12.5px;
  font-weight:600;
  color:var(--text-muted);
}

/* Em telas estreitas os radios quebram de linha em vez de espremer o texto
   (§36) — "Composição Auxiliar" é longo o bastante para exigir isso. */
.oc-radio-group{
  flex-wrap:wrap;
}

/* ============ A LISTA DE RESULTADOS ============
   Era um <select size="8">. Um <option> é sempre renderizado em UMA linha pelo
   navegador — `white-space: normal` não tem efeito nele —, então descrições
   longas ficavam cortadas na largura do campo, sem tooltip e sem como ler.
   Agora é um listbox de verdade (ul/li com role), onde cada resultado é uma
   caixa comum e pode ocupar quantas linhas precisar. */
.oc-origem-lista{
  list-style:none;
  margin:0;
  padding:2px;
  width:100%;
  /* Altura por TETO, não fixa: com dois resultados a lista fica com a altura de
     dois; com vinte, rola. O <select size="8"> reservava as 8 linhas sempre. */
  max-height:320px;
  overflow-y:auto;
  overflow-x:hidden;      /* ler descrição nunca deve exigir rolagem lateral */
  border:1px solid var(--border);
  border-radius:8px;
  background:var(--surface);
}

.oc-origem-lista:empty{
  display:none;           /* sem resultados, nenhuma moldura vazia na tela */
}

.oc-origem-opcao{
  display:flex;
  align-items:baseline;
  gap:10px;
  padding:6px 8px;
  border-radius:6px;
  font-size:13.5px;
  line-height:1.45;       /* respira quando a descrição ocupa 2-3 linhas */
  cursor:pointer;
}

.oc-origem-opcao + .oc-origem-opcao{
  border-top:1px solid var(--border);
}

/* O código nunca quebra no meio: é um identificador, não texto corrido. */
.oc-origem-opcao-codigo{
  flex:0 0 auto;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
  font-weight:600;
  color:var(--text);
}

/* A descrição fica com toda a largura restante e quebra à vontade.
   `min-width:0` é o que permite um item de flex encolher abaixo do próprio
   conteúdo — sem ele o texto empurraria a linha e criaria rolagem lateral.
   `overflow-wrap:anywhere` cobre o caso de uma palavra única gigante. */
.oc-origem-opcao-desc{
  flex:1 1 auto;
  min-width:0;
  white-space:normal;
  overflow-wrap:anywhere;
  color:var(--text-muted);
}

.oc-origem-opcao:hover{
  background:rgba(29,95,214,.08) /* tom de --primary a 8% — o projeto nao tem token de hover */;
}

/* A opção corrente continua destacada mesmo com o foco na caixa de busca —
   é lá que o foco fica, no padrão combobox. */
.oc-origem-opcao[aria-selected="true"]{
  background:var(--primary);
}

.oc-origem-opcao[aria-selected="true"] .oc-origem-opcao-codigo,
.oc-origem-opcao[aria-selected="true"] .oc-origem-opcao-desc{
  color:#fff;
}

/* Depois de escolher, a descrição inteira segue à vista: a caixa de busca
   mostraria só o começo dela. */
.oc-origem-escolhido{
  margin:6px 0 0;
  padding:6px 8px;
  border-radius:6px;
  background:rgba(29,95,214,.08) /* tom de --primary a 8% — o projeto nao tem token de hover */;
  font-size:12.5px;
  line-height:1.45;
  color:var(--text);
  overflow-wrap:anywhere;
}

.oc-origem-escolhido-desc{
  color:var(--text-muted);
}

/* ============================================================
   "Adicionar item" da Composição — largura útil
   ============================================================
   `.oc-form` limita todo formulário do projeto a 760px, o que é bom para
   formulários de cadastro (linha de leitura curta) e ruim aqui: este painel tem
   ~1060px e sobrava um terço vazio à direita, enquanto as descrições do catálogo
   quebravam em 3 linhas ao lado do espaço não usado.

   O alargamento é SÓ deste formulário — mudar `.oc-form` mexeria em todas as
   telas do sistema. E não substitui a quebra de linha: com 1060px uma descrição
   de 237 caracteres ainda ocupa 2 linhas; a quebra é que resolve, a largura só
   reduz quantas. */
.oc-composicao-form-adicionar{
  max-width:none;
}

/* Sem isto o Coeficiente herdaria 1/3 da largura nova (~340px) para caber 8
   dígitos — desproporcional ao lado de um campo de busca. */
.oc-composicao-form-adicionar > .oc-field:not(.oc-field-full):not(.oc-fieldset-campo){
  max-width:260px;
}

/* ============================================================
   Relatório de erros da importação de Composições
   ============================================================ */
.oc-erros-resumo{
  list-style:none;
  margin:0 0 14px;
  padding:0;
  display:flex;
  flex-wrap:wrap;
  gap:8px;
}

.oc-erros-resumo li{
  padding:4px 10px;
  border:1px solid var(--border);
  border-radius:999px;
  background:var(--bg);
  font-size:12.5px;
  font-weight:600;
  color:var(--text);
}

.oc-erros-filtros{
  display:flex;
  flex-wrap:wrap;      /* em tela estreita cada filtro desce, sem scroll lateral */
  align-items:flex-end;
  gap:12px;
  margin-bottom:12px;
}

.oc-erros-filtros .oc-field{
  min-width:170px;
}

.oc-erros-baixar{
  margin-left:auto;    /* a ação fica na ponta, longe dos filtros */
}

.oc-erros-paginacao{
  display:flex;
  align-items:center;
  gap:12px;
  margin-top:12px;
}

/* A tabela de erros tem uma coluna de texto longo (mensagem + ação sugerida);
   sem isto ela empurraria a largura e criaria rolagem lateral. */
#oc-erros-corpo td{
  vertical-align:top;
  overflow-wrap:anywhere;
}

/* ============================================================
   Tabela dentro de formulário
   ============================================================
   `.oc-form` é um GRID de campos: `repeat(auto-fit, minmax(220px,1fr))` com
   `max-width:760px`. Isso é certo para campos — limita a linha de leitura e
   distribui os inputs em colunas.

   Uma tabela colocada dentro dele virava um item desse grid como qualquer
   outro: numa área de 1.281px, o formulário parava em 760px, o grid fazia 3
   colunas de 241px e a tabela ficava com UMA delas. A pré-visualização da
   importação de EAP+Serviços precisava de 2.143px e recebia 241px — 19% do
   espaço disponível, com o resto vazio à direita e todo o conteúdo empurrado
   para uma rolagem horizontal interna.

   Uma tabela nunca é um campo: é conteúdo de linha inteira. Vale para as três
   telas onde isso acontece (importação de EAP+Serviços, distribuição por marcos
   e o rateio da movimentação), por isso a regra é geral e não por página. */
.oc-form > .oc-table-wrap{
  grid-column:1 / -1;
  /* Item de grid: sem `min-width:0` uma tabela larga empurra a própria coluna
     e reintroduz a rolagem horizontal na página inteira. */
  min-width:0;
  width:100%;
}

/* E o formulário que CONTÉM uma tabela deixa de valer o teto de 760px — esse
   limite existe para linha de leitura de campos, não para dados tabulares. */
.oc-form:has(> .oc-table-wrap){
  max-width:none;
}

/* Os campos comuns desse formulário não devem esticar junto: um <select> de
   1.200px ao lado de uma tabela fica desproporcional. */
.oc-form:has(> .oc-table-wrap) > .oc-field > select,
.oc-form:has(> .oc-table-wrap) > .oc-field > input:not([type="radio"]):not([type="checkbox"]){
  max-width:420px;
}

/* ---- Pré-visualização da importação de EAP + Serviços ----
   `table.oc-table th/td` traz `white-space:nowrap` global, o que é certo para
   listagens de colunas curtas e errado aqui: Descrição e Status são texto
   corrido, e mantê-los numa linha só empurrava a tabela para 2.143px. Deixar
   ESSAS duas quebrarem faz a tabela caber na largura da tela; as colunas de
   CÓDIGO seguem sem quebra, porque um código partido no meio deixa de ser
   identificável. */
#oc-tabela-preview{
  table-layout:auto;
}

#oc-tabela-preview td[data-col="descricao"],
#oc-tabela-preview td[data-col="status"],
#oc-tabela-preview td[data-col="centro"]{
  white-space:normal;
  overflow-wrap:anywhere;
  min-width:180px;
}

/* Tipo, códigos e linha: nunca quebram. */
#oc-tabela-preview td[data-col="linha"],
#oc-tabela-preview td[data-col="tarefa"],
#oc-tabela-preview td[data-col="tipo"],
#oc-tabela-preview td[data-col="origem"]{
  white-space:nowrap;
}

/* Quantidade é número: alinhada à direita, como no resto do sistema. */
#oc-tabela-preview td[data-col="quantidade"],
#oc-tabela-preview th[data-col="quantidade"]{
  text-align:right;
  font-variant-numeric:tabular-nums;
}

/* ============================================================
   Itens da Composição (editar-composicao.php)
   ============================================================
   `table.oc-table th/td` traz `white-space:nowrap` global — certo para
   listagens de colunas curtas, errado aqui. Medido numa tela de 1.600px:

       espaço no card ..... 1.239px
       tabela precisava ... 2.041px   → rolagem horizontal
       coluna Descrição ... 1.297px   ← sozinha, numa linha só

   Uma descrição de 178 caracteres empurrava a tabela inteira e jogava
   Coeficiente, Valores e Ações para fora da área visível. */
.oc-tabela-itens-composicao{
  /* `fixed` é o que impede o conteúdo de ditar a largura: as colunas passam a
     obedecer o que está definido abaixo, e a Descrição fica com a sobra. */
  table-layout:fixed;
  width:100%;
}

/* Colunas de largura previsível — todas cabem no seu conteúdo real. */
/* 136px porque a badge mais larga é "Composição Auxiliar" (130px): o rótulo
   é identificação e não pode quebrar nem ser cortado (§6). */
.oc-tabela-itens-composicao th[data-col="origem"]{ width:136px; }
.oc-tabela-itens-composicao th[data-col="codigo"]{ width:116px; }
.oc-tabela-itens-composicao th[data-col="unidade"]{ width:76px; }
.oc-tabela-itens-composicao th[data-col="coeficiente"]{ width:132px; }
.oc-tabela-itens-composicao th[data-col="unitario"]{ width:140px; }
.oc-tabela-itens-composicao th[data-col="parcial"]{ width:132px; }
.oc-tabela-itens-composicao th[data-col="acoes"]{ width:76px; }  /* ícone, não texto — sobra vira Descrição */
/* Descrição não declara largura: com table-layout:fixed ela absorve a sobra. */

/* A Descrição é o único texto corrido da tabela — e o único que quebra. */
.oc-tabela-itens-composicao td[data-col="descricao"]{
  white-space:normal;
  overflow-wrap:anywhere;
  word-break:normal;      /* quebra por palavra; só parte palavra sem saída */
  line-height:1.45;       /* respira quando a descrição ocupa 2-3 linhas */
}

/* Códigos e badges de origem seguem inteiros: são identificadores, e um código
   partido no meio deixa de ser reconhecível. */
.oc-tabela-itens-composicao td[data-col="origem"],
.oc-tabela-itens-composicao td[data-col="codigo"],
.oc-tabela-itens-composicao td[data-col="unidade"]{
  white-space:nowrap;
}

/* Numéricos à direita, sem quebra: "1,234.56789012" não pode partir em duas
   linhas. As três colunas alinham entre si com dígitos de largura fixa. */
.oc-tabela-itens-composicao td[data-col="coeficiente"],
.oc-tabela-itens-composicao th[data-col="coeficiente"]{
  text-align:right;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}

/* As células de topo: com a Descrição ocupando várias linhas, conteúdo
   centralizado verticalmente ficaria desalinhado do texto ao lado. */
.oc-tabela-itens-composicao td{
  vertical-align:top;
}

/* Em tela estreita as colunas fixas somam mais que o disponível: aí a rolagem
   interna do .oc-table-wrap entra, e só ela — a PÁGINA nunca rola de lado. */
@media (max-width: 900px){
  .oc-tabela-itens-composicao{
    table-layout:auto;
    min-width:820px;
  }
}

/* Cabeçalhos podem quebrar: "Valor Unitário (Onerado)" precisa de 188px numa
   linha só, e reservar isso roubaria largura da Descrição em todas as linhas
   por causa de um rótulo. Em duas linhas ele cabe em 140px.

   O seletor precisa de `table.` na frente: a regra global é
   `table.oc-table th` (0,1,2) e venceria um `.classe th` (0,1,1). Foi
   exatamente o que aconteceu na primeira tentativa — o cabeçalho continuou
   sem quebrar e a coluna seguiu estourando. */
table.oc-tabela-itens-composicao th{
  white-space:normal;
  vertical-align:bottom;
}

/* A etiqueta "sem preço p/ UF" acompanha o valor: em vez de esticar a coluna,
   ela desce para a linha de baixo dentro da própria célula. */
.oc-tabela-itens-composicao td[data-col="unitario"]{
  white-space:normal;
}

/* ============================================================
   Confirmação digitada para exclusões em massa
   ============================================================
   Um só visual para Composições, Insumos e qualquer exclusão destrutiva
   futura — o markup vem de ocConfirmacaoDestrutivaHtml(), então não há como
   uma tela divergir da outra sem alguém decidir isso de propósito.

   O bloco é vermelho porque a ação é destrutiva, mas o estado do botão NÃO é
   comunicado por cor: ele carrega `disabled` de verdade. Quem navega por
   teclado esbarra num botão inoperante, e quem usa leitor de tela ouve
   "indisponível" — nenhum dos dois depende de enxergar o tom. */
.oc-confirmar-destrutivo{
  grid-column:1 / -1;
  margin-top:14px;
  padding:14px 16px;
  border:1px solid #e8cccb;
  border-left:3px solid #c5192d;
  border-radius:6px;
  background:#fef6f6;
}

.oc-confirmar-destrutivo-aviso{
  margin:0 0 10px;
  font-size:13px;
  line-height:1.5;
  color:var(--text);
}

.oc-confirmar-destrutivo code{
  padding:1px 6px;
  border-radius:4px;
  background:#fff;
  border:1px solid #e8cccb;
  font-weight:700;
  letter-spacing:.04em;
}

.oc-confirmar-destrutivo .oc-field{ max-width:280px; }

/* Fonte tabular e caixa alta ajudam a conferir o que se digitou — e deixam
   evidente que `excluir` minúsculo não é a mesma coisa. */
.oc-confirmar-destrutivo input{
  text-transform:uppercase;
  letter-spacing:.08em;
  font-variant-numeric:tabular-nums;
}

.oc-confirmar-destrutivo input[aria-invalid="true"]{
  border-color:#c5192d;
}

/* Botão desabilitado: o estado real já existe no HTML; isto só o torna
   visível. */
.oc-btn:disabled,
.oc-btn[disabled]{
  opacity:.5;
  cursor:not-allowed;
}

/* ============================================================
   Barras padrão das listagens (topo e rodapé)
   ============================================================
   Uma estrutura só para todas as tabelas de cadastro:

     topo   [ N registro(s) encontrado(s) ]  [ Exibir N por página ]  [ Colunas ]
     rodapé [ Mostrando X a Y de Z registros ]              [ « ‹ 1 2 3 › » ]

   Antes cada listagem inventava a sua frase acima da tabela — "Cadastro",
   "69 resultado(s)", "17949 resultado(s) — Página 1 de 898" — e o botão de
   Colunas flutuava sozinho num canto diferente em cada tela.

   Grid de três faixas e não flex com `space-between`: com espaço sobrando, o
   `space-between` empurra o bloco do meio para longe do centro conforme o
   texto da esquerda cresce, e "Exibir" dança de lugar entre 0 e 17.949
   registros. As três faixas seguram cada bloco no seu lugar. */
.oc-listagem-barra{
  /* ⚠️ Eram TRÊS faixas (1fr auto 1fr) para segurar o seletor no centro.
     Com o seletor movido para junto de "Colunas", a faixa do meio deixou
     de ter conteúdo — e uma coluna vazia entre os dois blocos abriria um
     vão que nada preenche. Duas faixas: contagem à esquerda, controles da
     tabela à direita. */
  display:grid;
  grid-template-columns:1fr auto;
  align-items:center;
  gap:10px 16px;
  /* Era 12px. A barra e o cabecalho da tabela sao a mesma unidade de
     leitura — 8px basta para separa-los sem abrir um vao. */
  margin:0 0 8px;
}

.oc-listagem-barra--rodape{
  /* Era 14px + 12px = 26px acima do texto. O rodape passa a ter o mesmo
     respiro da toolbar (§16: altura compacta e padding coerente). */
  margin:10px 0 0;
  padding-top:8px;
  /* No rodapé são dois blocos: intervalo à esquerda, navegação à direita. */
  grid-template-columns:1fr auto;

  /* ============ SEM `border-top` — A LINHA JÁ EXISTIA ============
     ⚠️ Aqui havia `border-top:1px solid var(--border)`, e ele era a "linha
     sobrando" no fim das tabelas. Não havia `<tr>` fantasma nem borda dupla
     numa célula: eram DUAS bordas de elementos diferentes, a 18px uma da outra
     — o `border-bottom` do último registro (que toda célula tem) e este
     `border-top` do rodapé. Lidas juntas, pareciam uma linha vazia entre a
     tabela e o "Mostrando X a Y".

     Some o `border-top`, e não o `border-bottom` do último registro, por três
     razões concretas — todas descobertas ao auditar antes de escrever a regra
     óbvia (`tbody tr:last-child td{border-bottom:0}`):

       · Há tabelas `.oc-table` com `<tfoot>` (Curva ABC, Explosão de
         Composição, editor de Composição). Tirar a borda do último registro
         colaria a linha "Total" no último item, sem divisória — justamente
         onde ela mais importa;
       · Há `.oc-table` ANINHADA dentro de `<td>` de outra `.oc-table` (Curva
         ABC). Um `tr:last-child` sem combinador de filho alcançaria as duas;
       · A tabela rola na horizontal dentro de `.oc-table-wrap`. A borda do
         registro acompanha a LARGURA DA TABELA; esta acompanhava a do
         container. Com scroll, as duas terminavam em pontos diferentes (§22).

     Tirando o `border-top`, a divisória entre dados e rodapé passa a ser a
     borda que o último registro já desenha — uma linha só, na largura certa,
     sem exceção para tfoot, tabela aninhada ou tabela vazia. */
}

.oc-listagem-total,
.oc-listagem-intervalo{
  margin:0;
  font-size:13px;
  color:var(--text-muted);
  font-variant-numeric:tabular-nums;
}

.oc-listagem-total{ font-weight:600; color:var(--text); }

/* ---- Faixa esquerda: filtros ativos ANTES da contagem ----
   A ordem de leitura é [o recorte] → [o resultado do recorte]. Antes os chips
   vinham depois do número (ou dentro do painel de filtros, mais acima), e o
   usuário lia "34 registro(s)" sem saber a que filtro aquilo respondia.

   O `gap` maior (18px) separa os dois grupos de informação: os chips são um
   conjunto de controles clicáveis, a contagem é texto. Com o gap de 8px dos
   chips entre si, eles pareceriam a mesma lista. */
.oc-listagem-barra-esquerda{
  display:flex;
  align-items:center;
  flex-wrap:wrap;
  gap:8px 18px;
  min-width:0;
}

/* Dentro da barra os chips não têm margem de bloco — quem espaça é a faixa. */
.oc-listagem-chips{
  display:contents;
}
.oc-listagem-chips .oc-chips{
  margin-top:0;
}
/* Compartimento vazio (nenhum filtro ativo, ou carcaça assíncrona antes do
   fragmento) não abre vão nenhum. */
.oc-listagem-chips:empty{ display:none; }

/* `.oc-listagem-barra-centro` foi removida junto com a faixa do meio: o
   seletor agora vive dentro de `.oc-listagem-barra-direita`.

   ⚠️ O `gap` era 8px e colava "Exibir N por página" em "Colunas" — os dois
   liam-se como um controle só. 16px é o mesmo respiro que separa os campos de
   filtro, e o botão "Compactar", que agora entra entre eles, também funciona
   como separador visual. Espaçamento pelo `gap` do grupo, nunca por margens
   individuais em cada controle. */
.oc-listagem-barra-direita{ justify-self:end; display:flex; align-items:center; gap:16px; }

/* O formulário de "Exibir N por página" vive dentro da barra: nada de grid
   próprio nem margem de bloco, senão ele desalinha dos vizinhos. */
.oc-listagem-barra .oc-form--inline{
  display:flex;
  align-items:center;
  gap:6px;
  margin:0;
  max-width:none;
}

.oc-listagem-barra .oc-form--inline label,
.oc-listagem-barra .oc-form--inline span{
  margin:0;
  font-size:13px;
  color:var(--text-muted);
  white-space:nowrap;
}

/* ---- Seletor de quantidade por página ----
   ⚠️ CLASSE COMPARTILHADA, e não um ajuste em Insumos.

   O `border-radius` vinha da regra genérica de `select` (6px) e destoava dos
   botões vizinhos da mesma toolbar. Corrigi-lo só na tela onde ele foi notado
   criaria duas aparências para o MESMO controle — que é exatamente o problema
   que a barra compartilhada existe para não ter. `.oc-per-page-select` é
   emitida por `ocListagemFormPorPagina()`, então toda listagem do sistema
   herda isto de uma vez.

   Altura, padding e alinhamento acompanham `.oc-btn-sm` da mesma linha: os
   três controles (seletor, Compactar, Colunas) precisam dividir a mesma linha
   de base, senão a toolbar parece desalinhada mesmo estando centrada. */
.oc-listagem-barra .oc-form--inline select,
.oc-per-page-select{
  min-height:var(--oc-toolbar-controle-h);
  height:var(--oc-toolbar-controle-h);
  padding:4px 30px 4px 10px;
  width:auto;
  border-radius:8px;
  vertical-align:middle;
  /* A seta nativa varia de tamanho entre navegadores e não acompanha a altura
     reduzida do controle. Uma seta desenhada mantém proporção e cor coerentes
     com o resto da toolbar; `background-size` fixa o tamanho dela. */
  appearance:none;
  -webkit-appearance:none;
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath fill='%236b7280' d='M1.4 1.6 6 6.2l4.6-4.6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 10px center;
  background-size:10px 7px;
}

.oc-per-page-select:hover{
  border-color:var(--primary);
}

/* O foco NUNCA é só a mudança de cor da borda: quem navega por teclado precisa
   de um anel visível também em alto contraste. Mesmo par usado nos demais
   controles do design system. */
.oc-per-page-select:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:1px;
  border-color:var(--primary);
}

/* A toolbar do gerenciador de colunas perde a margem que tinha quando ficava
   solta acima da tabela — dentro da barra ela é só o botão. */
.oc-listagem-barra-direita .oc-tm-toolbar{ margin:0; }

/* ---- "Compactar" ----
   Controle SECUNDÁRIO da tabela: mesma família visual de "Colunas" (mesmo
   `.oc-btn-sm`, mesmo corpo de texto, mesma altura), nunca a ênfase de uma
   ação primária. A altura é fixada junto com a do seletor para que os três
   controles compartilhem a linha de base. */
.oc-listagem-compactar{
  font-size:12.5px;
  min-height:var(--oc-toolbar-controle-h);
  height:var(--oc-toolbar-controle-h);
  display:inline-flex;
  align-items:center;
  gap:6px;
  white-space:nowrap;
}

.oc-listagem-compactar:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

/* ---- Telas estreitas: empilhar sem espremer ----
   Media query e não container query: `.oc-panel` não é um contêiner de
   consulta, e transformá-lo em um (`container-type:inline-size`) mexeria em
   TODOS os painéis do sistema para resolver o alinhamento de uma barra. Aqui
   a largura da janela basta — são três blocos curtos, não uma grade de
   campos como a dos filtros.

   ⚠️ O corte era 860px e empilhava cedo demais: medido em 768px (tablet), os
   três blocos somam ~520px numa faixa de 679px — cabiam folgados e mesmo assim
   viravam três linhas, gastando 100px de altura à toa justo onde a tela é mais
   curta. 640px é onde eles realmente começam a se espremer. */
@media (max-width: 640px){
  .oc-listagem-barra,
  .oc-listagem-barra--rodape{
    grid-template-columns:1fr;
    justify-items:start;
  }
  .oc-listagem-barra-direita{ justify-self:stretch; }
  .oc-listagem-barra-direita{ justify-content:flex-start; flex-wrap:wrap; }

  /* ---- Prioridade de leitura em tela estreita ----
     Linha 1 — filtros ativos (o recorte).
     Linha 2 — quantidade de registros (o resultado).
     Linha 3 — Exibir/Compactar/Colunas (as ferramentas).

     Empilhar, e não espremer: os chips têm largura imprevisível (um termo
     digitado pode ser longo) e disputariam a linha com a contagem, cortando um
     dos dois. `min-width:0` + `max-width:100%` mantêm tudo DENTRO do card. */
  .oc-listagem-barra-esquerda{
    flex-direction:column;
    align-items:flex-start;
    gap:8px;
    width:100%;
    max-width:100%;
  }
  .oc-listagem-chips .oc-chips{ max-width:100%; }
}

/* Termo longo não estoura o card nem sobrepõe a contagem: o chip quebra o
   texto em vez de empurrar a linha. Vale em qualquer largura — um critério de
   80 caracteres cabe mal até em 1920px. */
.oc-listagem-chips .oc-chip{
  max-width:min(100%, 320px);
  overflow-wrap:anywhere;
  text-align:left;
}

/* Área direita da toolbar: o compartimento do botão "Colunas" (que o
   table-manager esvazia e repovoa) e o das ações da página, lado a lado.
   Separados porque o JS limpa o primeiro — juntos, as ações sumiriam. */
.oc-listagem-barra-colunas,
.oc-listagem-barra-acoes{
  display:flex;
  align-items:center;
  gap:8px;
}

/* Os dois botões andam juntos: numa quebra de linha eles descem como um par,
   não um em cada linha. */
.oc-listagem-barra-direita{ flex-wrap:nowrap; }

/* O seletor "Exibir N por página" e o botão "Colunas" são controles da
   mesma natureza e agora andam colados: mesma altura, mesma linha de base,
   mesmo espaçamento do grupo (gap de `.oc-listagem-barra-direita`). */
.oc-listagem-barra-direita .oc-form--inline{ margin:0; }

@media (max-width: 860px){
  .oc-listagem-barra-direita{ flex-wrap:wrap; }
}

/* Título da listagem: mora FORA do card, entre o box de filtros e o da
   tabela. Menor que o título da página (que já nomeia a tela) — aqui ele só
   diz o que ESTA tabela lista. */
.oc-listagem-titulo{
  margin:22px 0 10px;
  font-size:15px;
  font-weight:600;
  color:var(--text);
}

/* Linha do título da listagem: nome à esquerda, ação de conjunto à direita.
   Flex, não float nem margem negativa — o título cresce, o botão fica colado
   na direita, e em tela estreita o botão desce sozinho sem estourar o card. */
.oc-listagem-cabecalho{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:12px;
  flex-wrap:wrap;
  /* Era 22px/10px. O titulo pertence ao box que vem logo abaixo; 16px
     acima o separa do bloco de filtros e 8px abaixo o cola no seu box. */
  margin:16px 0 8px;
}

/* Dentro da linha, o título perde a margem própria: quem espaça é o contêiner. */
.oc-listagem-cabecalho .oc-listagem-titulo{ margin:0; }

/* Aviso do modal de distribuição: informação, não erro. Fundo âmbar discreto —
   a operação continua permitida, então nada de vermelho. */
.oc-dist-aviso{
  margin:0 0 10px;
  padding:8px 12px;
  border-left:3px solid #d9a400;
  background:#fffbf0;
  border-radius:4px;
  font-size:13px;
  line-height:1.5;
  color:var(--text);
}

/* ============ FILTRO VISUAL "OCULTAR ITENS ZERADOS" ============
   Uma classe só, aplicada pelo JS (assets/js/eap-zerados.js), tanto na linha
   de Serviço quanto no <details> do nó de EAP que ficou sem descendente
   visível. `display:none` e não `visibility`/opacidade: a linha não pode
   ocupar espaço nem ser alcançada por tabulação enquanto está filtrada.

   ⚠️ Isto esconde, nunca exclui. Os subtotais dos nós e o total do Orçamento
   continuam vindo do servidor com TODOS os itens. */
.oc-eap-oculto-zerado {
  display: none !important;
}

/* ============ ALTERAÇÃO EM LOTE ============
   Marca de linha alterada e erro por linha. Discreto de propósito (§11): a
   informação útil é "mexi nesta", não um alarme. */
.oc-lote-alterado {
  border-color: var(--primary);
  background: var(--surface-2, #f4f8ff);
}
.oc-lote-linha-erro td {
  background: #fff6f6;
}
.oc-erro-inline {
  display: block;
  margin-top: 4px;
  font-size: 12px;
  color: #b3261e;
  text-align: right;
}

/* ============ ASSISTENTE DE ALTERAÇÃO EM LOTE (por etapa da EAP) ============
   §11 — Serviço e Verba precisam ser distinguíveis de relance: é o badge que
   explica por que uma linha tem seletor de Composição e a outra tem "—". */
.oc-tag-servico {
  color: #1a4f8a;
  background: #eaf2fb;
}
.oc-tag-verba {
  color: #7a4a00;
  background: #fdf3e3;
}

/* §16 — marca de linha alterada: reconhecível, não alarmante. */
.oc-assist-alterado > td {
  background: var(--surface-2, #f4f8ff);
}
.oc-assist-erro td {
  background: #fff6f6;
}

/* §18 — progresso em etapas REAIS (nós vazios nunca entraram na conta). */
.oc-assist-progresso .oc-assist-barra {
  height: 6px;
  border-radius: 3px;
  background: var(--border, #e2e6ee);
  overflow: hidden;
}
.oc-assist-progresso .oc-assist-barra > span {
  display: block;
  height: 100%;
  background: var(--primary);
  transition: width .2s ease;
}

/* Autocomplete por linha. `position:relative` na célula para a lista flutuar
   sobre a tabela em vez de esticá-la a cada busca. */
.oc-assist-comp {
  position: relative;
}
.oc-assist-comp-atual {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: space-between;
}
.oc-assist-comp-rotulo {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.oc-assist-comp-busca {
  display: flex;
  align-items: center;
  gap: 6px;
}
.oc-assist-comp-input {
  flex: 1 1 auto;
  min-width: 0;
}
.oc-assist-sugestoes {
  position: absolute;
  z-index: 20;
  left: 8px;
  right: 8px;
  margin: 2px 0 0;
  padding: 0;
  list-style: none;
  max-height: 260px;
  overflow-y: auto;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #e2e6ee);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(16, 24, 40, .12);
}
.oc-assist-sugestao,
.oc-assist-sugestao-vazia {
  display: flex;
  gap: 8px;
  padding: 6px 10px;
  font-size: 13px;
  cursor: pointer;
}
.oc-assist-sugestao-vazia {
  cursor: default;
  color: var(--text-muted, #667085);
}
.oc-assist-sugestao:hover,
.oc-assist-sugestao-ativa {
  background: var(--surface-2, #f4f8ff);
}
/* Código não quebra no meio; a descrição usa o resto da largura. */
.oc-assist-sugestao-codigo {
  flex: 0 0 auto;
  font-weight: 600;
  white-space: nowrap;
}
.oc-assist-sugestao-descricao {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* §2/§9 — bloco de progresso do assistente: uma linha de identificação, uma
   barra, e nada de altura desperdiçada. O padding menor que o do painel padrão
   é deliberado: este bloco é orientação, não conteúdo. */
.oc-panel.oc-assist-progresso {
  padding-top: 14px;
  padding-bottom: 14px;
}
.oc-assist-etapa-linha {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
.oc-assist-etapa-nome {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* §7 — o pai é contexto, não título: some primeiro quando falta espaço. */
.oc-assist-etapa-pai {
  color: var(--text-muted, #667085);
  font-size: 12px;
  margin-right: 4px;
}
.oc-assist-etapa-contador {
  flex: 0 0 auto;
  color: var(--text-muted, #667085);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
/* Em tela estreita a linha empilha em vez de espremer a descrição. */
@media (max-width: 640px) {
  .oc-assist-etapa-linha {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }
  .oc-assist-etapa-nome {
    white-space: normal;
  }
}

/* §1/§6 — a célula de Composição no caso normal contém só o botão. O rótulo
   completo só aparece quando o Item DIVERGIU da Composição, e aí ele trunca com
   reticências (o `title` mostra o texto inteiro) em vez de esticar a coluna. */
.oc-assist-comp-atual .oc-assist-comp-rotulo {
  font-size: 12px;
  color: var(--text-muted, #667085);
}
/* §4 — origem ausente: visível, não silenciosa. */
.oc-assist-comp-vazia {
  font-size: 12px;
  color: #b3261e;
  white-space: nowrap;
}

/* §4/§34 — item marcado para exclusão: reconhecível SEM depender de cor. O
   texto "Será excluído" é a informação; a opacidade só reforça. Nada some da
   tela — o usuário precisa poder marcar de volta antes de sair da etapa (§5). */
.oc-assist-excluir > td {
  opacity: .55;
}
.oc-assist-marca-excluir {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  color: #b3261e;
  background: #fdf4f3;
  opacity: 1;
}
/* A marca não pode desbotar junto com a linha: é ela que explica o desbotado. */
.oc-assist-excluir > td .oc-assist-marca-excluir {
  opacity: 1;
}
/* §16 — preço de Serviço: mostrado, não editável. */
.oc-assist-preco-fixo {
  color: var(--text-muted, #667085);
  font-variant-numeric: tabular-nums;
}
.oc-text-center {
  text-align: center;
}

/* ============ ASSISTENTE: A TABELA PRECISA CABER ============
   ⚠️ A REGRA GLOBAL `table.oc-table th, table.oc-table td { white-space: nowrap }`
   é o que quebrava esta tela. Ela serve às listagens (código, valor, status —
   textos curtos que não devem quebrar), mas aqui a Descrição tem 60+ caracteres:
   sem quebra, a coluna cresce até a largura de UMA linha, a tabela ultrapassa o
   contêiner e o `.oc-table-wrap{overflow-x:auto}` esconde Tipo, Composição,
   Quantidade e Valor Unitário atrás de uma barra de rolagem. As colunas nunca
   sumiram do HTML — foram empurradas para fora da vista.

   `table-layout: fixed` fecha a causa em vez de tratar o sintoma: as larguras
   declaradas no `<th>` passam a MANDAR, e o conteúdo se acomoda dentro delas.
   Sem isso, o navegador continuaria negociando largura pelo conteúdo e uma
   descrição longa voltaria a empurrar tudo. */
table.oc-assist-tabela {
  table-layout: fixed;
  width: 100%;
}
/* Especificidade acima de `table.oc-table td` (0,1,2) — sem a segunda classe a
   regra global venceria por ordem de origem em qualquer refatoração do arquivo. */
table.oc-table.oc-assist-tabela th,
table.oc-table.oc-assist-tabela td {
  white-space: normal;
  overflow-wrap: anywhere;   /* §7 — quebra até dentro de palavras muito longas */
  vertical-align: top;
}
/* §9/§10 — os campos acompanham a coluna. Largura fixa em px voltaria a estourar
   a célula assim que a tabela encolhesse. */
table.oc-assist-tabela .oc-assist-qtd,
table.oc-assist-tabela .oc-assist-preco,
table.oc-assist-tabela .oc-assist-comp-input {
  width: 100%;
  min-width: 0;
}
/* O botão da Composição não quebra no meio da palavra. */
table.oc-assist-tabela .oc-assist-comp-atual .oc-btn,
table.oc-assist-tabela .oc-assist-comp-busca .oc-btn {
  white-space: nowrap;
  flex: 0 0 auto;
}
table.oc-assist-tabela .oc-assist-comp-atual {
  flex-wrap: wrap;
  gap: 4px;
}
/* §8 — o rótulo da Composição divergente quebra em vez de reservar largura. */
table.oc-assist-tabela .oc-assist-comp-rotulo {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
/* O código e o badge de tipo não quebram: são curtos e a quebra só os deformaria. */
table.oc-assist-tabela .oc-tag {
  white-space: nowrap;
}

/* ============ ASSISTENTE: FILTRAR / OCULTAR ============
   §12/§23 — `display:none`, nunca remoção do DOM. A linha oculta continua
   dentro do formulário e continua sendo gravada ao Avançar: filtro é
   visualização, não seleção do que será processado. */
.oc-assist-oculto {
  display: none;
}
/* §15 — os dois campos e o Limpar numa linha só; empilham no estreito. */
.oc-assist-filtros {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border, #e2e6ee);
}
.oc-assist-filtro {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 220px;
  min-width: 0;
  margin: 0;
}
.oc-assist-filtro > span {
  font-size: 12px;
  color: var(--text-muted, #667085);
}
.oc-assist-filtro > input {
  width: 100%;
  min-width: 0;
}
.oc-assist-visiveis {
  margin: 0 0 6px;
  white-space: nowrap;
}

/* Variante NEUTRA do chip, para os critérios de "Ocultar" (§31 — esconder não
   é destruir, então nada de vermelho). Reaproveita `.oc-chip` do design system;
   só troca as cores, na mesma linguagem de `.oc-chip-limpar`. */
.oc-chip-ocultar{
  background:var(--bg);
  color:var(--text-muted);
  border-color:var(--border);
}
.oc-chip-ocultar:hover{
  background:var(--surface-2,#eef0f4);
}

/* ============ LINHA APONTADA NA ESTRUTURA ANALÍTICA ============
   Numa árvore com 1.001 Serviços e 256 nós, relacionar o Código lá na esquerda
   com o Valor Total lá na direita é trabalho de olho. Este destaque resolve isso
   e nada mais.

   ===================== POR QUE SÓ CSS =====================
   `.oc-eap-linha` já existia em TODOS os tipos de linha (nó, vazia, Serviço,
   Verba) — não foi preciso classe nova. E o destaque inteiro, inclusive o de
   "menu aberto", sai de seletores: nenhum listener é registrado, nenhuma linha é
   percorrida, nada é recalculado ao mover o ponteiro. Esta página já pagou caro
   por trabalho por linha; mover o mouse não pode custar nada.

   ===================== POR QUE `:has()` =====================
   O menu de contexto é um `<details class="oc-menu">` DENTRO da célula de ações
   da própria linha. `:has(.oc-menu[open])` lê esse estado direto do DOM, sem
   classe intermediária mantida por JavaScript — que seria uma segunda verdade
   sobre "qual menu está aberto", livre para dessincronizar. `:has()` já é usado
   em três outros pontos deste arquivo.

   ⚠️ O seletor olha `.oc-menu[open]`, não `details[open]`: o nó de EAP é ele
   próprio um `<details>`, e a forma genérica acenderia toda etapa expandida.

   ===================== SEM DESLOCAMENTO =====================
   Só `background-color` muda. Nada de borda que ocupe espaço, padding, tamanho
   de fonte ou transform — o texto não pode andar um pixel quando o ponteiro
   passa por cima (§4). */
.oc-eap-linha:hover,
.oc-eap-linha:focus-within,
.oc-eap-linha:has(.oc-menu[open]){
  background-color:var(--surface-2,#f1f3f7);
  border-radius:6px;
}

/* §5 — o nó de EAP já tem fundo próprio (`--bg`), que é o que o distingue de um
   Serviço. O destaque dele PARTE desse fundo e escurece um passo, em vez de
   substituí-lo pelo mesmo cinza das outras linhas: a hierarquia continua legível
   com o ponteiro em cima. */
.oc-eap-no-item > summary.oc-eap-linha:hover,
.oc-eap-no-item > summary.oc-eap-linha:focus-within,
.oc-eap-no-item > summary.oc-eap-linha:has(.oc-menu[open]){
  background-color:var(--border,#e2e6ee);
}

/* O cabeçalho de colunas não é uma linha de dado — não há nada para relacionar
   nem para abrir nele. */
.oc-eap-linha-cabecalho:hover,
.oc-eap-linha-cabecalho:focus-within{
  background-color:transparent;
  border-radius:0;
}

/* Transição curta só na cor. Sem ela o destaque "pisca" ao percorrer a lista;
   com mais que isso viraria animação, que não é o pedido. */
.oc-eap-linha{
  transition:background-color .12s ease;
}
@media (prefers-reduced-motion:reduce){
  .oc-eap-linha{ transition:none; }
}

/* §25 — destaque muito breve na linha recém-movida, só para o olho reencontrá-la.
   Some sozinho; não é estado, é confirmação. Só `background-color`, como o hover:
   nada que desloque a linha no momento em que o usuário procura por ela. */
.oc-eap-linha.oc-eap-linha-movida{
  background-color:#e6f0fb;
}
@media (prefers-reduced-motion:reduce){
  .oc-eap-linha.oc-eap-linha-movida{ background-color:#e6f0fb; }
}

/* Escopo do filtro: no mesmo box, compacto, sem virar um bloco à parte (§23). */
.oc-escopo-filtro{
  display:flex;
  align-items:center;
  gap:10px;
  flex-wrap:wrap;
  margin-top:4px;
}
.oc-escopo-filtro label{
  display:inline-flex;
  align-items:center;
  gap:6px;
  margin:0;
  cursor:pointer;
}

/* Botão "Sair" que precisa parecer link.
   O logout é POST + CSRF (nunca GET — um <img> em qualquer página dispararia
   um logout por GET), mas visualmente ele convive com links ao lado. */
.oc-sair-inline { display: inline; }
.oc-sair-inline button {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--oc-link, #2563eb);
  text-decoration: underline; cursor: pointer;
}
.oc-sair-inline button:hover { text-decoration: none; }

/* =====================================================================
 * V1.00.304 — TELA DE ENTRADA EM DUAS COLUNAS
 *
 * Vale só para o login (`.oc-auth-body--split`). As demais telas de
 * autenticação continuam no cartão central de sempre — nada abaixo as alcança.
 *
 * Alturas: `100dvh` com `100vh` de reserva. Em celular, a barra de endereço
 * que aparece e some faz `100vh` estourar a viewport e criar rolagem que não
 * deveria existir; `dvh` acompanha a barra. Navegador antigo fica com `vh`,
 * que é o comportamento de antes.
 * ===================================================================== */
.oc-auth-body--split{
  margin:0;
  min-height:100vh;
  min-height:100dvh;
  background:var(--surface);
}
.oc-split{
  display:grid;
  grid-template-columns:52% 48%;   /* §1 */
  min-height:100vh;
  min-height:100dvh;
}

/* ---------------------------------------------------------------- ESQUERDA */
.oc-split__aside{
  position:relative;
  overflow:hidden;
  display:flex;
  align-items:center;
  padding:48px 56px;
  /* Azul do próprio ObraCusto (--primary) descendo para o azul da sidebar.
     Nenhuma cor nova foi introduzida para esta tela. */
  background:linear-gradient(150deg,#1d5fd6 0%,#1a4fb4 45%,#151a2b 100%);
  color:#fff;
}
/* Brilho suave atrás do conteúdo — dá profundidade sem imagem externa. */
.oc-split__aside::after{
  content:"";
  position:absolute;
  width:620px;height:620px;
  right:-180px;top:-160px;
  border-radius:50%;
  background:radial-gradient(circle,rgba(255,255,255,.14) 0%,rgba(255,255,255,0) 68%);
  pointer-events:none;
}
.oc-split__aside-conteudo{position:relative;z-index:1;max-width:520px;width:100%;}
.oc-split__logo{
  font-size:20px;
  font-weight:700;
  letter-spacing:-.01em;
  margin-bottom:56px;
}
.oc-split__headline{
  font-size:clamp(26px,2.6vw,38px);
  line-height:1.22;
  font-weight:700;
  letter-spacing:-.02em;
  margin:0 0 16px;
}
.oc-split__sub{
  font-size:15px;
  line-height:1.6;
  margin:0 0 40px;
  color:rgba(255,255,255,.82);
  max-width:42ch;
}

/* --------------------------------------------------- MOCKUP DO PRODUTO (§3) */
.oc-split__mock{max-width:520px;}
.oc-mock{
  background:rgba(255,255,255,.07);
  border:1px solid rgba(255,255,255,.16);
  border-radius:12px;
  overflow:hidden;
  box-shadow:0 24px 60px rgba(8,12,24,.34);
  backdrop-filter:blur(2px);
}
.oc-mock__bar{
  display:flex;gap:6px;
  padding:10px 12px;
  background:rgba(255,255,255,.09);
  border-bottom:1px solid rgba(255,255,255,.12);
}
.oc-mock__bar span{width:8px;height:8px;border-radius:50%;background:rgba(255,255,255,.34);}
.oc-mock__corpo{display:flex;min-height:190px;}
.oc-mock__nav{
  width:52px;flex-shrink:0;
  padding:14px 10px;
  display:flex;flex-direction:column;gap:11px;
  background:rgba(8,12,24,.30);
  border-right:1px solid rgba(255,255,255,.08);
}
.oc-mock__nav i{display:block;height:6px;border-radius:3px;background:rgba(255,255,255,.26);}
.oc-mock__nav i:first-child{background:rgba(255,255,255,.62);}
.oc-mock__painel{flex:1;padding:14px 16px;display:flex;flex-direction:column;gap:12px;}
.oc-mock__kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;}
.oc-mock__kpi{
  background:rgba(255,255,255,.09);
  border:1px solid rgba(255,255,255,.10);
  border-radius:7px;
  padding:9px;
}
.oc-mock__kpi b{display:block;height:5px;width:52%;border-radius:3px;background:rgba(255,255,255,.30);margin-bottom:7px;}
.oc-mock__kpi u{display:block;height:9px;width:78%;border-radius:3px;background:rgba(255,255,255,.60);}
.oc-mock__curva{
  background:rgba(255,255,255,.06);
  border:1px solid rgba(255,255,255,.10);
  border-radius:7px;
  padding:8px 10px;
}
.oc-mock__curva svg{display:block;width:100%;height:58px;}
.oc-mock__curva polyline{
  fill:none;
  stroke:#fff;
  stroke-width:2.2;
  stroke-linecap:round;
  stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
}
.oc-mock__curva .oc-mock__curva--b{stroke:rgba(255,255,255,.38);stroke-dasharray:4 4;}
.oc-mock__linhas{display:flex;flex-direction:column;gap:7px;}
.oc-mock__linha{display:flex;align-items:center;gap:9px;}
.oc-mock__linha i{width:18px;height:5px;border-radius:3px;background:rgba(255,255,255,.42);flex-shrink:0;}
.oc-mock__linha b{height:5px;border-radius:3px;background:rgba(255,255,255,.18);flex:1;}
.oc-mock__linha:nth-child(2) b{flex:.74;}
.oc-mock__linha:nth-child(3) b{flex:.52;}

/* ---------------------------------------------------------------- DIREITA */
.oc-split__main{
  display:flex;
  align-items:center;
  justify-content:center;
  padding:40px 24px;
  background:var(--surface);
}
/* ⚠️ Largura no CONTAINER, não no componente do Clerk. Mexer na largura do
   card do Clerk é o caminho curto para a caixa dentro da caixa que o §14
   proíbe. */
.oc-split__form{width:100%;max-width:400px;}
.oc-split__marca-mobile{display:none;}
.oc-split__versao{
  margin:28px 0 0;
  text-align:center;
  font-size:11px;
  color:var(--text-muted);   /* §26 — presente, nunca dominante */
}
.oc-split__titulo{
  font-size:24px;
  font-weight:700;
  letter-spacing:-.02em;
  color:var(--text);
  margin:0 0 8px;
}
.oc-split__subtitulo{
  font-size:14.5px;
  line-height:1.55;
  color:var(--text-muted);
  /* §8 — vale para login, cadastro E criação da empresa: as três usam esta
     mesma moldura, então o respiro é ajustado num lugar só. */
  margin:0 0 30px;
}
.oc-split__rodape{
  margin:24px 0 0;
  font-size:12px;
  line-height:1.5;
  color:var(--text-muted);
  text-align:center;
}
/* ============ AVISO DE SESSÃO — ACOMPANHA O FORMULÁRIO ============
   ⚠️ Fica ACIMA do formulário, nunca no lugar dele. Sessão expirada/inválida
   é informação; o caminho para entrar continua logo abaixo. Discreto de
   propósito: quem precisa ganhar a atenção da coluna é o formulário, não o
   aviso (item 4 do pedido). `[hidden]` do HTML já o remove quando vazio —
   sem texto, nem a caixa existe. */
.oc-split__aviso{
  margin:0 0 18px;
  padding:11px 14px;
  border:1px solid #f0d9a8;
  border-left:3px solid #d99a2b;
  border-radius:8px;
  background:#fdf7ec;
  color:#7a5410;
  font-size:13.5px;
  line-height:1.5;
}
.oc-split__status{
  margin:16px 0 0;
  font-size:13px;
  color:var(--text-muted);
  text-align:center;
  min-height:20px;   /* reserva o espaço: a mensagem não empurra o layout */
}
/* Skeleton discreto enquanto o ClerkJS não chega (§22). */
.oc-split__skeleton{display:flex;flex-direction:column;gap:12px;}
.oc-split__skeleton i{
  display:block;height:44px;border-radius:9px;
  background:linear-gradient(90deg,#eef0f4 25%,#e3e6ec 37%,#eef0f4 63%);
  background-size:400% 100%;
  animation:ocSkeleton 1.4s ease infinite;
}
.oc-split__skeleton i:nth-child(2){height:14px;width:60%;align-self:center;}
@keyframes ocSkeleton{0%{background-position:100% 50%;}100%{background-position:0 50%;}}
@media (prefers-reduced-motion: reduce){
  .oc-split__skeleton i{animation:none;}
}

/* ------------------------------------------------------------ RESPONSIVO */
/* Tablet: o painel institucional encolhe em vez de sumir (§18). */
@media (max-width: 1100px){
  .oc-split{grid-template-columns:44% 56%;}
  .oc-split__aside{padding:40px 36px;}
  .oc-split__logo{margin-bottom:36px;}
  .oc-split__mock{display:none;}   /* o mockup é o primeiro a sair: é o que menos informa */
}
/* Mobile: uma coluna. O painel visual sai inteiro e sobra a marca no topo. */
@media (max-width: 820px){
  .oc-split{grid-template-columns:1fr;min-height:auto;}
  .oc-split__aside{display:none;}
  .oc-split__main{
    min-height:100vh;
    min-height:100dvh;
    align-items:flex-start;
    padding:36px 20px 28px;
  }
  .oc-split__marca-mobile{
    display:block;
    font-size:19px;
    font-weight:700;
    color:var(--primary);
    letter-spacing:-.01em;
    margin-bottom:26px;
  }
  .oc-split__titulo{font-size:21px;}
}
/* Notebook 1366×768 (§25): aperta o vertical sem espremer o formulário. */
@media (min-width: 821px) and (max-height: 800px){
  .oc-split__aside{padding:32px 48px;}
  .oc-split__logo{margin-bottom:32px;}
  .oc-split__sub{margin-bottom:26px;}
  .oc-split__subtitulo{margin-bottom:22px;}
  .oc-mock__corpo{min-height:150px;}
}

/* =====================================================================
 * V1.00.304 — Administração do Catálogo Padrão (app/platform/)
 *
 * Quatro classes pequenas, e só as que a tela realmente usa. Não há um
 * "design system" novo aqui: cores, raio e botões vêm do :root e das classes
 * já existentes (.oc-btn, .oc-panel, .oc-table). O que falta no projeto é
 * apenas o campo de texto pequeno e a confirmação em linha.
 * ===================================================================== */
.oc-panel-head{margin-bottom:10px;}
.oc-tabs{display:flex;gap:6px;flex-wrap:wrap;}
.oc-input-sm{
  height:32px;
  padding:0 10px;
  font:inherit;
  font-size:13.5px;
  color:var(--text);
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:8px;
  max-width:220px;
}
.oc-input-sm:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:1px;
  border-color:var(--primary);
}
/* Confirmação em linha: o formulário de exclusão só aparece depois de um
   clique deliberado, e `<details>` faz isso sem uma linha de JavaScript —
   inclusive com teclado, que é o que um `onclick` costuma esquecer. */
.oc-inline-confirm{display:inline-block;}
.oc-inline-confirm > summary{
  cursor:pointer;
  list-style:none;
  display:inline-flex;
  align-items:center;
}
.oc-inline-confirm > summary::-webkit-details-marker{display:none;}
.oc-inline-confirm[open] > summary{outline:2px solid var(--primary);outline-offset:1px;}
.oc-inline-confirm form{
  position:absolute;
  z-index:20;
  margin-top:6px;
  padding:12px;
  width:min(320px,80vw);
  text-align:left;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--radius);
  box-shadow:0 8px 28px rgba(15,18,28,.16);
}

/* ============================================================
   ONBOARDING — criação da organização (V1.00.305)
   ============================================================
   Reaproveita INTEIRAMENTE a moldura .oc-split do login/cadastro: fundo,
   coluna institucional, tipografia, sombra e comportamento responsivo já vêm
   de lá. O que segue são só os componentes que aquela tela não tinha —
   indicador de etapa, cartões de escolha e as mensagens de campo.

   ⚠️ Nenhuma cor nova: todas saem das variáveis do tema, para que a tela
   acompanhe o modo claro/escuro sem uma segunda paleta para manter. */

/* Formulário próprio precisa de mais respiro que o widget do Clerk (§3). */
.oc-split__form--larga{max-width:520px;}

/* ---- Indicador de etapa (§5) ---------------------------------------- */
.oc-etapas{
  display:flex;
  align-items:center;
  gap:8px;
  margin:0 0 20px;
  font-size:12px;
  color:var(--text-muted);
}
.oc-etapa{display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.oc-etapa__marca{
  width:18px;height:18px;border-radius:50%;
  display:inline-flex;align-items:center;justify-content:center;
  font-size:10.5px;font-weight:700;line-height:1;
  border:1px solid var(--border);
}
.oc-etapa--feita .oc-etapa__marca{
  background:var(--primary);border-color:var(--primary);color:#fff;
}
.oc-etapa--atual{color:var(--text);font-weight:600;}
.oc-etapa--atual .oc-etapa__marca{
  border-color:var(--primary);color:var(--primary);
}
.oc-etapas__traco{flex:0 0 14px;height:1px;background:var(--border);}

/* ---- Cartões de escolha PF/PJ (§7/§8) -------------------------------- */
.oc-opcoes{
  display:grid;
  /* ⚠️ `minmax(0,1fr)`, NUNCA `1fr`: item de grid tem `min-width:auto` e se
     recusa a ficar menor que o próprio conteúdo. Com `1fr 1fr`, o rótulo
     "Pessoa Jurídica" impunha um piso maior que a coluna e ESCAPAVA da
     borda em vez de quebrar. Este é o defeito relatado. */
  grid-template-columns:repeat(2, minmax(0, 1fr));
  gap:12px;
  margin:0;
  border:0;padding:0;              /* é um <fieldset> */
}
.oc-opcao{
  display:flex;
  align-items:center;   /* §2 — conteúdo centrado na altura do cartão */
  gap:11px;
  min-width:0;          /* deixa o cartão encolher junto com a coluna */
  min-height:52px;      /* §2 — os dois cartões têm a MESMA altura. O piso
                           também protege o caso de um rótulo futuro quebrar
                           em duas linhas: o outro acompanha, via grid. */
  padding:14px 16px;
  border:1.5px solid var(--border);
  border-radius:10px;
  cursor:pointer;
  background:var(--surface);
  transition:border-color .12s ease, background-color .12s ease, box-shadow .12s ease;
}
.oc-opcao:hover{border-color:var(--text-muted);}

/* ⚠️ O input NÃO é escondido: `display:none` o tira da ordem de tabulação e
   quebra a navegação por teclado — o cartão viraria clicável só com mouse. Ele
   fica visível e pequeno, e o cartão inteiro é rotulado por ele (<label>),
   então clicar em qualquer ponto seleciona. */
/* ⚠️ ESTA É A CAUSA DO TEXTO ESCAPANDO DOS CARTÕES.
   `.oc-field input{width:100%}` vale para TODO input dentro de um campo —
   inclusive o rádio. Medido no navegador: o rádio ficava com 218px, ocupava
   o cartão inteiro e empurrava o texto para fora da borda. O projeto já
   trata disso para checkbox (`.oc-field label:has(input[type="checkbox"])
   input{width:auto}`); faltava a mesma linha para rádio.
   `flex:0 0 auto` sozinho não resolve: ele governa o crescimento, não a
   largura declarada. */
.oc-opcao input[type="radio"]{
  width:auto;
  margin:0;
  flex:0 0 auto;
  cursor:pointer;
  /* ⚠️ Sem `accent-color`, o ponto do rádio marcado sai na cor padrão do
     navegador — magenta no Chrome. Ficava um ponto roxo dentro de um cartão
     azul, e a única cor do produto que a pessoa via ali era a errada. */
  accent-color:var(--primary);
}
/* `min-width:0` também aqui: um filho flex herda o mesmo piso automático do
   item de grid, e sem isto o texto voltaria a empurrar a borda. */
.oc-opcao__texto{display:flex;flex-direction:column;gap:2px;line-height:1.3;min-width:0;flex:1 1 auto;}
/* `break-word` e não `anywhere`: os dois quebram palavra longa, mas
   `anywhere` também entra no cálculo do tamanho mínimo intrínseco, e num
   contêiner apertado deixa o navegador reduzir o bloco à largura de um
   caractere. Aqui é só rede de segurança para um rótulo futuro; a largura
   do cartão quem garante é a regra do rádio, acima. */
.oc-opcao__titulo{font-size:14px;font-weight:600;color:var(--text);overflow-wrap:break-word;}
.oc-opcao__doc{font-size:12px;color:var(--text-muted);overflow-wrap:break-word;}

/* Estado selecionado: dois caminhos de propósito. `:has()` cobre o caso sem
   JavaScript; a classe cobre navegador antigo sem `:has()`. Um sozinho deixaria
   a seleção invisível em algum lugar — e §8 pede que ela NÃO dependa do radio. */
.oc-opcao--ativa,
.oc-opcao:has(input[type="radio"]:checked){
  border-color:var(--primary);
  background:color-mix(in srgb, var(--primary) 6%, transparent);
}
/* Foco visível no cartão quando o rádio recebe o teclado (§21). */
.oc-opcao:focus-within{
  outline:2px solid var(--primary);
  outline-offset:2px;
}

/* ---- Mensagens de campo (§14) ---------------------------------------- */
.oc-campo-ajuda{
  display:block;
  margin:6px 0 0;
  font-size:12.5px;          /* §11 — discreto, não minúsculo */
  line-height:1.45;
  color:var(--text-muted);
}
.oc-campo-erro{
  display:block;
  margin:6px 0 0;
  font-size:12.5px;
  line-height:1.45;
  color:var(--oc-erro-texto);
  font-weight:500;
}
.oc-campo-erro strong{display:block;font-weight:600;}

/* ---- Nota do teste gratuito (§13) ------------------------------------ */
.oc-trial-nota{
  margin:4px 0 16px;
  font-size:12.5px;
  color:var(--text-muted);
  text-align:center;
}

/* ---- Botão principal do onboarding (§12) ----------------------------- */
.oc-btn--onboarding{
  width:100%;
  justify-content:center;
  min-height:46px;           /* maior que os 36px do padrão: é A ação da tela */
  font-size:15px;
  font-weight:600;
}

/* ---- Erro geral do formulário ---------------------------------------- */
.oc-form-erro{
  margin:0 0 18px;
  padding:11px 14px;
  border-radius:8px;
  font-size:13.5px;
  line-height:1.45;
  color:var(--oc-erro-texto);
  background:var(--oc-erro-fundo);
  border:1px solid var(--oc-erro-borda);
}

/* ---- Mobile (§20) ----------------------------------------------------- */
@media (max-width:640px){
  .oc-opcoes{grid-template-columns:1fr;}   /* empilhados */
  .oc-etapas{font-size:11.5px;gap:6px;}
  .oc-split__form--larga{max-width:100%;}
}

/* Saída das telas de onboarding (V1.00.313) — ver ocAuthPageSaida().
   Discreta de propósito: é uma escapatória, não uma ação que se ofereça. */
.oc-auth-saida{
  margin:22px 0 0;
  text-align:center;
  font-size:12.5px;
  line-height:1.6;
  color:var(--text-muted);
}
.oc-auth-saida strong{font-weight:600;color:var(--text);}
.oc-auth-saida form{display:inline;}
/* Botão que parece link: logout é mutação e precisa de <form>, mas visualmente
   um botão grande aqui competiria com "Criar empresa e começar". */
.oc-link-botao{
  background:none;border:0;padding:0;margin:0;
  font:inherit;color:var(--primary);
  text-decoration:underline;cursor:pointer;
  min-height:0;
}
.oc-link-botao:hover{text-decoration:none;}

/* Aviso de modo relaxado de validação (V1.00.314) — só aparece em
   desenvolvimento. Âmbar, não vermelho: não é erro do usuário, é um estado
   do ambiente que ele precisa enxergar. */
.oc-campo-aviso{
  display:block;
  margin:6px 0 0;
  padding:7px 10px;
  border-radius:6px;
  font-size:12px;
  line-height:1.45;
  color:#8a5a00;
  background:#fff8e6;
  border:1px solid #f0d79a;
}

/* ==========================================================================
   MENU LATERAL EXPANSÍVEL (V1.00.316)
   ==========================================================================
   Recolhido por padrão; expande ao passar o mouse, ao receber foco de teclado,
   ou permanentemente quando o usuário o FIXA.

   ⚠️ A CLASSE `oc-sidebar-collapsed` FOI PRESERVADA, e de propósito. Ela já
   significava "a barra está estreita agora" e é a âncora de dezenas de regras
   existentes — rótulos escondidos, ícones centralizados, tooltips — além do


/* ---- Desktop/tablet largo -------------------------------------------------
   No mobile (≤820px) a barra vira drawer e nada aqui se aplica. */
@media (min-width:821px){

  /* Duas medidas INDEPENDENTES, e é essa separação que evita o "pulo":
       largura da BARRA      → 64px, vira 244px no hover e ao fixar
       espaço RESERVADO      → 64px, vira 244px SÓ ao fixar

     ⚠️ Se fossem a mesma medida, passar o mouse empurraria a página inteira
     180px para a direita e de volta a cada vez. Reservando sempre a faixa
     estreita, a expansão por hover apenas SOBREPÕE o conteúdo. */
  /* ⚠️ A LARGURA É DECLARADA POR ESTADO, não por uma variável que troca.

     A primeira versão fazia `width:var(--oc-sidebar-largura)` e mudava a
     variável na classe do <body>. Funciona, mas depende de o navegador
     reinterpolar `width` quando o valor chega por herança de custom
     property — e propriedade personalizada NÃO é animável: ela salta, e só
     por consequência o `width` recalcula. Alguns navegadores resolvem isso
     num quadro só, e a barra ABRE DE UMA VEZ depois da espera, em vez de
     crescer ao longo dos 500ms.

     Declarando a largura em cada regra, o que muda é o próprio `width` — o
     caso mais simples e o único que toda engine interpola sem ressalva. */
  .oc-sidebar{width:var(--sidebar-collapsed-width);}
  .oc-layout{padding-left:var(--sidebar-collapsed-width);}

  body.oc-sidebar-flutuante .oc-sidebar,
  body.oc-sidebar-pinned .oc-sidebar{width:var(--sidebar-expanded-width);}

  /* Só FIXADA o conteúdo cede espaço; no hover a barra apenas sobrepõe. */
  body.oc-sidebar-pinned .oc-layout{padding-left:var(--sidebar-expanded-width);}

  /* ⚠️ `position:fixed` tira a barra do fluxo do flex — é o que permite ela
     crescer POR CIMA do conteúdo. O espaço reservado passa a vir do
     padding-left de .oc-layout, não mais da largura de um item flex. */
  .oc-sidebar{
    position:fixed;
    top:0;
    left:0;
    bottom:0;
    z-index:var(--oc-z-sidebar);
    /* §3 — só `width`; nada de animar sombra ou cor, que forçam repintura
       da barra inteira a cada quadro. */
    transition:width .2s ease;
  }



  /* §17 — sombra só quando flutua sobre o conteúdo. Fixada, a barra faz parte
     do layout e uma sombra ali só sujaria a divisa. */
  body.oc-sidebar-flutuante:not(.oc-sidebar-pinned) .oc-sidebar{
    box-shadow:2px 0 20px rgba(0,0,0,.22);
  }

  /* §15 — cabeçalho e rodapé fixos; só a navegação rola. Sem isto, uma lista
     longa de módulos empurraria o bloco do usuário para fora da tela. */
  .oc-sidebar{overflow:hidden;}
  .oc-brand{flex-shrink:0;}
  .oc-nav{overflow-y:auto;overflow-x:hidden;min-height:0;flex:1;}
  .oc-auth-block,
  .oc-sidebar-footer{flex-shrink:0;}

  /* §13 — recolhida, o rodapé do usuário mostra só o avatar/inicial. Deixar o
     nome e o e-mail espremidos em 64px produz reticências ilegíveis. */
  body.oc-sidebar-collapsed .oc-auth-user-info,
  body.oc-sidebar-collapsed .oc-org-switch,
  body.oc-sidebar-collapsed .oc-auth-user-sair,
  body.oc-sidebar-collapsed .oc-auth-user-sair-form{
    display:none;
  }
  body.oc-sidebar-collapsed .oc-auth-user{justify-content:center;}
  body.oc-sidebar-collapsed .oc-auth-block{padding-left:0;padding-right:0;}

  /* §7 — recolhida, a marca vira só a inicial. Empilhada: a inicial em cima,
     o botão de modo embaixo. Antes eram lado a lado e o botão simplesmente
     não cabia nos 64px. */
  body.oc-sidebar-collapsed .oc-brand{
    flex-direction:column;
    justify-content:center;
    align-items:center;
    gap:8px;
  }
  /* O monograma "OM" que ficava aqui (::before) foi para o header global
     (V1.00.477): a marca aparece uma vez só na tela. */

  /* ⚠️ O BOTÃO DE MODO NUNCA PODE SUMIR — DEFEITO REAL, RELATADO.
     Esta regra era `display:none`, e fazia sentido enquanto "recolhida" era
     sempre um estado passageiro: bastava o hover para a barra abrir e o botão
     aparecer.

     Com o modo "Manter fechado" isso deixou de valer. Ali a barra fica estreita
     para SEMPRE e nunca expande — então o botão ficava permanentemente
     invisível, e o usuário ficava preso no modo sem nenhum caminho de volta
     pela interface. Foi exatamente o que aconteceu: "não está abrindo o menu
     lateral" seguido de "qual botão eu uso?".

     O controle que TIRA de um estado nunca pode depender de sair daquele
     estado. Agora ele é visível em qualquer largura e em qualquer modo. */
  body.oc-sidebar-collapsed .oc-sidebar-toggle{
    display:flex;
    margin-left:0;
  }

  /* Fixada, o botão indica que um novo clique solta. */
  body.oc-sidebar-pinned .oc-sidebar-toggle{
    background:rgba(255,255,255,.16);
    border-color:rgba(255,255,255,.32);
  }
}



/* ==========================================================================
   MODO "MANTER FECHADO" — submenu como flyout, sem alargar a barra
   ==========================================================================
   No modo 'fechado' a barra nunca expande, nem por hover nem por clique. Mas o
   submenu precisa continuar alcançável: sem isto, "Catálogo", "Contatos",
   "Obras", "Financeiro" e "Administração" ficariam inacessíveis, porque os
   filhos deles só existem dentro do grupo.

   A saída é o flyout: o <details> abre normalmente (o JS deixa de interceptar o
   clique nesse modo) e os filhos são posicionados AO LADO do ícone, sobre o
   conteúdo — a barra continua com 64px.

   ⚠️ `position:absolute` a partir do `.oc-nav-grupo`, que recebe
   `position:relative` só aqui. A barra tem `overflow-y:auto`, então um painel
   mais alto que a área visível seria cortado; `max-height` + `overflow-y:auto`
   próprios resolvem sem depender do contexto da barra.

   Só no desktop: no celular a barra vira drawer de largura cheia e o submenu
   inline é o comportamento correto — daí a media query. */
@media (min-width:821px){
  /* Enquanto um flyout esta aberto a barra deixa de recortar. Fora disso ela
     volta a rolar normalmente — sem isto, uma tela baixa perderia a rolagem
     dos icones. A classe e posta e tirada por assets/js/sidebar.js. */
  body.oc-sidebar-modo-fechado.oc-sidebar-flyout-aberto .oc-sidebar{
    overflow:visible;
  }
  body.oc-sidebar-modo-fechado .oc-nav-grupo{
    position:relative;
  }
  body.oc-sidebar-modo-fechado .oc-nav-grupo[open] > .oc-nav-filhos{
    display:flex;
    /* ⚠️ `fixed`, e nao `absolute`. A barra tem `overflow-x:hidden` (para os
       rotulos nao vazarem quando ela e estreita), e um filho posicionado por
       `absolute` continua sendo RECORTADO por esse overflow: medido, o painel
       existia no DOM na posicao certa (x=70, fora da barra) e nao aparecia na
       tela. `fixed` sai do contexto de recorte.

       O preco e que `top`/`left` passam a ser da viewport, entao quem os define
       e o JS quando o grupo abre (assets/js/sidebar.js). Vale a troca: qualquer
       solucao com `absolute` dependeria de a barra nunca precisar rolar. */
    position:fixed;
    /* ⚠️ ACIMA da barra, nao 60. O painel e descendente de `.oc-sidebar`, que
       tem `z-index:65` e cria o contexto de empilhamento: um filho com 60
       fica abaixo do proprio pai, e o conteudo da pagina passava por cima —
       medido, `elementFromPoint` no meio do painel devolvia `.oc-content-panel`.
       Dentro do contexto do pai, o valor precisa apenas ser positivo; 1 basta
       e nao inventa uma camada nova na escala do projeto. */
    z-index:1;
    /* ⚠️ NASCE INVISIVEL, ATE TER COORDENADAS.
       O <details> vira `[open]` e o CSS acima ja o torna `position:fixed`
       — mas `top`/`left` so chegam no evento `toggle`, que e ASSINCRONO.
       Entre uma coisa e outra o navegador pinta um quadro com o painel na
       posicao estatica, sobre a propria barra e recortado por ela: o vulto
       escuro que aparecia por um instante a cada clique.

       `visibility` (nao `display`) porque o painel precisa continuar
       ocupando caixa para o JS medir. Quem revela e `posicionarFlyout()`,
       DEPOIS de escrever as coordenadas. */
    visibility:hidden;
    min-width:210px;
    /* ⚠️ `opacity:1` EXPLICITO. A base `.oc-sidebar .oc-nav-filhos` deixa o
       submenu em `opacity:0`, e quem o revela e
       `body:not(.oc-sidebar-collapsed) ...` — condicao que NUNCA vale no modo
       fechado, onde a barra fica estreita para sempre. Sem declarar aqui, o
       painel ficava posicionado e dimensionado corretamente (medido: 210x164
       em x=76) e simplesmente invisivel. */
    opacity:1;
    max-height:70vh;
    overflow-y:auto;
    padding:6px;
    margin-left:6px;
    background:var(--sidebar-bg);
    border:1px solid rgba(255,255,255,.18);
    border-radius:10px;
    box-shadow:0 12px 32px rgba(0,0,0,.38);
  }
  /* Dentro do flyout ha espaco para o rotulo: as regras que escondem o texto
     valem para a COLUNA estreita, nao para este painel.

     ⚠️ `display` sozinho NAO bastava, e `width:auto` tambem nao. A barra
     estreita esconde o rotulo com `max-width:0` + `opacity:0` +
     `translateX(-4px)` — para ele encolher com animacao em vez de sumir de
     uma vez. Medido: com `width:auto` declarado, o texto continuava com 0px,
     porque quem o prendia era o `max-width`. Sao essas tres que precisam
     voltar ao valor da barra larga. */
  /* Revelado so quando o JS ja ancorou o painel ao lado do icone. */
  body.oc-sidebar-modo-fechado .oc-nav-grupo[open] > .oc-nav-filhos[data-flyout-pronto="1"]{
    visibility:visible;
  }

  body.oc-sidebar-modo-fechado .oc-nav-grupo[open] > .oc-nav-filhos .oc-nav-text{
    display:inline-block;
    max-width:180px;   /* mesmo teto da barra larga */
    opacity:1;
    transform:none;    /* a barra estreita desloca o rotulo -4px ao encolher */
    overflow:hidden;
  }
  body.oc-sidebar-modo-fechado .oc-nav-grupo[open] > .oc-nav-filhos a.oc-nav-filho{
    justify-content:flex-start;
    padding-left:10px;
    white-space:nowrap;
  }
}

/* ==========================================================================
   DRAWER DO CELULAR — o estado "recolhida" não se aplica aqui
   ==========================================================================
   ⚠️ ESTE BLOCO PRECISA SER O ÚLTIMO. As regras que centralizam ícones na
   barra estreita (`body.oc-sidebar-collapsed .oc-nav-grupo > summary`, por
   volta da linha 3520) vêm DEPOIS do `@media (max-width:820px)` original, e
   media query não acrescenta especificidade — então elas venciam o reset de
   lá e continuavam valendo dentro do drawer.

   O sintoma, medido em 375px: "Catálogo", "Contatos", "Obras" e
   "Administração" apareciam centralizados no meio de uma lista alinhada à
   esquerda, porque só esses quatro são <summary> de grupo. A primeira
   tentativa de correção foi escrita no bloco antigo e não surtiu efeito —
   o problema nunca foi o seletor, era a ordem.

   Nada disso aparecia antes da V1.00.316: a barra nascia ABERTA, então
   `oc-sidebar-collapsed` quase nunca estava no <body>. Inverter o padrão
   expôs uma regra que sempre esteve larga demais. */
@media (max-width:820px){
  body.oc-sidebar-collapsed .oc-nav a,
  body.oc-sidebar-collapsed .oc-nav-grupo > summary{justify-content:flex-start;}
  body.oc-sidebar-collapsed .oc-nav a.oc-nav-filho{padding-left:30px;}
  body.oc-sidebar-collapsed .oc-nav-chevron{display:inline-block;}

  /* O drawer mostra o menu inteiro: submenus abertos aparecem normalmente. */
  body.oc-sidebar-collapsed .oc-nav-grupo[open] > .oc-nav-filhos{display:flex;}

  /* A marca compacta "OM" é exclusiva da barra estreita do desktop. */
  body.oc-sidebar-collapsed .oc-brand::before{content:none;}
  body.oc-sidebar-collapsed .oc-brand{justify-content:flex-start;}
}
/* O <summary> do grupo nunca recebeu o padding/gap dos links — diferença de


/* ==========================================================================
   SUAVIDADE DA ABERTURA/FECHAMENTO (V1.00.324)
   ==========================================================================
   §8 — os tempos vivem aqui, num lugar só. */
:root{
  --sidebar-open-duration:500ms;
  --sidebar-close-duration:500ms;
  /* ⚠️ Os rótulos acompanham a LARGURA, não um número próprio. Com a barra
     em 500ms e o texto em 250ms, o texto terminaria de aparecer com a barra
     ainda na metade do caminho — e o resto do movimento seria uma faixa
     escura crescendo ao lado de um texto já parado. Mantida a mesma
     proporção de antes (~5/6 da abertura). */
  --sidebar-label-duration:420ms;
  /* Desaceleração forte no fim: o movimento "chega" em vez de parar de
     repente. É o que dá a sensação de peso sem parecer lento. */
  --sidebar-easing:cubic-bezier(0.22, 1, 0.36, 1);
}

@media (min-width:821px){

  /* ⚠️ DURAÇÕES DIFERENTES PARA ABRIR E FECHAR, sem JavaScript nenhum.
     O truque é a base valer para o FECHAMENTO e o estado largo sobrescrever
     com a de ABERTURA: ao ganhar a classe, a transição em curso é a do estado
     novo (300ms); ao perdê-la, volta a valer a da base (350ms). Tentar isso
     no JS exigiria trocar `style.transitionDuration` a cada evento — e uma
     troca perdida deixaria a barra com a duração errada até o próximo hover. */
  .oc-sidebar{
    transition:width var(--sidebar-close-duration) var(--sidebar-easing);
  }
  body.oc-sidebar-flutuante .oc-sidebar,
  body.oc-sidebar-pinned .oc-sidebar{
    transition-duration:var(--sidebar-open-duration);
  }

  .oc-layout{
    transition:padding-left var(--sidebar-close-duration) var(--sidebar-easing);
  }
  body.oc-sidebar-pinned .oc-layout{
    transition-duration:var(--sidebar-open-duration);
  }

  /* ---- §5/§6 — rótulos ------------------------------------------------
     ⚠️ ANTES era `display:none`, que NÃO anima: o texto sumia e reaparecia de
     um quadro para o outro, e a barra deslizava com um buraco no lugar dos
     rótulos. `display` não tem valor intermediário — não existe meio-nenhum.

     `max-width` anima e, chegando a zero, o texto deixa de ocupar espaço na
     barra estreita, que era a única coisa que o `display:none` garantia.
     `overflow:hidden` corta o excesso durante o percurso em vez de deixar o
     texto vazar sobre o ícone. */
  .oc-sidebar .oc-nav-text{
    display:inline-block;
    max-width:180px;
    opacity:1;
    transform:none;
    overflow:hidden;
    white-space:nowrap;
    vertical-align:middle;
    transition:opacity var(--sidebar-label-duration) var(--sidebar-easing),
               transform var(--sidebar-label-duration) var(--sidebar-easing),
               max-width var(--sidebar-label-duration) var(--sidebar-easing);
  }

  body.oc-sidebar-collapsed .oc-sidebar .oc-nav-text{
    display:inline-block;   /* vence o display:none herdado da regra antiga */
    max-width:0;
    opacity:0;
    transform:translateX(-4px);
  }

  /* A marca acompanha o mesmo desaparecimento, em vez de piscar. */
  body.oc-sidebar-collapsed .oc-brand strong,
  body.oc-sidebar-collapsed .oc-brand span{
    display:inline-block;
    max-width:0;
    opacity:0;
    overflow:hidden;
    white-space:nowrap;
    transition:opacity var(--sidebar-label-duration) var(--sidebar-easing),
               max-width var(--sidebar-label-duration) var(--sidebar-easing);
  }
  .oc-brand strong,
  .oc-brand span{
    max-width:180px;
    opacity:1;
    transition:opacity var(--sidebar-label-duration) var(--sidebar-easing),
               max-width var(--sidebar-label-duration) var(--sidebar-easing);
  }

  /* ---- §7 — submenu ---------------------------------------------------
     Mais curto que a barra de propósito: um submenu que termina depois do
     movimento que o revelou parece atrasado em relação ao próprio menu. */
  .oc-nav-grupo[open] > .oc-nav-filhos{
    animation:ocSubmenuEntra 350ms var(--sidebar-easing);
  }
}

@keyframes ocSubmenuEntra{
  from{opacity:0;transform:translateY(-4px);}
  to{opacity:1;transform:none;}
}

/* §9 — nada de `transition:all`. Cada regra acima nomeia exatamente o que
   anima: width, padding-left, opacity, transform e max-width. */

/* ==========================================================================
   MOVIMENTO REDUZIDO — o que obedece e o que não obedece
   ==========================================================================
   ⚠️ ESTA MÁQUINA TEM AS ANIMAÇÕES DO WINDOWS DESLIGADAS
   (`MinAnimate=0` no registro), e o Chrome traduz isso em
   `prefers-reduced-motion: reduce`. Enquanto a barra respeitava a
   preferência com `transition:none`, ela SALTAVA de 64px para 244px — e
   aumentar a duração de 300ms para 1600ms não mudava absolutamente nada,
   porque não havia transição para durar. Quatro rodadas de ajuste de tempo
   se perderam nisso: o número estava certo o tempo todo, e o problema era
   uma regra de acessibilidade a três telas de distância.

   O deslizamento da barra passa a rodar SEMPRE, por pedido explícito e
   repetido. É um movimento pequeno, lateral, sem rotação, sem paralaxe e
   sem piscar — o tipo que a preferência existe para tolerar; o que ela
   protege de verdade é giro, salto e deslocamento grande de tela inteira.

   O que CONTINUA obedecendo: a entrada do submenu, que é keyframe puro e
   dispensável. Quem pediu menos animação continua recebendo menos. */
@media (prefers-reduced-motion: reduce){
  .oc-nav-grupo[open] > .oc-nav-filhos{animation:none;}
}

/* ==========================================================================
   MENU LATERAL TRANSLÚCIDO (V1.00.333)
   ==========================================================================
   ⚠️ `background: rgba(...)`, NUNCA `opacity` no <aside>.

   `opacity:.75` no elemento apagaria JUNTO o texto, os ícones, os separadores
   e o bloco do usuário — o menu inteiro ficaria lavado, não translúcido. Além
   disso `opacity` cria um contexto de empilhamento próprio, o que romperia o
   z-index:65 que coloca a barra acima do cabeçalho.

   Aplicando a transparência só no FUNDO, o conteúdo do menu continua em 100%:
   é o vidro que deixa ver através, não o texto que desbota.

   `backdrop-filter` desfoca o que está atrás. Sem ele, com a barra expandida
   por cima da tabela, as linhas do conteúdo apareceriam nítidas ATRÁS dos
   rótulos e as duas camadas de texto competiriam. Com o desfoque, o que passa
   é só a cor — que é o efeito pretendido. Navegador sem suporte simplesmente
   ignora a linha e fica com o fundo translúcido puro; nada quebra. */
:root{
  /* 1 = opaco, 0 = invisível. 0.75 = 25% de transparência. */
  --sidebar-opacidade:.75;
  --sidebar-bg-translucido:rgba(21, 26, 43, var(--sidebar-opacidade));
}

.oc-sidebar{
  background:var(--sidebar-bg-translucido);
  backdrop-filter:blur(14px) saturate(140%);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
}



/* ==========================================================================
   GRADIENTE DA SIDEBAR (V1.00.334)
   ==========================================================================
   Azul no canto superior esquerdo evoluindo na diagonal até preto no inferior
   direito. Escopo: SOMENTE a barra lateral (§14) — nenhum botão, link, input,
   tabela, card ou badge do resto do OrçaMeta é tocado.

   ⚠️ `--sidebar-bg` (a cor sólida antiga) NÃO foi removida: ela ainda é usada
   por outro componente, um balão flutuante lá pela linha 1100. Trocar o valor
   dela teria repintado aquilo junto, sem ninguém pedir. */
:root{
  --sidebar-gradient:linear-gradient(
      135deg,
      #1D5ED4 0%,
      #174BAA 28%,
      #102F6E 52%,
      #081734 75%,
      #000000 100%
  );
}

.oc-sidebar{
  background:var(--sidebar-gradient);

  /* ⚠️ SEM `backdrop-filter` e SEM alfa aqui, ao contrário da versão translúcida
     anterior. "Preto absoluto no canto inferior" e "75% de transparência" são
     incompatíveis: sobre o fundo claro do app, #000000 a 0,75 renderiza cinza
     escuro, nunca preto. O pedido do gradiente é explícito quanto ao ponto
     final, então a opacidade venceu. */

  /* ⚠️ TAMANHO FIXO DO GRADIENTE — este detalhe é o §15.
     Um `linear-gradient(135deg, …)` é calculado sobre a CAIXA do elemento. Com
     a largura animando de 64px para 244px, o ângulo de 135° reprojeta a cada
     quadro: as faixas de cor deslizariam para dentro da barra durante toda a
     abertura, e o efeito lido seria de piscar. Fixando o tamanho na largura
     EXPANDIDA, a pintura é sempre a mesma e o que muda é só quanto dela está
     visível — a barra "revela" o gradiente em vez de recalculá-lo. */
  background-size:var(--sidebar-expanded-width) 100%;
  background-repeat:no-repeat;
  background-attachment:local;
}

/* §4/§6 — nenhuma área interna pinta fundo próprio: cabeçalho, navegação,
   submenus, bloco da organização e rodapé ficam transparentes e deixam o
   gradiente do contêiner atravessar sem emenda. */
.oc-sidebar .oc-brand,
.oc-sidebar .oc-nav,
.oc-sidebar .oc-nav-filhos,
.oc-sidebar .oc-nav-grupo,
.oc-sidebar .oc-auth-block,
.oc-sidebar .oc-auth-user,
.oc-sidebar .oc-org-switch,
.oc-sidebar .oc-sidebar-footer{
  background:transparent;
}

/* §7/§8 — seleção e hover por BRANCO TRANSLÚCIDO.
   ⚠️ O item ativo usava `var(--primary)`, que é exatamente o azul do início do
   gradiente: no topo da barra ele desapareceria contra o próprio fundo, e o
   usuário perderia a única pista de onde está. Branco translúcido funciona nos
   dois extremos — sobre o azul e sobre o preto. */
.oc-sidebar .oc-nav a:hover,
.oc-sidebar .oc-nav-grupo > summary:hover{
  background:rgba(255,255,255,.08);
}
.oc-sidebar .oc-nav a.active{
  background:rgba(255,255,255,.14);
  color:#fff;
}
.oc-sidebar .oc-nav a.active .oc-nav-icon{opacity:1;}

/* §11 — divisores em branco translúcido. Com `--border` (#e2e5ea, cinza claro)
   a linha ficava clara demais no topo azul; com cinza escuro, sumiria na
   região preta. Branco a 15% resolve os dois extremos. */
.oc-sidebar .oc-auth-block{
  border-top:1px solid rgba(255,255,255,.15);
}

/* §9/§10/§12 — texto e ícones legíveis do topo azul ao rodapé preto. */
.oc-sidebar .oc-nav a,
.oc-sidebar .oc-nav-grupo > summary,
.oc-sidebar .oc-brand strong,
.oc-sidebar .oc-auth-user-nome{
  color:#fff;
}
.oc-sidebar .oc-nav-icon{opacity:1;}
.oc-sidebar .oc-brand span,
.oc-sidebar .oc-auth-user-email,
.oc-sidebar .oc-org-switch-label{
  color:rgba(255,255,255,.72);
}
.oc-sidebar .oc-org-switch-select{
  background:rgba(255,255,255,.08);
  border-color:rgba(255,255,255,.2);
  color:#fff;
}



/* §18 — o drawer do celular usa o MESMO gradiente. A largura dele é diferente
   (min(82vw,300px)), então o tamanho do fundo acompanha, senão o preto ficaria
   fora da tela e o drawer inteiro pareceria azul. */
@media (max-width:820px){
  .oc-sidebar{
    background:var(--sidebar-gradient);
    background-size:100% 100%;
  }
}

/* ==========================================================================
   ESCALA DE CAMADAS (V1.00.336)
   ==========================================================================
   ⚠️ Antes os números viviam soltos pelo arquivo, e a ordem entre eles era
   descoberta por acidente. Dois erros reais estavam ali:



.oc-menu-painel.oc-menu-flutuante{z-index:var(--oc-z-menu-contexto);}

.oc-modal-movimento{z-index:var(--oc-z-modal);}

/* ==========================================================================
   SUBMENU INLINE — accordion dentro da própria sidebar (V1.00.338)
   ==========================================================================
   Substitui por completo o flyout, que foi removido. Não existe mais painel
   flutuante, segunda coluna, portal, `position:fixed` de navegação nem cálculo
   de top/left: o submenu é conteúdo da barra, e é o navegador que o posiciona.

   ⚠️ `max-height` em vez de `display`. `display:none` não anima — o submenu
   apareceria de um quadro para o outro enquanto a barra desliza suavemente ao
   lado, e o descompasso é justamente o que se nota. Com `max-height` +
   `overflow:hidden` o grupo fechado ocupa zero e o aberto cresce, sem trocar de
   display e sem saltar.

   O valor 480px é um TETO, não uma altura: o grupo real mede bem menos, então
   a transição termina antes e ninguém vê o excesso. Alto o bastante para o
   maior grupo (Administração) caber quando ganhar mais itens. */
.oc-sidebar .oc-nav-filhos{
  display:flex;
  flex-direction:column;
  overflow:hidden;
  max-height:0;
  opacity:0;
  transition:max-height 220ms var(--sidebar-easing, ease),
             opacity 180ms var(--sidebar-easing, ease);
}

/* Aberto E com espaço para ler: os dois ao mesmo tempo.
   ⚠️ Na barra estreita o submenu fica fechado mesmo com o <details> aberto —
   64px não comportam rótulo nenhum, e era exatamente esse vazio que o flyout
   existia para preencher. Agora o caminho é outro: clicar no grupo EXPANDE a
   barra (assets/js/sidebar.js) e o submenu aparece aqui dentro. */
body:not(.oc-sidebar-collapsed) .oc-sidebar .oc-nav-grupo[open] > .oc-nav-filhos{
  max-height:480px;
  opacity:1;
}

/* §13 — subitem: recuo maior, tipografia menor, texto mais discreto. */
.oc-sidebar .oc-nav a.oc-nav-filho{
  padding-left:38px;
  font-size:13.5px;
  color:rgba(255,255,255,.82);
}
.oc-sidebar .oc-nav a.oc-nav-filho:hover{
  background:rgba(255,255,255,.08);
  color:#fff;
}
.oc-sidebar .oc-nav a.oc-nav-filho.active{
  background:rgba(255,255,255,.14);
  color:#fff;
  font-weight:600;
}

/* §14 — chevron discreto, girando ao abrir. */
.oc-sidebar .oc-nav-chevron{
  margin-left:auto;
  opacity:.6;
  font-size:12px;  /* chevron vazado: tamanho de texto, nao de triangulo cheio */
}
body.oc-sidebar-collapsed .oc-sidebar .oc-nav-chevron{display:none;}

@media (prefers-reduced-motion: reduce){
  /* O chevron saiu daqui: ele nao tem mais transicao para desligar — o glifo
     TROCA em vez de girar. */
  .oc-sidebar .oc-nav-filhos{transition:none;}
}

/* ==========================================================================
   ESCALA DE CAMADAS (V1.00.339)
   ==========================================================================
   ⚠️ ESTE BLOCO JÁ FOI APAGADO UMA VEZ, por mim, e o efeito foi silencioso: os
   USOS (`z-index:var(--oc-z-sidebar)`) continuaram no arquivo, mas sem a
   definição a declaração inteira é inválida e o elemento fica SEM z-index. A
   sidebar, o menu de contexto e o modal perderam a camada de uma vez, e nada
   no CSS acusa isso — some do computed style e pronto.

   Aconteceu porque removi o painel lateral com uma expressão regular que
   procurava o nome dele; o comentário deste bloco citava esse nome, a captura
   atravessou o comentário e levou o `:root` junto. Fica o registro: regex sobre
   CSS não distingue comentário de código.

   Os nomes valem mais que os números — quem criar uma camada nova escolhe pela
   intenção e a ordem sai certa. Nada de 9999. */
:root{
  --oc-z-conteudo:1;
  --oc-z-sidebar:65;        /* acima do .oc-topbar (60) */
  --oc-z-sidebar-overlay:70;
  --oc-z-drawer:80;
  --oc-z-header:90;         /* header global (V1.00.477) — acima do drawer, que nasce abaixo dele */
  --oc-z-dropdown:500;
  --oc-z-menu-contexto:700;
  --oc-z-toast:900;
  --oc-z-modal-fundo:1000;
  --oc-z-modal:1100;
}

/* ==========================================================================
   LARGURAS DA SIDEBAR (V1.00.339 — restauradas)
   ==========================================================================
   ⚠️ Estas duas variáveis foram apagadas por engano junto com a remoção do
   painel lateral, e o sintoma foi discreto: `width:var(--sidebar-collapsed-width)`
   virou declaração inválida, a barra ficou SEM largura própria e passou a herdar
   a regra do drawer (`min(82vw,300px)`) — 308px medidos onde deviam ser 64.

   Uma variável ausente não avisa: a linha simplesmente deixa de existir para o
   navegador. Por isso agora há uma varredura que confere se toda `var(--…)`
   usada no arquivo tem definição. */
:root{
  --sidebar-collapsed-width:64px;
  --sidebar-expanded-width:244px;
}

/* ==========================================================================
   ÍCONES DA NAVEGAÇÃO — caixa única e 150% (V1.00.340)
   ==========================================================================
   MEDIDO ANTES DE MUDAR (não presumido):
     ícone .......... font-size 14px nos links, 15px nos grupos
     caixa .......... 18px de largura
     gap ............ 10px
     padding do item  9px 10px

   ⚠️ E a medição revelou DUAS COLUNAS onde deveria haver uma:
     centro do ícone   links X=33  |  grupos X=23
     início do rótulo  links X=48  |  grupos X=36
     altura do item    links 38px  |  grupos 22px

   Os `<summary>` dos grupos tinham o mesmo padding dos links numa regra
   escopada ao desktop com mouse — que saiu junto com a remoção do painel
   lateral. Sem ela, grupo e link passaram a ser caixas diferentes, e nenhuma
   régua vertical passava pelos dois. Por isso a regra abaixo trata LINK e
   SUMMARY como a mesma coisa: são o mesmo item de menu, e a única diferença
   legítima entre eles é o chevron à direita.

   ⚠️ OS ÍCONES SÃO EMOJI (`&#128218;`, `&#9881;`…), não SVG. Isso importa:
   emoji é TEXTO, então quem o dimensiona é `font-size`, e cada família traz a
   sua própria linha de base — foi daí que vieram as caixas de 20px e 22px para
   o mesmo `width:18px`. A caixa fixa com `inline-flex` + `align-items:center`
   tira a linha de base da conta: o glifo passa a ser centrado na caixa, não
   pendurado na baseline do texto ao lado (§5/§11).

   NOVO: 14px × 1,5 = 21px de glifo, numa caixa de 26px. Sem `transform:scale`
   (§3) — escala não muda o espaço ocupado, e o ícone acabaria transbordando a
   caixa de 18px em vez de ocupar uma maior. */

/* Item principal: link e grupo com a MESMA caixa. */
.oc-sidebar .oc-nav > a,
.oc-sidebar .oc-nav-grupo > summary{
  display:flex;
  align-items:center;
  gap:10px;
  padding:8px 10px;
  border-radius:8px;
  font-size:14px;
  line-height:1.25;
}

/* Caixa do ícone — idêntica para todos, e é ela que forma a coluna única. */
.oc-sidebar .oc-nav > a > .oc-nav-icon,
.oc-sidebar .oc-nav-grupo > summary > .oc-nav-icon{
  flex:0 0 26px;
  width:26px;
  height:26px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  font-size:21px;      /* 14 × 1,5 */
  line-height:1;
  text-align:center;
  opacity:1;
}

/* §16 — subitem NÃO cresce: ficaria mais forte que o pai. Mantém o tamanho de
   antes, com caixa própria menor, e continua alinhado por ser flex. */
.oc-sidebar .oc-nav a.oc-nav-filho > .oc-nav-icon{
  flex:0 0 18px;
  width:18px;
  height:18px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  font-size:14px;
  line-height:1;
}

/* §15 — o chevron tem coluna própria à direita, fora da caixa do ícone. */
.oc-sidebar .oc-nav-grupo > summary > .oc-nav-chevron{
  flex:0 0 auto;
  margin-left:auto;
}

/* §17 — recolhida: a caixa de 26px é centrada nos 64px da barra. O `gap` some
   junto com o rótulo, senão empurraria o ícone para a esquerda.

   ⚠️ ESCOPADO AO DESKTOP. No celular o <body> também carrega
   `oc-sidebar-collapsed` — é o estado PADRÃO desde a V1.00.316 — mas lá a
   barra é um drawer de 300px com os rótulos à mostra. Sem este `@media`, a
   regra centralizava cada linha do drawer isoladamente e os ícones viravam
   sete colunas diferentes. Medido em 375px: X de 458 a 508. */
@media (min-width:821px){
  body.oc-sidebar-collapsed .oc-sidebar .oc-nav > a,
  body.oc-sidebar-collapsed .oc-sidebar .oc-nav-grupo > summary{
    justify-content:center;
    gap:0;
    padding-left:0;
    padding-right:0;
  }
}

/* ============ Coluna "Ação" da tabela de planos (assinatura.php) ============
   Duas periodicidades por plano. Lado a lado, os dois botões somavam 333px e
   empurravam a tabela para 1358px dentro de um painel de 1095px — a tabela
   inteira ganhava rolagem horizontal e as colunas Plano/Mensal saíam de vista.

   Empilhados, a coluna cabe e o ciclo fica explícito acima de cada botão, em
   vez de dentro do rótulo (onde era o que mais alargava). `align-items:stretch`
   deixa os dois botões com a MESMA largura — desalinhados, pareceriam ações de
   naturezas diferentes. */
table.oc-table.oc-tabela-planos td.oc-plano-acoes{
  white-space:nowrap;   /* o rótulo do botão nunca quebra no meio */
}
.oc-plano-acoes .oc-plano-acao{
  display:flex;
  flex-direction:column;
  align-items:stretch;
  gap:2px;
}
.oc-plano-acoes .oc-plano-acao + .oc-plano-acao{
  margin-top:8px;       /* separa mensal de anual sem precisar de borda */
}
.oc-plano-acao-ciclo{
  font-size:11.5px;
  color:var(--text-muted);
  text-transform:uppercase;
  letter-spacing:.03em;
}
.oc-plano-acoes .oc-btn{
  justify-content:center;
  width:100%;
}

/* ============ Tabela de planos (assinatura.php) ============
   ⚠️ `table.oc-table th, td` declara `white-space:nowrap` GLOBALMENTE. Nas
   listagens isso é correto: quem recorta o excesso é o table-manager, com
   reticências e largura de coluna própria. Esta tabela NÃO é gerenciada por
   ele — e com nowrap a descrição do plano e a linha de economia anual ("equivale
   a 114,75/mês · economize R$ 267,00") viravam uma linha indivisível cada,
   empurrando a tabela para 1148px dentro de um painel de 1095px. A rolagem
   horizontal resultante escondia justamente a coluna Ação, no extremo direito.

   Aqui o texto pode quebrar: é conteúdo descritivo, não um valor que precise
   ser lido numa linha só. Preço e botões seguem indivisíveis logo abaixo. */
/* ⚠️ O prefixo `table.oc-table` NAO e decoracao — e o que faz a regra valer.
   `table.oc-table td` (0,1,2) vence `.oc-tabela-planos td` (0,1,1), e a
   primeira tentativa desta correcao ficou inerte por isso: o nowrap global
   continuou ganhando e a tabela seguiu com 1148px. Mesma licao ja registrada
   nos blocos de `.oc-money-cell` acima. */
table.oc-table.oc-tabela-planos th,
table.oc-table.oc-tabela-planos td{
  white-space:normal;
}
/* Um valor monetário quebrado no meio ("R$ 1.377,​00") é ilegível. */
table.oc-table.oc-tabela-planos .oc-plano-preco{
  white-space:nowrap;
}

/* ============ MODAL GLOBAL DE CONFIRMAÇÃO DE EXCLUSÃO (V1.00.427) ============
   Herda .oc-modal (raio, borda, sombra do backdrop, superfície) — o padrão
   visual é o mesmo de todo modal do sistema, e não um desenho paralelo. */
.oc-modal-confirmar{
  width:min(440px, calc(100vw - 32px));
}
.oc-modal-confirmar-cab{
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:12px;
}
.oc-modal-confirmar-cab h2{
  margin:0;
  font-size:1.05rem;
}
.oc-modal-confirmar-x{
  border:0;
  background:none;
  color:var(--text-muted);
  font-size:1.4rem;
  line-height:1;
  padding:0 4px;
  cursor:pointer;
  border-radius:6px;
}
.oc-modal-confirmar-x:hover{ color:var(--text); }
.oc-modal-confirmar-x:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }
.oc-modal-confirmar p{
  margin:12px 0 0;
  color:var(--text);
}
/* O número do estrago em massa. Maior que o texto de propósito: nas exclusões
   por varredura ele é o único freio que resta depois que a palavra digitada
   saiu — esconder isso no meio da frase seria devolver o problema. */
.oc-modal-confirmar-destaque{
  margin-top:10px;
  padding:10px 12px;
  border-radius:8px;
  background:#fdf4f3;
  border:1px solid #f0d3d1;
  color:#b3261e;
  font-weight:600;
  font-size:1.05rem;
}
.oc-modal-confirmar-acoes{
  display:flex;
  justify-content:flex-end;
  gap:10px;
  margin-top:20px;
}
/* Mobile: empilhado e de largura cheia, com o "Não" em cima — o polegar
   alcança primeiro a saída segura, nunca a ação destrutiva. */
@media (max-width:480px){
  .oc-modal-confirmar-acoes{
    flex-direction:column-reverse;
    align-items:stretch;
  }
  .oc-modal-confirmar-acoes .oc-btn{ width:100%; justify-content:center; }
}

/* ==========================================================================
   MODO COMPACTO DAS TABELAS  (`body.oc-tabelas-compactas`)
   ==========================================================================
   Alterna a DENSIDADE da visualização. Não esconde coluna nem registro — quem
   decide o que aparece é o botão "Colunas", e as duas coisas são separadas de
   propósito.

   ⚠️ A classe fica no `<body>`, não em cada `<table>`. A toolbar (seletor de
   quantidade, "Colunas", "Compactar") vive FORA da tabela, na barra da
   listagem; só um estado no ancestral comum alcança os dois ao mesmo tempo. É
   também o que faz a preferência valer para todas as listagens de uma vez.

   ⚠️ A fonte NÃO diminui. Reduzir corpo de texto é o atalho fácil para caber
   mais linha e o que primeiro torna a tabela ilegível. O que encolhe é o
   ESPAÇO: padding vertical, padding horizontal das colunas curtas, e os vãos
   da toolbar. */

/* ---- Densidade da grade ---- */
/* O token muda no BODY, então a barra superior — que também o usa —
   reacompanha o novo recuo sem nenhuma regra própria (§9). */
body.oc-tabelas-compactas{ --oc-tabela-padding-x:8px; }

body.oc-tabelas-compactas table.oc-table th,
body.oc-tabelas-compactas table.oc-table td{
  padding:3px var(--oc-tabela-padding-x);
}

/* Colunas curtas apertam mais no horizontal; a Descrição não, porque é ela que
   precisa da largura — comprimir a coluna de texto para caber mais nada é o
   oposto do objetivo (§16/§17). */
body.oc-tabelas-compactas table.oc-table th[data-tm-key="codigo"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="codigo"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="categoria"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="categoria"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="unidade"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="unidade"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="tipo"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="tipo"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="uf"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="uf"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="status"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="status"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="origem"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="origem"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="cidade"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="cidade"],
body.oc-tabelas-compactas table.oc-table th[data-tm-key="acoes"],
body.oc-tabelas-compactas table.oc-table td[data-tm-key="acoes"]{
  padding-left:5px;
  padding-right:5px;
}

/* Botões de ação menores e mais juntos — sem sumir, e sem descer do alvo de
   toque confortável em ponteiro fino. */
body.oc-tabelas-compactas table.oc-table td.oc-actions-cell > * + *{
  margin-left:4px;
}
body.oc-tabelas-compactas table.oc-table td.oc-actions-cell .oc-btn-icon-only{
  width:24px;
  min-width:24px;
  height:24px;
  min-height:24px;
}
body.oc-tabelas-compactas table.oc-table td.oc-actions-cell .oc-btn{
  min-height:24px;
  padding-top:2px;
  padding-bottom:2px;
}

/* ---- Toolbar reduzida ----
   Os textos somem da TELA, nunca da árvore de acessibilidade: os botões têm
   `aria-label` e `title` (postos por table-manager.js), e o seletor tem
   `aria-label` próprio. `display:none` remove o elemento também para o leitor
   de tela — por isso o nome nunca dependeu deste texto. */
body.oc-tabelas-compactas .oc-listagem-barra .oc-form--inline label,
body.oc-tabelas-compactas .oc-listagem-barra .oc-form--inline span,
body.oc-tabelas-compactas .oc-listagem-barra-direita .oc-btn-label{
  display:none;
}

/* Sem o texto, os dois botões viram quadrados do tamanho do seletor. */
body.oc-tabelas-compactas .oc-listagem-barra-direita .oc-btn{
  width:var(--oc-toolbar-controle-h);
  min-width:var(--oc-toolbar-controle-h);
  padding-left:0;
  padding-right:0;
  gap:0;
  justify-content:center;
}

/* O grupo aperta junto: com três controles pequenos, 16px de vão passa a
   parecer um vazio no meio da barra. */
body.oc-tabelas-compactas .oc-listagem-barra-direita{
  gap:8px;
}

/* ---- Estado ativo do botão Compactar ----
   ⚠️ Não é só cor (§23). Fundo, borda e cor mudam juntos, porque um estado
   sinalizado apenas por matiz desaparece para quem tem baixa visão de cor — e
   `aria-pressed` já cobre o leitor de tela, mas não o olho. */
.oc-listagem-compactar[aria-pressed="true"]{
  background:var(--primary-soft, #eef2ff);
  border-color:var(--primary);
  color:var(--primary-dark, var(--primary));
}

/* O ícone acompanha a cor do botão em qualquer estado. */
.oc-icone{
  width:15px;
  height:15px;
  display:block;
  flex:0 0 auto;
}

/* No celular o alvo de toque não encolhe junto com a densidade: os botões da
   toolbar sobem via `--oc-toolbar-controle-h` (36px, mais adiante); aqui ficam
   os botões de AÇÃO das linhas, que economizar transformaria em erro de
   clique. */
@media (max-width: 640px){
  body.oc-tabelas-compactas table.oc-table td.oc-actions-cell .oc-btn-icon-only{
    width:28px;
    min-width:28px;
    height:28px;
    min-height:28px;
  }
}

/* ==========================================================================
   TOOLBAR DA TABELA — OS TRÊS CONTROLES TÊM A MESMA ALTURA
   ==========================================================================
   Seletor de quantidade, "Colunas" e "Compactar" ficam lado a lado na mesma
   linha; qualquer diferença de altura entre eles é imediatamente visível.

   ⚠️ UMA declaração para os três, e não uma por controle. Era assim que a
   divergência nasceu: o seletor e o "Compactar" fixavam `height:32px` cada um,
   e o "Colunas" ficava com o `min-height:36px` herdado de `.oc-btn` — 4px mais
   alto, no meio dos outros dois. Ninguém tinha escrito "36" ali; ele veio de
   um componente genérico que nada tem a ver com esta barra.

   `min-height` junto com `height` é necessário: `.oc-btn` declara
   `min-height:36px`, e um `height:32px` sozinho PERDE para um `min-height`
   maior — a caixa nunca encolheria. Declarar os dois é o que faz o token valer.

   `box-sizing:border-box` vem do reset global (`*{box-sizing:border-box}` no
   topo do arquivo), então a altura já inclui padding e borda nos três. Está
   repetido aqui de propósito: é a garantia de que os 32px são a caixa INTEIRA,
   e não some se o reset mudar.

   ⚠️ O alinhamento vertical vem do `align-items:center` de
   `.oc-listagem-barra-direita` — nada de `margin-top`, `translateY` ou
   `position:relative` para acertar um controle isolado. */
.oc-listagem-barra-direita .oc-per-page-select,
.oc-listagem-barra-direita .oc-tm-btn-colunas,
.oc-listagem-barra-direita .oc-listagem-compactar{
  height:var(--oc-toolbar-controle-h);
  min-height:var(--oc-toolbar-controle-h);
  box-sizing:border-box;
}

/* A LARGURA continua livre: o seletor precisa caber "100" mais a seta; os
   botões são quadrados. A exigência é só a altura. */

/* ---- Celular: alvo de toque acima do mínimo confortável ----
   32px é uma caixa boa para ponteiro fino e apertada para o dedo. O token sobe
   para 36px, e os três sobem JUNTOS — que é o ponto de existir um token. */
@media (max-width: 640px){
  .oc-listagem-barra-direita{
    --oc-toolbar-controle-h:36px;
  }
}

/* ==========================================================================
   FILTRO COM VALOR ATIVO  (`.oc-field--ativo`)
   ==========================================================================
   Marca discreta no campo que está efetivamente filtrando. Quem aplica é
   `assets/js/filtros.js`, só em formulários que declaram
   `data-oc-destacar-ativos` — hoje, um teste de UX em Composições.

   ⚠️ O destaque é do CAMPO, não do foco. `:focus` já tem o seu tratamento
   (borda azul + anel), e é passageiro; este permanece depois que o usuário sai
   do campo — é essa permanência que informa qual recorte está em vigor.

   Deliberadamente sutil: fundo azul muito claro e borda azul suave, texto
   inalterado. Um destaque forte transformaria o painel numa fileira de campos
   coloridos e competiria com o botão "Filtrar", que é a ação. */
.oc-field--ativo input,
.oc-field--ativo select,
.oc-field--ativo textarea{
  background:#f5f8ff;
  border-color:#c3d4f7;
}

/* O rótulo acompanha em peso, não em cor: escurecer o texto do rótulo
   sinalizaria "erro" ou "obrigatório" no vocabulário de formulário. */
.oc-field--ativo > label{
  font-weight:600;
}

/* ⚠️ O foco continua vencendo o destaque. Sem isto, um campo ativo em foco
   mostraria a borda de "ativo" no lugar do anel de foco — e quem navega por
   teclado perderia a única pista de onde está. */
.oc-field--ativo input:focus,
.oc-field--ativo select:focus,
.oc-field--ativo textarea:focus{
  border-color:var(--primary);
}

/* ---- Estados do campo de filtro: a ordem importa ----
   §24 — a pilha de estados é normal → ativo → hover → foco → desabilitado, e
   cada um precisa continuar legível sobre o anterior.

   O `:hover` do campo ativo escurece um pouco a borda sem mexer no fundo: um
   hover que trocasse o fundo faria o campo parecer neutro justo quando o
   ponteiro está sobre ele. */
.oc-field--ativo input:hover,
.oc-field--ativo select:hover,
.oc-field--ativo textarea:hover{
  border-color:#9dbaf0;
}

/* ⚠️ §25 — desabilitado NUNCA usa a aparência de ativo. Um campo que não
   filtra parecendo um filtro em uso é pior que nenhum destaque: convida a
   procurar um valor que não existe. O JS já não marca campo desabilitado;
   isto cobre o caso de a classe chegar por outro caminho. */
.oc-field--ativo input:disabled,
.oc-field--ativo select:disabled,
.oc-field--ativo textarea:disabled{
  background:var(--bg);
  border-color:var(--border);
  color:var(--text-muted);
}

/* ============================================================
   GRADE PADRÃO DOS FORMULÁRIOS Novo/Editar  (.oc-form-grade)
   ------------------------------------------------------------
   `.oc-form` puro usa `repeat(auto-fit,minmax(220px,1fr))`: a coluna
   nasce da largura disponível, então não dá para dizer "Código 15% /
   Tipo 15% / Nome 70%". Aqui a grade é POR LINHA — cada `.oc-form-linha`
   tem seu próprio template — e é isso que permite proporções diferentes
   em linhas vizinhas sem inventar um denominador comum de 100 colunas.

   Proporções em `fr`, nunca em `%`: o `gap` é descontado ANTES do
   reparto de `fr`, então 15fr/15fr/70fr nunca estoura o container;
   15%+15%+70%+gap estouraria.

   Breakpoints por CONTAINER (não por viewport) porque a sidebar muda a
   largura útil: a mesma tela de 1024px é "tablet" com sidebar aberta e
   "desktop" sem ela. 900px = teto do tablet, 600px = mobile — os mesmos
   valores já usados nos painéis de filtro. */
.oc-form.oc-form-grade{
  display:block;              /* quem é grid são as linhas, não o form */
  max-width:none;             /* 7 papéis numa linha não cabem em 760px */
  container-type:inline-size;
  container-name:oc-form;
}

.oc-form-grade .oc-form-linha{
  display:grid;
  gap:16px 18px;
  /* `start`, não `end`: os rótulos têm todos a mesma altura, então
     alinhar pelo topo alinha os campos. Com `end`, um campo que ganha
     `.oc-field-error` empurraria os vizinhos para baixo. */
  align-items:start;
  margin-bottom:16px;
}

.oc-form-linha--15-15-70{ grid-template-columns:15fr 15fr 70fr; }
.oc-form-linha--20-80{ grid-template-columns:20fr 80fr; }
.oc-form-linha--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
.oc-form-linha--2{ grid-template-columns:repeat(2,minmax(0,1fr)); }
.oc-form-linha--1{ grid-template-columns:1fr; }
.oc-form-linha--4{ grid-template-columns:repeat(4,minmax(0,1fr)); }
/* Obras — seção Obra: Código 20 / Nome 40 / Tipo 20 / Área 20. */
.oc-form-linha--20-40-20-20{ grid-template-columns:20fr 40fr 20fr 20fr; }
/* Obras — Endereço: CEP 20 / Logradouro 60 / Número 20. */
.oc-form-linha--20-60-20{ grid-template-columns:20fr 60fr 20fr; }
/* Obras — Prazos: Status 30 / Observações 70. */
.oc-form-linha--30-70{ grid-template-columns:30fr 70fr; }
/* Obras — Participantes: Pessoa 60 / Papel 20 / Principal 20. */
.oc-form-linha--60-20-20{ grid-template-columns:60fr 20fr 20fr; }

/* ---- Título de seção dentro do formulário ----
   O pedido chama de "tag": um rótulo curto que separa Obra, Endereço,
   Responsáveis e Prazos sem quebrar o formulário em vários cards. É um <h2>
   de verdade (não um <div> estilizado) para o leitor de tela também enxergar
   as seções. */
.oc-form-grade .oc-form-secao{
  margin:0 0 12px;
  font-size:12.5px;
  font-weight:700;
  letter-spacing:.04em;
  text-transform:uppercase;
  color:var(--text-muted);
}
.oc-form-grade .oc-form-secao + .oc-form-linha{ margin-top:0; }
.oc-form-grade .oc-form-secao:not(:first-child){
  margin-top:6px;
  padding-top:16px;
  border-top:1px solid var(--border);
}

/* ---- Subtítulo de um bloco (ex.: "Adicionar participante") ----
   Substitui o `style="margin-top:18px"` que estava inline na página: mesma
   respiração, agora num lugar só. */
.oc-subsecao{
  margin:22px 0 12px;
  font-size:15px;
  font-weight:700;
  color:var(--text);
}

/* ---- Campo cujo controle é uma caixa de seleção ----
   Sem rótulo em cima, ele mediria ~20px contra os ~64px de um campo com
   rótulo + controle, e ficaria colado no topo da linha. Encostar no fim e
   ocupar a mesma faixa de altura do controle põe a caixa no eixo dos
   selects vizinhos. */
.oc-form-grade .oc-field--caixa{ align-self:end; }
.oc-form-grade .oc-field--caixa > label{
  min-height:var(--oc-form-controle-h);
}
/* Rótulo de um grupo de controles (ex.: Papéis). Não é <label> porque não
   aponta para um controle único — o grupo é anunciado por aria-labelledby. */
.oc-form-grade .oc-field-rotulo{
  font-size:12.5px;
  font-weight:600;
  color:var(--text-muted);
}

/* Mesma altura visual para input/select/date/botão; textarea é a exceção. */
.oc-form-grade{ --oc-form-controle-h:38px; }
.oc-form-grade .oc-field > input:not([type="checkbox"]):not([type="radio"]),
.oc-form-grade .oc-field > select{
  height:var(--oc-form-controle-h);
}
.oc-form-grade .oc-form-actions .oc-btn{
  min-height:var(--oc-form-controle-h);
}

/* Ações: repartidas por igual entre os botões que a linha REALMENTE tem —
   50/50 com dois, 100% com um. Com `repeat(2,...)` fixo, um formulário de uma
   ação só (o de Participantes) deixaria metade da faixa vazia. No celular o
   fluxo vira linha e cada botão ocupa 100%. */
.oc-form-grade .oc-form-actions{
  display:grid;
  grid-auto-flow:column;
  grid-auto-columns:minmax(0,1fr);
  gap:16px 18px;
  margin-top:4px;
}
.oc-form-grade .oc-form-actions .oc-btn{
  width:100%;
  justify-content:center;
  /* `height` junto do `min-height`: o botao passa a medir EXATAMENTE o mesmo
     que o input e o select, e nao so "pelo menos". Sem isso, um rotulo maior
     ou um icone mais alto faria o botao crescer e desalinhar a borda de baixo. */
  height:var(--oc-form-controle-h);
}

/* ---- Ações DENTRO de uma linha da grade ----
   `display:contents`: Salvar e Cancelar viram células da própria linha, com a
   mesma largura de um campo. Enquanto o wrapper for uma caixa, os dois dividem
   UMA célula e o `gap` é contado duas vezes — os botões saem visivelmente mais
   estreitos que o Status ao lado. É a mesma solução já usada no painel de
   filtros (`.oc-filtros-linhas .oc-form-actions`). */
.oc-form-grade .oc-form-linha > .oc-form-actions{
  display:contents;
}

/* ⚠️ ALINHAMENTO VERTICAL DOS BOTOES NUMA LINHA DE CAMPOS.
   A linha usa `align-items:start` porque os rotulos tem todos a mesma altura e
   alinhar pelo topo alinha os campos — e porque uma mensagem de erro precisa
   crescer para BAIXO, sem empurrar os vizinhos.

   Um botao nao tem rotulo. Com `start`, ele encostava no topo da faixa, ou
   seja, na altura do ROTULO do campo ao lado, e nao na do controle: o Salvar
   aparecia ~26px acima do select Status.

   `align-self:end` resolve na estrutura. O campo ocupa a faixa inteira com o
   controle encostado no fim; o botao tambem encosta no fim e tem a mesma
   altura, entao as bordas de cima E de baixo coincidem. Nada de margin-top
   magico nem translateY. */
.oc-form-grade .oc-form-linha > .oc-form-actions > .oc-btn{
  align-self:end;
}

/* --- Papéis: múltipla seleção, nunca um <select> de escolha única ------
   Dois blocos em vez de um só porque o tablet pede 4×25% e depois
   3×33,33%; com um grid único de 4 colunas a segunda linha sairia com
   três itens de 25% e um vão. No desktop os blocos viram
   `display:contents` e os 7 papéis voltam a ser um grid só. */
.oc-papeis-grade{
  display:grid;
  grid-template-columns:repeat(7,minmax(0,1fr));
  gap:8px 10px;
}
.oc-papeis-grade .oc-papeis-bloco{ display:contents; }

.oc-papel-opcao{
  display:flex;
  align-items:center;
  gap:8px;
  min-width:0;
  padding:8px 10px;
  border:1px solid var(--border);
  border-radius:8px;
  background:#fff;
  font-size:13.5px;
  font-weight:500;
  color:var(--text);
  cursor:pointer;
}
.oc-papel-opcao > input[type="checkbox"]{
  width:auto;
  flex:none;
  margin:0;
}
.oc-papel-opcao:hover{ border-color:var(--primary); }
.oc-papel-opcao:focus-within{ box-shadow:0 0 0 3px rgba(29,95,214,.12); }
.oc-papel-opcao:has(input:checked){
  border-color:var(--primary);
  background:rgba(29,95,214,.06);
}

/* --- Tablet: uma linha de 4 campos pode virar 2 + 2 (`--t2`) ----------
   Opt-in: Endereço continua com Complemento/Bairro/Cidade/UF em 4×25%, e só
   as linhas marcadas quebram. Não precisa dissolver a linha — os 4 itens
   caem sozinhos em duas faixas quando a grade passa a ter 2 colunas. */
@container oc-form (min-width:601px) and (max-width:900px){
  .oc-form-grade .oc-form-linha--t2{
    grid-template-columns:repeat(2,minmax(0,1fr));
  }
}

/* --- Tablet: campos mantêm as proporções; só os Papéis quebram ------- */
@container oc-form (max-width:900px){
  .oc-papeis-grade{
    display:block;
  }
  .oc-papeis-grade .oc-papeis-bloco{
    display:grid;
    gap:8px 10px;
  }
  .oc-papeis-grade .oc-papeis-bloco--1{
    grid-template-columns:repeat(4,minmax(0,1fr));
  }
  .oc-papeis-grade .oc-papeis-bloco--2{
    grid-template-columns:repeat(var(--oc-papeis-cols-2,3),minmax(0,1fr));
    margin-top:8px;
  }
}

/* --- Mobile: uma coluna, nada lado a lado ---------------------------- */
@container oc-form (max-width:600px){
  .oc-form-grade .oc-form-actions{
    grid-auto-flow:row;
  }
  .oc-form-grade .oc-form-linha,
  .oc-form-grade .oc-form-actions,
  .oc-papeis-grade,
  .oc-papeis-grade .oc-papeis-bloco,
  .oc-papeis-grade .oc-papeis-bloco--1,
  .oc-papeis-grade .oc-papeis-bloco--2{
    grid-template-columns:1fr;
  }
  .oc-form-grade .oc-field,
  .oc-form-grade .oc-field > input,
  .oc-form-grade .oc-field > select,
  .oc-form-grade .oc-field > textarea,
  .oc-papel-opcao{
    width:100%;
    min-width:0;
  }
}

/* ==========================================================================
   LIMPAR SEM NADA PARA LIMPAR
   ==========================================================================
   Com zero filtros ativos o "Limpar" não tem função — mas continua clicável,
   porque desabilitar de verdade tiraria o alvo de foco do teclado sem avisar
   ninguém. O que muda é o PESO: ele para de competir com o "Filtrar" ao lado.

   ⚠️ O gatilho é a AUSÊNCIA do contador. `ocFiltrosSumario()` só emite
   `.oc-filtros-badge` quando há filtro ativo, então `:not(:has(...))` já é a
   pergunta certa — sem atributo novo no markup, sem JS, e sem uma segunda
   contagem que poderia divergir da do servidor. */
.oc-filtros:not(:has(.oc-filtros-badge)) .oc-btn-limpar{
  opacity:.6;
  font-weight:500;
}
.oc-filtros:not(:has(.oc-filtros-badge)) .oc-btn-limpar:hover,
.oc-filtros:not(:has(.oc-filtros-badge)) .oc-btn-limpar:focus-visible{
  opacity:1;
}

/* ==========================================================================
   SKELETON / SHIMMER  (includes/skeleton.php)
   ==========================================================================
   Enquanto os dados não chegam, a tela mostra o ESQUELETO do conteúdo final
   em vez de um vazio ou de um spinner central. Duas razões concretas:

     · o layout não pula quando os dados chegam (o bloco já ocupa a altura
       final — a linha da tabela mede o mesmo que uma linha de verdade);
     · o usuário vê ONDE cada coisa vai aparecer, então a espera é lida como
       progresso e não como travamento.

   ⚠️ ANIMAÇÃO SÓ DE `background-position`. É a única propriedade aqui que o
   navegador compõe sem recalcular layout: um `box-shadow` ou um `::after` com
   `transform` animado em 10 linhas × 8 colunas custaria 80 camadas. Uma
   `linear-gradient` de 200% deslizando resolve com uma pintura por quadro. */
:root{
  --oc-skeleton-base:#e9ecef;
  --oc-skeleton-brilho:#f6f8fa;
}

.oc-skeleton{
  display:block;
  border-radius:6px;
  background-color:var(--oc-skeleton-base);
  background-image:linear-gradient(
    90deg,
    var(--oc-skeleton-base) 0%,
    var(--oc-skeleton-brilho) 50%,
    var(--oc-skeleton-base) 100%
  );
  background-size:200% 100%;
  background-repeat:no-repeat;
  animation:oc-shimmer 1.5s ease-in-out infinite;
  /* Sem isto, um leitor de tela anunciaria dezenas de blocos vazios. O
     contêiner é que carrega `aria-busy`; as peças são decoração. */
  color:transparent;
  user-select:none;
}

@keyframes oc-shimmer{
  from{ background-position:150% 0; }
  to{ background-position:-50% 0; }
}

/* Peças. A altura é a do controle real correspondente, para o conteúdo
   verdadeiro entrar sem deslocar nada (§11). */
.oc-skeleton-texto{ height:12px; margin:4px 0; }
.oc-skeleton-texto--curto{ width:40%; }
.oc-skeleton-texto--medio{ width:65%; }
.oc-skeleton-texto--longo{ width:90%; }
.oc-skeleton-titulo{ height:16px; width:45%; margin:0 0 10px; }
.oc-skeleton-campo{ height:var(--oc-form-controle-h,38px); border-radius:8px; }
.oc-skeleton-botao{ height:var(--oc-form-controle-h,38px); width:120px; border-radius:8px; }
.oc-skeleton-valor{ height:22px; width:55%; }
.oc-skeleton-grafico{ height:220px; border-radius:10px; }

.oc-skeleton-card{
  border:1px solid var(--border);
  border-radius:10px;
  padding:14px 16px;
  background:var(--surface);
}

/* Linha de tabela: cada célula recebe uma barra com largura variável, para o
   bloco não virar um paredão uniforme. */
tr.oc-skeleton-row > td{
  vertical-align:middle;
}
tr.oc-skeleton-row > td > .oc-skeleton{
  height:12px;
}

/* §4 — quem pediu menos movimento não recebe animação nenhuma; o esqueleto
   continua, estático, porque a informação dele é o LAYOUT, não o brilho. */
@media (prefers-reduced-motion:reduce){
  .oc-skeleton{
    animation:none;
    background-image:none;
  }
}

/* §17 — a troca para o conteúdo real é curta o bastante para não parecer
   transição, só para não parecer corte seco. */
.oc-conteudo-carregado{
  animation:oc-fade-conteudo .15s ease-out;
}
@keyframes oc-fade-conteudo{
  from{ opacity:.35; }
  to{ opacity:1; }
}
@media (prefers-reduced-motion:reduce){
  .oc-conteudo-carregado{ animation:none; }
}

/* ==========================================================================
   BARRA SUPERIOR DA LISTAGEM — alinhada com a primeira coluna
   ==========================================================================
   O contador ("90 registro(s) encontrado(s)") nascia colado na borda do
   painel, enquanto o "CÓDIGO" logo abaixo começava 10px adentro: dois textos
   que o olho lê como uma coluna só, desencontrados.

   ⚠️ A correção NÃO é um `margin-left` escolhido a olho. A barra passa a usar
   o MESMO token de recuo horizontal das células (`--oc-tabela-padding-x`), o
   que resolve os três casos de uma vez: desktop, celular e modo compacto — no
   compacto o token cai para 8px no `body` e a barra acompanha sozinha.

   O recuo vale para os dois lados, então a toolbar da direita também encosta
   no mesmo eixo da última coluna, em vez de vazar 10px além dela. */
.oc-listagem-barra--topo{
  margin:0 0 18px;
  padding-inline:var(--oc-tabela-padding-x, 10px);
}

/* ==========================================================================
   AÇÕES DA LINHA NO MODO COMPACTO — agrupadas em [⋯]
   ==========================================================================
   assets/js/acoes-compactas.js marca os controles originais com
   `.oc-acoes-original` e acrescenta um `<details class="oc-menu">` na célula.
   Os originais continuam no HTML — escondidos, não removidos —, porque é neles
   que vivem o CSRF, a confirmação e a rota. O item do menu apenas os aciona.

   ⚠️ V1.00.487: a regra deixou de ser escopada a `body.oc-tabelas-compactas`.
   O menu vale em qualquer modo de tabela — antes a mesma linha tinha duas
   aparências (três botões no normal, um [⋮] no compacto).

   Quem marca com `.oc-acoes-original` é o JS: sem ele nada é escondido, e os
   botões continuam visíveis e funcionando. */
td.oc-actions-cell > .oc-acoes-original{
  display:none;
}

td.oc-actions-cell > .oc-acoes-menu{
  display:inline-block;
  vertical-align:middle;
}

/* §12 — a coluna pode estreitar, mas o alvo de toque não. 32px é o mesmo
   quadrado que os botões da toolbar usam no modo compacto. */
td.oc-actions-cell > .oc-acoes-menu > summary{
  min-width:32px;
  min-height:32px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  font-size:16px;
  line-height:1;
}

/* ==========================================================================
   CENTRALIZAÇÃO VERTICAL — uma linha, uma altura
   ==========================================================================
   `vertical-align:middle` já está declarado em `table.oc-table th, td`. O que
   faltava era garantir que nenhum WRAPPER dentro da célula criasse uma caixa
   com altura própria: um `div` com `display:block` e `min-height` faz a célula
   parecer desalinhada mesmo com a célula certa.

   ⚠️ A exceção é a coluna de texto longo. Ela mantém o texto começando no
   topo — mas isso NÃO muda a altura estrutural da linha: a `<tr>` continua
   sendo uma só, e Código, Unidade, Status e Ações continuam no centro dessa
   altura, que é exatamente o que o §17 pede. */
table.oc-table td > .oc-celula-conteudo{
  display:flex;
  align-items:center;
  min-height:0;
}

/* Badges de status nunca colados no topo quando a descrição quebra em três
   linhas: a célula é middle e o badge é inline-block, então ele acompanha. */
table.oc-table td[data-tm-key="status"]{
  vertical-align:middle;
}

/* §22 — nenhuma altura fixa na linha. A linha cresce com o conteúdo e a
   centralização se resolve sobre a altura real, não sobre um número. */
table.oc-table tr{
  height:auto;
}

/* ==========================================================================
   CAMPO OBRIGATÓRIO — asterisco no rótulo  (design system)
   ==========================================================================
   ⚠️ O ASTERISCO NÃO É ESCRITO EM LUGAR NENHUM DO HTML.

   Ele é derivado do atributo `required` do próprio controle. Isso resolve de
   uma vez os três problemas que a alternativa manual teria:

     1. Não é possível marcar um campo opcional por engano. O marcador só
        existe onde o navegador já bloqueia o envio — não há uma segunda
        declaração de obrigatoriedade para divergir da primeira.
     2. Campos CONDICIONAIS acertam sozinhos: quando um script liga ou desliga
        `required`, o asterisco acompanha no mesmo quadro, sem código extra.
     3. Formulário novo herda o padrão sem ninguém lembrar de nada — que é o
        pedido do §34/§36. Escrever `required` é a única coisa a fazer.

   A auditoria que precedeu esta regra conferiu, formulário por formulário, que
   o `required` do HTML corresponde às validações "Informe X" do servidor.
   Onde o servidor só valida FORMATO ("UF inválida", "Área inválida", "Status
   inválido"), o campo não tem `required` — e corretamente não recebe
   asterisco: ninguém precisa preencher aquilo agora.

   ACESSIBILIDADE: o `required` já é anunciado pelo leitor de tela (os
   navegadores o mapeiam para `aria-required`), então o asterisco é reforço
   VISUAL. Por isso `aria-hidden` — sem ele, o campo seria lido como
   "Código estrela, obrigatório". O caractere existe de fato (§6): a
   obrigatoriedade nunca é indicada só por cor. */
.oc-field:has(> input[required], > select[required], > textarea[required]) > label::after,
.oc-field:has(> input[aria-required="true"], > select[aria-required="true"], > textarea[aria-required="true"]) > label::after{
  content:" *";
  color:#b3261e;
  font-weight:700;
  /* `white-space:nowrap` no rótulo inteiro deixaria um rótulo longo estourar a
     coluna no celular. O que não pode quebrar é o par "palavra *", e é isso
     que o espaço rígido garante. */
  white-space:nowrap;
}

/* ⚠️ A caixa de seleção é a exceção: `label:has(input[type=checkbox])` envolve
   o controle, então a regra acima colaria o asterisco DEPOIS do texto da opção
   ("Participante principal *"), o que marcaria a opção e não o grupo — o
   oposto do §24/§25. Grupos obrigatórios declaram-se no rótulo do grupo. */
.oc-field > label:has(input[type="checkbox"])::after,
.oc-field > label:has(input[type="radio"])::after{
  content:none;
}

/* Rótulo de GRUPO (Papéis, Tipo…) quando o grupo inteiro é obrigatório: a
   página marca o rótulo com `data-oc-obrigatorio`, porque não há um único
   controle com `required` de onde derivar. */
.oc-field > .oc-field-rotulo[data-oc-obrigatorio]::after,
.oc-field > label[data-oc-obrigatorio]::after{
  content:" *";
  color:#b3261e;
  font-weight:700;
  white-space:nowrap;
}

/* §10 — campo que o usuário NÃO pode preencher agora não leva marcador.
   `readonly` mantém o valor no envio e por isso alguns formulários o combinam
   com `required` (é o caso do Valor Original de um título já com pagamentos:
   obrigatório na criação, imutável depois). Sem esta exceção, o asterisco
   pediria uma ação impossível. */
.oc-field:has(> input[required][readonly], > input[required][disabled],
              > select[required][disabled], > textarea[required][readonly],
              > textarea[required][disabled]) > label::after{
  content:none;
}

/* ==========================================================================
   HEADER GLOBAL (V1.00.477)
   ==========================================================================
   Barra horizontal fixa no topo de TODA página autenticada, acima da sidebar
   e do conteúdo. Markup: includes/layout.php → ocPageStart().

   ⚠️ A SIDEBAR NÃO FOI REDESENHADA. O que muda para ela são as três regras
   "deslocamento" logo abaixo — começar em `top: altura do header` em vez de
   0, no desktop e no drawer, e o overlay do drawer idem. Largura, gradiente,
   estados, animações, submenus e assets/js/sidebar.js ficam como estavam.

   ⚠️ A altura é uma variável que vale 0 FORA do header. O painel da
   plataforma (app/platform/_layout.php) usa as mesmas classes de layout e
   não tem header: com 0, nada muda para ele. */
:root{ --app-header-height:0px; }
body.oc-com-header{ --app-header-height:56px; }

/* deslocamento 1 — o conteúdo começa abaixo do header. `padding-top` com
   box-sizing:border-box: a altura travada em 100vh (§78) passa a descontar o
   header sozinha, sem uma segunda conta de altura em lugar nenhum. */
body.oc-com-header .oc-layout{ padding-top:var(--app-header-height); }
/* deslocamento 2 — sidebar fixa (desktop) e drawer (celular) abaixo dele.
   ⚠️ `height:auto` é parte do deslocamento, não enfeite: a regra base da barra
   tem `height:100%`, que numa caixa `fixed` é a viewport inteira e VENCE o
   `bottom:0`. Com o topo em 0 isso nunca apareceu; com o topo em 56px, a barra
   passava 56px da borda de baixo e cortava o fim do menu — medido: altura 800
   numa janela de 800. Com `auto`, quem define a altura são `top` e `bottom`
   (= 100vh − header), e o scroll interno continua o de sempre. */
body.oc-com-header .oc-sidebar{ top:var(--app-header-height); height:auto; }
/* deslocamento 3 — o escurecimento do drawer também não cobre o header. */
body.oc-com-header .oc-sidebar-overlay{ top:var(--app-header-height); }

.oc-app-header{
  position:fixed;
  top:0; left:0; right:0;
  height:var(--app-header-height);
  z-index:var(--oc-z-header);
  /* Altura ÚNICA de todo controle do header (V1.00.479): ícones, organização,
     usuário e hambúrguer. 40px = área de toque mínima pedida; o ícone em si
     continua com 20px. Alinhamento é estrutural — flex + align-items:center em
     cada nível, nunca margin-top/translateY por elemento. */
  --app-header-controle:40px;
  display:flex;
  align-items:center;
  gap:16px;
  padding:0 16px 0 18px;
  background:var(--surface);
  border-bottom:1px solid var(--border);
  box-shadow:0 1px 2px rgba(16,24,40,.04);
}

/* Pular para o conteúdo — invisível até receber foco pelo teclado. */
.oc-skip-link{
  position:fixed;
  left:-9999px;
  top:10px;
  z-index:calc(var(--oc-z-header) + 1);
  background:var(--text);
  color:#fff;
  padding:8px 14px;
  border-radius:8px;
  font-size:14px;
}
.oc-skip-link:focus{ left:12px; text-decoration:none; }
.oc-main:focus{ outline:none; }

.oc-app-header__sr{
  position:absolute !important;
  width:1px; height:1px;
  overflow:hidden;
  clip:rect(0 0 0 0);
  white-space:nowrap;
}

/* ---- marca ------------------------------------------------------------- */
.oc-app-header__marca{
  display:flex;
  align-items:center;
  gap:10px;
  min-width:0;
  flex-shrink:0;
  color:var(--text);
  text-decoration:none;
}
.oc-app-header__marca:hover{ text-decoration:none; }
.oc-app-header__marca:focus-visible,
.oc-app-header__icone:focus-visible,
.oc-app-header__usuario-botao:focus-visible,
.oc-app-header__org-select:focus-visible{
  outline:2px solid var(--primary);
  outline-offset:2px;
}
/* O monograma usa o MESMO gradiente da sidebar: a marca conversa com a barra
   logo abaixo dela sem inventar cor nova. */
.oc-app-header__monograma{
  width:32px; height:32px;
  flex-shrink:0;
  border-radius:8px;
  background:var(--sidebar-gradient);
  background-size:64px 64px;
  color:#fff;
  font-weight:800;
  font-size:13.5px;
  letter-spacing:-.02em;
  display:flex;
  align-items:center;
  justify-content:center;
}
.oc-app-header__nome{ font-size:17px; font-weight:700; letter-spacing:.2px; white-space:nowrap; }
.oc-app-header__versao{ font-size:11px; color:var(--text-muted); white-space:nowrap; }

/* ---- ações globais ------------------------------------------------------ */
/* Grupo único da direita: UM `gap`, nenhuma margem avulsa — a distância entre
   Notificações, Ajuda, Organização e Usuário é sempre a mesma. */
.oc-app-header__acoes{
  margin-left:auto;
  display:flex;
  align-items:center;
  gap:12px;
  min-width:0;
}
.oc-app-header__icone{
  width:var(--app-header-controle);
  height:var(--app-header-controle);
  flex-shrink:0;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  border:1px solid transparent;
  border-radius:8px;
  background:transparent;
  color:var(--text-muted);
  cursor:pointer;
  padding:0;
}
.oc-app-header__icone:hover{ background:var(--bg); color:var(--text); }
a.oc-app-header__icone:hover{ text-decoration:none; }
/* Ajuda na própria Central / painel de notificações aberto: o ícone fica
   marcado, como qualquer controle ativo do header. */
.oc-app-header__icone[aria-current="page"],
.oc-app-header__notif[open] > .oc-app-header__icone{ background:var(--bg); color:var(--text); }

/* ---- organização atual -------------------------------------------------- */
.oc-app-header__org{ min-width:0; flex:0 1 auto; display:flex; align-items:center; }
.oc-app-header__org-form{ margin:0; min-width:0; display:flex; align-items:center; }
.oc-app-header__org-select,
.oc-app-header__org-fixa{
  display:block;
  max-width:260px;
  height:var(--app-header-controle);
  line-height:calc(var(--app-header-controle) - 2px);
  padding:0 12px;
  border:1px solid var(--border);
  border-radius:8px;
  background:var(--surface);
  color:var(--text);
  font:inherit;
  font-size:13.5px;
  font-weight:600;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.oc-app-header__org-select{ padding-right:28px; cursor:pointer; }

/* ---- usuário ------------------------------------------------------------ */
.oc-app-header__usuario,
.oc-app-header__notif{ position:relative; flex-shrink:0; display:flex; align-items:center; }
.oc-app-header__usuario-botao{
  display:flex;
  align-items:center;
  gap:8px;
  height:var(--app-header-controle);
  padding:0 8px 0 4px;
  border-radius:8px;
  cursor:pointer;
  list-style:none;
  color:var(--text);
}
.oc-app-header__usuario-botao::-webkit-details-marker{ display:none; }
.oc-app-header__usuario-botao:hover,
.oc-app-header__usuario[open] > .oc-app-header__usuario-botao{ background:var(--bg); }
.oc-app-header__avatar{
  position:relative;
  width:30px; height:30px;
  flex-shrink:0;
  border-radius:50%;
  overflow:hidden;
  background:var(--sidebar-gradient);
  background-size:60px 60px;
  color:#fff;
  font-size:12px;
  font-weight:700;
  display:flex;
  align-items:center;
  justify-content:center;
}
.oc-app-header__avatar img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.oc-app-header__usuario-nome{
  max-width:140px;
  font-size:13.5px;
  font-weight:600;
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
/* Seta de dropdown desenhada por borda — NÃO é o par >/∨ da §113, que é
   exclusivo de expandir hierarquia. */
.oc-app-header__chevron{
  width:7px; height:7px;
  border-right:1.6px solid currentColor;
  border-bottom:1.6px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px);
  opacity:.6;
  flex-shrink:0;
}
.oc-app-header__painel{
  position:absolute;
  right:0;
  top:calc(100% + 8px);
  min-width:240px;
  max-width:300px;
  box-shadow:0 10px 28px rgba(16,24,40,.14);
}
.oc-app-header__painel-id{
  display:flex;
  flex-direction:column;
  gap:2px;
  padding:8px 10px 10px;
  margin-bottom:4px;
  border-bottom:1px solid var(--border);
  min-width:0;
}
.oc-app-header__painel-id strong{ font-size:13.5px; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.oc-app-header__painel-id span{ font-size:12px; color:var(--text-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ---- hambúrguer: a regra de sempre só o mostra no drawer (≤820px). */
.oc-app-header .oc-sidebar-hamburger{ padding:0; width:var(--app-header-controle); height:var(--app-header-controle); }

/* ---- responsividade -----------------------------------------------------
   O header nunca quebra linha nem cresce verticalmente: textos secundários
   saem primeiro, depois os rótulos, e o que sobra encolhe com reticências. */
@media (max-width:1100px){
  .oc-app-header__versao{ display:none; }
  .oc-app-header__acoes{ gap:10px; }
  .oc-app-header__org-select,
  .oc-app-header__org-fixa{ max-width:200px; }
  .oc-app-header__usuario-nome{ max-width:96px; }
}
@media (max-width:820px){
  .oc-app-header{ gap:10px; padding:0 12px; }
  /* A Ajuda CONTINUA no celular (V1.00.479); saem só os textos. */
  .oc-app-header__nome,
  .oc-app-header__usuario-nome,
  .oc-app-header__chevron{ display:none; }
  .oc-app-header__acoes{ gap:8px; }
  .oc-app-header__org-select,
  .oc-app-header__org-fixa{ max-width:132px; font-size:13px; padding:0 10px; }
  .oc-app-header__org-select{ padding-right:24px; }
  .oc-app-header__usuario-botao{ padding:0 2px; }
}
@media (max-width:380px){
  .oc-app-header__acoes{ gap:6px; }
  .oc-app-header__org-select,
  .oc-app-header__org-fixa{ max-width:96px; }
}

/* ==========================================================================
   ADMINISTRAÇÃO NA BASE DA SIDEBAR (V1.00.478)
   ==========================================================================
   Segundo <nav> da barra, com a MESMA classe `.oc-nav` da navegação principal
   — por isso cor, altura, hover, ativo, ícones, submenu inline e dica de nome
   continuam os de sempre sem uma linha de CSS nova para eles. O que esta
   regra acrescenta é só POSIÇÃO e DIVISOR.

   ⚠️ Estrutural, nunca `position:absolute`: a barra já é coluna flex e, no
   desktop, a navegação principal é `flex:1` com rolagem própria — então esta
   área fica sempre visível na base e a lista longa rola acima dela. No drawer
   do celular a barra rola inteira (como sempre rolou) e o `margin-top:auto`
   ainda empurra a Administração para baixo quando sobra espaço.

   O divisor é o mesmo branco translúcido que a barra já usava acima do bloco
   do usuário (§11 do gradiente) — linguagem visual da própria sidebar. */
.oc-sidebar .oc-nav.oc-nav-admin{
  flex:0 0 auto;
  margin-top:auto;
  min-height:auto;
  overflow:visible;
  padding-top:10px;
  border-top:1px solid rgba(255,255,255,.15);
}

/* ==========================================================================
   INTERRUPTOR (V1.00.480) — `input[type=checkbox].oc-switch` com role="switch"
   ==========================================================================
   Continua sendo um checkbox nativo: rótulo, foco, teclado (Espaço) e envio no
   formulário vêm de graça. Só o desenho muda. */
.oc-switch{
  -webkit-appearance:none;
  appearance:none;
  position:relative;
  flex-shrink:0;
  width:44px; height:24px;
  margin:0;
  border-radius:999px;
  background:#c3cad5;
  cursor:pointer;
  transition:background-color .15s ease;
}
.oc-switch::before{
  content:"";
  position:absolute;
  top:3px; left:3px;
  width:18px; height:18px;
  border-radius:50%;
  background:#fff;
  box-shadow:0 1px 2px rgba(16,24,40,.25);
  transition:transform .15s ease;
}
.oc-switch:checked{ background:var(--primary); }
.oc-switch:checked::before{ transform:translateX(20px); }
.oc-switch:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }
.oc-config-lista{ display:flex; flex-direction:column; }
.oc-config-linha{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  padding:12px 0;
  border-bottom:1px solid var(--border);
}
.oc-config-linha:last-child{ border-bottom:0; }
.oc-config-texto{ min-width:0; }
.oc-config-texto label{ display:block; font-weight:600; font-size:14.5px; cursor:pointer; }
.oc-config-texto p{ margin:2px 0 0; font-size:13px; color:var(--text-muted); }

/* ==========================================================================
   NOTIFICAÇÕES — painel do header (V1.00.479)
   ==========================================================================
   `position:fixed` ancorado logo abaixo do header e à direita da tela: no
   celular o sino não fica na borda, e um painel `absolute` preso a ele sairia
   pela esquerda. Largura e altura limitadas à viewport; a lista rola dentro. */
.oc-app-header__notif > summary{ list-style:none; }
.oc-app-header__notif > summary::-webkit-details-marker{ display:none; }
.oc-notif-painel{
  position:fixed;
  top:calc(var(--app-header-height) + 6px);
  right:12px;
  width:min(380px, calc(100vw - 24px));
  max-width:none;
  max-height:min(70vh, 520px);
  margin:0;
  padding:0;
  display:flex;
  flex-direction:column;
  overflow:hidden;
  box-shadow:0 12px 32px rgba(16,24,40,.16);
}
.oc-notif-cabeca{
  display:flex;
  align-items:baseline;
  gap:8px;
  padding:12px 16px;
  border-bottom:1px solid var(--border);
  font-size:14px;
}
.oc-notif-cabeca span{ font-size:12px; color:var(--text-muted); }
.oc-notif-lista{ list-style:none; margin:0; padding:4px; overflow-y:auto; }
.oc-notif-item > a,
.oc-notif-item > div{
  display:flex;
  gap:10px;
  padding:10px 12px;
  border-radius:8px;
  color:var(--text);
  text-decoration:none;
}
.oc-notif-item > a:hover{ background:var(--bg); text-decoration:none; }
.oc-notif-item > a:focus-visible{ outline:2px solid var(--primary); outline-offset:-2px; }
.oc-notif-cat{
  width:8px; height:8px;
  margin-top:6px;
  flex-shrink:0;
  border-radius:50%;
  background:var(--text-muted);
}
.oc-notif-cat--obra{ background:#1d5fd6; }
.oc-notif-cat--orcamento{ background:#7a5af5; }
.oc-notif-cat--planejamento{ background:#00918c; }
.oc-notif-cat--medicao{ background:#2f9e44; }
.oc-notif-cat--pessoa{ background:#d9822b; }
.oc-notif-cat--catalogo{ background:#667085; }
.oc-notif-cat--usuario{ background:#b3261e; }
.oc-notif-textos{ display:flex; flex-direction:column; gap:2px; min-width:0; }
.oc-notif-textos strong{ font-size:13.5px; }
.oc-notif-detalhe,
.oc-notif-meta{
  font-size:12.5px;
  color:var(--text-muted);
  white-space:nowrap;
  overflow:hidden;
  text-overflow:ellipsis;
}
.oc-notif-meta{ font-size:12px; }
.oc-notif-esqueleto{ display:flex; flex-direction:column; gap:4px; padding:12px; }
.oc-notif-vazio{ padding:24px 16px; text-align:center; font-size:13.5px; color:var(--text-muted); }
.oc-notif-vazio .oc-btn{ margin-top:10px; }

/* ==========================================================================
   CENTRAL DE AJUDA (V1.00.479) — app/ajuda.php
   ==========================================================================
   Componentes do próprio app: .oc-panel, .oc-btn e a paleta de tokens. */
.oc-ajuda-busca h2{ margin:0 0 10px; font-size:17px; }
.oc-ajuda-busca__campo{
  width:100%;
  max-width:560px;
  height:44px;
  padding:0 14px;
  border:1px solid var(--border);
  border-radius:8px;
  font:inherit;
  font-size:15px;
  background:var(--surface);
  color:var(--text);
}
.oc-ajuda-busca__campo:focus-visible{ outline:2px solid var(--primary); outline-offset:1px; }
.oc-ajuda-busca__status{ margin:8px 0 0; min-height:1.2em; font-size:13px; color:var(--text-muted); }
.oc-ajuda-categorias{
  display:grid;
  grid-template-columns:repeat(4, minmax(0,1fr));
  gap:12px;
  margin:16px 0 24px;
}
.oc-ajuda-cat{
  display:block;
  min-width:0;
  padding:14px 16px;
  border:1px solid var(--border);
  border-radius:var(--radius);
  background:var(--surface);
  color:var(--text);
  text-decoration:none;
}
.oc-ajuda-cat:hover{ border-color:var(--primary); text-decoration:none; }
.oc-ajuda-cat:focus-visible{ outline:2px solid var(--primary); outline-offset:2px; }
.oc-ajuda-cat strong{ display:block; font-size:14.5px; margin-bottom:4px; }
.oc-ajuda-cat span{ display:block; font-size:13px; color:var(--text-muted); }
.oc-ajuda-titulo{ font-size:18px; margin:0 0 12px; }
.oc-ajuda-grupo{ margin-bottom:20px; scroll-margin-top:16px; }
.oc-ajuda-grupo h3{
  margin:0 0 8px;
  font-size:13px;
  text-transform:uppercase;
  letter-spacing:.06em;
  color:var(--text-muted);
}
.oc-ajuda-faq{
  border:1px solid var(--border);
  border-radius:8px;
  background:var(--surface);
  margin-bottom:8px;
}
.oc-ajuda-faq > summary{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:12px;
  min-height:44px;
  padding:10px 16px;
  cursor:pointer;
  list-style:none;
  font-weight:600;
  font-size:14.5px;
}
.oc-ajuda-faq > summary::-webkit-details-marker{ display:none; }
/* +/− de "mostrar resposta": não é hierarquia, então não usa o par da §113. */
.oc-ajuda-faq > summary::after{ content:"+"; font-weight:400; font-size:18px; color:var(--text-muted); }
.oc-ajuda-faq[open] > summary::after{ content:"\2212"; }
.oc-ajuda-faq > summary:focus-visible{ outline:2px solid var(--primary); outline-offset:-2px; border-radius:8px; }
.oc-ajuda-faq__resposta{ padding:0 16px 14px; font-size:14px; line-height:1.55; }
.oc-ajuda-faq__resposta p{ margin:0 0 8px; }
.oc-ajuda-faq__links{ display:flex; flex-wrap:wrap; gap:6px 16px; font-size:13.5px; }
.oc-ajuda-vazio{ padding:16px; color:var(--text-muted); }
.oc-ajuda-colunas{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:16px; }
.oc-ajuda-colunas .oc-panel h2,
.oc-ajuda-contato h2{ margin:0 0 8px; font-size:16px; }
.oc-ajuda-guia{ margin:0; padding-left:1.2em; font-size:14px; line-height:1.55; }
.oc-ajuda-guia li{ margin-bottom:6px; }
.oc-ajuda-conteudos{ margin:0; padding-left:1.1em; font-size:14px; }
.oc-ajuda-contato{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:16px;
  flex-wrap:wrap;
}
.oc-ajuda-contato p{ margin:0; max-width:560px; font-size:14px; }
@media (max-width:1100px){
  .oc-ajuda-categorias{ grid-template-columns:repeat(2, minmax(0,1fr)); }
}
@media (max-width:640px){
  .oc-ajuda-categorias,
  .oc-ajuda-colunas{ grid-template-columns:1fr; }
  .oc-ajuda-contato .oc-btn{ width:100%; justify-content:center; min-height:44px; }
}
