/* ============================================================
   ELVEN OBSERVABILITY — CHANGELOG · ACABAMENTO
   Carregado DEPOIS de app.css e controls.css.

   Este arquivo só acrescenta: estados (carga, vazio, erro), toast,
   scrollbar, seleção de texto, microinterações e leitura. Não redefine
   paleta, tipografia, espaçamento nem layout — todos os valores abaixo
   saem dos tokens já aprovados em app.css.

   Regras da casa respeitadas: sem !important, sem box-shadow fora do
   toast, pesos 400/510/590, nada escala, nada translada mais que 4px,
   nenhuma transição acima de .2s, tudo funciona nos dois temas e imprime.

   Controles de formulário (select, input, botão) são território de
   controls.css — este arquivo não encosta neles para não duplicar regra.
   ============================================================ */

/* ------------------------------------------------------------
   0. Tokens locais do acabamento
   O toast inverte o fundo (usa --text-1 como superfície), então a cor
   semântica da faixa precisa vir do tema OPOSTO para ter contraste:
   no tema claro o toast é escuro e pede os tons vivos; no escuro ele é
   claro e pede os tons fechados. São os mesmos valores de marca, só
   cruzados. Nada aqui é cor nova.
   ------------------------------------------------------------ */
:root {
  --toast-ok: #00BFA5;
  --toast-erro: #FF5252;
  --toast-aviso: #FBBF24;
  --sb-thumb: var(--dot-muted);
  --sb-thumb-hover: var(--text-3);

  /* Ícones dos estados, em SVG de traço embutido. Entram por máscara e
     são pintados com background-color, então herdam o tema. A CSP
     permite: data: está liberado em img-src. */
  --ic-vazio: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3.25' y='4.75' width='17.5' height='14.5' rx='2.5'/%3E%3Cpath d='M3.25 13.5h4.5l1.5 2.5h5.5l1.5-2.5h4.5'/%3E%3C/svg%3E");
  --ic-erro: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.75'/%3E%3Cpath d='M12 7.5v5.25'/%3E%3Cpath d='M12 16.25h.01'/%3E%3C/svg%3E");
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --toast-ok: #00897B;
    --toast-erro: #C62828;
    --toast-aviso: #8A5A00;
  }
}
:root[data-theme="dark"] {
  --toast-ok: #00897B;
  --toast-erro: #C62828;
  --toast-aviso: #8A5A00;
}

/* ------------------------------------------------------------
   1. Skeleton de carregamento
   Reproduz a grade rail|conteúdo de uma entrada real (render.js reusa
   .entry e .rail no esqueleto), com as mesmas alturas de linha do
   conteúdo, para que a troca skeleton→dados não empurre a página.
   O brilho anima background-position — não pisca opacidade.
   ------------------------------------------------------------ */
.sk {
  display: block;
  border-radius: var(--r-sm);
  background-color: var(--surface);
  background-image: linear-gradient(90deg,
    transparent 0%,
    var(--surface-strong) 45%,
    var(--surface-strong) 55%,
    transparent 100%);
  background-repeat: no-repeat;
  background-size: 55% 100%;
  animation: sk-brilho 1.4s linear infinite;
}
@keyframes sk-brilho {
  from { background-position: 160% 0; }
  to   { background-position: -60% 0; }
}

/* Altura = line-height do texto que substitui (1.0625rem × 1.6 ≈ 1.7rem),
   com a barra ocupando o miolo e a folga virando respiro vertical. */
.sk-linha { height: 1rem; margin: .35rem 0; }
.sk-titulo { height: 2.25rem; margin: 1.5rem 0 1.25rem; }  /* .etitle: 2rem/1.125 */
.sk-titulo.sk-h1 { height: 3.0625rem; margin: 0 0 1.5rem; } /* .phead h1: 3rem/1.02 */

