/* ═══════════════════════════════════════════════════════════════════════
   SOPHIA VIEIRA DE MELO E SILVA · BAILE DE INVERNO — Edição Nº 15
   Redesign 2.0: uma noite contada como um filme, em três atos.

   Duas regras governam este arquivo:
   1. UM grande efeito por seção. Empilhar efeitos mata o luxo — vira
      carnaval caro. Cada seção tem o seu e mais nenhum.
   2. Nada de gradiente colorido, brilho, glitter ou raio grande. O luxo
      aqui é o espaço vazio, o fio de prata e o tempo lento.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* Paleta: AZUL MARINHO + PRATA — tema "Uma noite em Nova York" (cliente, 13/11/2026).
     Os nomes vêm do convite de inverno original e foram mantidos de propósito: são lidos
     em centenas de regras abaixo; o que muda aqui é o VALOR. */
  --gelo: #F5F8FC;
  --gelo-fundo: #E8EEF7;
  --noite: #0B1C34;
  --noite-2: #122743;
  --breu: #050E1C;
  --prata: #C7CFDC;
  --glacial: #7FA6CF;

  --tinta: #0B1C34;
  --tinta-2: #4A5A73;
  --tinta-3: #7E8BA0;

  --serif: "Playfair Display", "Times New Roman", Georgia, serif;
  --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 1s;

  --margem: clamp(20px, 6vw, 96px);
  --vao: clamp(120px, 16vw, 200px);
  --fio: 1px solid rgba(11, 28, 52, 0.14);
  --fio-claro: 1px solid rgba(199, 207, 220, 0.22);
}

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

/* 🛑 O `display` do autor VENCE o `display:none` que o navegador dá a
   [hidden] — mesma especificidade, e a folha do autor ganha. Sem este
   !important, `.modal{display:flex}` deixa o modal fechado ocupando a tela
   inteira, invisível (opacidade 0) e interceptando TODO clique da página.
   O sintoma não é "modal aberto", é "o site parou de responder". */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--noite);
  color: var(--gelo);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.75;
  letter-spacing: 0.005em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
body.is-loading { overflow: hidden; }
body.modal-aberto { overflow: hidden; }

img { display: block; max-width: 100%; height: auto; }
video { display: block; }
button { font: inherit; color: inherit; }

::selection { background: var(--prata); color: var(--noite); }

:focus-visible { outline: 2px solid var(--glacial); outline-offset: 4px; border-radius: 1px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.pular {
  position: absolute; top: -100px; left: var(--margem); z-index: 200;
  background: var(--gelo); color: var(--noite); padding: 12px 20px;
  font-size: 13px; text-decoration: none;
}
.pular:focus { top: 16px; }

.rotulo {
  font-size: 0.68rem; font-weight: 500; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--tinta-3);
  margin: 0 0 clamp(24px, 4vw, 44px);
}
.titulo-secao {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(2.4rem, 6.4vw, 5rem);
  line-height: 1.02; letter-spacing: -0.025em; margin: 0 0 28px;
}
.titulo-secao em { font-style: italic; color: var(--prata); }

.linha { display: block; overflow: hidden; }
.linha > span { display: block; }

/* ═══════════ LOADER ═══════════ */

.loader {
  position: fixed; inset: 0; z-index: 300;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 28px;
  background: var(--gelo);
  clip-path: inset(0 0 0 0);
}
.loader__nome {
  margin: 0; font-family: var(--serif);
  font-size: clamp(1.9rem, 6vw, 3.6rem); font-weight: 400;
  letter-spacing: -0.02em; line-height: 1.1; text-align: center; color: var(--tinta);
}
.loader__palavra { display: inline-block; }
.loader__letra { display: inline-block; opacity: 0; transform: translateY(0.42em); }
.loader__fio {
  display: block; width: min(220px, 40vw); height: 1px;
  background: var(--prata); transform: scaleX(0); transform-origin: left;
}
body:not(.is-loading) .loader { pointer-events: none; }

/* ═══════════ FÓLIO E PROGRESSO ═══════════ */

.folio {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  display: flex; justify-content: space-between; gap: 12px;
  padding: 22px var(--margem);
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase;
  /* 🛑 Aqui havia `mix-blend-mode: difference`, que funciona nos extremos e
     FALHA no meio: sobre a foto cinza-azulada da capa, |0x9A − 0x7A| dá um
     marrom escuro e o fólio praticamente some. Fundo médio é o pior caso do
     `difference`, e é justamente o que uma foto tem.
     A cor agora é decidida pela SEÇÃO (`data-tema` no HTML): determinístico,
     legível sempre, e não depende do que a foto por acaso tem atrás. */
  color: var(--prata);
  text-shadow: 0 1px 12px rgba(8, 10, 14, 0.55);
  transition: opacity 0.6s var(--ease), color 0.6s var(--ease), text-shadow 0.6s var(--ease);
}
.folio.folio--claro {
  color: var(--tinta-2);
  text-shadow: 0 1px 10px rgba(247, 248, 251, 0.8);
}
.progresso.progresso--claro { background: rgba(11, 28, 52, 0.12); }
.progresso.progresso--claro span { background: var(--tinta-2); }
body.is-loading .folio, body.is-loading .progresso { opacity: 0; }
.folio__ato { transition: opacity 0.5s var(--ease); }

/* ═══════════ CONTROLE DE SOM ═══════════
   Canto inferior direito, o único canto que nenhuma seção usa para texto.
   Discreto de propósito: é um controle, não um enfeite — e num convite o som
   é oferta, nunca imposição.

   🛑 Alvo de 44px de verdade (o `padding` conta), porque no celular ele vive
   perto da borda, que é onde o dedo erra mais. */
.som {
  position: fixed; right: var(--margem); bottom: clamp(16px, 3vh, 28px);
  z-index: 95;
  display: inline-flex; align-items: center; gap: 9px;
  min-height: 44px; padding: 10px 14px;
  background: rgba(8, 10, 14, 0.42);
  backdrop-filter: blur(6px);
  border: var(--fio-claro); border-radius: 999px;
  color: var(--prata); cursor: pointer;
  font-size: 0.58rem; letter-spacing: 0.2em; text-transform: uppercase;
  transition: color 0.5s var(--ease), border-color 0.5s var(--ease), background-color 0.5s var(--ease);
}
.som:hover, .som:focus-visible { color: var(--gelo); border-color: rgba(196, 203, 214, 0.5); }
/* Sobre seção sophia o véu escuro não serve: inverte junto com o fólio. */
.folio--claro ~ .som,
body:has(.folio--claro) .som {
  background: rgba(247, 248, 251, 0.5); color: var(--tinta-2);
  border-color: rgba(11, 28, 52, 0.16);
}
.som__rotulo { line-height: 1; }

/* As três barrinhas: paradas quando mudo, dançando quando tocando. É o único
   jeito de o estado ser legível sem escrever "ligado/desligado" na tela. */
.som__onda { display: inline-flex; align-items: flex-end; gap: 2px; height: 11px; }
.som__onda i {
  display: block; width: 2px; height: 3px;
  background: currentColor; border-radius: 1px;
  transition: height 0.3s var(--ease);
}
.som[aria-pressed="true"] .som__onda i { animation: som-onda 1.1s ease-in-out infinite; }
.som[aria-pressed="true"] .som__onda i:nth-child(2) { animation-delay: 0.18s; }
.som[aria-pressed="true"] .som__onda i:nth-child(3) { animation-delay: 0.36s; }
@keyframes som-onda {
  0%, 100% { height: 3px; }
  50%      { height: 11px; }
}
body.is-loading .som { opacity: 0; pointer-events: none; }

.progresso {
  position: fixed; top: 0; left: 0; right: 0; height: 1px; z-index: 91;
  background: rgba(196, 203, 214, 0.14);
  transition: opacity 0.6s var(--ease);
}
.progresso span {
  display: block; height: 100%; width: 0%;
  background: var(--prata); transform-origin: left;
}

/* ═══════════ 01 · ABERTURA ═══════════
   O efeito desta seção é o VÍDEO CONDUZIDO PELO SCROLL. Nada mais. */

