/* ==========================================================================
   Botao — h-9 36px, px-4 16px, gap-2 8px, rounded-md 8px, text-sm, 500,
   sem borda na base, foco: borda ring + anel de 3px em ring/50
   (registry new-york-v4)
   ========================================================================== */

.btn {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  height: 2.25rem;
  padding-inline: 1rem;
  border-radius: var(--raio-md);
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  white-space: nowrap;
  user-select: none;
  outline: none;
  transition: all var(--duracao-rapida) var(--curva);
}

.btn:focus-visible {
  outline: none;
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  pointer-events: none;
  opacity: 0.5;
}

.btn > svg {
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  pointer-events: none;
}

.btn-primario {
  background-color: var(--primary);
  color: var(--primary-foreground);
}

.btn-primario:hover {
  background-color: color-mix(in oklab, var(--primary) 90%, transparent);
}

.btn-secundario {
  background-color: var(--secondary);
  color: var(--secondary-foreground);
}

.btn-secundario:hover {
  background-color: color-mix(in oklab, var(--secondary) 80%, transparent);
}

.btn-contorno {
  border: 1px solid var(--border);
  background-color: var(--background);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.btn-contorno:hover {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.btn-contorno:focus-visible {
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.escuro .btn-contorno {
  border-color: var(--input);
  background-color: color-mix(in oklab, var(--input) 30%, transparent);
}

.escuro .btn-contorno:hover {
  background-color: color-mix(in oklab, var(--input) 50%, transparent);
}

.btn-fantasma:hover {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.escuro .btn-fantasma:hover {
  background-color: color-mix(in oklab, var(--accent) 50%, transparent);
}

.btn-destrutivo {
  background-color: var(--destructive);
  color: #fff;
}

.btn-destrutivo:hover {
  background-color: color-mix(in oklab, var(--destructive) 90%, transparent);
}

.btn-destrutivo:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--destructive) 20%, transparent);
}

.escuro .btn-destrutivo {
  background-color: color-mix(in oklab, var(--destructive) 60%, transparent);
}

.escuro .btn-destrutivo:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--destructive) 40%, transparent);
}

.btn-link {
  color: var(--primary);
  text-underline-offset: 4px;
}

.btn-link:hover {
  text-decoration: underline;
}

.btn-sm {
  height: 2rem;
  padding-inline: 0.75rem;
  gap: 0.375rem;
  border-radius: var(--raio-md);
}

.btn-lg {
  height: 2.5rem;
  padding-inline: 1.5rem;
  border-radius: var(--raio-md);
}

.btn-icone {
  width: 2.25rem;
  height: 2.25rem;
  padding-inline: 0;
}

.btn-largura-total {
  width: 100%;
}

/* Alvo de toque: 44px e a recomendacao da Apple. Divergencia deliberada
   dos 36px do shadcn, que e uma metrica pensada para mouse. */
@media (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn-icone { min-width: 44px; }
}

/* ==========================================================================
   Campo — h-9 36px, px-3 12px, py-1 4px, rounded-md 8px, borda input,
   shadow-xs.
   text-base ate 767px de proposito: 16px impede o Safari do iPhone
   de dar zoom automatico ao focar o campo.
   ========================================================================== */

.campo-grupo {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.rotulo {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1;
  font-weight: 500;
  user-select: none;
}

.campo {
  display: block;
  width: 100%;
  min-width: 0;
  height: 2.25rem;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--input);
  border-radius: var(--raio-md);
  background-color: transparent;
  color: var(--foreground);
  font-size: 1rem;
  line-height: 1.5rem;
  outline: none;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  transition:
    border-color var(--duracao-rapida) var(--curva),
    box-shadow var(--duracao-rapida) var(--curva);
}

@media (min-width: 768px) {
  .campo {
    font-size: 0.875rem;
    line-height: 1.25rem;
  }
}

.campo::placeholder {
  color: var(--muted-foreground);
}

.campo:focus-visible {
  border-color: var(--ring);
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.campo:disabled {
  pointer-events: none;
  cursor: not-allowed;
  opacity: 0.5;
  background-color: color-mix(in oklab, var(--input) 50%, transparent);
}

.campo[aria-invalid="true"] {
  border-color: var(--destructive);
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--destructive) 20%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.escuro .campo {
  background-color: color-mix(in oklab, var(--input) 30%, transparent);
}

.campo-ajuda {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
}

.campo-erro {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--destructive);
}

@media (pointer: coarse) {
  .campo { min-height: 44px; }
}

/* ==========================================================================
   Alerta — rounded-lg 10px, px-4 16px, py-3 12px, text-sm,
   grid de colunas 0/1fr com row-gap de 2px (registry new-york-v4)
   ========================================================================== */

.alerta {
  position: relative;
  display: grid;
  grid-template-columns: 0 1fr;
  align-items: start;
  width: 100%;
  row-gap: 0.125rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-lg);
  background-color: var(--card);
  color: var(--card-foreground);
  font-size: 0.875rem;
  line-height: 1.25rem;
  text-align: left;
}

.alerta:has(> svg) {
  grid-template-columns: 1rem 1fr;
  column-gap: 0.75rem;
}

.alerta > svg {
  grid-row: span 2;
  width: 1rem;
  height: 1rem;
  transform: translateY(2px);
  color: currentColor;
}

.alerta-titulo {
  grid-column-start: 2;
  min-height: 1rem;
  font-weight: 500;
  letter-spacing: -0.025em;
}

.alerta-descricao {
  grid-column-start: 2;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

.alerta-destrutivo {
  color: var(--destructive);
}

.alerta-destrutivo .alerta-descricao {
  color: color-mix(in oklab, var(--destructive) 90%, transparent);
}

/* ==========================================================================
   Card — rounded-xl 14px, borda de 1px, shadow-sm, py-6 24px, gap-6 24px.
   Nao define font-size: herda os 16px do corpo.
   ========================================================================== */

.card {
  --card-espaco: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: var(--card-espaco);
  padding-block: var(--card-espaco);
  border: 1px solid var(--border);
  border-radius: var(--raio-xl);
  background-color: var(--card);
  color: var(--card-foreground);
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}

.card-cabecalho {
  display: grid;
  align-items: start;
  gap: 0.5rem;
  padding-inline: var(--card-espaco);
}

.card-cabecalho:has(.card-acao) {
  grid-template-columns: 1fr auto;
}

.card-titulo {
  line-height: 1;
  font-weight: 600;
}

.card-descricao {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

.card-acao {
  grid-column-start: 2;
  grid-row: span 2;
  grid-row-start: 1;
  align-self: start;
  justify-self: end;
}

.card-conteudo {
  padding-inline: var(--card-espaco);
}

.card-rodape {
  display: flex;
  align-items: center;
  padding-inline: var(--card-espaco);
}

/* ==========================================================================
   Badge — px-2 8px, py-0.5 2px, pilula completa, text-xs.
   Sem altura fixa: a altura vem do padding.
   ========================================================================== */

.badge {
  display: inline-flex;
  width: fit-content;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  padding: 0.125rem 0.5rem;
  overflow: hidden;
  border: 1px solid transparent;
  border-radius: 9999px;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  white-space: nowrap;
}

.badge > svg {
  width: 0.75rem;
  height: 0.75rem;
  pointer-events: none;
}

.badge-primario {
  background-color: var(--primary);
  color: var(--primary-foreground);
}

.badge-secundario {
  background-color: var(--secondary);
  color: var(--secondary-foreground);
}

.badge-contorno {
  border-color: var(--border);
  color: var(--foreground);
}

.badge-destrutivo {
  background-color: var(--destructive);
  color: #fff;
}

/* ==========================================================================
   Avatar e separador
   ========================================================================== */

.avatar {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  overflow: hidden;
  border-radius: 9999px;
  background-color: var(--muted);
  color: var(--muted-foreground);
  font-size: 0.75rem;
  line-height: 1;
  font-weight: 500;
  user-select: none;
}

.avatar > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar-sm { width: 1.5rem; height: 1.5rem; font-size: 0.625rem; }
.avatar-lg { width: 2.5rem; height: 2.5rem; font-size: 0.875rem; }

.separador {
  flex-shrink: 0;
  background-color: var(--border);
}

.separador[data-orientacao="horizontal"] { width: 100%; height: 1px; }
.separador[data-orientacao="vertical"] { width: 1px; align-self: stretch; }

/* ==========================================================================
   Tabela — th h-10, px-2, text-sm, linhas com border-b
   ========================================================================== */

.tabela-envolucro {
  position: relative;
  width: 100%;
  overflow-x: auto;
}

.tabela {
  width: 100%;
  border-collapse: collapse;
  caption-side: bottom;
  font-size: 0.875rem;
  line-height: 1.25rem;
}

.tabela th {
  height: 2.5rem;
  padding-inline: 0.5rem;
  text-align: left;
  vertical-align: middle;
  font-weight: 500;
  white-space: nowrap;
  color: var(--foreground);
}

.tabela td {
  padding: 0.5rem;
  vertical-align: middle;
}

.tabela thead tr,
.tabela tbody tr {
  border-bottom: 1px solid var(--border);
}

.tabela tbody tr:last-child {
  border-bottom: 0;
}

.tabela tbody tr {
  transition: background-color var(--duracao-rapida) var(--curva);
}

.tabela tbody tr:hover {
  background-color: color-mix(in oklab, var(--muted) 50%, transparent);
}

.tabela th.celula-direita,
.tabela td.celula-direita {
  text-align: right;
}

.tabela-vazia {
  padding: 1.5rem 0.5rem;
  text-align: center;
  color: var(--muted-foreground);
}

/*
  Abaixo de 768px a tabela vira lista de cartoes. Rolagem horizontal
  em tabela e hostil no toque; o rotulo de cada campo vem do
  data-rotulo emitido pelo componente.
*/
@media (max-width: 767px) {
  .tabela-responsiva thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
  }

  .tabela-responsiva,
  .tabela-responsiva tbody,
  .tabela-responsiva tr,
  .tabela-responsiva td {
    display: block;
    width: 100%;
  }

  .tabela-responsiva tr {
    margin-bottom: 0.5rem;
    padding: 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--raio-lg);
    background-color: var(--card);
  }

  .tabela-responsiva tr:last-child {
    border-bottom: 1px solid var(--border);
  }

  .tabela-responsiva td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.25rem 0;
    text-align: right;
  }

  .tabela-responsiva td::before {
    content: attr(data-rotulo);
    flex-shrink: 0;
    color: var(--muted-foreground);
    font-weight: 500;
    text-align: left;
  }
}