.sk-w-4 { width: 40%; }
.sk-w-5 { width: 50%; }
.sk-w-6 { width: 60%; }
.sk-w-7 { width: 70%; }
.sk-w-8 { width: 80%; }
.sk-w-9 { width: 90%; }
.sk-w-full { width: 100%; }

/* No rail o espaçamento já vem do gap do flex; margem dupla afastaria
   demais e o esqueleto ficaria mais alto que a entrada real. */
.rail .sk-linha { margin: 0; height: .875rem; max-width: 100%; }
.rail .sk-linha.sk-w-4 { height: 1.375rem; }  /* faixa da .vchip */

/* Linha de badges: mesma altura do .badge-dep (1.125rem + .375 de padding). */
.sk-badges { display: flex; gap: .375rem; align-items: center; }
.sk-badges .sk-linha { height: 1.5rem; margin: 0; max-width: 8rem; border-radius: var(--r-md); }

/* Placeholder da barra de filtros: mesma hairline de baixo que .filters. */
.sk-barra {
  height: 2.25rem;
  margin: 2rem 0 1rem;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  background-color: var(--surface);
  background-image: linear-gradient(90deg,
    transparent 0%, var(--surface-strong) 45%, var(--surface-strong) 55%, transparent 100%);
  background-repeat: no-repeat;
  background-size: 55% 100%;
  animation: sk-brilho 1.4s linear infinite;
}

/* O esqueleto não é conteúdo: nunca deve reagir a hover nem receber foco. */
.sk-entrada { pointer-events: none; }

@media (max-width: 899px) {
  .rail .sk-linha { max-width: 7rem; }
}

/* ------------------------------------------------------------
   2. Estado vazio
   Ícone desenhado em SVG embutido no CSS e pintado com máscara, para
   herdar a cor do tema em vez de ficar preto no escuro.
   ------------------------------------------------------------ */
.estado-vazio {
  max-width: 30rem;
  margin: 0 auto;
  padding: 5rem 0 4rem;
  text-align: center;
}
.estado-vazio__icone {
  display: block;
  width: 2rem;
  height: 2rem;
  margin: 0 auto 1.25rem;
  background-color: var(--text-3);
  opacity: .55;
  -webkit-mask: var(--ic-vazio) center / contain no-repeat;
  mask: var(--ic-vazio) center / contain no-repeat;
}
.estado-vazio h2 {
  font-size: 1.125rem;
  line-height: 1.4;
  letter-spacing: -.012em;
  color: var(--text-1);
}
.estado-vazio p {
  margin-top: .5rem;
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--text-3);
  text-wrap: pretty;
}
.estado-vazio__acoes {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  justify-content: center;
  margin-top: 1.5rem;
}

/* ------------------------------------------------------------
   3. Estado de erro de API
   --danger aparece só na régua da esquerda e no ícone. O fundo continua
   sendo superfície normal: erro se lê, não se grita.
   ------------------------------------------------------------ */
.estado-erro {
  position: relative;
  max-width: 44rem;
  margin: 4rem auto;
  padding: 1.25rem 1.375rem;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-left: 3px solid var(--danger);
  border-radius: 0 var(--r-lg) var(--r-lg) 0;
}
/* Só abre espaço para o ícone quando ele existe — o <noscript> usa a
   mesma classe sem ícone e não pode ficar com um recuo órfão. */
.estado-erro:has(.estado-erro__icone) { padding-left: 3.5rem; }
.estado-erro__icone {
  position: absolute;
  left: 1.375rem;
  top: 1.5rem;
  width: 1.25rem;
  height: 1.25rem;
  background-color: var(--danger);
  -webkit-mask: var(--ic-erro) center / contain no-repeat;
  mask: var(--ic-erro) center / contain no-repeat;
}
.estado-erro h1,
.estado-erro h2 {
  font-size: 1.125rem;
  line-height: 1.4;
  letter-spacing: -.012em;
  color: var(--text-1);
}
.estado-erro p {
  margin-top: .5rem;
  font-size: .9375rem;
  line-height: 1.6;
  color: var(--text-2);
  text-wrap: pretty;
}
.estado-erro__dica { color: var(--text-3); }
.estado-erro__acoes {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: 1.25rem;
}