.hero { position: relative; height: 320vh; }         /* pista de rolagem do pin */
.hero__palco {
  position: sticky; top: 0;
  height: 100svh; overflow: hidden;
  display: grid; align-content: end;
  padding: 0 var(--margem) clamp(48px, 9vh, 96px);
  isolation: isolate;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
.hero__fallback, .hero__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 35%;
}
.hero__video { opacity: 0; transition: opacity 1.2s var(--ease); }
.hero.tem-video .hero__video { opacity: 1; }
.hero.tem-video .hero__fallback { opacity: 0; transition: opacity 1.2s var(--ease); }
.hero__veu {
  position: absolute; inset: 0;
  background: linear-gradient(to top,
    rgba(8,10,14,0.92) 0%, rgba(8,10,14,0.42) 42%, rgba(8,10,14,0.16) 100%);
}
/* ═══════════ NÉVOA (Vanta FOG) ═══════════
   Camada de fundo, ao contrário da neve: a neve passa NA FRENTE de tudo, a
   névoa fica ATRÁS de tudo. São os dois extremos da profundidade, e é a
   distância entre elas que dá volume ao que está no meio.

   🛑 `pointer-events: none` pelo mesmo motivo da neve, e aqui vale repetir: é
   uma camada que cobre a seção inteira. Sem isto, a névoa do RSVP engoliria o
   clique do formulário — e o sintoma não seria "tem uma névoa estranha", seria
   "o site não responde", que é invisível em teste de API.

   🛑 O canvas é criado pelo Vanta DENTRO deste elemento, em tempo de execução.
   As regras de tamanho abaixo existem porque o Vanta dimensiona pelo
   `getBoundingClientRect` do pai no momento do `init` — se a seção crescer
   depois (fonte carregada, imagem entrando), o canvas fica com o tamanho
   antigo. `width/height: 100%` faz o navegador esticar o que já foi
   desenhado, o que é impreciso porém invisível numa névoa borrada, e é muito
   melhor que uma faixa sem desenho na borda de baixo. */
.vanta-camada {
  position: absolute; inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 2.4s var(--ease);
}
.vanta-camada canvas {
  position: absolute !important; inset: 0;
  width: 100% !important; height: 100% !important;
}
/* `screen` no herói: a névoa só pode SOMAR luz sobre a foto. Em opacidade
   normal ela escureceria as sombras do retrato — e o retrato é o assunto. */
.hero .vanta-camada { mix-blend-mode: screen; }
/* 0.42 e não 0.5: `screen` CLAREIA, e o retrato precisa manter as sombras —
   é delas que vem o volume do rosto. A névoa aqui é o ar entre a câmera e ela,
   não um filtro por cima dela. */
.hero .vanta-camada.tem-vanta { opacity: 0.42; }
/* No RSVP não há foto embaixo, só o azul-meia-noite chapado: aqui a névoa
   pode aparecer inteira, e é ela que tira o "fundo sólido" da seção. */
.vanta-camada--rsvp { z-index: 0; }
.vanta-camada--rsvp.tem-vanta { opacity: 0.62; }

/* ═══════════ NEVE EM PRIMEIRO PLANO ═══════════
   Camada fixa sobre o site inteiro — os flocos atravessam texto, foto,
   vídeo, card e o próprio modal.

   🛑 `pointer-events: none` não é detalhe, é o que torna a camada possível:
   um <canvas> fixo cobrindo a tela com z-index 9999 engoliria TODO clique
   da página sem isso, e o sintoma seria "o site não responde" — invisível
   em qualquer teste de API, porque a requisição nunca sairia.
   🛑 Fica FORA de qualquer seção: dentro de um pai com `transform` (os pins
   do GSAP têm), `position: fixed` passa a se ancorar nesse pai e a neve
   deixaria de acompanhar o scroll. */
.luzes {
  position: fixed; inset: 0;
  width: 100vw; height: 100svh;
  z-index: 9999;
  pointer-events: none;
  opacity: 0;
  transition: opacity 2.2s var(--ease);
}
.luzes.is-visivel { opacity: 1; }

/* ═══════════ ATMOSFERA — pulso azul e reflexo no chão ═══════════
   Fica logo abaixo da neve e acima de todo o conteúdo.

   `mix-blend-mode: screen` faz o brilho SOMAR ao que está atrás: sobre o
   azul-meia-noite ele acende; sobre o branco gelo, screen com branco é
   branco, então some sozinho. É o comportamento certo em vez de um véu
   azul lavando a seção sophia dos presentes. */
.atmosfera {
  position: fixed; inset: 0;
  z-index: 9998;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0;
  transition: opacity 2.6s var(--ease);
}
.atmosfera.is-visivel { opacity: 1; }

/* Luz azul que respira, vindo de cima. Ciclo longo (11s) e amplitude curta:
   pulso rápido vira sinal de alerta, não atmosfera. */
.atmosfera__pulso {
  position: absolute; left: 50%; top: -22%;
  width: 130vmax; height: 100vmax;
  transform: translateX(-50%);
  background: radial-gradient(ellipse at 50% 0%,
    rgba(169, 195, 217, 0.30) 0%,
    rgba(120, 155, 195, 0.13) 38%,
    rgba(11, 28, 52, 0) 68%);
  animation: pulso-azul 11s ease-in-out infinite;
}
@keyframes pulso-azul {
  0%, 100% { opacity: 0.55; transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;    transform: translateX(-50%) scale(1.07); }
}

/* Reflexo no chão: a luz que bate no piso do salão. Faixa baixa, e dentro
   dela um brilho mais largo que a tela deslizando devagar — é o
   deslocamento que faz parecer reflexo, e não um degradê parado. */
.atmosfera__chao {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 26vh; overflow: hidden;
  background: linear-gradient(to top,
    rgba(169, 195, 217, 0.18) 0%,
    rgba(169, 195, 217, 0.06) 42%,
    rgba(169, 195, 217, 0) 100%);
}
.atmosfera__chao i {
  position: absolute; left: -60%; bottom: -10%;
  width: 80%; height: 130%;
  background: radial-gradient(ellipse at 50% 100%,
    rgba(233, 238, 246, 0.34) 0%, rgba(233, 238, 246, 0) 70%);
  animation: reflexo-chao 17s linear infinite;
}
@keyframes reflexo-chao {
  from { transform: translateX(0); }
  to   { transform: translateX(200%); }
}

/* As três fases ocupam a MESMA célula: uma substitui a outra no mesmo ponto
   da tela. Em fluxo normal, o texto pularia de altura a cada troca — e o que
   deveria ser um letreiro viraria um salto. */
.hero__tipo { position: relative; z-index: 3; display: grid; }
.hero__fase { grid-area: 1 / 1; align-self: end; }

.hero__ato {
  margin: 0 0 18px; overflow: hidden;
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.1rem, 3vw, 1.9rem);
  letter-spacing: 0.02em; color: var(--prata);
}
.hero__nome {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: clamp(2.9rem, 12.5vw, 11rem);
  line-height: 0.92; letter-spacing: -0.035em; color: var(--gelo);
}
.hero__quinze {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: clamp(6rem, 26vw, 22rem);
  line-height: 0.78; letter-spacing: -0.06em;
  color: var(--prata); opacity: 0.9;
}
.hero__data {
  margin: 10px 0 0;
  font-size: 0.68rem; letter-spacing: 0.32em; text-transform: uppercase;
  color: var(--glacial);
}

.hero__cue {
  position: absolute; left: var(--margem); bottom: clamp(20px, 4vh, 40px);
  z-index: 3; display: flex; align-items: center; gap: 14px;
  min-height: 44px; text-decoration: none; color: var(--prata);
}
.hero__cue .rotulo { margin: 0; color: inherit; }
.hero__fio {
  display: block; width: 64px; height: 1px; background: currentColor;
  transform-origin: left; animation: fio 2.6s var(--ease) infinite;
}
@keyframes fio {
  0%, 100% { transform: scaleX(0.25); opacity: 0.5; }
  50%      { transform: scaleX(1);    opacity: 1; }
}

/* ═══════════ 02 · A CARTA ═══════════
   O efeito desta seção é a TIPOGRAFIA CINÉTICA. */

.carta { position: relative; background: var(--gelo); overflow: hidden; }
.carta__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 2s var(--ease);
}
.carta.tem-video .carta__video { opacity: 0.14; }   /* atmosfera, não protagonista */
.carta__palco {
  position: relative; z-index: 1;
  min-height: 100svh; display: flex; flex-direction: column; justify-content: center;
  padding: var(--vao) var(--margem);
  max-width: 1100px; margin-inline: auto;
}
.carta__texto {
  margin: 0 0 1.1em;
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.5rem, 4.2vw, 3.1rem);
  line-height: 1.32; letter-spacing: -0.02em;
  color: var(--tinta);
}
.carta__texto--dois { font-size: clamp(1.25rem, 3.2vw, 2.3rem); }

/* A epígrafe abre a carta. Maior que o corpo e recuada por um fio de prata —
   é o gesto formal de um convite, e é ela que dá o tom antes de qualquer
   explicação. As aspas são tipográficas e entram pelo CSS: no HTML elas
   poluiriam o texto que a Sophia escreveu. */
