/*
  ╔════════════════════════════════════════════════════════════════════╗
  ║  CENÁRIO NOTURNO DO iFLUXO — folha reutilizável                   ║
  ║                                                                    ║
  ║  Esta folha, o bloco `.cenario` do HTML e `cenario.js` formam um   ║
  ║  conjunto fechado: não dependem de nada do site institucional e    ║
  ║  podem ser levados para o aplicativo como estão.                   ║
  ║                                                                    ║
  ║  O que ajustar está logo abaixo, em `:root`.                       ║
  ╚════════════════════════════════════════════════════════════════════╝

  Duas regras de desempenho que valem mais que o resto do arquivo:

  1. Nenhum `filter: blur()` em camada animada. Gradiente radial já é
     suave; borrá-lo não muda o desenho e obriga o navegador a
     rasterizar a camada inteira a cada quadro. Aqui isso custava três
     quartos da taxa de quadros.
  2. Só `transform` e `opacity` animam. São as duas propriedades que o
     compositor resolve sem refazer layout nem pintura.
*/

:root {
  /* --------------------------------------------- cores do cenário */
  --cen-ceu-alto: #05040a;
  --cen-ceu-meio: #0a0714;
  --cen-ceu-baixo: #140d26;
  --cen-horizonte: #1b1134;
  --cen-roxo: rgba(124, 58, 237, 0.34);
  --cen-magenta: rgba(236, 72, 153, 0.16);

  /* ------------------------------------------ ajuste das animações */
  /* Respiro do halo e da luz do horizonte. */
  --cen-respiro: 11s;
  /* Travessia das duas névoas. */
  --cen-nevoa-a: 44s;
  --cen-nevoa-b: 67s;
  /* Altura do horizonte. */
  --cen-altura: min(52vh, 460px);

  /* Deslocamento do parallax, em pixels. O JavaScript escreve aqui e o
     CSS só lê — assim o script não disputa `transform` com as
     animações. */
  --px: 0;
  --py: 0;
}

.cenario {
  position: fixed;
  inset: 0;
  /*
    Negativo, e o conteúdo sem `z-index` nenhum. Pôr o cenário em 0 e o
    conteúdo em 1 seria o caminho óbvio — e criaria contexto de
    empilhamento no conteúdo, que isola qualquer `mix-blend-mode` usado
    acima. Esta página não usa mistura, mas quem reaproveitar o cenário
    pode usar.
  */
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
}

/* ------------------------------------------------------------- céu */

.ceu {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    var(--cen-ceu-alto) 0%,
    var(--cen-ceu-meio) 42%,
    var(--cen-ceu-baixo) 78%,
    var(--cen-horizonte) 100%
  );
}

/* A luz que a cidade joga no céu. Separada do degradê para poder
   variar de intensidade sem repintar o fundo inteiro. */
.luz-horizonte {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 70% at 50% 108%, var(--cen-roxo), transparent 62%),
    radial-gradient(90% 55% at 78% 92%, var(--cen-magenta), transparent 66%);
  animation: cen-pulsar calc(var(--cen-respiro) * 1.7) ease-in-out infinite;
}

@keyframes cen-pulsar {
  0%,
  100% {
    opacity: 0.82;
  }
  50% {
    opacity: 1;
  }
}

/* O halo alto, atrás de onde a marca costuma ficar. */
.halo {
  position: absolute;
  top: 34%;
  left: 50%;
  width: min(120vmin, 1040px);
  aspect-ratio: 1;
  translate: calc(-50% + var(--px) * 1px) calc(-50% + var(--py) * 1px);
  will-change: transform, opacity;
  background: radial-gradient(
    circle,
    rgba(139, 92, 246, 0.26) 0%,
    rgba(124, 58, 237, 0.1) 38%,
    transparent 64%
  );
  animation: cen-respirar var(--cen-respiro) ease-in-out infinite;
}

@keyframes cen-respirar {
  0%,
  100% {
    opacity: 0.8;
    scale: 1;
  }
  50% {
    opacity: 1;
    scale: 1.05;
  }
}

/* ------------------------------------------------------- horizonte */

.horizonte {
  position: absolute;
  inset: auto 0 0;
  width: 100%;
  height: var(--cen-altura);
  display: block;
}

/* Cada camada anda um tanto diferente: é isso, e não o desenho, que
   produz a profundidade. */
