/* ====================================================
   SigX FX  |  sigx-fx.css  (+ libs/sigx-fx.js)
   Animações portadas do Magic UI (github.com/magicuidesign/magicui),
   que é React + Tailwind + motion. Aqui é CSS puro + um JS pequeno,
   sem dependência, com as cores da marca (Signal Orange) no lugar
   do roxo/rosa padrão de lá.

   Catálogo (equivalente no Magic UI → classe aqui) — ver docs/animacoes.md:
     Blur Fade            → .fx-blur-fade / [data-fx-blur-fade]
     Number Ticker        → [data-fx-ticker]                        (JS)
     Magic Card           → .fx-magic-card                          (JS: posição do mouse; só borda)
     Border Beam          → .fx-border-beam                         (JS: injeta o feixe)
     Shine Border         → .fx-shine-border
     Shimmer Button       → .fx-shimmer
     Pulsating Button     → .fx-pulse
     Rainbow Button       → .fx-rainbow
     Animated Shiny Text  → .fx-shiny-text
     Animated Gradient Text / Aurora Text → .fx-gradient-text
     Marquee              → .fx-marquee > .fx-marquee-track
     Ripple               → .fx-ripple                              (JS: injeta os círculos)
     Dot Pattern          → .fx-dot-pattern
     Grid Pattern         → .fx-grid-pattern
     Meteors              → .fx-meteors                             (JS: injeta os meteoros)
     Animated List        → .fx-list-in
     Confetti             → SigxFx.confetti()                       (JS: canvas-confetti sob demanda)

   Tudo respeita prefers-reduced-motion: quem pede menos movimento vê o
   estado final, sem animação.
   ==================================================== */

:root {
  --fx-from: #FF9050;                       /* --sigx-orange-lt */
  --fx-to:   #FF5100;                       /* --sigx-orange    */
  --fx-deep: #8B1A00;                       /* --sigx-orange-dk */
  --fx-ease-out: cubic-bezier(.22, 1, .36, 1);
}

/* ---------------------------------------------------------------
   Blur Fade — entrada com desfoque + leve deslocamento.
   Magic UI: duration .4s, offset 6px, blur 6px, delay .04s * índice.
   O JS (SigxFx.blurFade) aplica nos cards da página com o índice
   em --fx-i; à mão: <div class="fx-blur-fade" style="--fx-i:2">.
   --------------------------------------------------------------- */
.fx-blur-fade {
  animation: fx-blur-fade var(--fx-duration, .45s) var(--fx-ease-out) both;
  animation-delay: calc(var(--fx-delay, 0s) + var(--fx-i, 0) * var(--fx-stagger, .05s));
}
@keyframes fx-blur-fade {
  from { opacity: 0; filter: blur(var(--fx-blur, 6px)); transform: translateY(var(--fx-offset, 8px)); }
  to   { opacity: 1; filter: none;                      transform: none; }
}

/* Entrada da página inteira (portal autenticado). O <html> recebe .fx-entrada por um
   script inline no <head> do Application.cshtml — antes do primeiro paint, então não
   há "pisca" — e perde a classe ~1,6 s depois. Tirar a classe importa: animação com
   fill em transform/filter cria stacking context e prenderia dropdowns e popovers de
   um card embaixo do card seguinte.
   Não roda em página pré-renderizada (speculation rules): ela já chega pronta e a
   view transition do sigx-brand.css faz a troca. */
.fx-entrada .page-header,
.fx-entrada .page-body > .container-xl > :not(.modal):not(.offcanvas):not(script):not(style):not(.sticky-top) {
  animation: fx-blur-fade .45s var(--fx-ease-out) both;
}
.fx-entrada .page-body > .container-xl > :nth-child(2) { animation-delay: .05s; }
.fx-entrada .page-body > .container-xl > :nth-child(3) { animation-delay: .10s; }
.fx-entrada .page-body > .container-xl > :nth-child(4) { animation-delay: .15s; }
.fx-entrada .page-body > .container-xl > :nth-child(5) { animation-delay: .20s; }
.fx-entrada .page-body > .container-xl > :nth-child(n+6) { animation-delay: .25s; }
/* Linhas de cards (.row > .col): cada coluna entra um pouco depois da anterior. */
.fx-entrada .page-body > .container-xl > .row > :nth-child(2) { animation: fx-blur-fade .45s .06s var(--fx-ease-out) both; }
.fx-entrada .page-body > .container-xl > .row > :nth-child(3) { animation: fx-blur-fade .45s .12s var(--fx-ease-out) both; }
.fx-entrada .page-body > .container-xl > .row > :nth-child(4) { animation: fx-blur-fade .45s .18s var(--fx-ease-out) both; }
.fx-entrada .page-body > .container-xl > .row > :nth-child(n+5) { animation: fx-blur-fade .45s .24s var(--fx-ease-out) both; }
/* Item que entra só ao rolar até ele (IntersectionObserver no JS). */
.fx-blur-fade-wait { opacity: 0; }

