/* head embebido no site dos cards — escoped, sem tocar no body/html dos cards */
#np-react-head { font-family: var(--font-display), system-ui, -apple-system, sans-serif; color: var(--np-text); -webkit-font-smoothing: antialiased; }
#np-react-head ::selection { background: var(--np-accent); color: var(--np-void); }

/* ═══════════════════════════════════════════════════════════════
   NÚCLEO PARCEIRO — HEADER
   As cores vêm todas de lib/tokens.js, injetadas como custom
   properties no <html>. Não escreva hex neste arquivo.
   ═══════════════════════════════════════════════════════════════ */

*,
*::before,
*::after {
  box-sizing: border-box;
}


/* ── Estrutura do hero ────────────────────────────────────────
   Altura de 220vh: é a pista de scroll que o travelling da
   câmera precisa. O conteúdo visível fica num bloco sticky, então
   a cena permanece parada na tela enquanto o scroll avança — o
   movimento todo acontece DENTRO da cena, não na página.        */

.np-hero {
  position: relative;
  /* Pista de scroll para os DOIS atos: a queda em espiral até o
     núcleo e a revelação do outro lado. Curta demais e a queda vira
     um tranco; longa demais e o usuário rola no vazio esperando algo
     acontecer. 320vh dá ~2 telas de queda e ~1 de desfecho. */
  height: 320vh;
}

.np-hero__viewport {
  position: sticky;
  top: 0;
  height: 100vh;
  width: 100%;
  overflow: hidden;
  /* Cria contexto de empilhamento próprio: sem isso o canvas
     WebGPU e o overlay disputam camada de composição no Safari. */
  isolation: isolate;
}

.np-hero__scene,
.np-hero__scene canvas {
  position: absolute;
  inset: 0;
  width: 100% !important;
  height: 100% !important;
  display: block;
}

.np-canvas {
  position: absolute;
  inset: 0;
}

/* Enquanto a cena não pintou o primeiro frame, um gradiente
   estático segura o lugar. Evita o flash branco — que num site
   sobre primeira impressão seria irônico demais. */
.np-hero__scene::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 80% at 50% 62%, var(--np-deep) 0%, var(--np-void) 68%);
  opacity: 1;
  transition: opacity 900ms ease-out;
  pointer-events: none;
  z-index: -1;
}

.np-hero[data-ready='true'] .np-hero__scene::before {
  opacity: 0;
}

/* ── Scrim ────────────────────────────────────────────────────
   Escurece topo e base. Garante contraste AA para a UI mesmo se
   a névoa passar clara por baixo — contraste que depende de um
   shader é contraste que uma hora falha.                        */

.np-hero__scrim {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    /* Elipse larga atrás da frase. A faixa da galáxia passa
       exatamente por trás da segunda linha, e o contraste do texto
       não pode depender do que o shader está fazendo naquele frame.
       Ampla e fraca o bastante para ler como sombra da própria
       nébula, não como um retângulo escuro por baixo do texto. */
    radial-gradient(72% 40% at 50% 44%, rgb(1 2 10 / 0.5) 0%, rgb(1 2 10 / 0) 74%),
    linear-gradient(to bottom, rgb(1 2 10 / 0.72) 0%, rgb(1 2 10 / 0) 22%),
    linear-gradient(to top, rgb(1 2 10 / 0.68) 0%, rgb(1 2 10 / 0) 30%);
  z-index: 1;
}

.np-hero__ui {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: clamp(1.25rem, 3.2vw, 2.75rem);
  /* O overlay não intercepta o ponteiro: a gravidade do cursor
     precisa funcionar em 100% da tela, inclusive sobre a frase. */
  pointer-events: none;
}

.np-hero__ui > * {
  pointer-events: auto;
}

/* ── Marca ────────────────────────────────────────────────────── */

.np-brand {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  color: var(--np-text);
  text-decoration: none;
  transition: opacity 300ms ease;
  /* `backwards`, não `forwards`.
     Uma animação com fill `forwards` continua "possuindo" a
     propriedade depois de terminar, e no cascade ela ganha até de
     estilo inline — o JS do scroll escreveria opacity e nada
     aconteceria. Com `backwards` o estado inicial vale só ANTES do
     delay; terminada a entrada, a propriedade volta ao elemento. */
  animation: np-fade-down 1s cubic-bezier(0.22, 1, 0.36, 1) 260ms backwards;
}

.np-brand__logo {
  width: clamp(150px, 15vw, 210px);
  height: auto;
  display: block;
}

/* ── A frase ──────────────────────────────────────────────────── */

.np-copy {
  align-self: center;
  justify-self: center;
  text-align: center;
  max-width: min(92vw, 1180px);
  will-change: transform, opacity;
}

.np-headline {
  margin: 0;
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 400;
  /* clamp com vw: a frase mantém a MESMA proporção com a tela em
     qualquer largura. Breakpoints fariam a composição "pular". */
  font-size: clamp(2.35rem, 7.6vw, 7.4rem);
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--np-text);
}