.carta__epigrafe {
  margin: 0 0 1.1em; padding-left: clamp(16px, 3vw, 28px);
  border-left: 1px solid var(--prata);
  font-family: var(--serif); font-weight: 400; font-style: italic;
  font-size: clamp(1.7rem, 4.8vw, 3.4rem);
  line-height: 1.24; letter-spacing: -0.02em; color: var(--tinta);
  text-wrap: balance;
}
.carta__epigrafe::before { content: "\201C"; }
.carta__epigrafe::after  { content: "\201D"; }
.carta__epigrafe em { font-style: italic; color: var(--tinta-2); }
.carta.esfriando .carta__epigrafe { color: var(--gelo); border-left-color: var(--glacial); }
.carta.esfriando .carta__epigrafe em { color: var(--prata); }
.carta.esfriando .destaque { color: var(--glacial); }
/* Cada palavra é um alvo do scroll. `inline-block` para poder transformá-la. */
.palavra { display: inline-block; white-space: pre; }
.destaque { font-style: italic; }
.carta__assina {
  margin: 2em 0 0; font-family: var(--serif); font-style: italic;
  font-size: 1.15rem; color: var(--tinta-2);
}
/* A seção esfria conforme se lê: gelo → meia-noite. */
.carta.esfriando { background: var(--noite); }
.carta.esfriando .carta__texto { color: var(--gelo); }
.carta.esfriando .carta__assina { color: var(--prata); }

/* ═══════════ 03 · O ENSAIO ═══════════
   O efeito desta seção é o PARALLAX/REVELAÇÃO das fotos. */

.ensaio { background: var(--noite); padding: var(--vao) 0 0; }
.ensaio > .rotulo { padding-inline: var(--margem); color: var(--tinta-3); }

.ensaio__um {
  margin: 0 auto; padding-inline: var(--margem);
  max-width: 1100px;
}
/* 🛑 O quadro que RECORTA é obrigatório para o parallax: a foto sobe e desce
   dentro dele com uma folga de escala. Sem o recorte, o mesmo deslocamento
   abriria uma faixa vazia na borda de cima ou de baixo a cada rolagem. */
.ensaio__um-quadro { display: block; overflow: hidden; }
.ensaio__um img {
  width: 100%; aspect-ratio: 4/5; object-fit: cover;
  will-change: transform;
}
.ensaio__um figcaption,
.ensaio__legenda { margin-top: 14px; font-size: 0.82rem; color: var(--tinta-3); }

/* pin & zoom */
.ensaio__zoom { height: 260vh; position: relative; margin-top: var(--vao); }
.ensaio__zoom-palco {
  position: sticky; top: 0; height: 100svh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 16px; padding: 0 var(--margem); overflow: hidden;
}
.ensaio__detalhe {
  position: relative; margin: 0; overflow: hidden;
  width: min(620px, 88vw); aspect-ratio: 4/5;
}
.ensaio__detalhe img, .ensaio__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  will-change: transform;
}
.ensaio__video { opacity: 0; transition: opacity 1.2s var(--ease); }
.ensaio__zoom.tem-video .ensaio__video { opacity: 1; }

/* galeria horizontal.
   🛑 `overflow-x: clip`, NÃO `hidden`: `hidden` cria um contêiner de rolagem,
   e o `position: sticky` do trilho passaria a grudar NELE em vez de na
   janela — o pin morre e a galeria nunca anda. `clip` corta sem criar
   contêiner. Sem um dos dois, o trilho (2.200px) faz a PÁGINA INTEIRA rolar
   de lado no celular. */
.ensaio__horizontal {
  height: 300vh; position: relative; margin-top: var(--vao);
  overflow-x: clip;
}
.ensaio__horizontal > .ensaio__trilho {
  position: sticky; top: 0; height: 100svh;
  display: flex; align-items: center; gap: clamp(20px, 4vw, 60px);
  padding-inline: var(--margem);
  will-change: transform;
}
.ensaio__quadro { flex: 0 0 auto; margin: 0; }
.ensaio__quadro img { height: min(66vh, 620px); width: auto; object-fit: cover; }
.ensaio__quadro--larga img { height: min(48vh, 440px); }
.ensaio__quadro--texto {
  flex: 0 0 auto; width: min(340px, 70vw);
  display: flex; align-items: center;
}
.ensaio__quadro--texto p {
  margin: 0; font-family: var(--serif);
  font-size: clamp(1.4rem, 3vw, 2.2rem); line-height: 1.25; color: var(--prata);
}

.ensaio__retrato {
  margin: var(--vao) auto 0; padding-inline: var(--margem);
  max-width: 900px; overflow: hidden;
}
.ensaio__retrato img {
  width: 100%; aspect-ratio: 4/5; object-fit: cover; will-change: transform;
}

/* ═══════════ 04 · TRANSIÇÃO ═══════════
   O efeito desta seção é o CLIP-PATH/LUZ que vira o salão. */

.transicao { height: 240vh; position: relative; background: var(--breu); }
.transicao__palco {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.transicao__img, .transicao__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  will-change: transform;
}
.transicao__video { opacity: 0; transition: opacity 1s var(--ease); }
.transicao.tem-video .transicao__video { opacity: 1; }
.transicao.tem-video .transicao__img { opacity: 0; }
.transicao__escuro {
  position: absolute; inset: 0; background: var(--breu); opacity: 0;
}
.transicao__luz {
  position: absolute; left: 50%; top: 50%; translate: -50% -50%;
  width: 40vmax; height: 40vmax; border-radius: 50%;
  background: radial-gradient(circle, rgba(169,195,217,0.55), rgba(169,195,217,0) 62%);
  opacity: 0; transform: scale(0.05); will-change: transform, opacity;
}
.transicao__titulo {
  position: relative; z-index: 2; margin: 0; overflow: hidden;
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(2.6rem, 10vw, 8rem);
  line-height: 1; letter-spacing: -0.03em; color: var(--gelo);
}
.transicao__titulo > span { display: block; }

/* ═══════════ 05 · O "15" ═══════════
   O efeito desta seção é o 3D. Por isso ela não tem mais nada. */

.numeral { position: relative; height: 220vh; background: var(--breu); }
.numeral__palco {
  position: sticky; top: 0; height: 100svh; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.numeral__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.numeral__plano-b {
  width: auto; height: min(52vh, 460px);
  opacity: 0.85; transition: opacity 0.8s var(--ease);
}
.numeral.tem-3d .numeral__plano-b { opacity: 0; pointer-events: none; }
.numeral__legenda {
  position: absolute; bottom: clamp(40px, 8vh, 90px);
  margin: 0; font-family: var(--serif); font-style: italic;
  font-size: 0.95rem; letter-spacing: 0.28em; text-transform: lowercase;
  color: var(--tinta-3);
}

/* ═══════════ 06 · BAILE DE INVERNO ═══════════
   O efeito desta seção é o VÍDEO DE FUNDO + parallax de camadas. */

.baile { position: relative; overflow: hidden; background: var(--noite); }
.baile__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 2s var(--ease);
}
.baile.tem-video .baile__video { opacity: 0.5; }
.baile__veu {
  position: absolute; inset: 0;
  background: linear-gradient(to right, rgba(11,28,52,0.94) 0%, rgba(11,28,52,0.62) 62%, rgba(11,28,52,0.4) 100%);
}
/* ═══════════ A CIDADE — camadas da noite de Nova York (14/11/2026) ═══════════
   Técnica do SELENE (scrolltide): a profundidade vem de camadas deslizando uma
   sobre a outra, cada uma na sua velocidade, e não de 3D nem de foto. O `fx.js`
   move cada `[data-parallax]` por `yPercent` — por isso NENHUMA camada usa
   `translate` no CSS: o GSAP reescreve o transform inteiro e a centralização
   sumiria. Centro aqui é `left:-9% / width:118%`, que também é a folga lateral
   que o movimento precisa para não abrir borda vazia. */
.skyline { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.skyline__cam {
  position: absolute; left: -9%; width: 118%; max-width: none; height: auto;
  display: block;
}
/* Longe é mais claro e mais desfocado: no ar da cidade, distância tira contraste
   antes de tirar tamanho — é isso que faz o fundo recuar sem precisar encolher. */
.skyline__cam--fundo  { bottom: 16%; opacity: 0.5; filter: blur(0.7px); }
.skyline__cam--meio   { bottom: 5%;  opacity: 0.92; }
.skyline__cam--frente { bottom: 0;   opacity: 1; }

/* 🛑 O TEXTO SEMPRE VENCE. A cidade é fundo, e fundo com 600 janelas acesas é o
   pior lugar do mundo para pousar um botão de contorno fino. Este véu escurece
   a faixa onde o conteúdo vive e desaparece antes do horizonte, então a cidade
   continua visível embaixo e legível por trás. */
.skyline::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to bottom,
    rgba(11, 28, 52, 0.0) 0%,
    rgba(11, 28, 52, 0.55) 34%,
    rgba(11, 28, 52, 0.78) 56%,
    rgba(11, 28, 52, 0.46) 80%,
    rgba(11, 28, 52, 0.0) 100%);
}

/* A lua: um disco de prata, a cor da festa. Fica no canto oposto ao texto para
   não disputar leitura com o título. */
