/* ══════════════════════════════════════════════════════════════════
   MOTOR · engine.css
   Tokens, reset e a moldura visual que toda Devolução Imersiva usa sem
   exceção: trava de rolagem, grão de filme, vinheta. Nada aqui é
   específico de uma tela; o que é específico mora em proposta.html.
   A proposta importa daqui, não duplica. Se faltar um token ou uma
   classe, a correção é aqui, não um arquivo paralelo.
   Validado em produção no protótipo da Ciane Lopes, 11/ago/2026.
═══════════════════════════════════════════════════════════════════ */

:root {
  --roxo:      #4a1c93;
  --roxo-deep: #2d0f5e;
  --menta:     #41c9a6;
  --amarelo:   #f2d34e;
  --vermelho:  #f94141;
  --preto:     #080510;
  --branco:    #f7f5fa;

  --font: 'Quicksand', sans-serif;

  --sp-xs:   8px;
  --sp-sm:  16px;
  --sp-md:  32px;
  --sp-lg:  64px;
  --sp-xl: 120px;

  /* Camadas, declaradas num lugar só pra não brigarem entre si.
     Uma proposta nova NUNCA cria um z-index solto: se precisar de uma
     camada nova, o número entra aqui. */
  --z-proposta:  10;
  --z-vinheta:   11;
  --z-cortina:   20;
  --z-grao:      40;

  /* Perfil de movimento padrão. marca.perfil_movimento troca estes
     dois valores via engine.js antes de qualquer coisa animar, ver
     PERFIS lá. Existem aqui pra nunca ficar sem valor caso o JS falhe
     ao carregar: a página anima no padrão em vez de travar sem CSS. */
  --mov-duracao: 0.6s;
  --mov-easing: cubic-bezier(.22, 1, .36, 1);
}

/* motor/ mora na raiz do projeto, junto de assets/, então o caminho é
   só um nível acima daqui: ../assets/fontes/. */
@font-face {
  font-family: 'Quicksand';
  src: url('../assets/fontes/quicksand.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}

* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  background: var(--preto);
  color: var(--branco);
  font-family: var(--font);
}

/* ── Trava de rolagem ─────────────────────────────────────────────
   Mora SÓ no body, e alterna hidden ↔ visible, nunca `auto`, nunca no
   html. Qualquer overflow diferente de `visible` num ancestral vira
   contêiner de rolagem, e todo `position: sticky` lá dentro passa a
   grudar NELE em vez da janela, ou seja, para de grudar. Foi o que
   aconteceu com `overflow-x: hidden` no html: as telas presas
   deslizaram junto com o resto. Com o overflow só no body, o
   navegador repassa pra janela sem transformar ninguém em contêiner. */
body { overflow: hidden; }
body.liberado { overflow: visible; }

/* Barra de rolagem nativa some, de propósito: a página é uma
   narrativa em telas cheias, não um documento pra navegar por
   posição de scroll. */
html, body { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar, body::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* Exigido pelo Lenis: ele mesmo controla a posição, então a altura não
   pode ficar presa e o scroll-behavior nativo tem que sair da frente,
   senão os dois disputam quem move a página. Só se aplica quando o
   Lenis de fato carregou (a classe html.lenis é dele). */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }

::selection { background: var(--menta); color: var(--preto); }

/* ── Grão vivo ────────────────────────────────────────────────────
   A textura é o feTurbulence do hero real da Famulli. O que faz ela
   parecer viva: o elemento é 3x maior que a tela e desloca pra fora
   dela, então salta em passos grandes (5% a 35%) sem nunca mostrar a
   borda. steps() faz os saltos secos, não deslizados, isso lê como
   grão de filme, não como textura escorregando.
   -115%/-110% (em vez de -100%) cobrem o pior caso de deslocamento do
   keyframe de 35%, que empurra 1,05 tela: com -100% a borda de cima
   entrava na tela por uns instantes e, como o blend clareia, aparecia
   como uma faixa preta no topo da tela. */
.grao {
  position: fixed;
  top: -115%;
  left: -110%;
  width: 300%;
  height: 300%;
  z-index: var(--z-grao);
  pointer-events: none;
  opacity: .20;
  mix-blend-mode: screen;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
  background-size: 180px 180px;
  animation: graoVivo 6s steps(10) infinite;
  will-change: transform;
}
@keyframes graoVivo {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-5%, -10%); }
  20% { transform: translate(-15%, 5%); }
  30% { transform: translate(5%, -25%); }
  40% { transform: translate(-5%, 25%); }
  50% { transform: translate(-15%, 10%); }
  60% { transform: translate(15%, 0); }
  70% { transform: translate(0, 15%); }
  80% { transform: translate(5%, 35%); }
  90% { transform: translate(-10%, 10%); }
}
@media (prefers-reduced-motion: reduce) {
  .grao { animation: none; }
}

/* Maior que a tela de propósito, igual ao grão. Sangrando pra fora dos
   quatro lados (-10vh/-10vw) porque camadas do tamanho exato da tela
   vazam quando a barra de endereço do celular some: a tela cresce
   60-100px embaixo antes desta camada acompanhar, e como o grão já
   cobre e clareia ali mas a vinheta ainda não escurece, sobra um
   retângulo mais claro no rodapé. Sangrando, não existe mais faixa
   descoberta pra revelar. */
.vinheta {
  position: fixed;
  inset: -10vh -10vw;
  z-index: var(--z-vinheta);
  pointer-events: none;
  background: radial-gradient(ellipse at center, transparent 33%, rgba(3, 1, 8, .65) 83%);
}