/* ==========================================================================
   Dropdown — animado so com opacity e transform
   ========================================================================== */

.dropdown {
  position: relative;
  display: inline-flex;
}

.dropdown-gatilho {
  display: inline-flex;
}

.dropdown-painel {
  position: absolute;
  top: calc(100% + 0.25rem);
  z-index: 50;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 12rem;
  padding: 0.25rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-lg);
  background-color: var(--popover);
  color: var(--popover-foreground);
  /* shadow-md do Tailwind. Preto puro de proposito: sombra derivada de
     --foreground virava brilho branco no tema escuro. */
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  pointer-events: none;
  transition:
    opacity var(--duracao-rapida) var(--curva),
    transform var(--duracao-rapida) var(--curva);
}

.dropdown-inicio { left: 0; }
.dropdown-fim { right: 0; }

[data-aberto] > .dropdown-painel {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/*
  Menu que abre para cima. Usado quando o gatilho fica no rodape de um
  container — abrir para baixo jogaria os itens para fora da tela, e o
  usuario nao encontraria acoes como "Sair".
*/
.dropdown-painel.dropdown-cima {
  top: auto;
  bottom: calc(100% + 0.25rem);
  transform: translateY(4px) scale(0.98);
}

[data-aberto] > .dropdown-painel.dropdown-cima {
  transform: translateY(0) scale(1);
}

/*
  Menu que abre ao lado do gatilho, alinhado pela base — e o que o shadcn
  usa no menu do usuario da sidebar. No celular nao ha espaco lateral,
  entao ele volta a abrir para baixo.
*/
.dropdown-painel.dropdown-lado {
  top: auto;
  bottom: 0;
  left: calc(100% + 0.25rem);
  right: auto;
  transform: translateX(-4px) scale(0.98);
}

[data-aberto] > .dropdown-painel.dropdown-lado {
  transform: translateX(0) scale(1);
}

@media (max-width: 767px) {
  /*
    No celular nao ha espaco lateral, e o gatilho fica no rodape do
    drawer: abrir para baixo jogaria o painel para fora da tela. Entao
    ele abre para cima, ocupando a largura do proprio gatilho.
  */
  .dropdown-painel.dropdown-lado {
    top: auto;
    bottom: calc(100% + 0.25rem);
    left: 0;
    right: 0;
    transform: translateY(4px) scale(0.98);
  }

  [data-aberto] > .dropdown-painel.dropdown-lado {
    transform: translateY(0) scale(1);
  }
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.375rem 0.5rem;
  border-radius: var(--raio-md);
  font-size: 0.875rem;
  line-height: 1.25rem;
  text-align: left;
  transition: background-color var(--duracao-rapida) var(--curva);
}

.dropdown-item > svg {
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  color: var(--muted-foreground);
  pointer-events: none;
}

.dropdown-item-destrutivo > svg {
  color: currentColor;
}

.dropdown-item:hover,
.dropdown-item:focus-visible {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.dropdown-item-destrutivo {
  color: var(--destructive);
}

.dropdown-separador {
  height: 1px;
  margin: 0.25rem -0.25rem;
  background-color: var(--border);
}

@media (pointer: coarse) {
  .dropdown-item { min-height: 44px; }
}

/* ==========================================================================
   Combobox — select com busca ao digitar. Nao existe bloco de Combobox
   no registry do shadcn com medida propria: o shadcn monta o dele em
   cima de Popover + Command, e nenhum dos dois esta neste kit. Por isso
   nada aqui e numero novo -- o gatilho e o proprio .campo (mesma altura,
   padding, raio, borda, sombra e foco, sem classe extra de tamanho); o
   padding, o item e a sombra do painel sao os mesmos de .dropdown-painel/
   .dropdown-item logo acima -- so a MECANICA de exibir/esconder o painel
   e diferente (ver o proximo comentario), abrir e fechar (clique fora,
   Escape) continua reaproveitando o dropdown, ver a secao "combobox" de
   ui.js.
   ========================================================================== */

.combobox {
  position: relative;
  display: block;
  width: 100%;
}

/*
  Atributo popover="manual" (nao "auto"): o painel vai para a top layer,
  a mesma camada de um <dialog> aberto via showModal() -- e o que resolve
  o painel ficando cortado dentro de .dialogo-corpo (overflow-y: auto),
  documentado em ui/LEIA-ME.md. "manual" em vez de "auto" e deliberado:
  um popover "auto" fecha sozinho com Escape e clique fora (luz-dismiss
  nativa) por cima do MESMO clique fora e do MESMO Escape que a secao
  "dropdown" de ui.js ja trata via data-aberto -- os dois mecanismos
  disputando o mesmo fechamento é receita para dessincronizar data-aberto
  do estado real do popover. "manual" nao faz luz-dismiss nenhuma: quem
  chama showPopover()/hidePopover() (ui.js, abrirCombobox/fecharCombobox)
  continua sendo a UNICA fonte de verdade, exatamente como antes.

  display:none (a folha do navegador aplica em [popover]:not(:popover-
  open)) e o que resolve o segundo sintoma: fechado, o painel deixa de
  ocupar caixa nenhuma, entao para de contar no scrollHeight de
  .dialogo-corpo -- a barra fantasma medida no LEIA-ME (132px de conteudo
  contra 308px de scrollHeight) desaparece de graca, sem precisar de
  pointer-events:none para simular "invisivel e inerte" como antes.

  Posicao: um popover top layer se posiciona contra a VIEWPORT, nao
  contra o gatilho (perde o "position: absolute" relativo a .combobox).
  top/left/width chegam por CSSOM (ui.js, medirEPosicionarPainelDoCombobox)
  a partir de gatilho.getBoundingClientRect() -- nunca por atributo style
  no HTML, a CSP bloqueia isso em silencio. right/bottom ficam explicitos
  em auto para nao competir com left/top/width contra o inset:0 que a
  folha do navegador aplica a todo [popover].
*/
.combobox-painel {
  position: fixed;
  top: 0;
  left: 0;
  right: auto;
  bottom: auto;
  margin: 0;
  /*
    Fechado e display:none, aberto e flex (na regra :popover-open abaixo).
    O par e o que faz o allow-discrete da transicao ter para onde ir: com
    display:flex tambem aqui na base nao havia segundo valor, o painel
    fechado continuava ocupando espaco, e a regra do agente do usuario
    ([popover]:not(:popover-open){display:none}) perdia por ser de origem
    mais fraca que qualquer regra de autor.

    O sintoma era uma barra de rolagem fantasma no corpo do dialogo:
    caixa invisivel, mas contada na altura de rolagem.
  */
  display: none;
  flex-direction: column;
  width: max-content;
  height: auto;
  overflow: visible;
  padding: 0.25rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-lg);
  background-color: var(--popover);
  color: var(--popover-foreground);
  /* mesma shadow-md de .dropdown-painel, preto puro pelo mesmo motivo. */
  box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  /*
    display/overlay entram na transicao so com allow-discrete: as duas
    propriedades sao discretas (nunca interpolam, so trocam de valor de
    uma vez), entao isto NAO viola a regra de "anima-se so transform e
    opacity" (movimento.css) -- so atrasa a troca para display:none ate
    opacity/transform terminarem de animar, em vez de cortar o painel no
    meio do fade. Sem isto o fechamento nao anima (display:none aplicaria
    no mesmo instante do clique) -- o navegador ignora "allow-discrete"
    numa propriedade que nao suporta (nao quebra em navegador antigo).
  */
  transition:
    opacity var(--duracao-rapida) var(--curva),
    transform var(--duracao-rapida) var(--curva),
    display var(--duracao-rapida) allow-discrete,
    overlay var(--duracao-rapida) allow-discrete;
}

.combobox-painel:popover-open {
  display: flex;
  opacity: 1;
  transform: translateY(0) scale(1);
}

/* Estado de partida da entrada -- sem isto o navegador nao tem "de onde"
   animar na primeira pintura com o painel ja em :popover-open (mesmo
   raciocinio do par acima com allow-discrete). Ignorado sem quebra em
   navegador sem suporte a @starting-style: o painel so deixa de animar
   a entrada, continua abrindo. */
@starting-style {
  .combobox-painel:popover-open {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
  }
}

/*
  Altura maxima da lista: nao vem de registry nenhum (Command nao esta
  neste kit), entao nao e medida calibrada -- ferramentas/calibrar.js
  nao confere esta propriedade. E um teto de engenharia, para uma lista
  de 500 opcoes nao esticar o painel a pagina inteira; a rolagem fica
  contida aqui dentro, mesmo raciocinio de .quadro-envolucro/.tabela-
  envolucro (rolar por dentro, nunca a pagina).
*/
.combobox-lista {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  max-height: 14rem;
  overflow-y: auto;
  overflow-x: hidden;
}

.combobox-item {
  display: flex;
  align-items: center;
  padding: 0.375rem 0.5rem;
  border-radius: var(--raio-md);
  font-size: 0.875rem;
  line-height: 1.25rem;
  cursor: pointer;
  transition: background-color var(--duracao-rapida) var(--curva);
}

/*
  aria-selected marca a opcao ativa (a que aria-activedescendant do
  campo aponta), nao "a opcao escolhida no passado" -- ver ui.js. Mesmo
  par de cores de .dropdown-item:hover/:focus-visible.
*/
.combobox-item[aria-selected="true"] {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.combobox-item[hidden] {
  display: none;
}

/*
  Opcao "criar a partir do texto digitado" (2026-09-06, ver
  recursos/componentes/ui/combobox.php e a secao "combobox" de ui.js):
  cor de destaque para se distinguir de uma opcao comum na mesma lista,
  sem inventar um segundo visual de item -- ainda .combobox-item por
  baixo, so a cor do texto muda.
*/
.combobox-item--criar {
  color: var(--primary);
}

/*
  "Nenhum resultado": padding-block reaproveita 1.5rem, o mesmo
  --card-espaco de .card (a variavel e local aquela regra, por isso o
  valor solto aqui em vez do var()); entrelinha 1.625 e cor vem de
  .vazio-descricao, o placeholder discreto mais proximo que o kit ja
  tem -- o mesmo tipo de emprestimo que quadro_coluna_vazia() fez.
*/
.combobox-vazio {
  padding-block: 1.5rem;
  font-size: 0.875rem;
  line-height: 1.625;
  color: var(--muted-foreground);
  text-align: center;
}

@media (pointer: coarse) {
  .combobox-item { min-height: 44px; }
}

/* Trava a rolagem de fundo quando o drawer ou um dialogo esta aberto. */
body[data-travado] {
  overflow: hidden;
}

/* ==========================================================================
   Dialogo — <dialog> nativo. showModal() da de graca foco preso, Escape,
   camada superior e fundo inerte; o CSS aqui e so o que o navegador nao
   da de graca: aparencia da caixa, ::backdrop, cabecalho/corpo/rodape e
   rolagem interna quando o conteudo e mais alto que a viewport.

   Medidas do Dialog do registry (new-york-v4, DialogContent): max-w-lg
   (32rem) a partir do breakpoint sm (640px, o proprio breakpoint do
   Tailwind na classe "sm:max-w-lg" -- nao os 768px que o resto deste
   kit usa como linha celular/desktop, ver LEIA-ME.md), largura de
   calc(100% - 2rem) abaixo disso, padding p-6 (24px, o mesmo
   --card-espaco de .card), gap-4 (16px), rounded-lg (--raio-lg),
   border, bg-background e shadow-lg. ::backdrop reproduz bg-black/50
   em oklch. DialogTitle e text-lg/leading-none/600; DialogDescription
   e text-sm/text-muted-foreground.

   Sem medida nova aqui alem da propria altura maxima vertical: o
   registry nao tem resposta para "conteudo mais alto que a tela" (o
   componente do shadcn nao rola por dentro sozinho). max-height usa o
   mesmo respiro de 2rem do max-width horizontal, por simetria --
   mesmo tipo de emprestimo que .combobox-lista fez com seu
   max-height de 14rem, dito sem rodeio no LEIA-ME.

   "display" mora numa regra separada, so para [open]: a folha do
   proprio navegador ja esconde um <dialog> fechado com
   "dialog:not([open]) { display: none }", mas essa regra e de
   user-agent -- e a origem, nao a especificidade, quem decide a
   cascata entre origens diferentes, e QUALQUER regra de autor (mesmo
   um seletor de classe simples) vence uma regra de user-agent normal,
   nao importa a especificidade. Um "display: flex" solto direto em
   .dialogo (sem o [open]) venceria a regra do navegador e manteria o
   dialogo fechado visivel, ocupando lugar no fluxo da pagina -- foi
   exatamente o que aconteceu ao testar isto num navegador de verdade.
   As demais propriedades abaixo podem continuar em .dialogo sem
   condicao nenhuma: elas nao competem com nada do user-agent, e
   getComputedStyle ainda le seus valores em pixel mesmo com o dialogo
   fechado (display: none), o que e o que ferramentas/calibrar.js
   depende para conferir a caixa sem precisar abri-la primeiro.
   ========================================================================== */

.dialogo[open] {
  display: flex;
}

/*
  base.css zera margin em "*" (reset global do kit, usado o tempo todo
  em botao/campo/etc.) -- e um reset de AUTOR, entao ele vence a regra
  de USER-AGENT que centraliza o <dialog> ("dialog { margin: auto }"),
  a mesma questao de origem-antes-de-especificidade do "display" acima,
  so que essa nao tem como ficar so em "[open]": sem restaurar o
  margin:auto aqui, o dialogo nasce colado no canto superior esquerdo
  da viewport em vez de centralizado -- confirmado num navegador de
  verdade (getBoundingClientRect dava x:0, y:0 antes desta regra).
*/
.dialogo {
  margin: auto;
  flex-direction: column;
  gap: 1rem;
  width: calc(100% - 2rem);
  max-width: calc(100% - 2rem);
  max-height: calc(100vh - 2rem);
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-lg);
  background-color: var(--background);
  color: var(--foreground);
  /* overflow (nao so max-height) e o que impede o dialogo de crescer
     mais que a viewport e empurrar a pagina -- quem rola por dentro e
     .dialogo-corpo, ver abaixo; aqui so garante que cabecalho e rodape
     nunca saiam da caixa. */
  overflow: hidden;
  /* shadow-lg do Tailwind, preto puro (mesma razao de .dropdown-painel
     e do cartao arrastado de .quadro -- sombra derivada de
     --foreground vira brilho no tema escuro). */
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

@media (min-width: 640px) {
  .dialogo {
    max-width: 32rem;
  }
}

/*
  bg-black/50 do registry, em oklch (preto puro, --foreground vira
  quase branco no escuro e a sobreposicao teria o efeito invertido).
  ::backdrop e um pseudo-elemento da camada superior -- so existe
  enquanto o <dialog> estiver aberto via showModal(), nao precisa de
  [open] no seletor.
*/
/*
  O DESFOQUE E OPT-IN, por token, e vem zerado: bg-black/50 sem filtro e
  o que o registry do new-york-v4 define, e o kit nao sai do registry por
  conta propria. Quem quiser vidro fosco liga --dialogo-desfoque no
  marca.css do seu produto -- a mesma regra de toda personalizacao aqui.
*/
.dialogo::backdrop {
  background-color: oklch(0 0 0 / 50%);
  backdrop-filter: blur(var(--dialogo-desfoque, 0px));
  -webkit-backdrop-filter: blur(var(--dialogo-desfoque, 0px));
}

.dialogo-cabecalho {
  display: flex;
  flex-shrink: 0;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.dialogo-cabecalho-texto {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.dialogo-titulo {
  font-size: 1.125rem;
  line-height: 1;
  font-weight: 600;
}

.dialogo-descricao {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

.dialogo-fechar {
  flex-shrink: 0;
}

/*
  Quem rola por dentro e o corpo, nao a caixa inteira -- cabecalho e
  rodape ficam sempre visiveis (nunca somem rolando junto do
  formulario, o caso de uso que motivou esta tarefa). flex: 1 1 auto
  com min-height: 0 e o que permite este filho encolher abaixo do seu
  conteudo dentro de um flex column com altura maxima -- sem o
  min-height o corpo ignoraria o limite do pai e a rolagem nunca
  apareceria (efeito colateral conhecido de flexbox).
*/
/*
  O CORPO ROLA POR DENTRO, e isso CORTA O ANEL DE FOCO se ninguem der
  espaco a ele.

  Definir overflow num eixo faz o outro parar de ser 'visible': pela
  especificacao, 'overflow-x: visible' COMPUTA para 'auto' quando
  overflow-y nao e visible. Entao o corpo passa a recortar na horizontal
  tambem, no limite exato da caixa — e o anel de foco do campo, que e um
  box-shadow de 3px para fora, fica cortado dos dois lados. A pessoa
  clica no campo e ve um talho vertical na borda, sem nada no console
  explicando.

  padding com margin negativa de mesmo valor: o anel ganha 4px de folga
  dentro da area que rola, e o campo continua alinhado com o cabecalho e
  o rodape do dialogo, que nao tem essa folga. Somam zero no layout.

  padding-block pela mesma razao, para o primeiro e o ultimo campo: no
  topo e no fim da rolagem o anel encostaria na borda de cima e de
  baixo.
*/
.dialogo-corpo {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 4px;
  margin: -4px;
}

.dialogo-rodape {
  display: flex;
  flex-shrink: 0;
  flex-direction: column-reverse;
  gap: 0.5rem;
}

@media (min-width: 640px) {
  .dialogo-rodape {
    flex-direction: row;
    justify-content: flex-end;
  }
}

/* ==========================================================================
   Sidebar — 16rem, 3rem colapsada, 18rem como drawer no celular
   ========================================================================== */

.app {
  display: flex;
  min-height: 100svh;
  /*
    Variante "inset" da sidebar. O fundo da pagina inteira passa a ser
    --sidebar; a sidebar fica sobre ele sem borda, parecendo parte do
    fundo, e o conteudo vira um painel de --background com cantos
    arredondados e sombra. E o que separa visualmente as duas areas sem
    precisar de borda.
  */
  background-color: var(--sidebar);
}

.sidebar {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  min-width: 0;
  width: var(--sidebar-largura);
  padding: 0.5rem;
  background-color: var(--sidebar);
  color: var(--sidebar-foreground);
  /* Preserva o respiro de 8px do modo inset e ainda respeita a barra
     de gestos do iOS, que e maior que isso. */
  padding-bottom: max(0.5rem, env(safe-area-inset-bottom));
  /* Excecao documentada em movimento.css: a largura e a unica
     propriedade de layout animada no sistema. */
  transition: width var(--duracao-media) var(--curva);
}

.sidebar-rodape {
  padding: 0.5rem;
}

/*
  Altura fixa igual a do cabecalho da aplicacao (3rem). Sem isso as duas
  linhas horizontais — a de baixo do seletor de condominio e a de baixo do
  cabecalho — ficam em alturas diferentes, e o desencontro salta aos olhos.
  O align-items tambem equilibra o espaco acima e abaixo do titulo.
*/
.sidebar-cabecalho {
  display: flex;
  align-items: center;
  height: 3rem;
  padding-inline: 0.5rem;
  border-bottom: 1px solid var(--sidebar-border);
}

.sidebar-rodape {
  margin-top: auto;
  border-top: 1px solid var(--sidebar-border);
}

.sidebar-corpo {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 0.5rem;
}

.sidebar-grupo {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}

.sidebar-grupo-titulo {
  padding: 0.375rem 0.5rem;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 500;
  color: var(--muted-foreground);
}

.sidebar-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 2rem;
  padding-inline: 0.5rem;
  border-radius: var(--raio-md);
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  transition:
    background-color var(--duracao-rapida) var(--curva),
    color var(--duracao-rapida) var(--curva);
}

.sidebar-item > svg {
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
}

.sidebar-item:hover {
  background-color: var(--sidebar-accent);
  color: var(--sidebar-accent-foreground);
}

.sidebar-item[aria-current="page"] {
  background-color: var(--sidebar-accent);
  color: var(--sidebar-accent-foreground);
}

.sidebar-contagem {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 1.25rem;
  height: 1.25rem;
  margin-left: auto;
  padding-inline: 0.375rem;
  border-radius: 9999px;
  background-color: var(--sidebar-accent);
  color: var(--muted-foreground);
  font-size: 0.6875rem;
  line-height: 1;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.sidebar-item[aria-current="page"] .sidebar-contagem {
  background-color: var(--background);
  color: var(--foreground);
}

/*
  Submenu recolhivel — SidebarMenuSub do bloco sidebar-07.

  O estado vive so em aria-expanded do gatilho: o CSS le o atributo, e
  nao ha classe paralela para as duas coisas discordarem.

  grid-template-rows de 0fr para 1fr e o unico jeito de animar altura
  desconhecida sem medir no JS. O overflow:hidden e obrigatorio; sem ele
  o conteudo vaza durante a transicao.
*/
.sidebar-item-pai {
  width: 100%;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
  /*
    So a familia herda. O atalho `font: inherit` tambem redefine
    font-size, line-height e font-weight — e como esta regra vem depois
    de .sidebar-item, atropelava os 14px/20px dela e o item com submenu
    saia um degrau maior que o item sem submenu.
  */
  font-family: inherit;
  color: inherit;
}

.sidebar-chevron {
  display: inline-flex;
  margin-left: auto;
  transition: transform var(--duracao-rapida) var(--curva);
}

.sidebar-chevron > svg {
  width: 1rem;
  height: 1rem;
}

.sidebar-item-pai[aria-expanded="true"] .sidebar-chevron {
  transform: rotate(90deg);
}

/* Com badge, o chevron perde o margin-left:auto para o badge assumi-lo. */
.sidebar-item-pai .sidebar-contagem + .sidebar-chevron {
  margin-left: 0.25rem;
}

.sidebar-submenu {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--duracao-rapida) var(--curva);
}

.sidebar-item-pai[aria-expanded="true"] + .sidebar-submenu {
  grid-template-rows: 1fr;
}

/*
  A lista inteira e o UNICO filho da grade. grid-template-rows dimensiona
  so as trilhas explicitas: com varios <li> como filhos diretos, do
  segundo em diante cada um cai numa trilha implicita de altura auto, que
  a transicao nunca toca — e o submenu nunca fecha.

  min-height: 0 vence o minimo automatico do item de grade; overflow
  hidden e o que corta o conteudo enquanto a trilha encolhe.
*/
.sidebar-submenu-lista {
  min-height: 0;
  overflow: hidden;
  margin: 0;
  padding: 0;
  list-style: none;
}

.sidebar-subitem {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  height: 1.75rem;
  /* Recuo de 1rem alinhado ao texto do pai, que tem icone de 1rem + gap
     de 0.5rem, mais 0.5rem de padding do proprio item. */
  margin-left: 1rem;
  padding-inline: 0.5rem;
  border-left: 1px solid var(--sidebar-border);
  border-radius: 0 var(--raio-md) var(--raio-md) 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
  white-space: nowrap;
  overflow: hidden;
  transition:
    background-color var(--duracao-rapida) var(--curva),
    color var(--duracao-rapida) var(--curva);
}

.sidebar-subitem:hover {
  background-color: var(--sidebar-accent);
  color: var(--sidebar-accent-foreground);
}

.sidebar-subitem[aria-current="page"] {
  background-color: var(--sidebar-accent);
  color: var(--sidebar-accent-foreground);
  font-weight: 500;
}

.sidebar-subitem:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--sidebar-ring) 50%, transparent);
}

/*
  Sidebar colapsada: submenu e chevron somem junto com os rotulos. Um
  submenu em flyout exigiria posicionamento e captura de foco — e outro
  componente, nao uma variante deste.
*/
[data-sidebar="colapsada"] .sidebar-submenu,
[data-sidebar="colapsada"] .sidebar-chevron {
  display: none;
}

[data-sidebar="colapsada"] .sidebar-contagem {
  display: none;
}

.sidebar-item:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--sidebar-ring) 50%, transparent);
}