.skyline__lua {
  position: absolute; right: 12%; top: 11%;
  width: clamp(44px, 6vw, 92px); aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(circle at 36% 34%, #fff, var(--prata) 58%, #8fa3bd);
  opacity: 0.88;
  box-shadow: 0 0 90px 26px rgba(199, 207, 220, 0.16);
}
/* O brilho do horizonte: a cidade acende o céu POR BAIXO. Sem isso o skyline
   parece recortado e colado; com isso, parece que a luz vem de dentro dele. */
.skyline__brilho {
  position: absolute; inset: auto 0 0 0; height: 46%;
  background: linear-gradient(to top,
    rgba(127, 166, 207, 0.22) 0%,
    rgba(127, 166, 207, 0.10) 38%,
    transparent 100%);
}
@media (max-width: 760px) {
  /* No celular a cena é mais alta que larga: a cidade sobe para não virar faixa
     fina no rodapé, e a lua sai da frente do título. */
  .skyline__cam--fundo  { bottom: 20%; }
  .skyline__cam--meio   { bottom: 10%; }
  .skyline__lua { right: 8%; top: 7%; }
}

.baile__palco {
  position: relative; z-index: 1;
  min-height: 100svh; display: flex; flex-direction: column; justify-content: center;
  padding: var(--vao) var(--margem);
  max-width: 1100px; margin-inline: auto;
}
.baile__titulo {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(2.8rem, 9vw, 7rem);
  line-height: 0.94; letter-spacing: -0.03em;
  margin: 0 0 clamp(44px, 7vw, 80px); color: var(--gelo);
  will-change: transform;
}
.baile__titulo .linha:last-child > span { font-style: italic; color: var(--prata); }

.ficha { margin: 0 0 clamp(44px, 6vw, 70px); will-change: transform; }
.ficha__item {
  display: grid; grid-template-columns: 1fr; gap: 6px;
  padding: 22px 0; border-top: var(--fio-claro);
}
.ficha__item:last-child { border-bottom: var(--fio-claro); }
.ficha dt {
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--prata); opacity: 0.75;
}
.ficha dd {
  margin: 0; font-family: var(--serif);
  font-size: clamp(1.15rem, 2.4vw, 1.7rem); line-height: 1.35; color: var(--gelo);
}
.ficha__endereco { font-family: var(--sans); font-size: 0.88rem; line-height: 1.65; color: var(--prata); }

/* ═══════════ 07 · TRAJE ═══════════
   O efeito desta seção é a INTERAÇÃO de material. */

.traje {
  position: relative; overflow: hidden;
  padding: var(--vao) var(--margem);
  background: var(--noite);
  transition: background-color 1s var(--ease);
}
.traje .rotulo { color: var(--tinta-3); }
.traje__titulo {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(2rem, 5.5vw, 4rem);
  line-height: 1.05; letter-spacing: -0.025em; margin: 0 0 18px; color: var(--gelo);
}
.traje__titulo em { font-style: italic; color: var(--prata); }
.traje__nota { margin: 0 0 clamp(40px, 6vw, 64px); color: var(--tinta-3); font-size: 0.9rem; }

/* Cartela de cores. 18 tons em 3 famílias — a lista é da aniversariante, e a
   direção de arte da página NÃO pode estreitá-la (ela liberou quente e
   castanho, que brigam com o azul-meia-noite daqui; manda quem dá a festa).
   🛑 Por isso o chip é PEQUENO: com 18 cores, o bloco de 132px de altura que
   havia aqui viraria uma parede. Chip de 56px cabe e continua clicável. */
.tons-grupo { position: relative; z-index: 2; margin-bottom: clamp(26px, 4vw, 38px); max-width: 900px; }
.tons-grupo__nome {
  margin: 0 0 12px; font-size: 0.58rem; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--tinta-3);
}
.tons {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 10px;
}
.tom {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  width: 92px; min-height: 44px; padding: 6px 4px 8px;
  background: none; border: 0; cursor: pointer; text-align: center;
}
.tom__chip {
  display: block; width: 56px; height: 56px; border-radius: 2px;
  background: var(--cor); box-shadow: inset 0 0 0 1px rgba(247,248,251,0.14);
  transition: transform 0.45s var(--ease), box-shadow 0.45s var(--ease);
}
.tom__nome {
  font-size: 0.6rem; letter-spacing: 0.08em; line-height: 1.25;
  color: var(--tinta-3); transition: color 0.45s var(--ease);
}
.tom:hover .tom__chip, .tom:focus-visible .tom__chip { transform: scale(1.09); }
.tom[aria-pressed="true"] .tom__chip {
  transform: scale(1.14);
  box-shadow: inset 0 0 0 1px rgba(247,248,251,0.3), 0 0 0 2px var(--gelo);
}
.tom[aria-pressed="true"] .tom__nome { color: var(--gelo); }

/* ═══ As duas proibidas ═══
   Fica por ÚLTIMO e não é botão: não há o que "experimentar" aqui. A tarja
   diagonal diz "não" sem precisar da palavra, e a palavra vem junto porque
   tarja sozinha não chega a quem usa leitor de tela. */
.proibidas {
  position: relative; z-index: 2;
  margin-top: clamp(34px, 5vw, 48px); padding-top: clamp(24px, 3vw, 32px);
  border-top: var(--fio-claro); max-width: 900px;
}
.proibidas__nome {
  margin: 0 0 14px; font-size: 0.58rem; letter-spacing: 0.22em;
  text-transform: uppercase; color: var(--prata);
}
.proibidas__lista {
  list-style: none; margin: 0 0 14px; padding: 0;
  display: flex; flex-wrap: wrap; gap: 12px 26px;
}
.proibidas__lista li {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--serif); font-size: 1.05rem; color: var(--gelo);
}
.proibidas__chip {
  position: relative; width: 34px; height: 34px; border-radius: 2px;
  background: var(--c); box-shadow: inset 0 0 0 1px rgba(11,28,52,0.25);
}
.proibidas__chip::after {
  content: ""; position: absolute; inset: -1px;
  background: linear-gradient(to bottom right, transparent calc(50% - 1px),
              #B4453F calc(50% - 1px), #B4453F calc(50% + 1px), transparent calc(50% + 1px));
}
.proibidas__nota { margin: 0; font-size: 0.85rem; color: var(--tinta-3); max-width: 46ch; }

/* A amostra é o "material": um véu do tom escolhido cobrindo a seção. */
.traje__amostra {
  position: absolute; inset: 0; z-index: 0;
  opacity: 0; transition: opacity 1s var(--ease), background 1s var(--ease);
  pointer-events: none;
}
.traje.tem-tom .traje__amostra { opacity: 0.22; }

/* ═══════════ 08 · CONTAGEM ═══════════
   O efeito desta seção são os NÚMEROS. Tela inteira, nada competindo. */

.contagem {
  position: relative; overflow: hidden;
  min-height: 100svh; display: flex; align-items: center; justify-content: center;
  padding: var(--vao) var(--margem); background: var(--breu); text-align: center;
}
.contagem__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 2s var(--ease);
}
.contagem.tem-video .contagem__video { opacity: 0.35; }
.contagem__palco { position: relative; z-index: 1; }
.contagem__numero {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: clamp(6rem, 30vw, 26rem);
  line-height: 0.82; letter-spacing: -0.05em; color: var(--gelo);
  font-variant-numeric: tabular-nums;
}
.contagem__unidade {
  margin: 8px 0 0;
  font-size: 0.72rem; letter-spacing: 0.42em; text-transform: uppercase; color: var(--prata);
}
.contagem__resto {
  margin: clamp(32px, 6vh, 60px) 0 0;
  font-family: var(--serif); font-size: clamp(1.1rem, 3vw, 1.7rem);
  letter-spacing: 0.04em; color: var(--tinta-3);
  font-variant-numeric: tabular-nums;
}
.contagem__legenda { margin: 14px 0 0; font-size: 0.82rem; color: var(--tinta-3); }
/* Troca de dígito: o número sobe e o novo entra por baixo. */
.contagem__numero.trocando { animation: troca 0.5s var(--ease); }
@keyframes troca {
  0%   { transform: translateY(0);     opacity: 1; }
  45%  { transform: translateY(-24%);  opacity: 0; }
  55%  { transform: translateY(20%);   opacity: 0; }
  100% { transform: translateY(0);     opacity: 1; }
}

/* ═══════════ 09 · PRESENTES ═══════════
   Área FUNCIONAL: aqui a animação recua de propósito. */

.presentes { padding: var(--vao) var(--margem); background: var(--gelo); color: var(--tinta); }
.presentes .rotulo { color: var(--tinta-3); }
.presentes .titulo-secao { color: var(--tinta); }
.presentes .titulo-secao em { color: var(--tinta-2); }
.presentes__cabeca { max-width: 62ch; margin-bottom: clamp(48px, 7vw, 80px); }
.presentes__nota { color: var(--tinta-2); margin: 0; max-width: 48ch; }

