/* ==========================================================================
   Ilux Flow · layout.css
   Contêineres, grid, botões e utilitários compartilhados.
   ========================================================================== */

.wrap {
  width: min(1160px, 100%);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.sec { padding-block: var(--sec); }

.sec-head { margin-bottom: clamp(36px, 5vw, 64px); }
.sec-head h2 { margin-bottom: 18px; }

/* etiqueta de seção, sem caixa-alta, conforme o brief.
   Tinha um tracinho de 18px antes do texto, em gradiente da marca. Saiu: lido
   na página ele era um travessao desenhado, e travessao a gente não usa aqui.
   A etiqueta se sustenta pela cor e pelo peso. */
.eyebrow {
  display: inline-block;
  font-size: .86rem;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--pink-dk);
  margin-bottom: 16px;
}

/* --- botões -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 44px;
  padding: 13px 22px;
  border: 0;
  border-radius: 999px;
  font-size: .97rem;
  font-weight: 600;
  letter-spacing: -.012em;
  cursor: pointer;
  transition: transform .18s ease, box-shadow .18s ease,
              background-color .18s ease, color .18s ease;
}

/* Texto INK, não branco: branco sobre o gradiente da marca fica em 3,1:1 e
   reprova em AA. Ink no mesmo gradiente dá 5,2:1 no stop pior. */
.btn-primary {
  background: var(--flow);
  color: var(--ink);
  box-shadow: 0 6px 20px -8px rgba(255, 74, 28, .55);
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 12px 26px -10px rgba(255, 74, 28, .6); }
.btn-primary:active { transform: translateY(0); }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  box-shadow: inset 0 0 0 1.5px var(--line);
}
.btn-ghost:hover { box-shadow: inset 0 0 0 1.5px rgba(19, 30, 43, .3); }

.btn-light {
  background: var(--white);
  color: var(--ink);
  box-shadow: 0 8px 24px -12px rgba(19, 30, 43, .35);
}
.btn-light:hover { transform: translateY(-1px); }

.btn-sm { min-height: 40px; padding: 10px 18px; font-size: .92rem; }

/* --- grid genérico ------------------------------------------------------- */
.grid { display: grid; gap: clamp(16px, 2.2vw, 26px); }
.g-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

@media (max-width: 900px) {
  .g-3, .g-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .g-2, .g-3, .g-4 { grid-template-columns: minmax(0, 1fr); }
}

/* --- utilitários --------------------------------------------------------- */
.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;
}

.on-cream-2 { background: var(--cream-2); }
.on-white   { background: var(--white); }
.on-ink     { background: var(--ink); color: var(--white); }

.hairline { border-top: 1px solid var(--line); }

/* conteúdo largo rola no próprio contêiner, não no body */
.scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}

/* grão, overlay de textura sobre gradiente, gerado em SVG inline (sem request) */
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: .5;
  mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.36'/%3E%3C/svg%3E");
}

/* <picture> só existe para oferecer o WebP; ela não pode virar uma caixa no
   meio do caminho, senão os posicionamentos de .ribbon-wrap e .invite-art
   passariam a resolver contra ela em vez do pai real. */
picture { display: contents; }

/* --- máscaras de texto (SplitText) --------------------------------------
   Os invólucros são criados pelo plugin, com `overflow: clip` e o aria já
   resolvidos. A folga vertical do recorte é aplicada em split.js, sobre a
   lista `masks` que o próprio plugin publica, o nome da classe do invólucro
   é montado por ele e não é contrato estável. */
.split-w, .split-l { will-change: transform; }

/* --- estado inicial das animações ---------------------------------------
   Só esconde se o JS assumiu o controle. Sem .js na raiz, tudo fica visível,
   a página funciona sem JavaScript e é indexável. O script no <head> devolve
   a página a esse estado se o GSAP não assumir em 2,5s, e nem chega a aplicar
   estes estados quando a página é aberta como arquivo local. */
.js .reveal { opacity: 0; }

/* nav: é o único bloco acima da dobra que já estava montado no primeiro
   quadro. Sem este portão o gsap.from teria que escondê-la DEPOIS de ela
   aparecer, e o load piscaria. */
.js .nav .brand,
.js .nav-links a,
.js .nav-in > .btn { opacity: 0; }

/* cabeçalho de seção: etiqueta, título e lede entram juntos.
   Sem isto os títulos apareciam de imediato enquanto o bloco abaixo animava,
   que é o que fazia as seções parecerem desconexas. */