.sidebar-sobreposicao {
  display: none;
}

/* Colapsada: so icones. */
[data-sidebar="colapsada"] .sidebar {
  width: var(--sidebar-largura-icone);
}

[data-sidebar="colapsada"] .sidebar-rotulo,
[data-sidebar="colapsada"] .sidebar-grupo-titulo {
  display: none;
}

[data-sidebar="colapsada"] .sidebar-item {
  justify-content: center;
  padding-inline: 0;
}

/* Na faixa de icones sobram 32px uteis; o padding lateral impediria
   qualquer conteudo de 24px de ficar centrado. */
[data-sidebar="colapsada"] .sidebar-cabecalho,
[data-sidebar="colapsada"] .sidebar-rodape {
  padding-inline: 0;
}

/*
  Desktop: a sidebar acompanha a rolagem em vez de crescer com a pagina.

  Sem isto ela e um item de flex de altura automatica, entao estica ate a
  altura do documento: numa pagina de 1284px com janela de 700px, o
  rodape — onde ficam o usuario e o "Sair" — parava 515px abaixo da
  dobra, e so aparecia rolando a pagina inteira ate o fim. O
  `overflow-y: auto` de .sidebar-corpo nunca entrava em acao, porque a
  sidebar tinha espaco de sobra.

  Com altura de uma tela e `sticky`, o rodape fica sempre visivel e e o
  menu que rola por dentro quando nao cabe. E o mesmo tratamento que
  .cabecalho ja recebia do outro lado.

  Divergencia estrutural do shadcn, que resolve o mesmo problema com a
  sidebar em `fixed` mais um espacador da mesma largura no fluxo. Aqui
  nao ha espacador, e `sticky` chega ao mesmo resultado sem acrescentar
  elemento nenhum nem mexer na largura do conteudo.

  So a partir de 768px: abaixo disso a sidebar e um drawer `fixed`, logo
  abaixo, e `sticky` nao teria sentido.
*/
@media (min-width: 768px) {
  .sidebar {
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100svh;
  }
}