.grade-presentes {
  display: grid; grid-template-columns: 1fr;
  gap: clamp(16px, 2vw, 24px);
  max-width: 1400px;
}
.lista__estado { padding: 40px 0; color: var(--tinta-3); font-size: 0.9rem; margin: 0; grid-column: 1/-1; }

.presente {
  position: relative;
  display: flex; flex-direction: column; gap: 10px;
  padding: clamp(24px, 3vw, 34px);
  background: #fff;
  border: var(--fio);
  transition: border-color 0.5s var(--ease), opacity 0.6s var(--ease);
}
.presente:hover { border-color: rgba(11, 28, 52, 0.34); }

/* ── HOLOFOTE (porte do SpotlightCard do React Bits) ──────────────────
   O JS só escreve --mx/--my; quem desenha é o CSS. Assim o ponteiro nunca
   dispara layout: muda uma variável, e a única coisa que repinta é este
   pseudo-elemento.

   ⚖️ O original é um halo BRANCO para card escuro. Estes cards são brancos
   sobre gelo — halo branco aqui não existiria. A luz é glacial, e o que se
   lê não é "o card acendeu", é "a luz fria do salão bateu no papel".

   🛑 `inset: 0` mantém o brilho DENTRO da caixa sem precisar de
   `overflow: hidden` — que cortaria o anel de foco (`outline-offset: 4px`)
   do botão lá dentro e tiraria do teclado a única pista de onde ele está. */
.presente::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  pointer-events: none;
  background: radial-gradient(190px circle at var(--mx, 50%) var(--my, 0%),
    rgba(169, 195, 217, 0.34), rgba(169, 195, 217, 0) 68%);
  opacity: 0; transition: opacity 0.55s var(--ease);
}
.presente.tem-luz::after { opacity: 1; }
/* O conteúdo sobe acima do brilho: pseudo-elemento pinta depois dos filhos
   sem posicionamento, e o texto ficaria embaixo de um véu azul. */
.presente > * { position: relative; z-index: 1; }
.presente__loja {
  font-size: 0.6rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--tinta-3);
}
.presente__nome {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.3rem, 2.4vw, 1.75rem); line-height: 1.2; letter-spacing: -0.015em;
}
.presente__desc { margin: 0; font-size: 0.88rem; color: var(--tinta-2); flex: 1; }
.presente__valor {
  margin: 6px 0 0; font-family: var(--serif);
  font-size: clamp(1.2rem, 2.2vw, 1.6rem); color: var(--tinta);
}
.presente__acao { margin-top: 14px; }
.presente.is-reservado { opacity: 0.5; background: transparent; }
.presente.is-reservado .presente__nome { text-decoration: line-through; text-decoration-thickness: 1px; }
.selo {
  display: inline-flex; align-items: center; gap: 10px; min-height: 44px;
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--tinta-2);
}
.selo::before { content: ""; width: 26px; height: 1px; background: var(--prata); }

/* ═══════════ 10 · CONFIRMAÇÃO ═══════════ */

.rsvp { position: relative; background: var(--noite); padding: var(--vao) var(--margem); }
.rsvp::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(to right, transparent, rgba(196,203,214,0.45) 22%, rgba(196,203,214,0.16) 60%, transparent);
}
/* Acima da névoa. Sem isto o formulário empataria em z-index com a camada do
   Vanta e a ordem passaria a depender do DOM — que é justamente o que muda
   quando a cena de sucesso troca `.rsvp__grade` pela `.lista-cena`. */
.rsvp__grade, .rsvp .lista-cena { position: relative; z-index: 1; }
.rsvp__grade {
  display: grid; grid-template-columns: 1fr; gap: clamp(48px, 7vw, 90px);
  max-width: 1200px; margin-inline: auto;
}
.rsvp .rotulo { color: var(--prata); }
.rsvp__nota { color: var(--tinta-3); font-size: 0.9rem; max-width: 34ch; }

