/* =========================================================
   NORMA — barra de aviso (Banner)

   Ficheiro próprio, e não mais um bloco em enhancements.css: essa folha
   está em 478 linhas e o limite deste repositório são 500 (CLAUDE.md).
   Acrescentar aqui ~90 linhas passava-o, e um ficheiro que passa o limite
   convida o próximo a passá-lo também.

   PORQUÊ CSS E NÃO CLASSES TAILWIND. O componente de origem é inteiramente
   Tailwind. Aqui o Tailwind entra sem Preflight e as duas camadas que
   importamos (`theme` e `utilities`) ficam ABAIXO das regras sem camada de
   public/css/*.css -- uma utility não ganha uma propriedade que estas
   folhas já definam. Ver DESIGN.md §10. As superfícies da Norma
   desenham-se aqui; o Tailwind fica para o interior dos componentes
   shadcn.
   ========================================================= */

.norma-banner {
  position: relative;

  display: flex;
  align-items: center;
  justify-content: center;

  /* Herda a métrica da .announcement-bar que esta barra passou a
     sustentar: mesmo recheio, mesmo peso, mesmo corpo. A adopção do
     componente não é uma mudança de aspecto -- a página fica igual e ganha
     um botão de fechar. */
  padding: var(--space-sm) var(--space-md);
  font-weight: 500; /* DS: Poppins 400/500 only (25/09/2026) */
  font-size: 1rem;
  line-height: 24px;

  /* Fundo prussian: a superfície escura que o sistema já usa para uma
     banda de afirmação. Branco sobre ele mede 18,55:1. */
  background: var(--color-prussian-blue);
  color: var(--color-white);

  /* Contém o degradê animado, que é `position: absolute`. */
  isolation: isolate;
  overflow: hidden;
}

/* Colada só quando pedida. Fica POR CIMA do cabeçalho, que também é colado
   (z-index: 50 em styles.css). 51 e não 999: o cabeçalho é o único vizinho
   nesta pilha, e um número alto inventado esconde a relação em vez de a
   declarar. */
.norma-banner.is-sticky {
  position: sticky;
  top: 0;
  z-index: 51;
}

.norma-banner-text {
  margin: 0;
  text-align: center;
  /* Por cima do degradê, não por baixo. */
  position: relative;
  z-index: 1;
}

/* Só quando há botão: numa barra estreita o alvo de 44px não pode comer a
   última palavra. Sem botão não há nada de que fugir, e o recuo
   descentrava o texto. */
.norma-banner.is-dismissible .norma-banner-text {
  padding-inline-end: var(--space-2xl);
}

/* Dodger sobre prussian mede 6,82:1 -- é a cor de link que a
   .announcement-bar já usava, e passa. Mantida por isso. */
.norma-banner a {
  color: var(--color-dodger-blue);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ---- O degradê que se move ----

   A MESMA RAMPA DO HERÓI, valor a valor.

   Não "parecida": é a rampa de `ShaderBackground` copiada
   (src/components/ui/shader-background.tsx, UNIFORMS.colors), porque as
   duas superfícies aparecem uma por cima da outra na primeira tela e
   duas aproximações do mesmo azul lêem-se como um erro, não como uma
   família:

     prussian #011428              o fundo
     onyx #0a0a0a                  a profundidade
     dodger a 35% sobre prussian   o brilho     -> rgb(27,69,114)
     cool horizon a 28%            a aresta dele -> rgb(28,62,99)

   As percentagens são as do herói e existem por uma razão que continua a
   valer aqui: dodger puro é uma cor CLARA -- branco sobre ele mede
   2,72:1, abaixo do piso de 4,5:1 -- e num fundo que se move não há como
   prometer que o ponto claro fica longe do texto. Misturar os azuis para
   baixo faz do piso uma propriedade da PALETA: não existe posição
   nenhuma da animação em que o texto possa falhar.

   Medido, os quatro degraus com branco por cima: prussian 18,55:1, onyx
   19,80:1, o brilho 9,82:1, a aresta 10,92:1. O pior caso de todo o
   ciclo é 9,82:1 contra um piso de 4,5:1 -- e o pior caso é a pergunta
   certa a fazer a um fundo animado, não o fotograma que calhou testar.

   O original desta barra trazia magenta rgba(231,77,255), vermelho
   rgba(255,0,0) e verde rgba(131,255,166) -- exactamente a violação pela
   qual o FlowField foi apagado a 6 set 2026. */
.norma-banner-sweep {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;

  /* Os quatro valores do herói. Misturados COM prussian e não com
     `transparent`: assim cada paragem é a cor final exacta que o shader
     produz, em vez de depender do fundo que calhe estar por baixo. */
  --banner-glow: color-mix(in srgb, var(--color-dodger-blue) 35%, var(--color-prussian-blue));
  --banner-edge: color-mix(in srgb, var(--color-cool-horizon) 28%, var(--color-prussian-blue));

  /* As paragens escuras são LARGAS e as azuis estreitas, o que copia a
     distribuição do herói e não só os seus valores. O herói é um campo
     grande, quase todo escuro, com brilho esparso; as mesmas quatro cores
     espalhadas por igual numa faixa de 40px davam uma barra
     visivelmente mais clara do que a banda por baixo dela -- mesma
     paleta, densidade errada, e a olho isso lê-se como cor diferente.
     Medido: com paragens iguais a média da barra era rgb(10,31,54)
     contra rgb(2,13,34) do herói. */
  background-image: repeating-linear-gradient(
    70deg,
    var(--color-prussian-blue) 0%,
    var(--color-onyx) 12%,
    var(--banner-glow) 24%,
    var(--color-onyx) 36%,
    var(--color-prussian-blue) 46%,
    var(--banner-edge) 58%,
    var(--color-prussian-blue) 72%
  );
  background-size: 200% 100%;

  /* Esbatido para baixo, para não desenhar uma aresta dura contra o
     prussian nem competir com o filete do cabeçalho por baixo. O topo a
     0,62 e não a 0,9: deixa o prussian da barra por baixo continuar a
     ser o fundo, com o degradê a passar por cima em vez de o substituir
     -- é o que aproxima a densidade da do herói. */
  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.14));

  animation: norma-banner-sweep 18s linear infinite;
}