/* Celular e tablet em pe: drawer. */
@media (max-width: 767px) {
  .sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    z-index: 50;
    width: var(--sidebar-largura-celular);
    transform: translateX(-100%);
    transition: transform var(--duracao-media) var(--curva);
  }

  [data-drawer="aberto"] .sidebar {
    transform: translateX(0);
  }

  [data-sidebar="colapsada"] .sidebar {
    width: var(--sidebar-largura-celular);
  }

  [data-sidebar="colapsada"] .sidebar-rotulo,
  [data-sidebar="colapsada"] .sidebar-grupo-titulo {
    display: block;
  }

  [data-sidebar="colapsada"] .sidebar-item {
    justify-content: flex-start;
    padding-inline: 0.5rem;
  }

  .sidebar-sobreposicao {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 40;
    background-color: oklch(0 0 0 / 50%);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--duracao-media) var(--curva);
  }

  [data-drawer="aberto"] .sidebar-sobreposicao {
    opacity: 1;
    pointer-events: auto;
  }

  .sidebar-item {
    height: 2.75rem;
  }
}

/* ==========================================================================
   Cabecalho da aplicacao
   ========================================================================== */

.conteudo {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

@media (min-width: 768px) {
  .conteudo {
    margin: 0.5rem 0.5rem 0.5rem 0;
    border-radius: var(--raio-xl);
    background-color: var(--background);
    box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
  }

  [data-sidebar="colapsada"] .conteudo {
    margin-left: 0.5rem;
  }
}

.cabecalho {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 0.25rem;
  height: 3rem;
  padding-inline: 1rem;
  padding-top: env(safe-area-inset-top);
  border-bottom: 1px solid var(--border);
  background-color: var(--background);
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
}

.cabecalho .separador[data-orientacao="vertical"] {
  height: 1rem;
  align-self: center;
  margin-inline: 0.5rem;
}

.cabecalho-titulo {
  font-size: 1rem;
  line-height: 1.5rem;
  font-weight: 500;
}

@media (min-width: 1024px) {
  .cabecalho {
    gap: 0.5rem;
    padding-inline: 1.5rem;
  }
}

/*
  A area de conteudo e o container de referencia das grades internas.

  No shadcn e o div com "@container/main", e as grades do painel medem
  ele, nao a janela. A diferenca importa: com a sidebar de 256px aberta,
  uma janela de 1280px deixa so 1000px de conteudo. Medir a janela faria
  a grade abrir 4 colunas num espaco que comporta 2.
*/
.pagina {
  padding: 1rem;
  container-type: inline-size;
  container-name: main;
}

@media (min-width: 768px) {
  .pagina { padding: 1.5rem; }
}

/* ==========================================================================
   Grafico de barras e barra de progresso

   A altura de cada barra chega por data-altura e e aplicada pelo ui.js
   via CSSOM: a CSP proibe atributo style, e escrever pela CSSOM nao e
   bloqueado. Sem JS as barras ficam no piso, e o rotulo com o valor
   continua legivel.
   ========================================================================== */

.grafico-barras {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  height: 12rem;
  padding-top: 1rem;
}

.grafico-coluna {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
}

.grafico-barra {
  width: 100%;
  height: var(--altura, 4px);
  min-height: 4px;
  border-radius: var(--raio-sm);
  background-color: var(--primary);
  transition: height var(--duracao-media) var(--curva);
}

.grafico-coluna:hover .grafico-barra {
  background-color: color-mix(in oklab, var(--primary) 80%, transparent);
}

.grafico-rotulo {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
  white-space: nowrap;
}

.progresso {
  width: 100%;
  height: 0.5rem;
  overflow: hidden;
  border-radius: 9999px;
  background-color: var(--secondary);
}

.progresso-preenchimento {
  height: 100%;
  width: var(--altura, 0%);
  border-radius: 9999px;
  background-color: var(--primary);
  transition: width var(--duracao-media) var(--curva);
}

/* Variacao de metrica no cabecalho do card */
/*
  Numero da metrica. No bloco dashboard-01 o shadcn escreve
  "text-2xl font-semibold tabular-nums @[250px]/card:text-3xl": 24px por
  padrao, 30px so quando o proprio card passa de 250px.

  O tamanho responde a largura do card, nao a da janela — daí container
  query, e nao media query. Fixar 30px, como estava aqui, faz "R$ 18.480"
  quebrar em duas linhas num painel de quatro colunas, que e exatamente o
  caso que a regra do shadcn evita.

  A entrelinha vem de .card-titulo (leading-none, no shadcn). Nao ha
  letter-spacing: o shadcn nao aplica nenhum neste titulo.
*/
.card-metrica {
  container-type: inline-size;
  container-name: card;
}

.metrica-valor {
  font-size: 1.5rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

@container card (min-width: 250px) {
  .metrica-valor {
    font-size: 1.875rem;
  }
}

/* ==========================================================================
   Abas — TabsList h-9 36px, raio lg 10px, padding 3px, fundo --muted;
   TabsTrigger 14px/500, px-2 8px, py-1 4px, raio md 8px, borda
   transparente de 1px, altura calc(100% - 1px), cor foreground/60;
   ativa: fundo --background e shadow-sm (registry new-york-v4)
   ========================================================================== */

/*
  O envolucro existe pela mesma razao do .tabela-envolucro: com muitos
  filtros a lista passa da largura do celular, e rolar a faixa de abas e
  melhor do que empurrar a pagina inteira para o lado.
*/
.abas-envolucro {
  max-width: 100%;
  overflow-x: auto;
  scrollbar-width: none;
}

.abas-envolucro::-webkit-scrollbar {
  display: none;
}

.abas {
  display: inline-flex;
  width: fit-content;
  align-items: center;
  justify-content: center;
  height: 2.25rem;
  padding: 3px;
  border-radius: var(--raio-lg);
  background-color: var(--muted);
  color: var(--muted-foreground);
}

.abas-item {
  position: relative;
  display: inline-flex;
  flex: 1 1 0;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  height: calc(100% - 1px);
  padding: 0.25rem 0.5rem;
  border: 1px solid transparent;
  border-radius: var(--raio-md);
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  white-space: nowrap;
  color: color-mix(in oklab, var(--foreground) 60%, transparent);
  transition:
    background-color var(--duracao-rapida) var(--curva),
    color var(--duracao-rapida) var(--curva),
    box-shadow var(--duracao-rapida) var(--curva);
}

.abas-item > svg {
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  pointer-events: none;
}

.abas-item:hover {
  color: var(--foreground);
}

.abas-item:focus-visible {
  outline: none;
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
}

.abas-item[aria-current="page"] {
  background-color: var(--background);
  color: var(--foreground);
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}

.escuro .abas-item {
  color: var(--muted-foreground);
}

.escuro .abas-item[aria-current="page"] {
  border-color: var(--input);
  background-color: color-mix(in oklab, var(--input) 30%, transparent);
  color: var(--foreground);
}

/* ==========================================================================
   Selecao — <select> nativo com a aparencia do SelectTrigger: h-9 36px,
   px-3 12px, raio md 8px, borda --input, shadow-xs, seta chevron-down de
   16px em --muted-foreground com opacidade 0.5 (registry new-york-v4).
   Herda de .campo, inclusive os 16px no celular que impedem o zoom
   automatico do Safari do iPhone.
   ========================================================================== */

.selecao-envolucro {
  position: relative;
  display: block;
  width: 100%;
}

.selecao {
  appearance: none;
  -webkit-appearance: none;
  /* 12px de px-3 + 16px do icone + 8px de gap-2 */
  padding-inline-end: 2.25rem;
  cursor: pointer;
}

/* Enquanto o placeholder esta selecionado, a cor e a de um placeholder. */
.selecao:has(> option[value=""]:checked) {
  color: var(--muted-foreground);
}

/*
  O popup do <select> e desenhado pelo sistema e nao herda os tokens.
  Sem isto, no tema escuro o Chrome no Windows pinta texto claro sobre
  fundo claro. Divergencia do registry, que nunca abre um select nativo.
*/
.selecao option {
  background-color: var(--popover);
  color: var(--popover-foreground);
}

.selecao-seta {
  position: absolute;
  top: 50%;
  right: 0.75rem;
  width: 1rem;
  height: 1rem;
  margin-top: -0.5rem;
  color: var(--muted-foreground);
  opacity: 0.5;
  pointer-events: none;
}

/* ==========================================================================
   Area de texto — min-h-16 4rem, px-3 12px, py-2 8px, field-sizing:
   content onde houver suporte (registry new-york-v4). O resto — borda,
   raio, sombra, foco, invalido — vem do .campo.
   ========================================================================== */

.area-texto {
  height: auto;
  min-height: 4rem;
  padding-block: 0.5rem;
  /* Sem isto o usuario pode arrastar o campo para alem da coluna e
     empurrar a pagina para o lado. O eixo vertical continua livre. */
  resize: vertical;
}

@supports (field-sizing: content) {
  .area-texto {
    field-sizing: content;
    resize: none;
  }
}

/* Reafirma o piso de 4rem: a regra de toque do .campo baixaria para 44px. */
@media (pointer: coarse) {
  .area-texto { min-height: 4rem; }
}

/* ==========================================================================
   Etapas — sem stepper no registry. Circulo de 2rem (o size-8 do
   ItemMedia), rotulo 14px/500 (o Label), linha de 1px em --border (o
   Separator), gap-2 entre circulo e rotulo, gap-3 em volta da linha.
   ========================================================================== */

.etapas {
  display: flex;
  align-items: center;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.etapa {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 auto;
  min-width: 0;
}

.etapa:last-child {
  flex: 0 1 auto;
}

.etapa-marcador {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2rem;
  height: 2rem;
  border: 1px solid transparent;
  border-radius: 9999px;
  font-size: 0.875rem;
  line-height: 1;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
}

.etapa-marcador > svg {
  width: 1rem;
  height: 1rem;
}

.etapa-rotulo {
  font-size: 0.875rem;
  line-height: 1.25rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.etapa-linha {
  flex: 1 1 auto;
  min-width: 1rem;
  height: 1px;
  margin-inline: 0.75rem;
  background-color: var(--border);
}

.etapa[data-estado="concluida"] .etapa-marcador {
  background-color: var(--primary);
  color: var(--primary-foreground);
}

.etapa[data-estado="concluida"] .etapa-rotulo {
  color: var(--muted-foreground);
}

.etapa[data-estado="concluida"] .etapa-linha {
  background-color: var(--primary);
}

.etapa[data-estado="atual"] .etapa-marcador {
  border-color: var(--primary);
  background-color: var(--background);
  color: var(--foreground);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 15%, transparent);
}

.etapa[data-estado="atual"] .etapa-rotulo {
  color: var(--foreground);
}

.etapa[data-estado="futura"] .etapa-marcador {
  background-color: var(--muted);
  color: var(--muted-foreground);
}

.etapa[data-estado="futura"] .etapa-rotulo {
  color: var(--muted-foreground);
}

/*
  No celular quatro rotulos nao cabem lado a lado. Some com todos menos o
  da etapa atual: a numeracao continua visivel, e o usuario continua
  sabendo onde esta e quanto falta.
*/
@media (max-width: 639px) {
  .etapa:not([data-estado="atual"]) .etapa-rotulo {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }

  .etapa-linha {
    min-width: 0.5rem;
    margin-inline: 0.375rem;
  }
}

/* ==========================================================================
   Area de upload — sem dropzone no registry; medidas do Empty: raio lg,
   borda tracejada, p-6 24px subindo para p-12 48px a partir de 768px,
   gap-6 24px; icone num quadrado de 2.5rem em --muted com SVG de 1.5rem
   e mb-2; titulo 18px/500 com tracking -0.025em; descricao 14px/1.625
   em --muted-foreground; bloco de acao gap-4 e no maximo 24rem.
   ========================================================================== */

.area-upload {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  gap: 1.5rem;
  padding: 1.5rem;
  border: 1px dashed var(--border);
  border-radius: var(--raio-lg);
  text-align: center;
  text-wrap: balance;
}

@media (min-width: 768px) {
  .area-upload { padding: 3rem; }
}

.area-upload-cabecalho {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  max-width: 24rem;
}

.area-upload-icone {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: 0.5rem;
  border-radius: var(--raio-lg);
  background-color: var(--muted);
  color: var(--foreground);
}

.area-upload-icone > svg {
  width: 1.5rem;
  height: 1.5rem;
  pointer-events: none;
}

.area-upload-titulo {
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 500;
  letter-spacing: -0.025em;
}

.area-upload-descricao {
  font-size: 0.875rem;
  line-height: 1.625;
  color: var(--muted-foreground);
}

.area-upload-conteudo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  width: 100%;
  min-width: 0;
  max-width: 24rem;
  font-size: 0.875rem;
}

.area-upload-nota {
  font-size: 0.75rem;
  line-height: 1rem;
  color: var(--muted-foreground);
}

/*
  O input de arquivo some da tela, mas continua na ordem de tabulacao —
  esconde-lo com display:none o tiraria do teclado junto. Como ele fica
  invisivel, o anel de foco precisa aparecer no botao seguinte, que e o
  que o usuario enxerga no lugar dele.
*/
.area-upload-arquivo {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

.area-upload-arquivo:focus-visible + .btn {
  border-color: var(--ring);
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

/* ==========================================================================
   Lista de descricao — sem description list no registry. Medidas do
   Field: rotulo e valor em 14px/20px, rotulo em --muted-foreground,
   gap-1 entre os dois, gap-4 entre pares e gap-6 entre colunas.
   Empilhada no celular, duas colunas a partir de 768px.
   ========================================================================== */

.lista-descricao {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1rem;
}

@media (min-width: 768px) {
  .lista-descricao {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 1.5rem;
  }

  .lista-descricao-simples {
    grid-template-columns: minmax(0, 1fr);
  }
}

.lista-descricao-item {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.lista-descricao-rotulo {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

.lista-descricao-valor {
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.25rem;
}

/* ==========================================================================
   Interruptor — trilho 1.15rem x 2rem, polegar 1rem, deslocamento
   calc(100% - 2px). Variante sm: 0.875rem x 1.5rem, polegar 0.75rem.
   (registry new-york-v4, switch.tsx)

   Divergencia deliberada: o shadcn monta um <button> do Radix com um
   <span> de polegar dentro, porque o React ja esta ali. Aqui o elemento
   e um <input type="checkbox"> e o polegar e um ::after. As medidas sao
   as mesmas; o que muda e que assim o interruptor liga e desliga sem uma
   linha de JavaScript, que e a premissa do kit.
   ========================================================================== */

.interruptor {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  width: 2rem;
  height: 1.15rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 9999px;
  background-color: var(--input);
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  transition:
    background-color var(--duracao-rapida) var(--curva),
    border-color var(--duracao-rapida) var(--curva),
    box-shadow var(--duracao-rapida) var(--curva);
}

.interruptor::after {
  content: "";
  display: block;
  width: 1rem;
  height: 1rem;
  border-radius: 9999px;
  background-color: var(--background);
  pointer-events: none;
  transition: transform var(--duracao-rapida) var(--curva);
  transform: translateX(0);
}

.interruptor:checked {
  background-color: var(--primary);
}

/* 100% e a largura do proprio polegar; os 2px descontam a borda do trilho. */
.interruptor:checked::after {
  transform: translateX(calc(100% - 2px));
}

.interruptor:focus-visible {
  outline: none;
  border-color: var(--ring);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
}

.interruptor:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.interruptor-sm {
  width: 1.5rem;
  height: 0.875rem;
}

.interruptor-sm::after {
  width: 0.75rem;
  height: 0.75rem;
}

.escuro .interruptor {
  background-color: color-mix(in oklab, var(--input) 80%, transparent);
}

.escuro .interruptor::after {
  background-color: var(--foreground);
}

.escuro .interruptor:checked {
  background-color: var(--primary);
}

.escuro .interruptor:checked::after {
  background-color: var(--primary-foreground);
}

/* O trilho tem 18px de altura: pequeno demais para um dedo. O alvo cresce
   por area clicavel, nao por tamanho do desenho — aumentar o trilho seria
   divergir das medidas do shadcn. */
@media (pointer: coarse) {
  .interruptor {
    outline-offset: 0;
    margin-block: 0.8125rem;
  }
}

/* ==========================================================================
   Paginacao — nav centrado, lista com gap de 4px. Cada link e um .btn:
   contorno na pagina atual, fantasma nas demais, como no registry, que
   monta PaginationLink sobre buttonVariants.
   (registry new-york-v4, pagination.tsx)
   ========================================================================== */

.paginacao {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-inline: auto;
}

.paginacao-lista {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* px-2.5 no registry, contra os 16px de um .btn normal. */
.paginacao-anterior,
.paginacao-proxima {
  gap: 0.25rem;
  padding-inline: 0.625rem;
}

/* O rotulo some no celular e sobra a seta, como o "hidden sm:block". */
.paginacao-rotulo {
  display: none;
}

@media (min-width: 640px) {
  .paginacao-rotulo {
    display: block;
  }
}

.paginacao-reticencias {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
}

.paginacao-reticencias > svg {
  width: 1rem;
  height: 1rem;
}

/* ==========================================================================
   Estado vazio — gap 24px, padding 24px (48px a partir de 768px),
   titulo text-lg/500 com tracking-tight, descricao text-sm/relaxed.
   (registry new-york-v4, empty.tsx)
   ========================================================================== */

.vazio {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  min-width: 0;
  padding: 1.5rem;
  border-radius: var(--raio-lg);
  border-style: dashed;
  text-align: center;
  text-wrap: balance;
}

@media (min-width: 768px) {
  .vazio {
    padding: 3rem;
  }
}

/*
  O registry declara "border-dashed" sem largura: quem usa decide se ha
  borda. Como o reset do kit zera a largura de todas as bordas, sem este
  modificador a borda tracejada nunca aparece.
*/
.vazio-contornado {
  border-width: 1px;
}

.vazio-cabecalho {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  max-width: 24rem;
  text-align: center;
}

.vazio-midia {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.5rem;
  background-color: transparent;
}

.vazio-midia > svg {
  flex-shrink: 0;
  pointer-events: none;
}

.vazio-midia-icone {
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--raio-lg);
  background-color: var(--muted);
  color: var(--foreground);
}

.vazio-midia-icone > svg {
  width: 1.5rem;
  height: 1.5rem;
}

.vazio-titulo {
  font-size: 1.125rem;
  line-height: 1.75rem;
  font-weight: 500;
  letter-spacing: -0.025em;
}

/* text-sm/relaxed: 14px com entrelinha 1.625, nao os 1.25rem do text-sm. */
.vazio-descricao {
  font-size: 0.875rem;
  line-height: 1.625;
  color: var(--muted-foreground);
}

.vazio-descricao a {
  text-decoration: underline;
  text-underline-offset: 4px;
}

.vazio-descricao a:hover {
  color: var(--primary);
}

.vazio-conteudo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  width: 100%;
  max-width: 24rem;
  min-width: 0;
  font-size: 0.875rem;
  text-wrap: balance;
}

/* ==========================================================================
   Quadro (kanban) — sem referencia no registry do shadcn, entao nenhum
   numero aqui e novo: cada medida vem de um componente que ja existe no
   kit, citado no comentario da regra.
   ========================================================================== */

/*
  Envolucro e quem rola de lado — mesmo padrao de .tabela-envolucro
  (position relative, overflow-x auto). tabindex="0" (no HTML, nao aqui)
  e o que deixa esse container alcancavel so de teclado quando os
  cartoes nao tem nada focavel dentro; o anel de foco vem de graca da
  regra global :focus-visible em base.css, sem precisar repetir aqui.
*/
.quadro-envolucro {
  position: relative;
  width: 100%;
  overflow-x: auto;
}

.quadro {
  display: flex;
  align-items: flex-start;
  gap: 1rem; /* g-4, o mesmo espacamento de .linha entre grupos de controle */
}

/*
  Largura minima da coluna: reaproveita --sidebar-largura (16rem), a
  unica medida de "coluna confortavel" que ja existe no kit, em vez de
  inventar um numero novo so para o quadro. flex-shrink 0 e quem
  garante que a coluna nao espreme abaixo disso — e o que forca o
  quadro a rolar de lado em vez de espremer o conteudo.
*/
.quadro-coluna {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  gap: 0.5rem; /* g-2 */
  width: var(--sidebar-largura);
}

.quadro-coluna-cabecalho {
  display: flex;
  flex-direction: column;
  gap: 0.25rem; /* g-1 */
  padding-inline: 0.25rem;
}

.quadro-coluna-titulo-linha {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

/* Mesma familia de .card-titulo: 600 de peso, sem tamanho proprio alem
   do herdado — aqui fixado em text-sm porque e titulo de coluna, um
   nivel abaixo do titulo de card. */
.quadro-coluna-titulo {
  font-size: 0.875rem;
  font-weight: 600;
}

/* Igual a .card-descricao: text-sm em --muted-foreground. */
.quadro-coluna-legenda {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
}

/*
  Corpo da coluna: fundo --muted, o mesmo token usado no icone de
  .vazio-midia-icone, e raio --raio-lg (10px), a mesma familia de
  .vazio — um container de conteudo, nao um cartao elevado. Cresce por
  dentro (os cartoes empilham com gap) e nunca encolhe abaixo do
  min-content, entao uma coluna vazia mantem a largura da coluna
  cheia ao lado.
*/
.quadro-coluna-corpo {
  display: flex;
  flex-direction: column;
  gap: 0.5rem; /* g-2 */
  min-height: 3rem;
  /*
    Rola por dentro em vez de esticar a pagina: uma coluna com 185
    cartoes empurrava o cabecalho das OUTRAS colunas para fora da tela,
    e o quadro so se lia rolando a pagina inteira de volta. 70vh deixa o
    cabecalho do quadro sempre visivel.
  */
  max-height: 70vh;
  overflow-y: auto;
  padding: 0.5rem;
  border-radius: var(--raio-lg);
  background-color: var(--muted);
}

/*
  OS CARTOES NAO ENCOLHEM. Sem isto, o max-height acima quebra a coluna
  inteira: num container flex de altura limitada o filho tem
  flex-shrink 1 por padrao, entao 44 cartoes de 238px se espremem para
  caber em 70vh e cada um vira uma tira de 18px com o conteudo
  transbordando para fora. A coluna fica uma pilha de retangulos
  cinzentos vazios — foi exatamente o que aconteceu em 08/09/2026,
  quando a rolagem vertical entrou.

  flex-shrink e o que faz o corpo ROLAR em vez de comprimir.
*/
.quadro-coluna-corpo > * {
  flex-shrink: 0;
}

/*
  COLUNA ESTREITA (recolhida).

  A largura e a unica coisa que muda por classe; o corpo continua no
  fluxo e continua recebendo drop — ver o comentario de
  quadro_coluna_inicio(). Os cartoes somem, o corpo nao.

  O titulo gira com writing-mode em vez de transform sozinho porque
  writing-mode reserva a caixa certa: so rotacionar deixaria a coluna
  larga o suficiente para o texto deitado, que e o que se quer evitar.
  E vai por CLASSE, nunca por atributo style — a CSP deste projeto
  bloqueia style inline sem erro no console, e a coluna renderizaria com
  o texto deitado sem ninguem entender por que.
*/
.quadro-coluna-estreita {
  width: 3.5rem;
}

.quadro-coluna-estreita .quadro-coluna-titulo-linha {
  flex-direction: column;
  gap: 0.5rem;
  padding-block: 0.5rem;
}

.quadro-coluna-estreita .quadro-coluna-titulo {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
}

/*
  A CONTAGEM SOBE PARA O TOPO quando a coluna recolhe.

  Aberta, ela vem depois do titulo — e a ordem de leitura natural, "o
  que e isto, e quantos tem". Recolhida, essa mesma ordem a empurra para
  baixo do titulo deitado, e como cada titulo tem um comprimento
  diferente ("Aberta" e "Em andamento") os numeros param em alturas
  diferentes: uma fileira de colunas recolhidas vira um ziguezague, e
  comparar 45 com 16 exige procurar cada um.

  No topo eles ficam todos na mesma linha, e a fileira inteira se le de
  uma vez.

  order, e nao trocar a ordem no HTML: a leitura do <button> continua
  "Roadmap 45", que e o que faz sentido para quem ouve. So o desenho
  muda.
*/
.quadro-coluna-estreita .quadro-coluna-titulo-linha > .badge {
  order: -1;
}

.quadro-coluna-estreita .quadro-coluna-corpo > * {
  display: none;
}

.quadro-coluna-estreita .quadro-coluna-legenda {
  display: none;
}

/* O botao herda a linha do titulo; so limpa o que <button> traz de
   fabrica. A area de clique e a linha inteira, nao um icone dentro
   dela — e e a linha inteira nos dois estados, senao acertar o mesmo
   botao dependeria de a coluna estar aberta ou recolhida. */
.quadro-coluna-alternar {
  width: 100%;
  padding-inline: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: inherit;
  cursor: pointer;
}

/*
  A SETA E A UNICA AFORDANCIA, e por isso ela nao pode ser discreta
  demais: sem nada indicando, um cabecalho que recolhe ao ser clicado e
  uma surpresa, e a primeira vez que alguem descobre e por acidente.

  Ela aponta para a direita recolhida (o sentido de "abre para ca") e
  gira 90 graus para baixo quando aberta — mesmo vocabulario do
  sidebar-submenu e do grupo da tabela de projetos.

  Fica em --muted-foreground para nao competir com o titulo, e ganha a
  cor do texto no hover do cabecalho inteiro.
*/
.quadro-coluna-alternar-icone {
  display: inline-flex;
  flex-shrink: 0;
  color: var(--muted-foreground);
  transition: transform var(--duracao-rapida) ease, color var(--duracao-rapida) ease;
}

/*
  O TAMANHO PRECISA ESTAR AQUI. O <svg> do Lucide sai com viewBox e sem
  width/height: quem dimensiona e sempre a regra do componente que o
  recebe (.btn > svg, .badge > svg, .alerta > svg). Sem uma regra
  propria o icone renderiza com 0x0 e some — e foi o que aconteceu desde
  que a coluna estreita ganhou o chevron: ele estava no HTML, com a
  classe certa, invisivel.

  1rem e o mesmo do icone dentro de um botao, que e o que isto e.
*/
.quadro-coluna-alternar-icone > svg {
  width: 1rem;
  height: 1rem;
  pointer-events: none;
}

.quadro-coluna-alternar[aria-expanded="true"] .quadro-coluna-alternar-icone {
  transform: rotate(90deg);
}

.quadro-coluna-alternar:hover .quadro-coluna-alternar-icone {
  color: var(--foreground);
}

/*
  Recolhida a seta some. Numa fatia de 3,5rem com o titulo deitado ela
  seria o terceiro elemento empilhado num espaco que ja tem titulo e
  contagem — e o rotulo na vertical ja diz, sozinho, que aquilo e uma
  coluna fechada.
*/
.quadro-coluna-estreita .quadro-coluna-alternar-icone {
  display: none;
}

.quadro-coluna-alternar:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--raio-md);
}

/* Placeholder discreto da coluna vazia: mesma combinacao text-sm +
   --muted-foreground de .card-descricao e .quadro-coluna-legenda, sem
   borda tracejada nem icone — isso e o que difere de estado_vazio(). */
.quadro-coluna-vazia {
  padding-block: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: var(--muted-foreground);
  text-align: center;
}

/*
  Cartao: mesmo raio, borda, fundo e sombra de .card (--raio-xl,
  1px var(--border), var(--card), a sombra shadow-sm exata), so com
  padding menor (1rem em vez dos 24px de --card-espaco) porque o
  cartao do quadro e denso por natureza — varios lado a lado numa
  coluna estreita. 1rem ja aparece em .btn e .alerta como unidade de
  padding confortavel.
*/
.quadro-cartao {
  padding: 1rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-xl);
  background-color: var(--card);
  color: var(--card-foreground);
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}

/*
  Arrastar e soltar (ui.js), com Pointer Events -- nao o drag-and-drop
  nativo do HTML5, que nunca dispara em touch. Estas regras usam o
  atributo [data-ui-cartao], nao a classe .quadro-cartao: o cartao de um
  quadro de verdade (tarefas.php/chamados.php) e um <article
  class="cartao-comparacao">, a moldura do PRODUTO (marca.css) — o
  contrato de arrastar do kit precisa valer para ele tambem, entao fica
  preso ao atributo de identidade, nunca a aparencia. Ver o comentario de
  cartao_quadro_html() em recursos/componentes/app/cartao-quadro.php.

  touch-action: none evita que o navegador comece a rolar a pagina
  assim que o toque comeca sobre um cartao -- sem isso, o gesto de
  arrastar para reordenar e indistinguivel do gesto de rolar a tela, e o
  navegador ganha essa disputa antes do JS decidir (ele so teria a
  chance de chamar preventDefault() depois que a rolagem ja comecou, o
  que a maioria dos navegadores ignora). O efeito colateral e que rolar
  tocando exatamente num cartao para de funcionar; rolar pela folga
  entre cartoes, pelo padding da coluna ou pelo .quadro-envolucro
  continua igual.
*/
[data-ui-cartao] {
  touch-action: none;
  user-select: none;
  cursor: grab;
}

/*
  O cartao arrastado sai do fluxo (position: fixed) e segue o ponteiro
  por variaveis CSS que o ui.js escreve pela CSSOM
  (--arraste-x/--arraste-y/--arraste-largura/--arraste-altura) -- a CSP
  (style-src 'self') bloqueia atributo style, entao esta e a unica forma
  de mover algo pixel a pixel sem violar a politica (o mesmo caminho de
  aplicarAlturas() em ui.js). pointer-events: none e o que deixa
  document.elementFromPoint() enxergar a coluna por baixo do cartao para
  descobrir o alvo do drop.
*/
[data-ui-cartao][data-arrastando] {
  position: fixed;
  left: 0;
  top: 0;
  width: var(--arraste-largura);
  height: var(--arraste-altura);
  transform: translate3d(var(--arraste-x, 0), var(--arraste-y, 0), 0);
  cursor: grabbing;
  pointer-events: none;
  z-index: 50; /* mesmo z-index do dropdown-menu: e um elemento flutuante */
  /* shadow-lg do Tailwind, preto puro (mesma razao do dropdown-menu:
     sombra de --foreground vira brilho no tema escuro). */
  box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}

/* Cartao agarrado pelo teclado (espaco/enter): mesmo anel de destaque
   que .etapa[data-estado="atual"] .etapa-marcador ja usa para "estado
   atual, em andamento" -- nao inventa uma cor nova para o mesmo papel.
   Sem position: fixed aqui: o teclado move o proprio elemento na arvore
   a cada tecla, nunca precisa de um "fantasma" seguindo ponteiro. */
[data-ui-cartao][aria-grabbed="true"] {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 15%, transparent);
}

/* Indicador de insercao: o lugar exato onde o cartao cai ao soltar.
   Borda tracejada de .area-upload (unico tracejado do kit) e --raio-lg
   de .quadro-coluna-corpo -- nenhuma medida nova. */
.quadro-cartao-indicador {
  flex-shrink: 0;
  height: var(--arraste-altura, 3rem);
  border: 1px dashed var(--border);
  border-radius: var(--raio-lg);
  background-color: color-mix(in oklab, var(--primary) 15%, transparent);
}

/* Coluna sob o ponteiro durante o arraste: precisa ficar obvia mesmo
   com a coluna cheia de cartoes. */
.quadro-coluna[data-arraste-sobre] > .quadro-coluna-corpo {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 15%, transparent);
}

/* Nota dentro de um dropdown: contexto que a acao precisa ANTES de ser
   escolhida (por exemplo, que pausar aqui nao para a cobranca no
   gateway). Nao e item de menu — nao clica, nao recebe foco. */
.dropdown-nota {
  padding: 0.375rem 0.5rem 0.5rem;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--muted-foreground);
  border-bottom: 1px solid var(--border);
  margin-bottom: 0.25rem;
  max-width: 16rem;
}