.form { display: flex; flex-direction: column; gap: 34px; }
.campo { position: relative; display: flex; flex-direction: column; }
.campo label {
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--prata); margin-bottom: 12px;
}
.campo input, .campo select {
  appearance: none; background: transparent; border: 0;
  border-bottom: 1px solid rgba(196, 203, 214, 0.34); border-radius: 0;
  min-height: 44px; padding: 8px 0 12px;
  font-family: var(--serif); font-size: clamp(1.1rem, 2.2vw, 1.4rem);
  color: var(--gelo); transition: border-color 0.5s var(--ease);
}
.campo select {
  background-image: linear-gradient(45deg, transparent 50%, var(--prata) 50%),
                    linear-gradient(135deg, var(--prata) 50%, transparent 50%);
  background-position: right 8px center, right 2px center;
  background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; cursor: pointer;
}
.campo select option { background: var(--noite); color: var(--gelo); }
.campo input:focus, .campo select:focus { outline: none; border-bottom-color: var(--prata); }
.campo input:focus-visible, .campo select:focus-visible { outline: 2px solid var(--glacial); outline-offset: 6px; }
.campo input::placeholder { color: transparent; }
.campo.is-erro input { border-bottom-color: #D9A2A2; }
.campo__erro { margin: 8px 0 0; font-size: 0.78rem; color: #E3B4B4; min-height: 1em; }
.campo__dica { margin: 8px 0 0; font-size: 0.78rem; color: var(--tinta-3); }
.campo--oculto { display: none; }
.campo--oculto.is-visivel { display: flex; }
.campo--claro label { color: var(--tinta-3); }
.campo--claro input { color: var(--tinta); border-bottom-color: rgba(11, 28, 52, 0.2); }
.campo--claro input:focus { border-bottom-color: var(--noite); }
.campo--claro .campo__erro { color: #A34A4A; }
.form__estado { margin: 0; font-size: 0.85rem; color: var(--prata); min-height: 1.2em; }
.form__estado.is-erro { color: #E3B4B4; }

/* 11 · "VOCÊ ESTÁ NA LISTA" */
.lista-cena {
  position: relative; overflow: hidden;
  min-height: 72svh; display: flex; align-items: center; justify-content: center;
  text-align: center; background: var(--breu);
  margin: calc(var(--vao) * -1) calc(var(--margem) * -1) calc(var(--vao) * -1);
  padding: var(--vao) var(--margem);
}
.lista-cena__video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; transition: opacity 2.5s var(--ease);
}
.lista-cena.tem-video .lista-cena__video { opacity: 0.42; }
.lista-cena__luz {
  position: absolute; left: 50%; top: 46%; translate: -50% -50%;
  width: 60vmax; height: 60vmax; border-radius: 50%;
  background: radial-gradient(circle, rgba(169,195,217,0.30), rgba(169,195,217,0) 62%);
  opacity: 0; transform: scale(0.2);
}
.lista-cena__tipo { position: relative; z-index: 1; }
.lista-cena__titulo {
  margin: 0; font-family: var(--serif); font-weight: 400;
  font-size: clamp(2rem, 7vw, 4.6rem); line-height: 1.05; letter-spacing: -0.03em;
  color: var(--gelo);
}
.lista-cena__sub {
  margin: 18px 0 0; font-family: var(--serif);
  font-size: clamp(1.05rem, 2.6vw, 1.6rem); color: var(--prata);
}
.lista-cena__sub em { font-style: italic; }
.lista-cena__detalhe { margin: 26px 0 0; font-size: 0.85rem; color: var(--tinta-3); }

/* ═══════════ 12 · FINAL ═══════════ */

.final { position: relative; background: var(--breu); }
.final__palco {
  position: relative; height: 100svh; overflow: hidden;
  display: flex; align-items: center; justify-content: center; text-align: center;
}
.final__img, .final__video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  will-change: transform;
}
.final__video { opacity: 0; transition: opacity 1.2s var(--ease); }
.final.tem-video .final__video { opacity: 1; }
.final.tem-video .final__img { opacity: 0; }
.final__escuro { position: absolute; inset: 0; background: var(--breu); opacity: 0.45; }
.final__tipo { position: relative; z-index: 1; padding-inline: var(--margem); }
.final__nome {
  margin: 0; font-family: var(--serif);
  font-size: clamp(2rem, 7vw, 5rem); line-height: 1; letter-spacing: -0.03em; color: var(--gelo);
}
.final__quinze {
  margin: 6px 0 0; font-family: var(--serif);
  font-size: clamp(4rem, 16vw, 12rem); line-height: 0.86; letter-spacing: -0.05em;
  color: var(--prata); opacity: 0.9;
}

/* ── BRILHO QUE VARRE (porte do ShinyText do React Bits) ──────────────
   Um degradê de prata corre por DENTRO da letra. Usado num lugar só, no "15"
   do desfecho — o efeito da última seção é o fim do filme, e um segundo
   brilho em outra seção roubaria dele.

   ⚖️ O ritmo é a mudança que importa em relação ao original: lá a varredura é
   contínua e rápida, o que na prática vira glitter. Aqui o ciclo é de 9s e a
   luz passa em 2,6s — os outros 6,4s são o "15" parado, prata, sem nada
   acontecendo. Brilho que nunca para é enfeite; brilho que acontece de vez em
   quando é material pegando a luz.

   🛑 A classe é aplicada pelo JS só depois de conferir `background-clip: text`.
   Onde a propriedade não existe, `color: transparent` apagaria o número. */
.brilha {
  background-image: linear-gradient(100deg,
    var(--prata) 0%, var(--prata) 38%,
    #FFFFFF 46%, #EAF1F9 50%,
    var(--prata) 58%, var(--prata) 100%);
  background-size: 300% 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: varre-prata 9s var(--ease) infinite;
}
/* 🛑 A posição TEM que ficar entre 0% e 100%. Em `background-position`, a
   porcentagem alinha o ponto X do degradê ao ponto X da caixa — com o degradê
   a 300% de largura, 0% mostra o primeiro terço e 100% mostra o último. Sair
   desse intervalo (eu tinha escrito 130% e -30%) empurra o degradê para FORA
   da letra, e como `background-repeat` é `no-repeat` não sobra pintura
   nenhuma: com `color: transparent`, o "15" simplesmente DESAPARECE.
   Só o QA visual pegou — nenhum teste funcional falha por um número invisível,
   e o console fica limpo.
   Em repouso a posição é 100% (último terço = prata sólida); a varredura vai
   a 0% e, no caminho, o ponto 50% traz a faixa branca por dentro da letra. */
@keyframes varre-prata {
  0%, 71% { background-position: 100% 0; }
  100%    { background-position: 0% 0; }
}
.final__data {
  margin: 12px 0 0; font-size: 0.68rem; letter-spacing: 0.32em;
  text-transform: uppercase; color: var(--glacial);
}
.final__linha {
  margin: clamp(30px, 6vh, 56px) 0 0; font-family: var(--serif); font-style: italic;
  font-size: clamp(1rem, 2.4vw, 1.4rem); color: var(--prata);
}

.rodape {
  padding: clamp(56px, 8vw, 100px) var(--margem) clamp(40px, 5vw, 60px);
  display: flex; flex-direction: column; gap: 40px; align-items: flex-start;
  border-top: var(--fio-claro); color: var(--prata);
}
.rodape__amor {
  margin: 0; font-family: var(--serif);
  font-size: clamp(1.4rem, 3.6vw, 2.2rem); line-height: 1.25; color: var(--gelo);
}
.rodape__amor em { color: var(--prata); }
.rodape__credito {
  margin: 14px 0 0; font-size: 0.6rem; line-height: 1.7; color: var(--tinta-3);
  max-width: 52ch;
}
.rodape__credito em { font-style: italic; color: var(--prata); }
.rodape__credito a { color: var(--tinta-3); text-underline-offset: 4px; }
.rodape__credito a:hover, .rodape__credito a:focus-visible { color: var(--prata); }

/* ═══════════ GELO AO TOQUE ═══════════
   Toque na tela → o gelo se forma naquele ponto e DERRETE sozinho.

   ⚖️ Ancorado na PÁGINA, não na tela: o `--x`/`--y` vem de `pageX/pageY`, então
   o gelo fica grudado no lugar que a pessoa tocou — se ela rolar, ele desce
   junto com a foto e derrete lá. Preso ao viewport ele flutuaria sobre o
   conteúdo, e "congelar o local" viraria "congelar a tela".

   🛑 `pointer-events: none`, pelo mesmo motivo da neve e da névoa: é uma camada
   que cobre a página inteira. Sem isso, o primeiro toque engoliria todos os
   seguintes — e o sintoma seria "o site travou depois que eu cliquei".

   🔑 Abaixo da neve (9999) e da atmosfera (9998): os flocos continuam passando
   NA FRENTE do gelo, que é o que faz as duas camadas parecerem o mesmo inverno
   em vez de dois efeitos empilhados. */
.gelo-camada {
  position: absolute; top: 0; left: 0; width: 100%; height: 0;
  z-index: 9997; pointer-events: none;
}
.gelo {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--t); height: var(--t); margin: calc(var(--t) / -2) 0 0 calc(var(--t) / -2);
  pointer-events: none; will-change: transform, opacity;
  /* 🛑 A 1ª versão usava UMA conic repetida com agulhas longas e opacas: virou
     estrela de fogo de artifício, não gelo. Gelo é VÉU antes de ser espinho.
     Três mudanças consertaram:
       · o halo passa a dominar (as agulhas viram textura, não desenho);
       · DUAS conics de períodos primos entre si (7° e 19°) e ângulos
         diferentes — a sobreposição quebra a regularidade e as agulhas deixam
         de sair todas do mesmo ponto como raios de sol;
       · máscara curta (as agulhas morrem a 34% do raio) e mais desfoque.
     🔑 O que denuncia gelo falso é a REGULARIDADE: cristal de verdade nunca
     tem o mesmo ângulo duas vezes. */
  background:
    repeating-conic-gradient(from var(--g),
      rgba(233, 238, 246, 0.13) 0deg 1.2deg, rgba(233, 238, 246, 0) 1.2deg 7deg),
    repeating-conic-gradient(from calc(var(--g) * -1.7),
      rgba(214, 228, 242, 0.10) 0deg 2deg, rgba(214, 228, 242, 0) 2deg 19deg),
    radial-gradient(circle,
      rgba(233, 238, 246, 0.42) 0%,
      rgba(198, 219, 236, 0.26) 26%,
      rgba(169, 195, 217, 0.13) 48%,
      rgba(169, 195, 217, 0.04) 64%,
      rgba(169, 195, 217, 0) 76%);
  -webkit-mask-image: radial-gradient(circle, #000 0%, rgba(0,0,0,.85) 34%, transparent 66%);
  mask-image: radial-gradient(circle, #000 0%, rgba(0,0,0,.85) 34%, transparent 66%);
  filter: blur(1.3px);
  opacity: 0;
  animation: gelo-formar var(--d) cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* ═══ Nevasca sobre o gelo ═══
   Onde o gelo se forma, a neve cai MAIS FORTE — mais densa e mais rápida que a
   neve ambiente, como se aquele ponto tivesse virado o centro do inverno.

   ⚖️ Em DOM e não no shader da neve global: aquele canvas tem toda a física em
   atributos estáticos, calculados uma vez na carga. Injetar flocos no ponto do
   toque exigiria reescrever o buffer a cada clique — caro, e arriscaria a
   camada que roda no site inteiro. Uma dúzia de `<i>` com animação de CSS é
   composta pela GPU e morre sozinha.

   🔑 O contêiner é mais ALTO que o gelo (180%): a neve precisa atravessar o
   cristal e continuar caindo depois dele, senão parece que ela para no ar. */
.nevasca {
  position: absolute; left: var(--x); top: var(--y);
  width: var(--t); height: calc(var(--t) * 1.8);
  margin: calc(var(--t) / -2) 0 0 calc(var(--t) / -2);
  pointer-events: none; overflow: hidden;
}
.nevasca i {
  position: absolute; left: var(--fx); top: 0;
  width: var(--fs); height: var(--fs);
  /* 🛑 Era `border-radius: 50%` — bolinha. Floco de neve tem SEIS braços, com
     nós ao longo de cada um. O SVG abaixo é o cristal; a máscara o recorta a
     partir da cor, então basta trocar `background` para mudar o tom.
     ⚖️ Os desfocados (`--fb` alto) continuam redondos de propósito: neve fora
     de foco vira bokeh circular numa foto de verdade, e cristal borrado só
     viraria sujeira. É o mesmo critério do shader da neve global. */
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-50%20-50%20100%20100%22%3E%3Cpath%20d%3D%22M0.0%20-44.0L0.0%2044.0M38.1%20-22.0L-38.1%2022.0M38.1%2022.0L-38.1%20-22.0M0.0%20-20.0l10.6%20-7.5M0.0%20-20.0l-10.6%20-7.5M0.0%20-33.0l7.4%20-5.2M0.0%20-33.0l-7.4%20-5.2M17.3%20-10.0l11.8%205.5M17.3%20-10.0l1.1%20-13.0M28.6%20-16.5l8.2%203.8M28.6%20-16.5l0.8%20-9.0M17.3%2010.0l1.1%2013.0M17.3%2010.0l11.8%20-5.5M28.6%2016.5l0.8%209.0M28.6%2016.5l8.2%20-3.8M0.0%2020.0l-10.6%207.5M0.0%2020.0l10.6%207.5M0.0%2033.0l-7.4%205.2M0.0%2033.0l7.4%205.2M-17.3%2010.0l-11.8%20-5.5M-17.3%2010.0l-1.1%2013.0M-28.6%2016.5l-8.2%20-3.8M-28.6%2016.5l-0.8%209.0M-17.3%20-10.0l-1.1%20-13.0M-17.3%20-10.0l-11.8%205.5M-28.6%20-16.5l-0.8%20-9.0M-28.6%20-16.5l-8.2%203.8%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.6%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%2F%3E%3Ccircle%20r%3D%224.5%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%22-50%20-50%20100%20100%22%3E%3Cpath%20d%3D%22M0.0%20-44.0L0.0%2044.0M38.1%20-22.0L-38.1%2022.0M38.1%2022.0L-38.1%20-22.0M0.0%20-20.0l10.6%20-7.5M0.0%20-20.0l-10.6%20-7.5M0.0%20-33.0l7.4%20-5.2M0.0%20-33.0l-7.4%20-5.2M17.3%20-10.0l11.8%205.5M17.3%20-10.0l1.1%20-13.0M28.6%20-16.5l8.2%203.8M28.6%20-16.5l0.8%20-9.0M17.3%2010.0l1.1%2013.0M17.3%2010.0l11.8%20-5.5M28.6%2016.5l0.8%209.0M28.6%2016.5l8.2%20-3.8M0.0%2020.0l-10.6%207.5M0.0%2020.0l10.6%207.5M0.0%2033.0l-7.4%205.2M0.0%2033.0l7.4%205.2M-17.3%2010.0l-11.8%20-5.5M-17.3%2010.0l-1.1%2013.0M-28.6%2016.5l-8.2%20-3.8M-28.6%2016.5l-0.8%209.0M-17.3%20-10.0l-1.1%20-13.0M-17.3%20-10.0l-11.8%205.5M-28.6%20-16.5l-0.8%20-9.0M-28.6%20-16.5l-8.2%203.8%22%20stroke%3D%22%23fff%22%20stroke-width%3D%224.6%22%20stroke-linecap%3D%22round%22%20fill%3D%22none%22%2F%3E%3Ccircle%20r%3D%224.5%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E");
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: center; mask-position: center;
  background: rgba(250, 252, 255, 0.95);
  filter: blur(var(--fb));
  opacity: 0;
  animation: nevasca-cai var(--fd) linear var(--fa) forwards;
}
/* Os pequenos e desfocados voltam a ser redondos — ver o porquê acima. */
.nevasca i.redondo {
  -webkit-mask-image: none; mask-image: none;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(250, 252, 255, 0.95) 0%, rgba(226, 235, 246, 0.5) 54%, rgba(226, 235, 246, 0) 74%);
}

/* Cai reto e rápido, com uma deriva lateral curta. Sem senóide: aqui é neve
   ADENSADA por um instante, não o floco vagaroso da camada de fundo. */
@keyframes nevasca-cai {
  0%   { transform: translate(0, -16%); opacity: 0; }
  10%  { opacity: var(--fo); }
  78%  { opacity: var(--fo); }
  100% { transform: translate(var(--dx), 116%); opacity: 0; }
}

/* Forma RÁPIDO e derrete DEVAGAR — é assim que gelo se comporta, e é o que faz
   o gesto parecer consequência do toque em vez de enfeite programado. */
@keyframes gelo-formar {
  0%   { transform: scale(0.04); opacity: 0; }
  12%  { opacity: 0.72; }
  32%  { transform: scale(1); opacity: 0.6; }
  100% { transform: scale(1.08); opacity: 0; }
}

.rodape__folio {
  margin: 0; font-size: 0.62rem; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--tinta-3);
}

/* ═══════════ BOTÕES ═══════════ */

.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  align-self: flex-start; min-height: 52px; padding: 16px 34px;
  border: 1px solid currentColor; background: transparent; border-radius: 0;
  font-size: 0.68rem; font-weight: 500; letter-spacing: 0.2em; text-transform: uppercase;
  text-decoration: none; cursor: pointer; overflow: hidden; isolation: isolate;
  transition: color 0.5s var(--ease), border-color 0.5s var(--ease);
  will-change: transform;
}
.btn > span { position: relative; z-index: 1; }
.btn::before {
  content: ""; position: absolute; inset: 0; z-index: 0; background: var(--prata);
  transform: scaleY(0); transform-origin: bottom; transition: transform 0.55s var(--ease);
}
.btn:hover::before, .btn:focus-visible::before { transform: scaleY(1); transform-origin: top; }
.btn:hover, .btn:focus-visible { color: var(--noite); border-color: var(--prata); }
.btn--prata { color: var(--prata); }
.btn--noite { color: var(--noite); border-color: rgba(11, 28, 52, 0.34); }
.rsvp .btn--noite { color: var(--gelo); border-color: rgba(196, 203, 214, 0.4); }
.rsvp .btn--noite:hover, .rsvp .btn--noite:focus-visible { color: var(--noite); }
.btn[disabled] { opacity: 0.45; pointer-events: none; }
/* No toque não há hover: o feedback é a pressão. */
.btn:active { transform: scale(0.97); }

/* O botão de ação do card: 44px de alvo, fio embaixo do texto. */
.link-fio {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0;
  background: none; border: 0; cursor: pointer;
  font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--tinta); touch-action: manipulation;
}
.link-fio > span { position: relative; display: inline-block; padding: 3px 0; }
.link-fio > span::after {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: currentColor; transform: scaleX(1); transform-origin: right;
  transition: transform 0.5s var(--ease);
}
.link-fio:hover > span::after, .link-fio:focus-visible > span::after { transform: scaleX(0); transform-origin: right; }
.link-fio > span::before {
  content: ""; position: absolute; left: 0; bottom: 0; width: 100%; height: 1px;
  background: var(--glacial); transform: scaleX(0); transform-origin: left;
  transition: transform 0.5s var(--ease) 0.12s;
}
.link-fio:hover > span::before, .link-fio:focus-visible > span::before { transform: scaleX(1); }