.np-line {
  display: block;
}

/* Sem `font-style: italic`: a GROCKY tem um estilo só, e pedir
   itálico faria o navegador INCLINAR os glifos sinteticamente. Falso
   itálico numa geométrica de marca é visível na hora. A segunda linha
   se distingue pelo degradê, que é diferença de cor e não de desenho. */
.np-line--accent {
  background: linear-gradient(
    92deg,
    var(--np-text) 0%,
    var(--np-accent) 52%,
    var(--np-text) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.np-word {
  display: inline-block;
  white-space: nowrap;
}

/* ── Entrada por caractere ────────────────────────────────────
   Três números aqui já foram três vezes maiores, e o conjunto fazia
   a frase parecer QUEBRADA durante o carregamento:

   • blur 10px num corpo de ~100px borra cada glifo por mais da
     metade da largura do vizinho. Dois caracteres adjacentes no meio
     da animação viravam uma mancha só — que é exatamente a aparência
     de "letras empilhadas". 3px dá a mesma sensação de foco chegando
     sem que um glifo invada o outro.

   • rotateX(-52deg) sob `perspective` deslocava os caracteres
     horizontalmente conforme giravam, e somado ao borrão parecia
     desalinhamento. Só translação vertical resolve.

   • 24ms × 44 caracteres = mais de um segundo só de stagger, e a
     frase inteira levava ~2,8s para assentar. Tempo demais parecendo
     defeito.                                                       */

.np-char,
.np-space {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.5em, 0);
  filter: blur(3px);
}

/* A animação só existe DEPOIS de a cena estar pronta.
   Antes disso a frase rodava sozinha sobre uma tela preta enquanto o
   motor 3D ainda carregava — a atenção inteira num texto pela metade,
   sem nada em volta para explicá-lo. Agora frase e galáxia nascem
   juntas, que é o que a composição sempre quis dizer. */
.np-hero[data-ready='true'] .np-char,
.np-hero[data-ready='true'] .np-space {
  animation: np-char-in 0.85s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: calc(var(--char-index, 0) * 14ms + 120ms);
}

.np-space {
  filter: none;
}

/* ── Indicador de scroll ──────────────────────────────────────── */

.np-hint {
  justify-self: center;
  align-self: end;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.85rem;
  transition: opacity 300ms ease;
  /* Ver a nota em .np-brand: `backwards` para que o scroll possa
     apagar a dica depois que ela entrou. */
  animation: np-fade-up 1.1s cubic-bezier(0.22, 1, 0.36, 1) 1900ms backwards;
}

.np-hint__label {
  font-size: 0.66rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--np-text-muted);
}

.np-hint__rail {
  position: relative;
  display: block;
  width: 1px;
  height: 58px;
  background: linear-gradient(
    to bottom,
    rgb(255 255 255 / 0.28),
    rgb(255 255 255 / 0.02)
  );
  overflow: hidden;
}

.np-hint__spark {
  position: absolute;
  inset-inline: -1px;
  top: -30%;
  height: 30%;
  background: linear-gradient(to bottom, transparent, var(--np-accent));
  animation: np-spark 2.4s cubic-bezier(0.65, 0, 0.35, 1) infinite;
}

/* ── A travessia ──────────────────────────────────────────────
   Duas camadas empilhadas sobre a cena. Ambas com opacity
   controlada por JS a cada frame; nada de transition aqui — a
   posição no scroll é a única fonte de verdade, e uma transição
   CSS competiria com ela e criaria atraso.                      */

/* O PRETO FICA POR BAIXO DA LUZ, não por cima.
   Esta inversão é o efeito inteiro. Enquanto o branco cobre a tela,
   o preto atrás dele é invisível; quando o branco se CONTRAI, o
   preto vai sendo revelado das bordas para dentro. Ninguém pinta
   escuridão por cima de nada — o escuro sempre esteve lá, e é a luz
   que some. A versão anterior fazia crossfade de dois retângulos
   chapados, e um crossfade não tem direção: por isso lia como corte. */
.np-hero__blackout {
  position: absolute;
  inset: 0;
  z-index: 3;
  opacity: 0;
  pointer-events: none;
  /* A bruma residual mora AQUI, e não numa camada própria dentro do
     segundo ato. Assim ela nasce exatamente junto com o escuro, com
     zero código de sincronia — e nunca corre o risco de pairar por
     cima da galáxia no primeiro ato. */
  background:
    radial-gradient(60% 45% at 50% 52%, rgb(29 63 148 / 0.2) 0%, transparent 70%),
    radial-gradient(40% 30% at 62% 38%, rgb(92 47 166 / 0.13) 0%, transparent 72%),
    var(--np-void);
  will-change: opacity;
}

/* Contêiner só para ancorar a luz no ponto de fuga.
   z-index 5: a luz fica ACIMA do logo. É proposital — no auge do
   clarão o logo está literalmente coberto de branco, e o que o revela
   é a luz encolher para além dele. Assim ele nunca "aparece": ele
   estava lá o tempo todo, dentro da luz. */