/* O <form> de cada acao nao pode virar caixa: o item dentro dele precisa
   ocupar a largura do painel como qualquer .dropdown-item. */
.dropdown-painel form {
  display: contents;
}

/*
  Dialogo sem cabecalho do kit: quem desenha o topo e o conteudo. Existe
  para o dialogo cujo titulo so se sabe depois de carregar — a ficha de
  um cartao, cujo titulo e o nome do cartao. Ver dialogo_inicio().

  A unica diferenca e nao herdar o espaco reservado ao cabecalho: sem
  isto o conteudo comecaria com um respiro que nao tem cabecalho nenhum
  em cima.
*/
.dialogo-corpo--sem-cabecalho {
  margin-top: 0;
}

/*
  Dialogo amplo: para o conteudo que tem duas colunas.

  POR QUE AQUI A MEDIDA E DE VIEWPORT e nao de container, ao contrario do
  resto do projeto: um <dialog> modal vive na top layer, centrado sobre a
  janela inteira. Ele nao esta dentro de "main", nao encolhe com a
  sidebar aberta, e a largura que ele pode ocupar E a da janela. O aviso
  que vale para .grade-painel (medir viewport quando o conteudo mora ao
  lado da sidebar) nao se aplica a um elemento que ignora a sidebar.

  64rem so a partir de 60rem de janela: abaixo disso ele continua com a
  largura de sempre, e o conteudo empilha.
*/
@media (min-width: 60rem) {
  .dialogo--amplo {
    max-width: 64rem;
  }
}