/* ═══════════ MODAL ═══════════ */

.modal { position: fixed; inset: 0; z-index: 250; display: flex; }
.modal__fundo {
  position: absolute; inset: 0; background: rgba(8, 10, 14, 0.7);
  backdrop-filter: blur(3px); opacity: 0; transition: opacity 0.5s var(--ease);
}
.modal.is-aberto .modal__fundo { opacity: 1; }
.modal__folha {
  position: relative; margin: auto; width: min(560px, 100%);
  max-height: 100svh; overflow-y: auto; background: var(--gelo); color: var(--tinta);
  padding: clamp(32px, 6vw, 56px);
  transform: translateY(24px); opacity: 0;
  transition: transform 0.7s var(--ease), opacity 0.5s var(--ease);
}
.modal.is-aberto .modal__folha { transform: translateY(0); opacity: 1; }
.modal__x {
  position: absolute; top: 12px; right: 16px; background: none; border: 0; cursor: pointer;
  font-size: 28px; line-height: 1; color: var(--tinta-2); width: 44px; height: 44px;
}
.modal .rotulo { color: var(--tinta-3); }
.modal__titulo {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.6rem, 4.4vw, 2.3rem); line-height: 1.15; letter-spacing: -0.02em;
  margin: 0 0 8px;
}
.modal__valor { margin: 0 0 6px; font-family: var(--serif); font-size: 1.3rem; color: var(--tinta-2); }
.modal__desc { margin: 0 0 34px; font-size: 0.9rem; color: var(--tinta-2); }
.modal__confirmado { margin: 0 0 22px; font-size: 0.9rem; color: var(--tinta-2); }
.modal form { display: flex; flex-direction: column; gap: 28px; }
.modal .btn--prata, .modal .btn--noite { color: var(--tinta); border-color: rgba(11,28,52,0.34); width: 100%; }
.modal__link {
  display: block; margin: 24px auto 0; background: none; border: 0; cursor: pointer;
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--tinta-3);
  text-decoration: underline; text-underline-offset: 5px; min-height: 44px;
}
.pix__aviso { margin: 18px 0 0; font-size: 0.8rem; color: var(--tinta-3); }

/* ═══════════ ENTRADAS ═══════════ */

html.anim .reveal { opacity: 0; transform: translateY(40px); }
html.anim .reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
html.anim .hero__tipo [data-fase] { opacity: 0; }
html.anim .baile__titulo .linha > span { transform: translateY(105%); }
html.anim .transicao__titulo > span { transform: translateY(110%); }
html.anim [data-desfoque] img { filter: blur(18px); transform: scale(1.1); }
html.anim .presente { opacity: 0; transform: translateY(24px); }
html.anim .presente.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
html.anim .presente.is-reservado.is-in { opacity: 0.5; }

/* Título DESCOBERTO em vez de título que aparece: o recorte sobe e a letra
   surge por baixo dele. Fade é a animação de quem não escolheu nada; o
   recorte tem direção, e direção é o que separa movimento intencional de
   enfeite.
   🛑 O estado inicial vive sob `html.anim`, que só existe quando há GSAP E o
   movimento não está reduzido. Fora disso o título nasce visível — nunca há
   um caminho em que ele fique recortado esperando uma animação que não vem. */
html.anim [data-fx="clip"] { clip-path: inset(0 0 106% 0); }