/* ------------------------------------------------------------
   4. Toast
   Única exceção de sombra do projeto (é overlay). Faixa lateral de 3px
   como box-shadow interno para não brigar com o border-radius, e barra
   de auto-dismiss em CSS puro sincronizada com os timers do render.js
   (3,2s normal / 10s quando há botão de desfazer).
   ------------------------------------------------------------ */
.toast {
  position: relative;
  overflow: hidden;
  --toast-faixa: transparent;
  --toast-prog: currentColor;
  box-shadow: var(--shadow-pop), inset 3px 0 0 0 var(--toast-faixa);
  animation: toast-entra .18s cubic-bezier(.4, 0, .2, 1) both;
}
.toast--ok    { --toast-faixa: var(--toast-ok);    --toast-prog: var(--toast-ok); }
.toast--erro  { --toast-faixa: var(--toast-erro);  --toast-prog: var(--toast-erro); }
.toast--aviso { --toast-faixa: var(--toast-aviso); --toast-prog: var(--toast-aviso); }
.toast--ok, .toast--erro, .toast--aviso { padding-left: 1.125rem; }

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

.toast::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: var(--toast-prog);
  opacity: .4;
  animation: toast-progresso 3.2s linear forwards;
}
/* O toast com ação vive 10s (render.js). :has evita precisar de classe. */
.toast:has(button)::after { animation-duration: 10s; }
.toast--persistente::after { animation-name: none; opacity: 0; }
@keyframes toast-progresso {
  from { width: 100%; }
  to   { width: 0; }
}

/* ------------------------------------------------------------
   5. Scrollbar
   Coerente com os dois temas, e ainda visível: barra discreta não é
   barra ausente.
   ------------------------------------------------------------ */