/*
  CAMPO DE ARQUIVO COMPACTO.

  O <input type="file"> nativo desenha o proprio botao e o proprio texto,
  com a fonte do sistema operacional e sem nenhuma relacao com o resto da
  interface — e nao ha CSS que conserte, porque o widget e desenhado pelo
  navegador. O jeito e esconder e desenhar por cima: um <label> com cara
  de botao abre o seletor sem uma linha de JS, porque clicar num label e
  clicar no campo.

  A caixa e a mesma do .campo (altura, raio, borda), para a linha alinhar
  com o <select> de membros logo acima.
*/
.campo-arquivo {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  min-width: 0;
  height: 2.25rem;
  padding: 0.25rem 0.25rem 0.25rem 0.5rem;
  border: 1px solid var(--input);
  border-radius: var(--raio-md);
  background-color: transparent;
}

/* Mesma tecnica de .area-upload-arquivo: invisivel, mas na ordem de
   tabulacao. display:none tiraria do teclado. */
.campo-arquivo-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

.campo-arquivo-botao {
  flex-shrink: 0;
  cursor: pointer;
}

/* O input e invisivel, entao o anel de foco vai para o botao ao lado —
   e por isso o input vem ANTES dele no HTML. */
.campo-arquivo-input:focus-visible + .campo-arquivo-botao {
  border-color: var(--ring);
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.campo-arquivo-nomes {
  overflow: hidden;
  flex: 1 1 auto;
  min-width: 0;
  color: var(--muted-foreground);
  font-size: 0.875rem;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Com arquivo escolhido o texto deixa de ser um aviso e passa a ser
   conteudo. */
.campo-arquivo[data-escolhido] .campo-arquivo-nomes {
  color: var(--foreground);
}

/*
  ARRASTAR O ARQUIVO PARA CIMA DO CAMPO tambem funciona — e o gesto que
  se faz sem pensar depois de usar qualquer ferramenta moderna. O realce
  e o mesmo tom da coluna do quadro que recebe um cartao.
*/
/*
  Mesmo alvo de toque de .campo e .btn — 44px, recomendacao da Apple. Sem
  esta regra o campo de arquivo ficava 8px mais baixo que o <select> e o
  botao da mesma linha em qualquer tela de toque, e a linha desalinhava.
*/
@media (pointer: coarse) {
  .campo-arquivo { min-height: 44px; }
}

.campo-arquivo[data-arraste-sobre] {
  border-color: var(--ring);
  background-color: var(--muted);
}

/* ==========================================================================
   Barra empilhada — a composicao de um conjunto, em proporcao.

   Sem referencia no registry do shadcn (nao ha este componente la). As
   medidas saem das primitivas: altura de 1.5rem, que e a do badge; raio
   md; hairline de 1px em --border. O texto dentro da fatia e o 11px das
   legendas do kit.

   A LARGURA E A COR VEM POR data-*, escritas pela CSSOM (ver
   aplicarLarguras e aplicarCores em ui.js): as duas dependem do DADO, e
   a CSP bloqueia atributo style sem dizer nada.
   ========================================================================== */

.barra-empilhada {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.barra-empilhada-trilho {
  display: flex;
  overflow: hidden;
  height: 1.5rem;
  border: 1px solid var(--border);
  border-radius: var(--raio-md);
}

.barra-empilhada-fatia {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--largura, 0%);
  min-width: 0;
  background-color: var(--cor, var(--muted));
  color: var(--primary-foreground);
  text-decoration: none;
  transition: filter 120ms ease;
}

/*
  Fatia que e link responde ao ponteiro; a que nao e, nao finge que
  responde.
*/
a.barra-empilhada-fatia:hover,
a.barra-empilhada-fatia:focus-visible {
  filter: brightness(1.12);
}

a.barra-empilhada-fatia:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.barra-empilhada-texto {
  overflow: hidden;
  padding-inline: 0.5rem;
  font-size: 0.6875rem;
  line-height: 1;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.barra-empilhada-legenda {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.barra-empilhada-item {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--muted-foreground);
  font-size: 0.6875rem;
  line-height: 1.25;
  text-decoration: none;
}

a.barra-empilhada-item:hover {
  color: var(--foreground);
}

.barra-empilhada-ponto {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 2px;
  background-color: var(--cor, var(--muted-foreground));
}

@media (prefers-reduced-motion: reduce) {
  .barra-empilhada-fatia { transition: none; }
}

/* ==========================================================================
   Rosca. A composicao de um conjunto em fracao do TODO, com o total no
   meio -- a barra empilhada compara fatia com fatia, esta compara cada
   fatia com o total. Ate quatro fatias ela le melhor e ocupa menos
   largura; acima disso vira um anel de lasquinhas e a barra e a escolha.

   O TAMANHO VEM DE FORA, por --rosca-tamanho: um anel de 134px num
   cartao de painel e um de 72px ao lado de uma linha de tabela sao o
   mesmo componente, e o kit nao tem como saber quanto espaco ha.
   ========================================================================== */

.rosca {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.875rem;
}

.rosca-area {
  position: relative;
  flex: none;
  width: var(--rosca-tamanho, 8.5rem);
  aspect-ratio: 1;
}

.rosca-anel {
  display: block;
  width: 100%;
  height: 100%;
}

.rosca-trilho,
.rosca-parte {
  fill: none;
  stroke-width: 9;
}

/* O trilho fecha o anel quando as fatias nao fecham -- e o que faz uma
   rosca de uma fatia so parecer "parte de um todo" e nao um arco solto. */
.rosca-trilho { stroke: var(--border); }

.rosca-parte {
  stroke: var(--cor, var(--muted-foreground));
  stroke-linecap: round;
  transition: opacity 0.15s ease;
}

/* Fatia pequena demais para uma ponta redonda: a ponta reta e o que
   impede o arco de ficar MAIOR que a fracao que ele representa. */
.rosca-parte-fina { stroke-linecap: butt; }

/*
  Passar o mouse numa fatia apaga as outras. O :has() e de proposito:
  sem ele a regra dispararia tambem com o ponteiro no buraco do meio ou
  em cima do trilho, e o anel piscaria a cada vez que o mouse cruzasse o
  cartao. Onde :has() nao existir, nada apaga -- e so a leitura normal.
*/
.rosca-anel:has(.rosca-parte:hover) .rosca-parte { opacity: 0.3; }
.rosca-anel .rosca-parte:hover { opacity: 1; }

/*
  O centro nao recebe ponteiro: ele cobre o buraco do anel inteiro, e
  com eventos ele roubaria o hover das fatias de baixo.
*/
.rosca-centro {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  pointer-events: none;
  text-align: center;
  padding: 0 18%;
}

.rosca-centro strong {
  font-size: 1.5rem;
  line-height: 1.1;
  font-weight: 600;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.rosca-centro span {
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--muted-foreground);
}

.rosca-legenda {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.rosca-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.75rem;
  line-height: 1.25;
  color: var(--muted-foreground);
  text-decoration: none;
}

.rosca-ponto {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background-color: var(--cor, var(--muted-foreground));
}

.rosca-rotulo {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.rosca-valor {
  flex: none;
  color: var(--foreground);
  font-weight: 500;
}

/* Largura minima para a coluna de fracao: sem ela "9%" e "100%" nao
   alinham, e a legenda perde a leitura de cima para baixo. */
.rosca-fracao {
  flex: none;
  min-width: 2.25rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

a.rosca-item:hover .rosca-rotulo,
a.rosca-item:hover .rosca-valor { color: var(--foreground); }

a.rosca-item:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--raio-sm);
}

@media (prefers-reduced-motion: reduce) {
  .rosca-parte { transition: none; }
}

/* ==========================================================================
   Caixa de seleção (checkbox), do registry new-york-v4: 16px, raio 4px,
   borda de 1px, e quando marcada o fundo vira --primary com o "check"
   em --primary-foreground.

   O KIT NÃO TINHA NENHUMA até aqui — só o interruptor, que é outra
   coisa: ele liga UM estado com efeito imediato, a caixa escolhe VÁRIOS
   itens de um conjunto e o efeito vem no salvar.

   O "check" é o ícone check do Lucide em data: URI. Vai no
   background-image, e não num <svg> filho, porque <input> é elemento
   vazio e não aceita filho nenhum — e a CSP permite (img-src 'self'
   data:). Desenhar o tique com uma borda girada seria um ícone feito à
   mão, que é o que este projeto não faz.
   ========================================================================== */

.caixa {
  appearance: none;
  flex: none;
  width: 1rem;
  height: 1rem;
  margin: 0;
  border: 1px solid var(--input);
  border-radius: 0.25rem;
  background-color: var(--background);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 0.75rem;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.caixa:checked {
  border-color: var(--primary);
  background-color: var(--primary);
  /* Lucide: check — stroke em --primary-foreground (oklch 0.985 0 0). */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fbfbfb' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

.caixa:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
}

.caixa:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.caixas {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  border: 0;
}

/* Grade e não coluna: quatro opções curtas empilhadas desperdiçam a
   largura do diálogo e empurram o resto do formulário para baixo. */
.caixas-lista {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.5rem 1rem;
}

.caixa-opcao {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.875rem;
  line-height: 1.25rem;
  cursor: pointer;
}

/* A opção inteira é alvo de clique (o <label> envolve a caixa), e por
   isso o texto não seleciona ao clicar duas vezes rápido. */
.caixa-opcao > span { user-select: none; }

@media (prefers-reduced-motion: reduce) {
  .caixa { transition: none; }
}

/* ---------------------------------------------------------------- editor

   Editor de texto rico sobre contenteditable. A area imita o .campo:
   mesma borda, mesmo raio, mesmo foco — para nao parecer outra coisa
   dentro do mesmo formulario.

   .texto-rico tambem e usada SOZINHA, na leitura, e por isso as regras
   de conteudo ficam nela e nao em .editor-area.
*/
.editor {
  border: 1px solid var(--input);
  border-radius: var(--raio-md);
  background-color: transparent;
  box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  overflow: hidden;
  transition:
    border-color var(--duracao-rapida) var(--curva),
    box-shadow var(--duracao-rapida) var(--curva);
}

.escuro .editor {
  background-color: color-mix(in oklab, var(--input) 30%, transparent);
}

.editor:focus-within {
  border-color: var(--ring);
  box-shadow:
    0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent),
    0 1px 2px 0 rgb(0 0 0 / 0.05);
}

.editor-barra {
  display: flex;
  gap: 0.125rem;
  padding: 0.25rem;
  border-bottom: 1px solid var(--border);
}

.editor-botao {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 0;
  border-radius: var(--raio-sm);
  background: transparent;
  color: var(--muted-foreground);
  cursor: pointer;
}

.editor-botao:hover {
  background-color: var(--accent);
  color: var(--accent-foreground);
}

.editor-botao:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
}

.editor-botao svg {
  width: 0.875rem;
  height: 0.875rem;
}

.editor-area {
  min-height: 6rem;
  padding: 0.5rem 0.75rem;
  outline: none;
  /* Mesmo par do .campo: 16px no celular (o iOS da zoom ao focar abaixo
     disso). Na area vazia o que se digita nasce fora de qualquer <p> e
     herda este tamanho. */
  font-size: 1rem;
  line-height: 1.5rem;
}

@media (min-width: 768px) {
  .editor-area {
    font-size: 0.875rem;
    line-height: 1.25rem;
  }
}

/* O conteudo renderizado — na area e na leitura. */
.texto-rico > :first-child { margin-top: 0; }
.texto-rico > :last-child { margin-bottom: 0; }

.texto-rico p {
  margin: 0 0 0.5rem;
  font-size: 1rem;
  line-height: 1.6;
}

.texto-rico ul,
.texto-rico ol {
  margin: 0 0 0.5rem;
  padding-left: 1.25rem;
  font-size: 1rem;
  line-height: 1.6;
}

@media (min-width: 768px) {
  .texto-rico p,
  .texto-rico ul,
  .texto-rico ol {
    font-size: 0.875rem;
  }
}

.texto-rico li { margin: 0.125rem 0; }

.texto-rico a {
  color: var(--primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  overflow-wrap: anywhere;
}

/* ==========================================================================
   Barras de estado. Uma tira de barras finas, uma por item, pintadas pelo
   estado de cada um. O PHP so escreve data-estado; a cor e escolha daqui.

   O vocabulario generico (ok, atencao, erro, vazio) e pintado abaixo. Um
   produto com outras palavras escreve na folha dele uma regra
   [data-estado="palavra"] que defina --cor. Estado sem regra cai no cinza
   do --muted-foreground: a barra aparece, nunca some.

   O verde e o ambar sao os tokens --cor-ok e --cor-atencao, definidos em
   tokens.css (e la que mora a justificativa e o contraste). Esta folha so
   os le: uma segunda copia dos oklch divergiria na primeira correcao.
   ========================================================================== */

.barras-de-estado {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.barras-de-estado-tira {
  display: flex;
  align-items: stretch;
  gap: 2px;
  height: 1.5rem;
}

.barras-de-estado-barra {
  flex: 1 1 0;
  min-width: 2px;
  max-width: 0.75rem;
  border-radius: 2px;
  background-color: var(--cor, var(--muted-foreground));
}

.barras-de-estado-ponto {
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 2px;
  background-color: var(--cor, var(--muted-foreground));
}

.barras-de-estado [data-estado="ok"] { --cor: var(--cor-ok); }
.barras-de-estado [data-estado="atencao"] { --cor: var(--cor-atencao); }
.barras-de-estado [data-estado="erro"] { --cor: var(--destructive); }
.barras-de-estado [data-estado="vazio"] { --cor: var(--border); }

.barras-de-estado-legenda {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
}

.barras-de-estado-item {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--muted-foreground);
  font-size: 0.6875rem;
  line-height: 1.25;
}