/* ═══════════ MICROINTERAÇÕES ═══════════
   `hover: hover` e não largura: o que decide não é o tamanho da tela, é
   existir um ponteiro que possa pairar. Tablet grande com dedo não deve
   receber estado de hover — ele fica "grudado" depois do toque. */

@media (hover: hover) and (pointer: fine) {
  /* A foto da galeria respira ao passar o cursor. Dentro do quadro, com
     recorte — o zoom acontece por baixo da moldura, não empurrando o vizinho. */
  .ensaio__quadro { overflow: hidden; }
  .ensaio__quadro img { transition: transform 1.3s var(--ease); }
  .ensaio__quadro:hover img { transform: scale(1.045); }

  /* O card inteiro sobe 2px. É pouco de propósito: são 16 cards numa grade,
     e qualquer coisa maior transformaria a lista num campo de botões pulando. */
  .presente { transition: border-color 0.5s var(--ease), opacity 0.6s var(--ease), transform 0.6s var(--ease); }
  .presente:hover { transform: translateY(-2px); }
}

/* ═══════════ RESPONSIVO ═══════════ */

@media (min-width: 720px) {
  .grade-presentes { grid-template-columns: repeat(2, 1fr); }
  .rsvp__grade { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .rodape { flex-direction: row; justify-content: space-between; align-items: flex-end; }
  .tons { grid-template-columns: repeat(4, 1fr); }
}

@media (min-width: 1000px) {
  .grade-presentes { grid-template-columns: repeat(3, 1fr); }
  .ficha__item { grid-template-columns: minmax(140px, 0.28fr) 1fr; gap: 0 40px; align-items: baseline; }
  .hero__palco { align-content: center; }
  .hero__tipo { max-width: 60vw; }
}

@media (max-width: 719px) {
  /* 🛑 O fólio é FIXO: o conteúdo passa por baixo dele para sempre. No
     desktop isso funciona porque as três legendas moram nas margens, longe
     da coluna de texto. No celular não há margem — as três disputam a
     largura, quebram em duas linhas e caem EM CIMA do título do card, que
     vira uma sopa ilegível na seção de presentes (medido em 390px).

     Tentei duas correções mais brandas antes desta, e vale registrar por que
     as duas falham:
       · esconder só as legendas das pontas resolve a QUEBRA de linha, mas a
         do meio continua exatamente onde o título do card passa;
       · pôr um véu por baixo torna o FÓLIO legível e, no mesmo gesto, lava o
         título que está atrás. Troca-se um texto ilegível por outro.
     Nenhuma das duas resolve o problema de fundo: um rótulo FIXO no topo de
     uma tela de 390px vai coincidir com o conteúdo o tempo todo, porque não
     existe margem para ele morar.

     Então no celular fica só o FIO DE PROGRESSO, que diz a mesma coisa
     (onde estou no convite) ocupando 1px e não disputando com nada. O nome
     dos atos não se perde: ele é ornamento editorial de margem, e a
     narrativa dos três atos está no CONTEÚDO das seções, não neste rótulo.
     No desktop o fólio inteiro continua — lá as margens existem e ele faz o
     que veio fazer, que é dar cara de revista à página. */
  .folio { display: none; }
}

@media (max-width: 999px) {
  /* Sem pin no celular: prender e soltar seções custa caro e a rolagem
     perde a fluidez justamente onde ela mais importa. As mesmas cenas
     acontecem, só que ancoradas por `position: sticky` puro. */
  .ensaio__zoom { height: 180vh; }
  .ensaio__horizontal { height: 220vh; }
  .transicao { height: 180vh; }
  .numeral { height: 160vh; }
  .hero { height: 260vh; }
}

/* ═══════════ MOVIMENTO REDUZIDO ═══════════ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  html.anim .reveal, html.anim .presente { opacity: 1 !important; transform: none !important; }
  html.anim .presente.is-reservado { opacity: 0.5 !important; }
  html.anim .hero__tipo [data-fase] { opacity: 1 !important; }
  html.anim .baile__titulo .linha > span,
  html.anim .transicao__titulo > span { transform: none !important; }
  html.anim [data-desfoque] img { filter: none !important; transform: none !important; }
  html.anim [data-fx="clip"] { clip-path: none !important; }
  /* As três camadas ambientais somem: neve, atmosfera e névoa. São
     exatamente o tipo de movimento contínuo e periférico que a preferência
     por movimento reduzido existe para desligar — e o JS nem chega a montar
     o Vanta, então isto aqui é o segundo cinto, para o caso de a preferência
     mudar com a página já aberta. */
  .luzes, .atmosfera, .vanta-camada, .gelo-camada { display: none; }
  /* A varredura de prata vira prata parada: o "15" continua lá, inteiro. */
  .brilha {
    background-image: none; animation: none;
    -webkit-background-clip: border-box; background-clip: border-box;
    color: var(--prata);
  }
  .presente::after { display: none; }
  .hero__fio { animation: none; }
  /* Seções longas viram seções normais: sem pista de rolagem para um pin
     que não vai existir, senão sobra tela vazia. */
  .hero, .ensaio__zoom, .ensaio__horizontal, .transicao, .numeral { height: auto; }
  .hero__palco, .ensaio__zoom-palco, .transicao__palco, .numeral__palco { position: relative; height: auto; min-height: 100svh; }
  .ensaio__horizontal > .ensaio__trilho {
    position: relative; height: auto; flex-wrap: wrap; justify-content: center;
    padding-block: 40px;
  }
  .contagem__numero.trocando { animation: none; }
}

/* ═══════════════ PIX do presente ═══════════════ */
.pix {
  display: grid; gap: 18px; justify-items: center;
  margin: 26px 0 8px; padding: 24px 20px;
  background: rgba(247, 248, 251, 0.04); border: var(--fio-claro);
}
/* Fundo branco SEMPRE, mesmo no tema escuro: leitor de QR precisa de contraste
   claro no módulo claro, e QR invertido falha em boa parte dos apps de banco. */
.pix__qr {
  width: min(232px, 62vw); aspect-ratio: 1;
  padding: 12px; background: #fff; border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
}
.pix__qr svg { width: 100%; height: 100%; display: block; shape-rendering: crispEdges; }
.pix__valor {
  margin: 0; font-family: var(--serif); font-size: 1.15rem; text-align: center;
}
.pix .btn { align-self: center; }

/* ═══════════════ Mapa do salão ═══════════════ */
.ficha__endereco--toque {
  display: block; width: 100%; text-align: left;
  background: transparent; border: 0; padding: 0;
  color: inherit; font: inherit; cursor: pointer;
  border-bottom: 1px dashed rgba(196, 203, 214, 0.34);
  transition: border-color 0.4s var(--ease);
}
.ficha__endereco--toque:hover { border-bottom-color: var(--prata); }
.ficha__toque {
  display: block; margin-top: 8px;
  font-size: 0.6rem; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--glacial);
}
.mapa { display: grid; gap: 20px; justify-items: center; margin: 30px 0 6px; }
.mapa__moldura {
  width: 100%; aspect-ratio: 4 / 3; max-height: 420px;
  border: var(--fio-claro); background: rgba(247, 248, 251, 0.04);
  overflow: hidden;
}
@media (min-width: 760px) { .mapa__moldura { aspect-ratio: 16 / 9; } }
.mapa__moldura iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ═══════════════ Presente: vitrine, não transação (03/10/2026) ═══════════════
   ⚖️ Saíram o preço e o "Quero presentear" (Daniel). O card ficou com nome,
   descrição e loja — uma SUGESTÃO. Sem o botão, ele não é mais clicável, então
   perde o `cursor`, o realce de foco e o hover de "me toque": prometer
   interação onde não há nenhuma é o que faz a pessoa tocar e achar que quebrou. */
.presente--vitrine { cursor: default; }
.presente--vitrine:hover { border-color: var(--fio-cor, rgba(11, 28, 52, 0.16)); }
.presente--vitrine .presente__desc { margin-bottom: 0; }

/* Assinatura de quem fez. Discreta de propósito: é o convite dela, não um
   portfólio — mas quem gostar do site precisa saber a quem pedir um igual. */
.rodape__assinatura {
  margin: 18px 0 0; font-size: 0.68rem; letter-spacing: 0.1em; color: var(--tinta-3);
}
.rodape__assinatura a {
  color: inherit; text-decoration: none;
  border-bottom: 1px solid currentColor; padding-bottom: 1px;
  transition: color 0.4s var(--ease);
}
.rodape__assinatura a:hover, .rodape__assinatura a:focus-visible { color: var(--tinta-1); }

/* Convite individual: o aviso ocupa o lugar onde ficava o seletor de
   acompanhantes, para o formulário não ganhar um buraco. */
.rsvp__individual {
  margin: 0; padding: 14px 16px;
  /* Véu CLARO: a página é azul-meia-noite, então preto translúcido não aparece. */
  background: rgba(255, 255, 255, 0.04); border-left: 2px solid var(--glacial);
  font-size: 0.9rem; color: var(--tinta-2);
}