@keyframes norma-banner-sweep {
  from { background-position: 0 0; }
  to { background-position: 200% 0; }
}

/* O gémeo de movimento reduzido, e ele ESCREVE O ESTADO FINAL.
   `animation: none` sozinho bastaria aqui porque o degradê já é visível na
   posição inicial -- mas a regra da casa (DESIGN.md §7) é declarar o
   estado visível, não confiar em que o inicial calhe ser o certo. É a
   regra de maior risco do sistema: vários elementos partem de opacity 0. */
@media (prefers-reduced-motion: reduce) {
  .norma-banner-sweep {
    animation: none;
    background-position: 0 0;
  }
}

/* ---- Fechar ---- */

.banner-close {
  /* 44px nos dois eixos. Não é um valor por gosto: é o piso de toque, e é
     por isso que este botão não usa buttonVariants -- as medidas de série
     do shadcn (h-10 = 40px, h-9 = 36px, icon = 40px) ficam todas abaixo. */
  position: absolute;
  inset-inline-end: var(--space-sm);
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;

  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  /* Branco, não branco a 50%. O original punha o X a
     text-fd-muted-foreground/50; por cima de um fundo que se move, meio
     alfa é meio contraste no fotograma mais claro. Aqui o X é a única
     saída da barra e lê-se sempre. */
  color: var(--color-white);
  cursor: pointer;
  transition: background-color 150ms ease;
}

.banner-close .n-icon { --n-icon-size: 18px; }

.banner-close:hover {
  background: color-mix(in srgb, var(--color-white) 18%, transparent);
}

/* O degradê é decorativo e passa por baixo do texto, mas o anel de foco
   tem de ganhar a QUALQUER fotograma dele. Herdado de enhancements.css;
   repetido aqui só para garantir que nada nesta folha o tapa. */
.banner-close:focus-visible {
  outline: 2px solid var(--color-white);
  outline-offset: 2px;
}

/* A troca de cor no hover é feedback, não entrada -- mas a regra da casa
   é que tudo o que anima tem gémeo, e o gémeo escreve o estado final. */
@media (prefers-reduced-motion: reduce) {
  .banner-close { transition: none; }
}

/* ---- O cabeçalho por baixo ----

   As duas coisas são `position: sticky; top: 0`, portanto sem isto
   sobrepunham-se ao rolar. O cabeçalho afasta-se da altura da barra, e o
   scroll-padding dos âncoras soma as duas -- senão um link para #contato
   pára com o título escondido atrás do cabeçalho.

   `:has()` e não uma classe no <body>: quando a barra é dispensada o
   elemento sai do DOM, o seletor deixa de casar, e as duas regras
   desfazem-se sozinhas. Não há estado para limpar nem classe que possa
   ficar para trás. --banner-height é posta no <html> pelo componente e
   removida por ele; o 0px é a defesa para quando não está lá. */
body:has(.norma-banner.is-sticky) .site-header {
  top: var(--banner-height, 0px);
}

html:has(.norma-banner.is-sticky) {
  scroll-padding-top: calc(var(--header-height) + var(--banner-height, 0px));
}