.camada {
  transform: translate3d(calc(var(--px) * var(--fator, 0) * 1px), 0, 0);
  /* Cada camada vira uma superfície própria do compositor: o
     deslocamento do parallax passa a ser recomposição, não repintura, e
     o resto da página não é redesenhado junto a cada movimento do
     cursor. */
  will-change: transform;
}

.camada-serra {
  --fator: 0.3;
}
.camada-longe {
  --fator: 0.6;
  opacity: 0.95;
}
.camada-perto {
  --fator: 1;
}

.antenas line {
  stroke: rgba(167, 139, 250, 0.22);
  stroke-width: 1.2;
}

/*
  Cada janela tem período, atraso, piso e teto próprios, escritos no
  atributo `style` de cada retângulo. É o que impede o padrão de ficar
  perceptível: com três ou quatro variantes de classe, o olho acha o
  ritmo em poucos segundos.

  Atraso negativo faz a cidade já nascer acesa, em vez de ligar junto
  quando a página abre.
*/
/*
  Só parte das janelas varia. Numa cidade vista de longe a maioria fica
  acesa e parada, e é a minoria que muda que dá vida — animar todas as
  111 custava um terço da taxa de quadros para um efeito que ninguém
  distingue.
*/
.janelas rect.viva {
  animation: cen-janela var(--t, 9s) ease-in-out var(--d, 0s) infinite;
}

@keyframes cen-janela {
  0%,
  100% {
    opacity: var(--b, 0.3);
  }
  50% {
    opacity: var(--p, 0.9);
  }
}

/* --------------------------------------------------------- névoa */

.nevoa {
  position: absolute;
  inset: auto -20% 0;
  height: min(46vh, 420px);
  background: radial-gradient(
    60% 100% at 50% 100%,
    rgba(124, 58, 237, 0.16),
    transparent 70%
  );
}

.nevoa-a {
  animation: cen-arrastar var(--cen-nevoa-a) linear infinite;
}

.nevoa-b {
  opacity: 0.6;
  background: radial-gradient(
    55% 100% at 40% 100%,
    rgba(236, 72, 153, 0.12),
    transparent 72%
  );
  animation: cen-arrastar var(--cen-nevoa-b) linear infinite reverse;
}

@keyframes cen-arrastar {
  from {
    transform: translate3d(-8%, 0, 0);
  }
  to {
    transform: translate3d(8%, 0, 0);
  }
}

/* Campo de estrelas, partículas e estrelas cadentes. */
.ambiente {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Escurece as bordas, para que o centro seja o único lugar claro. */
.vinheta {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    120% 85% at 50% 45%,
    transparent 42%,
    rgba(5, 4, 10, 0.5) 78%,
    rgba(5, 4, 10, 0.9) 100%
  );
}

/* ---------------------------------------------------- adaptações */

/*
  Em tela de toque não há cursor, e a profundidade não pode depender
  dele. As camadas ganham uma deriva própria, muito lenta — longa o
  bastante para ninguém ver o laço se repetir.
*/
@media (pointer: coarse) {
  .camada {
    animation: cen-derivar calc(70s / max(var(--fator), 0.3)) ease-in-out infinite alternate;
  }
}

@keyframes cen-derivar {
  from {
    transform: translate3d(-6px, 0, 0);
  }
  to {
    transform: translate3d(6px, 0, 0);
  }
}

/* Numa tela estreita o recorte amplia os prédios até virarem blocos;
   menos altura devolve a escala, e a cidade volta a parecer distante. */
@media (max-width: 560px) {
  :root {
    --cen-altura: 32vh;
  }
  .horizonte {
    opacity: 0.88;
  }
}

@media (max-height: 560px) and (orientation: landscape) {
  :root {
    --cen-altura: 44vh;
  }
}

@media (min-aspect-ratio: 2.1/1) {
  :root {
    --cen-altura: min(62vh, 520px);
  }
  .halo {
    width: min(84vmin, 1040px);
  }
}

/*
  Movimento reduzido: a cidade continua lá, iluminada e bonita, mas
  parada. Nada pisca, nada arrasta, nada atravessa o céu — e o
  JavaScript lê a mesma preferência e não desenha partícula nenhuma.
*/
@media (prefers-reduced-motion: reduce) {
  .luz-horizonte,
  .halo,
  .janelas rect.viva,
  .nevoa,
  .camada {
    animation: none !important;
  }
  .halo,
  .camada {
    translate: none;
    transform: none;
  }
  .janelas rect.viva {
    opacity: var(--p, 0.8);
  }
}

@media (forced-colors: active) {
  .cenario {
    display: none;
  }
}