:root {
  scrollbar-width: thin;
  scrollbar-color: var(--sb-thumb) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
/* A borda transparente + background-clip dá o respiro lateral sem pintar
   a cor de fundo da página dentro de blocos que têm superfície própria
   (pre, tabela, slideover). */
::-webkit-scrollbar-thumb {
  background: var(--sb-thumb);
  background-clip: padding-box;
  border: 3px solid transparent;
  border-radius: var(--r-pill);
}
::-webkit-scrollbar-thumb:hover { background: var(--sb-thumb-hover); }
::-webkit-scrollbar-corner { background: transparent; }

/* Áreas com rolagem própria sobre superfície elevada (código, tabela,
   slideover) pedem barra menor: a de 10px domina o bloco. */
pre::-webkit-scrollbar,
.table-wrap::-webkit-scrollbar,
.jump::-webkit-scrollbar,
.chips::-webkit-scrollbar,
.slideover .so-panel::-webkit-scrollbar { width: 8px; height: 8px; }
pre::-webkit-scrollbar-thumb,
.table-wrap::-webkit-scrollbar-thumb,
.jump::-webkit-scrollbar-thumb,
.chips::-webkit-scrollbar-thumb,
.slideover .so-panel::-webkit-scrollbar-thumb { border-width: 2px; }

/* ------------------------------------------------------------
   6. Seleção de texto
   ------------------------------------------------------------ */
::selection { background: var(--accent-soft); color: var(--text-1); }
::-moz-selection { background: var(--accent-soft); color: var(--text-1); }

/* ------------------------------------------------------------
   7. Microinterações
   Só cor e borda mudam, sempre em ≤ .18s. Nada escala, nada quica.
   ------------------------------------------------------------ */
a { transition: color var(--t-fast); }

/* app.css já tem `transition: all` no chip — trocar por lista explícita
   evita animar largura/borda quando a contagem muda no filtro. */
.chip { transition: background-color var(--t-fast), border-color var(--t-fast), color var(--t-fast); }

.vchip { transition: filter var(--t-fast), background-color var(--t-fast), color var(--t-fast); }
.linkchip { transition: border-color var(--t-fast), color var(--t-fast); }
.tag { transition: background-color var(--t-fast), color var(--t-fast); }
.jump a { transition: color var(--t-fast), border-left-color var(--t-fast); }
.cstrip a { transition: color var(--t-fast), border-color var(--t-fast); }
.refs a, .morechg, .fmeta .clr, details.pol > summary { transition: color var(--t-fast); }
.mini, .rowbtns button { transition: color var(--t-fast), border-color var(--t-fast); }

/* Os badges de disponibilidade não são clicáveis: a transição existe
   para suavizar a troca de estado na filtragem, e o hover é sussurro —
   nada que sugira que dá para clicar. */
.badge-dep { transition: border-color var(--t-med), background-color var(--t-med), opacity var(--t-med); }
.badge-dep--sh:hover,
.badge-dep--saas:hover { border-color: color-mix(in srgb, var(--border) 55%, var(--text-3)); }

/* O título já tem transição de fundo; falta a cor do link auxiliar. */
.etitle { transition: background-color var(--t-fast), color var(--t-fast); }
a.etitle:hover { color: var(--text-1); }

/* Linhas de tabela: realce de leitura, hairline preservada. */
.table-wrap tbody tr td,
.atable tbody tr td { transition: background-color var(--t-fast); }
.table-wrap tbody tr:hover td,
.atable tbody tr:hover td { background: var(--surface); }

/* ------------------------------------------------------------
   8. Densidade e leitura
   ------------------------------------------------------------ */
p, li, .policy, .esummary, .chg-body, .asum, .up-sec p, .pol-body, .sub {
  text-wrap: pretty;
}
.policy, .esummary, .chg-body, .pol-body, .upnote-body {
  hanging-punctuation: first last;
}

/* Números, datas e versões alinhados em coluna em todo lugar que ainda
   não declarava tabular-nums. */
.vchip, .cstrip, .refs, .dflt, .diff-path, .meta-id, .soakline,
.rapplied, .tbadge, .tag, .act-item .ctag, .fmeta, .install .lbl,
.estado-vazio p, .estado-erro p, code, pre, td, th, time {
  font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------
   9. Impressão
   Complementa o bloco de app.css: os estados novos precisam sair em
   papel sem fundo colorido e sem esqueleto.
   ------------------------------------------------------------ */
@media print {
  .sk, .sk-barra, .sk-entrada, .pular-para-conteudo { display: none; }
  .estado-vazio { padding: 1.5rem 0; }
  .estado-erro {
    margin: 1.5rem 0;
    padding-left: 1rem;
    background: none;
    border: 0;
    border-left: 2px solid #999;
    page-break-inside: avoid;
  }
  .estado-erro:has(.estado-erro__icone) { padding-left: 1rem; }
  .estado-erro__icone, .estado-vazio__icone,
  .estado-erro__acoes, .estado-vazio__acoes { display: none; }
  .toast::after { display: none; }
  ::selection { background: none; }
}

/* ------------------------------------------------------------
   10. Foco de teclado
   O link de pular já existe como .skip; a classe .pular-para-conteudo
   é aceita como sinônimo para quem migrar a marcação.
   ------------------------------------------------------------ */
.skip,
.pular-para-conteudo {
  position: absolute;
  left: .75rem;
  top: -4rem;
  z-index: 99;
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  font-size: .875rem;
  font-weight: 510;
  color: var(--text-1);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  padding: .5rem .875rem;
  text-decoration: none;
  transition: top var(--t-med);
}
.skip:hover,
.pular-para-conteudo:hover { text-decoration: none; border-color: var(--accent); }
.skip:focus,
.skip:focus-visible,
.pular-para-conteudo:focus,
.pular-para-conteudo:focus-visible { top: .75rem; }

/* app.css aplica `border-radius: inherit` no :focus-visible global, o que
   achata o raio próprio dos elementos arredondados enquanto focados (uma
   pill vira retângulo). Aqui devolvemos o raio de cada família — mesmos
   tokens já usados no estado de repouso, nenhum valor novo. */
.chip:focus-visible,
.imp:focus-visible,
.tag:focus-visible,
.radios label:has(input:focus-visible) { border-radius: var(--r-pill); }
.vchip:focus-visible,
.linkchip:focus-visible,
.mini:focus-visible,
.toast button:focus-visible,
.skip:focus-visible,
.pular-para-conteudo:focus-visible { border-radius: var(--r-md); }
.seg button:focus-visible { border-radius: 4px; }
.clink:focus-visible,
.chg-anchor:focus-visible,
.rowbtns button:focus-visible { border-radius: var(--r-sm); }

/* Alvos que só aparecem no hover precisam aparecer também no foco de
   teclado — senão o atalho existe e ninguém vê onde está. */
.clink:focus-visible,
.chg-anchor:focus-visible,
.code-wrap .copy:focus-visible { opacity: 1; }

/* ------------------------------------------------------------
   11. Movimento reduzido
   app.css já encurta as durações; aqui desligamos de vez o que só faz
   sentido em movimento. Cancelar o NOME da animação é o que basta — a
   duração !important de app.css continua valendo e não conflita.
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .sk, .sk-barra { animation-name: none; background-image: none; }
  .toast { animation-name: none; }
  .toast::after { display: none; }
}

/* =========================================================
   Diálogo modal — usado pelo admin em ação irreversível.
   Escrito na integração: o repetidor de mudanças passou a
   emitir estas classes depois que o sistema de controles
   já estava fechado.
   ========================================================= */

.dialogo {
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg-raised);
  color: var(--text-1);
  padding: 0;
  max-width: min(28rem, calc(100vw - 2rem));
  /* Overlay é a única exceção à regra de não usar sombra: sem ela o
     diálogo não se descola do conteúdo atrás. */
  box-shadow: var(--shadow-pop);
}
.dialogo::backdrop {
  background: rgb(0 0 0 / 0.45);
  backdrop-filter: blur(2px);
}
.dialogo-corpo { padding: 1.25rem; }
.dialogo-titulo {
  font-size: 1.0625rem;
  font-weight: 590;
  letter-spacing: -0.01em;
  margin-bottom: 0.5rem;
}
.dialogo-texto {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--text-2);
  text-wrap: pretty;
}
.dialogo-acoes {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 1.25rem;
}
/* Em tela estreita os botões empilham na ordem inversa: a ação destrutiva
   fica embaixo, longe do polegar que vinha rolando a página. */
@media (max-width: 30rem) {
  .dialogo-acoes { flex-direction: column-reverse; }
  .dialogo-acoes .btn { width: 100%; }
}

/* Campo compacto e campo em linha. O admin escreve `.campo-inline` e o feed
   escreve `.campo--inline`; aceitar as duas grafias custa uma vírgula e
   evita uma caça a string em dois arquivos. */
.campo--compacto,
.campo.campo--compacto { margin-bottom: 0.625rem; }

.campo-inline,
.campo--inline {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-bottom: 0;
  flex-wrap: wrap;
}
.campo-inline > label,
.campo--inline > label,
.campo-inline > .lbl,
.campo--inline > .lbl { margin-bottom: 0; white-space: nowrap; }

/* A barra "Sua instalação" ganha respiro entre o seletor e o veredito, e o
   veredito passa a ocupar a linha inteira quando não couber ao lado. */
.install__campo { flex: 0 0 auto; }
.install.is-definida .verdict { color: var(--text-1); }