.np-hero__flash {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: grid;
  place-items: center;
  overflow: hidden;
  pointer-events: none;
}

/* A luz em si: um disco radial gigante que ESCALA.
   Animar `transform` mantém tudo na camada de composição — animar os
   stops do gradiente forçaria repaint de tela cheia a cada frame, que
   é exatamente o tipo de custo que não se paga no clímax da cena.

   A queda longa e suave do gradiente (branco sólido até 30%, depois
   ~70% de falloff) é o que faz o logo emergir ATRAVÉS do brilho em
   vez de ser descoberto por uma borda dura passando por cima dele. */
.np-hero__flash-core {
  display: block;
  /* 100vmin — e NÃO 300vmax, que foi a primeira tentativa.
     Um elemento com `will-change` ganha camada própria de
     composição, e a camada é alocada no tamanho PRÉ-transform. A
     300vmax isso dava 2700² px numa tela pequena e 11520² num
     monitor 4K: o Chrome simplesmente desiste de rasterizar camadas
     desse tamanho e o elemento some — estilos corretos, nada na
     tela. Dimensionado para o menor lado da viewport, a camada fica
     modesta e o `scale` faz o resto. Ampliar um gradiente suave não
     custa nitidez nenhuma: não há detalhe para perder. */
  width: 100vmin;
  aspect-ratio: 1;
  border-radius: 50%;
  opacity: 0;
  transform: scale(0.02);
  will-change: transform, opacity;
  /* Branco sólido até 55% para o disco cobrir a tela sem precisar de
     uma escala absurda; o falloff longo depois disso é o que faz o
     logo emergir ATRAVÉS do brilho em vez de ser descoberto por uma
     borda dura passando por cima dele. */
  background: radial-gradient(
    circle closest-side,
    #ffffff 0%,
    #ffffff 55%,
    rgb(226 240 255 / 0.92) 68%,
    rgb(168 206 255 / 0.55) 82%,
    rgb(96 150 230 / 0.16) 93%,
    rgb(70 120 200 / 0) 100%
  );
}

/* ── Segundo ato ──────────────────────────────────────────────── */

.np-finale {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: clamp(1.75rem, 4vw, 3rem);
  padding: clamp(1.5rem, 5vw, 3rem);
  text-align: center;
  pointer-events: none;
}

.np-finale__lockup {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 1.15rem;
  opacity: 0;
  will-change: opacity;
}

.np-finale__logo {
  width: clamp(260px, 30vw, 420px);
  height: auto;
  display: block;
}

.np-finale__line {
  position: relative;
  margin: 0;
  opacity: 0;
  will-change: opacity, transform;
  max-width: 15ch;
  font-family: var(--font-display), system-ui, sans-serif;
  font-weight: 400;
  font-size: clamp(1.9rem, 5.2vw, 4.6rem);
  line-height: 1.08;
  letter-spacing: -0.015em;
  color: var(--np-text);
}

/* ── Animações ────────────────────────────────────────────────── */

@keyframes np-char-in {
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    filter: blur(0);
  }
}

@keyframes np-fade-down {
  from {
    opacity: 0;
    transform: translate3d(0, -0.8rem, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

@keyframes np-fade-up {
  from {
    opacity: 0;
    transform: translate3d(0, 0.8rem, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

@keyframes np-spark {
  0% {
    top: -30%;
    opacity: 0;
  }
  18% {
    opacity: 1;
  }
  82% {
    opacity: 1;
  }
  100% {
    top: 100%;
    opacity: 0;
  }
}

/* ── Movimento reduzido ───────────────────────────────────────────
   Não é "desligar tudo": é entregar a mesma composição sem
   deslocamento e sem loop. O conteúdo continua íntegro.          */

@media (prefers-reduced-motion: reduce) {
  .np-hero[data-ready='true'] .np-char,
  .np-hero[data-ready='true'] .np-space {
    animation: np-fade-in 600ms ease-out forwards;
    animation-delay: calc(var(--char-index, 0) * 6ms + 120ms);
  }

  .np-char,
  .np-space {
    transform: none;
    filter: none;
  }

  .np-hint__spark {
    animation: none;
  }

  .np-hint__spark {
    opacity: 0.55;
    top: 35%;
  }
}

@keyframes np-fade-in {
  to {
    opacity: 1;
  }
}

/* ── Telas estreitas ──────────────────────────────────────────── */

@media (max-width: 640px) {
  .np-hero {
    /* Menos pista de scroll no celular: o polegar percorre a tela
       muito mais rápido que a roda do mouse, e o mesmo curso em
       vh leva menos gestos para vencer. */
    height: 260vh;
  }

  .np-finale__line {
    font-size: clamp(1.6rem, 8.2vw, 2.6rem);
    max-width: 13ch;
  }

  .np-headline {
    font-size: clamp(2rem, 10.5vw, 3.4rem);
    letter-spacing: -0.015em;
  }

  .np-brand__text {
    font-size: 0.7rem;
    letter-spacing: 0.12em;
  }
}