.js .sec-head > * { opacity: 0; }

/* A jornada não usa .sec-head, porque o cabeçalho dela é uma linha com os
   indicadores de estado ao lado. Mesmo tratamento, seletor próprio. */
.js .journey-head .eyebrow,
.js .journey-head h2,
.js .step-btn,
.js .journey > .wrap > .note { opacity: 0; }

/* citação da seção 6.3: quem esconde é a máscara de linha, montada pelo JS
   assim que o SplitText divide o texto. Até lá o bloco inteiro fica
   invisível, sem isto a citação apareceria pronta por um quadro. */
.js .value blockquote { opacity: 0; }

/* a marca da 6.3 se desenha forma por forma. O estado desmontado de cada
   forma é armado pelo próprio reveal.js, aqui só o <figure> fica invisível,
   para que nada pisque montado entre o primeiro paint e o boot do GSAP. */
.js .value-mark { opacity: 0; }

/* §6.2: os sintomas e os painéis entram animados; o trilho parte recolhido.
   Tudo sob .js, sem ele a seção é uma lista comum, inteira e visível. */
.js .pain-copy { opacity: 0; }
.js .pain-rail-fill { transform: scaleY(0); }
/* a sanfona dos parágrafos (altura zero até o JS abrir a do item ativo) está
   em sections.css, junto do resto do layout preso, só vale lá. Sem JS cada
   parágrafo fica aberto sob o seu título, em coluna comum. */

/* §6.3: os quatro pontos acendem junto com a cor chegando na marca.
   O item em si NÃO é escondido, quem entra é o texto dele, e o marcador
   entra pela --dot-scale. Escondendo o <li> o marcador sumiria junto. */
.js .value-points b,
.js .value-points p { opacity: 0; }
.js .value-points li { --dot-scale: 0; }

/* vitrine do produto: as duas molduras chegam animadas */
.js .shot-main,
.js .shot-aside { opacity: 0; }

/* recursos: o filete é desenhado da esquerda para a direita */
.js .feat .rule { transform: scaleX(0); }
.js .feat h3,
.js .feat p { opacity: 0; }

/* implantação: a linha começa recolhida e os círculos estouram quando ela passa */
.js .steps-line { transform: scaleX(0); transform-origin: left center; }
.js .step .num { opacity: 0; transform: scale(.55); }
.js .step h3,
.js .step p { opacity: 0; }
@media (max-width: 900px) {
  .js .steps-line { transform: scaleY(0); transform-origin: center top; }
}

/* convite */
.js .invite-card > * { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .js .nav .brand,
  .js .nav-links a,
  .js .nav-in > .btn,
  .js .reveal,
  .js .sec-head > *,
  .js .journey-head .eyebrow,
  .js .journey-head h2,
  .js .step-btn,
  .js .journey > .wrap > .note,
  .js .feat h3,
  .js .feat p,
  .js .step h3,
  .js .step p,
  .js .invite-card > * { opacity: 1; }
  .js .pain-copy { opacity: 1; }
  .js .pain-detail { opacity: 1; height: auto; margin-top: 6px; }
  .js .pain-rail-fill { transform: scaleY(1); }
  .js .value-mark,
  .js .shot-main,
  .js .shot-aside,
  .js .value-points b,
  .js .value-points p,
  .js .value blockquote { opacity: 1; }
  .js .value-points li { --dot-scale: 1; }
  .js .steps-line,
  .js .feat .rule,
  .js .step .num { transform: none; opacity: 1; }
}

/* --- aviso de protocolo -------------------------------------------------
   Só aparece quando a página foi aberta com duplo clique (file://), onde o
   navegador bloqueia módulos e fontes. Nunca aparece servida por HTTP. */
.is-file body::after {
  content: "Esta página está aberta como arquivo local (file://). O navegador bloqueia os módulos JS e a fonte da marca por CORS, as animações não rodam e a tipografia cai no fallback. Rode serve.cmd e abra http://127.0.0.1:8080.";
  position: fixed;
  inset: auto 0 0 0;
  z-index: 999;
  padding: 14px var(--pad);
  background: var(--ink);
  color: var(--white);
  font-size: .85rem;
  line-height: 1.45;
  box-shadow: 0 -8px 24px -16px rgba(19, 30, 43, .5);
}