/* ---------------------------------------------------------------
   Number Ticker — o número conta até o valor (spring no Magic UI,
   easeOutExpo aqui). Só a classe de "assentou" fica no CSS.
   --------------------------------------------------------------- */
[data-fx-ticker] { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------------
   Magic Card — holofote sutil que segue o mouse só na borda.
   O JS só escreve --fx-mx / --fx-my (px) no card sob o cursor.
   .fx-magic-card num card, ou .fx-magic-cards num container (vale
   para todo .card dentro dele — é como o grid do dashboard usa).
   z-index alto porque mapas Leaflet dentro do card empilham a 400+;
   pointer-events:none deixa tudo clicável por baixo.
   --------------------------------------------------------------- */
.fx-magic-card,
.fx-magic-cards .card {
  --fx-mx: -9999px;
  --fx-my: -9999px;
  --fx-size: 220px;
  position: relative;
  isolation: isolate;
}
/* Só o anel de 1px recebe o gradiente (máscara content-box x border-box). */
:is(.fx-magic-card, .fx-magic-cards .card)::before {
  content: "";
  position: absolute;
  inset: 0;                                  /* dentro do card: os cards do dashboard têm overflow:hidden */
  z-index: 1002;
  padding: 1px;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .3s ease;
  background: radial-gradient(var(--fx-size) circle at var(--fx-mx) var(--fx-my),
              var(--fx-to), transparent 70%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
:is(.fx-magic-card, .fx-magic-cards .card).fx-hover::before { opacity: .45; }

/* ---------------------------------------------------------------
   Border Beam — um feixe de luz percorrendo a borda.
   Magic UI: size 50, duration 6s, offset-path rect(0 auto auto 0 round R).
   O JS injeta <span class="fx-beam"> no elemento .fx-border-beam.
   Variáveis: --fx-beam-size, --fx-beam-duration, --fx-beam-delay.
   --------------------------------------------------------------- */
.fx-border-beam { position: relative; }
/* offset-path: rect() é recente (Chrome 116+, Firefox 122+, Safari 17.2+). Sem ele o
   feixe ficaria parado no canto superior esquerdo — então simplesmente não aparece. */
@supports not (offset-path: rect(0 auto auto 0 round 1px)) {
  .fx-beam { display: none; }
}
.fx-beam {
  position: absolute;
  inset: 0;
  border: var(--fx-beam-width, 1.5px) solid transparent;
  border-radius: inherit;
  pointer-events: none;
  z-index: 3;
  /* Mostra só a faixa da borda: o padding-box fica transparente. */
  -webkit-mask: linear-gradient(transparent, transparent) padding-box, linear-gradient(#000, #000) border-box;
  -webkit-mask-composite: source-in;
          mask: linear-gradient(transparent, transparent) padding-box, linear-gradient(#000, #000) border-box;
          mask-clip: padding-box, border-box;
          mask-composite: intersect;
}
.fx-beam::after {
  content: "";
  position: absolute;
  width: var(--fx-beam-size, 80px);
  aspect-ratio: 1;
  background: linear-gradient(to left, var(--fx-to), var(--fx-from), transparent);
  offset-path: rect(0 auto auto 0 round var(--fx-beam-size, 80px));
  offset-anchor: 90% 50%;
  animation: fx-beam var(--fx-beam-duration, 7s) linear infinite;
  animation-delay: var(--fx-beam-delay, 0s);
}
.fx-beam.fx-beam-2::after {
  background: linear-gradient(to left, var(--fx-deep), var(--fx-to), transparent);
  animation-delay: calc(var(--fx-beam-delay, 0s) - var(--fx-beam-duration, 7s) / 2);
}
@keyframes fx-beam {
  from { offset-distance: 0%; }
  to   { offset-distance: 100%; }
}

/* ---------------------------------------------------------------
   Shine Border — borda com brilho radial que passeia em volta.
   Magic UI: borderWidth 1, duration 14s, background-size 300%.
   --------------------------------------------------------------- */
.fx-shine-border { position: relative; }
.fx-shine-border::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: var(--fx-shine-width, 1.5px);
  border-radius: inherit;
  pointer-events: none;
  background-image: radial-gradient(transparent, transparent, var(--fx-from), var(--fx-to), var(--fx-deep), transparent, transparent);
  background-size: 300% 300%;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
  animation: fx-shine var(--fx-shine-duration, 14s) infinite linear;
}
@keyframes fx-shine {
  0%   { background-position: 0% 0%; }
  50%  { background-position: 100% 100%; }
  100% { background-position: 0% 0%; }
}

/* ---------------------------------------------------------------
   Shimmer Button — um reflexo atravessa o botão de tempos em tempos.
   (Magic UI: shimmer-slide + spin-around; aqui uma faixa diagonal,
   que funciona em qualquer .btn sem mudar o markup.)
   --------------------------------------------------------------- */
.fx-shimmer {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.fx-shimmer::after {
  content: "";
  position: absolute;
  inset: -2px auto -2px 0;
  width: 45%;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 0%, rgba(255, 255, 255, .45) 50%, transparent 100%);
  transform: translateX(-160%) skewX(-18deg);
  animation: fx-shimmer var(--fx-shimmer-duration, 3.2s) var(--fx-shimmer-delay, 1.2s) ease-in-out infinite;
}
/* z-index:-1 + isolation põe o reflexo atrás do texto/ícone, mas acima do fundo do botão. */
@keyframes fx-shimmer {
  0%       { transform: translateX(-160%) skewX(-18deg); }
  45%, 100% { transform: translateX(360%) skewX(-18deg); }
}
.fx-shimmer:hover::after,
.fx-shimmer:focus-visible::after { animation-play-state: paused; }

/* ---------------------------------------------------------------
   Pulsating Button — anel que pulsa em volta do botão.
   Magic UI: pulseColor, duration 1.5s, 0 → 8px.
   --------------------------------------------------------------- */
.fx-pulse {
  --fx-pulse-color: rgba(var(--sigx-orange-rgb, 255, 81, 0), .45);
  animation: fx-pulse var(--fx-pulse-duration, 2s) ease-out infinite;
}
.fx-pulse:hover,
.fx-pulse:focus-visible { animation-play-state: paused; }
@keyframes fx-pulse {
  0%   { box-shadow: 0 0 0 0 var(--fx-pulse-color); }
  70%  { box-shadow: 0 0 0 10px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}

/* ---------------------------------------------------------------
   Rainbow Button — gradiente que corre por baixo do botão.
   Magic UI: 5 cores HSL, 2s, background-position 0 → 200%.
   Aqui a "rainbow" é a escala da marca.
   --------------------------------------------------------------- */
.fx-rainbow {
  position: relative;
  border: 2px solid transparent !important;
  background:
    linear-gradient(var(--fx-rainbow-bg, #121213), var(--fx-rainbow-bg, #121213)) padding-box,
    linear-gradient(90deg, var(--fx-deep), var(--fx-to), var(--fx-from), #FFC48A, var(--fx-from), var(--fx-to), var(--fx-deep)) border-box !important;
  background-size: 100% 100%, 200% 100% !important;
  color: #fff !important;
  animation: fx-rainbow var(--fx-rainbow-duration, 3s) linear infinite;
}
.fx-rainbow::before {
  content: "";
  position: absolute;
  inset: auto 15% -18% 15%;
  height: 30%;
  z-index: -1;
  background: linear-gradient(90deg, var(--fx-deep), var(--fx-to), var(--fx-from), #FFC48A, var(--fx-from), var(--fx-to), var(--fx-deep));
  background-size: 200% 100%;
  filter: blur(.75rem);
  animation: fx-rainbow var(--fx-rainbow-duration, 3s) linear infinite;
}
[data-bs-theme="light"] .fx-rainbow { --fx-rainbow-bg: #18181b; }
@keyframes fx-rainbow {
  from { background-position: 0 0, 0% 0; }
  to   { background-position: 0 0, 200% 0; }
}

/* ---------------------------------------------------------------
   Animated Shiny Text — um brilho passa por cima do texto.
   Magic UI: shimmerWidth 100px, 8s, keyframes 0/30/60/90/100%.
   --------------------------------------------------------------- */
/* A cor do texto é semitransparente de propósito: é por ela que o brilho
   (recortado no texto) aparece. --fx-shiny-base / --fx-shiny-hi trocam as cores. */
.fx-shiny-text {
  --fx-shiny-width: 100px;
  color: var(--fx-shiny-base, rgba(var(--tblr-body-color-rgb, 24, 36, 51), .62));
  background: linear-gradient(to right, transparent, var(--fx-shiny-hi, rgba(var(--tblr-body-color-rgb, 24, 36, 51), .95)) 50%, transparent)
              no-repeat 0 0 / var(--fx-shiny-width) 100%;
  -webkit-background-clip: text;
          background-clip: text;
  animation: fx-shiny-text var(--fx-shiny-duration, 8s) infinite;
}
/* Variante na cor da marca (subheaders/badges laranja). */
.fx-shiny-text.fx-shiny-brand {
  --fx-shiny-base: rgba(var(--sigx-orange-rgb, 255, 81, 0), .78);
  --fx-shiny-hi: #FFC48A;
}
@keyframes fx-shiny-text {
  0%, 90%, 100% { background-position: calc(-100% - var(--fx-shiny-width)) 0; }
  30%, 60%      { background-position: calc(100% + var(--fx-shiny-width)) 0; }
}

/* ---------------------------------------------------------------
   Animated Gradient Text / Aurora Text — gradiente correndo dentro do texto.
   Magic UI: background-size 300%, 8s, background-position → 300% 0.
   --------------------------------------------------------------- */
.fx-gradient-text {
  background: linear-gradient(90deg, var(--fx-to), var(--fx-from), #FFC48A, var(--fx-from), var(--fx-to), var(--fx-deep), var(--fx-to));
  background-size: 300% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: fx-gradient-text var(--fx-gradient-duration, 8s) linear infinite;
}
@keyframes fx-gradient-text {
  to { background-position: 300% 0; }
}

/* ---------------------------------------------------------------
   Marquee — faixa rolando infinitamente.
   Magic UI: --duration 40s, --gap 1rem, repeat 4, pauseOnHover.
   <div class="fx-marquee"><div class="fx-marquee-track">…</div></div>
   O JS duplica o conteúdo da trilha para o laço não ter emenda.
   --------------------------------------------------------------- */
.fx-marquee {
  --fx-gap: 1rem;
  display: flex;
  overflow: hidden;
  gap: var(--fx-gap);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.fx-marquee-track {
  display: flex;
  flex-shrink: 0;
  justify-content: space-around;
  gap: var(--fx-gap);
  min-width: 100%;
  animation: fx-marquee var(--fx-marquee-duration, 40s) linear infinite;
}
.fx-marquee.fx-reverse .fx-marquee-track { animation-direction: reverse; }
.fx-marquee:hover .fx-marquee-track { animation-play-state: paused; }
@keyframes fx-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - var(--fx-gap))); }
}

/* ---------------------------------------------------------------
   Ripple — círculos concêntricos "respirando" atrás do conteúdo.
   Magic UI: mainCircleSize 210, opacity .24, numCircles 8, +70px cada,
   delay .06s * i, keyframes scale 1 → .9 → 1 em 2s.
   --------------------------------------------------------------- */
.fx-ripple {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, #fff, transparent);
          mask-image: linear-gradient(to bottom, #fff, transparent);
}
.fx-ripple > span {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(var(--fx-ripple-base, 210px) + var(--fx-i) * 70px);
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgba(var(--sigx-orange-rgb, 255, 81, 0), calc(.10 + var(--fx-i) * .03));
  background: rgba(var(--sigx-orange-rgb, 255, 81, 0), calc(.06 - var(--fx-i) * .006));
  opacity: calc(.6 - var(--fx-i) * .06);
  transform: translate(-50%, -50%) scale(1);
  animation: fx-ripple 2.2s ease infinite;
  animation-delay: calc(var(--fx-i) * .06s);
}
@keyframes fx-ripple {
  0%, 100% { transform: translate(-50%, -50%) scale(1); }
  50%      { transform: translate(-50%, -50%) scale(.9); }
}

/* ---------------------------------------------------------------
   Dot Pattern / Grid Pattern — fundos estáticos (SVG do Magic UI
   reescrito como gradiente, sem markup extra). Combine com
   .fx-pattern-fade para esmaecer nas bordas.
   --------------------------------------------------------------- */
.fx-dot-pattern {
  background-image: radial-gradient(rgba(var(--tblr-body-color-rgb, 24, 36, 51), .16) 1px, transparent 1px);
  background-size: var(--fx-pattern-size, 16px) var(--fx-pattern-size, 16px);
}
.fx-grid-pattern {
  background-image:
    linear-gradient(to right, rgba(var(--tblr-body-color-rgb, 24, 36, 51), .07) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(var(--tblr-body-color-rgb, 24, 36, 51), .07) 1px, transparent 1px);
  background-size: var(--fx-pattern-size, 40px) var(--fx-pattern-size, 40px);
}
.fx-pattern-fade {
  -webkit-mask-image: radial-gradient(ellipse at center, #000 30%, transparent 75%);
          mask-image: radial-gradient(ellipse at center, #000 30%, transparent 75%);
}

/* ---------------------------------------------------------------
   Meteors — riscos de luz caindo na diagonal.
   Magic UI: number 20, angle 215deg, duration 2–10s, delay .2–1.2s,
   translateX(-500px) com fade no fim.
   --------------------------------------------------------------- */
.fx-meteors {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.fx-meteors > span {
  position: absolute;
  top: -5%;
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: var(--fx-from);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .08);
  transform: rotate(215deg);
  animation: fx-meteor linear infinite;
}
.fx-meteors > span::before {
  content: "";
  position: absolute;
  top: 50%;
  width: 60px;
  height: 1px;
  transform: translateY(-50%);
  background: linear-gradient(90deg, var(--fx-to), transparent);
}
@keyframes fx-meteor {
  0%   { transform: rotate(215deg) translateX(0);      opacity: 1; }
  70%  { opacity: 1; }
  100% { transform: rotate(215deg) translateX(-600px); opacity: 0; }
}

/* ---------------------------------------------------------------
   Animated List — itens novos entram com escala + mola.
   Magic UI: initial scale 0 / opacity 0, spring stiffness 350 damping 40.
   Use em itens inseridos dinamicamente (SigxFx.listIn(el)).
   --------------------------------------------------------------- */
.fx-list-in {
  transform-origin: top center;
  animation: fx-list-in .5s cubic-bezier(.34, 1.36, .64, 1) both;
  animation-delay: calc(var(--fx-i, 0) * .06s);
}
@keyframes fx-list-in {
  from { opacity: 0; transform: scale(.85) translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ---------------------------------------------------------------
   Movimento reduzido: estado final, sem animação.
   --------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .fx-entrada .page-header,
  .fx-entrada .page-body > .container-xl > *,
  .fx-entrada .page-body > .container-xl > .row > *,
  .fx-blur-fade,
  .fx-list-in,
  .fx-beam::after,
  .fx-shine-border::after,
  .fx-shimmer::after,
  .fx-pulse,
  .fx-rainbow,
  .fx-rainbow::before,
  .fx-shiny-text,
  .fx-gradient-text,
  .fx-marquee-track,
  .fx-ripple > span,
  .fx-meteors > span {
    animation: none !important;
  }
  .fx-blur-fade-wait { opacity: 1; }
  .fx-beam,
  .fx-meteors,
  .fx-shimmer::after { display: none; }
}

/* Impressão: nada de brilho nem holofote no papel. */
@media print {
  .fx-beam, .fx-meteors, .fx-ripple,
  .fx-magic-card::before, .fx-magic-cards .card::before,
  .fx-shine-border::after, .fx-shimmer::after { display: none !important; }
}
