/* ============================================================
   motion.css — o sistema de movimento do site.

   Vive em ficheiro próprio, separado do site.css, por duas razões:
   é o que mais muda enquanto se afina, e é o único que se pode
   desligar por inteiro sem partir o layout — se este ficheiro não
   carregar, o site continua legível e usável, apenas parado.

   PRINCÍPIO: o movimento aqui é de folha de papel, não de brinquedo.
   Nada salta, nada faz mola, nada roda. As coisas revelam-se por
   baixo de uma máscara, o dourado acende onde o cursor passa, e o
   fundo desloca-se mais devagar do que o texto. É o que separa
   «vivo» de «irrequieto».

   Toda a animação está condicionada a `prefers-reduced-motion`:
   quem pediu menos movimento ao sistema recebe o estado final,
   imediatamente, sem uma única transição.

   AUDIENCE: pt-PT
   ============================================================ */

:root {
  --dur-rapida: 320ms;
  --dur-media: 620ms;
  --dur-lenta: 980ms;
  /* A mesma curva em todo o lado: arranque rápido, travagem longa.
     É o que dá a sensação de peso — de coisa que desacelera, não
     de coisa que pára. */
  --ease-saida: cubic-bezier(0.16, 1, 0.3, 1);
  --stagger: 90ms;
}

/* ============================================================
   1. REVELAÇÃO POR SCROLL
   O JavaScript marca os blocos com .b-reveal e, quando entram no
   ecrã, põe-lhes data-reveal="in". O atraso de cada um vem de
   --reveal-delay, calculado por posição entre irmãos: é isso que
   faz três cartões entrarem em cascata em vez de em bloco.
   ============================================================ */
/* O estado escondido vive todo dentro de `@media (scripting: enabled)`,
   e a razão é a diferença entre um site que anima e um site que se
   apaga.

   Um bloco marcado com .b-reveal só volta a aparecer quando o
   JavaScript lhe põe data-reveal="in". Se o JavaScript não correr —
   porque falhou a rede, porque uma extensão o bloqueou, porque o
   utilizador o desligou — esse bloco fica invisível PARA SEMPRE, e o
   texto desaparece do site sem deixar rasto.

   `scripting: enabled` é uma pergunta que o CSS faz ao navegador:
   «há JavaScript a correr?». Se não houver, este bloco inteiro é
   ignorado e os elementos ficam no seu estado natural — visíveis,
   parados, legíveis. Um navegador antigo que não conheça a pergunta
   também ignora o bloco, e o resultado é o mesmo: sem animação, mas
   com o conteúdo todo lá. É a falha a cair para o lado seguro. */
@media (scripting: enabled) {
  .b-reveal {
    opacity: 0;
    transform: translateY(28px);
    transition:
      opacity var(--dur-lenta) var(--ease-saida),
      transform var(--dur-lenta) var(--ease-saida);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
  }

  /* Variantes de direcção — é o que permite alternar os lados de
     secção para secção sem escrever CSS novo de cada vez. */
  .b-reveal--esq { transform: translateX(-36px); }
  .b-reveal--dir { transform: translateX(36px); }

  /* Revelação por máscara: o texto sobe por trás de uma janela que o
     corta. É o gesto mais editorial dos três, e o que se usa nos
     títulos de secção. */
  .b-mascara > .b-mascara__linha {
    transform: translateY(105%);
    transition: transform var(--dur-lenta) var(--ease-saida);
    transition-delay: var(--reveal-delay, 0ms);
  }

  /* Filete que se desenha da esquerda para a direita ao entrar em cena. */
  .b-filete {
    transform: scaleX(0);
    transition: transform var(--dur-lenta) var(--ease-saida);
    transition-delay: var(--reveal-delay, 0ms);
  }
}

/* Os estados finais ficam FORA da media query: são o estado natural
   de cada elemento, e é para aqui que tudo converge — com JavaScript,
   depois da animação; sem ele, desde o primeiro instante. */
.b-reveal[data-reveal='in'] { opacity: 1; transform: none; }
.b-reveal--esq[data-reveal='in'],
.b-reveal--dir[data-reveal='in'] { transform: none; }

.b-mascara { display: block; overflow: hidden; }
.b-mascara > .b-mascara__linha { display: block; }
.b-mascara[data-reveal='in'] > .b-mascara__linha { transform: none; }

.b-filete {
  display: block;
  height: var(--bw-focus);
  background: var(--gold);
  transform-origin: left center;
}
.b-filete[data-reveal='in'] { transform: scaleX(1); }

/* ============================================================
   2. A SOMBRA QUE SEGUE O CURSOR
   Um disco dourado difuso, fixo ao ecrã, que acompanha o rato com
   um ligeiro atraso e desaparece quando ele pára.

   Detalhes que fazem a diferença entre suave e lento:
   — o JavaScript posiciona por translate3d dentro de um
     requestAnimationFrame, uma vez por fotograma;
   — NÃO há transição no transform. Pôr uma transição num elemento
     que se move continuamente obriga o browser a interpolar
     centenas de animações encadeadas, e é isso que faz o site
     parecer pesado;
   — o esbatimento é feito com paragens do gradiente, não com
     filter: blur(), que recalcularia a área toda a cada fotograma.
   ============================================================ */
.b-cursor {
  position: fixed;
  top: 0;
  left: 0;
  width: 340px;
  height: 340px;
  border-radius: 50%;
  background: radial-gradient(
    circle at center,
    rgba(201, 169, 110, 0.20) 0%,
    rgba(201, 169, 110, 0.10) 24%,
    rgba(122, 91, 42, 0.04) 46%,
    transparent 64%
  );
  pointer-events: none;
  z-index: 5;
  transform: translate3d(-9999px, -9999px, 0);
  opacity: 0;
  transition: opacity var(--dur-rapida) var(--ease-saida);
  will-change: transform, opacity;
}
.b-cursor[data-activo='true'] { opacity: 1; }

/* Sobre fundos escuros o dourado tem de ser mais forte para se ver. */
.b-cursor[data-fundo='escuro'] {
  background: radial-gradient(
    circle at center,
    rgba(232, 213, 176, 0.16) 0%,
    rgba(201, 169, 110, 0.09) 26%,
    transparent 62%
  );
}

/* Num ecrã tátil não há cursor para seguir. */
@media (hover: none), (pointer: coarse) {
  .b-cursor { display: none; }
}

/* ============================================================
   3. CARTÕES QUE ACENDEM
   O mesmo princípio, mas dentro do elemento: um foco dourado
   centrado nas coordenadas --mx/--my, que o JavaScript actualiza
   enquanto o rato percorre o cartão.
   ============================================================ */
[data-hoverable] {
  position: relative;
  overflow: hidden;
  transition:
    transform var(--dur-media) var(--ease-saida),
    box-shadow var(--dur-media) var(--ease-saida),
    border-color var(--dur-media) var(--ease-saida);
  will-change: transform;
}
[data-hoverable]::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    240px circle at var(--mx, 50%) var(--my, 50%),
    rgba(201, 169, 110, 0.20),
    rgba(201, 169, 110, 0.05) 38%,
    transparent 62%
  );
  opacity: 0;
  transition: opacity var(--dur-media) var(--ease-saida);
  pointer-events: none;
  z-index: 0;
}
[data-hoverable]:hover {
  transform: translateY(-4px);
  border-color: var(--line-strong);
  box-shadow: 0 28px 56px -32px rgba(26, 23, 20, 0.30);
}
[data-hoverable]:hover::before { opacity: 1; }
/* O conteúdo tem de ficar por cima do foco, senão o gradiente
   passa-lhe à frente e o texto perde contraste. */
[data-hoverable] > * { position: relative; z-index: 1; }

/* ============================================================
   4. A ABERTURA DA PÁGINA INICIAL
   A única animação que corre sozinha, sem esperar por scroll.
   Três camadas com tempos diferentes: o brilho de fundo abre
   devagar, as linhas do título sobem em cascata por trás da
   máscara, e os botões chegam por último.
   ============================================================ */
.b-hero { position: relative; overflow: hidden; }

/* Camada de luz — dois focos dourados que se afastam do centro e
   respondem à posição do rato através de --px/--py (definidos pelo
   JavaScript, com muito pouco curso: é ambiente, não é um jogo). */
.b-hero__luz {
  position: absolute;
  inset: -20% -10%;
  pointer-events: none;
  z-index: 0;
  opacity: 0;
  background:
    radial-gradient(46% 52% at calc(28% + var(--px, 0px)) calc(32% + var(--py, 0px)),
                    rgba(201, 169, 110, 0.22), transparent 70%),
    radial-gradient(40% 46% at calc(78% - var(--px, 0px)) calc(68% - var(--py, 0px)),
                    rgba(232, 213, 176, 0.20), transparent 72%);
  animation: bLuzEntra 2400ms var(--ease-saida) 120ms forwards;
  transition: background-position var(--dur-lenta) var(--ease-saida);
}
@keyframes bLuzEntra { to { opacity: 1; } }

/* Grão: uma textura de ruído muito ténue por cima da luz, que tira
   ao gradiente o aspecto de plástico e lhe dá o de papel impresso.
   É um SVG embutido — nenhum pedido extra ao servidor. */
.b-hero__grao {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0.28;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='r'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23r)' opacity='0.42'/%3E%3C/svg%3E");
}

.b-hero__conteudo { position: relative; z-index: 2; }

/* Entrada em cascata no carregamento. Cada filho espera mais um
   tempo do que o anterior — sem JavaScript nenhum. */
/* Aqui a animação é CSS puro e corre mesmo sem JavaScript, mas o
   estado inicial continua a ser opacidade zero — e um navegador que
   não anime (por definição do utilizador, por modo de poupança) podia
   deixá-la assim. O `forwards` garante o estado final, e a media query
   garante que sem animação nada chega a esconder-se. */
.b-entrada > * {
  opacity: 0;
  transform: translateY(18px);
  animation: bSobe var(--dur-lenta) var(--ease-saida) forwards;
}
.b-entrada > *:nth-child(1) { animation-delay: calc(var(--stagger) * 1); }
.b-entrada > *:nth-child(2) { animation-delay: calc(var(--stagger) * 2); }
.b-entrada > *:nth-child(3) { animation-delay: calc(var(--stagger) * 3); }
.b-entrada > *:nth-child(4) { animation-delay: calc(var(--stagger) * 4); }
.b-entrada > *:nth-child(5) { animation-delay: calc(var(--stagger) * 5); }
.b-entrada > *:nth-child(6) { animation-delay: calc(var(--stagger) * 6); }
@keyframes bSobe { to { opacity: 1; transform: none; } }

/* ── QUEM JÁ CÁ ESTEVE NÃO É SAUDADO OUTRA VEZ ──────────────────
   Um pequeno script no <head> marca o documento com data-hero-visto
   quando a abertura já foi mostrada nesta sessão. A partir daí, a
   cascata e as máscaras do título não voltam a correr: quem vai a
   /servicos/ e volta ao início encontra a página composta, em vez de
   assistir à mesma apresentação pela terceira vez.

   A animação PERMANENTE — o pó dourado — não é afectada: essa é
   ambiente, e ambiente não se desliga. O que aqui pára é só a
   coreografia de chegada. */
[data-hero-visto] .b-entrada > * { opacity: 1; transform: none; animation: none; }
[data-hero-visto] .b-linha > span { transform: none; animation: none; }
[data-hero-visto] .b-hero__luz { opacity: 1; animation: none; }
[data-hero-visto] .b-scroll-dica { animation-delay: 0ms; }

/* O título da abertura entra linha a linha, por trás da máscara.
   Escreve-se em HTML com uma <span class="b-linha"> por linha —
   assim o texto continua a ser texto para quem lê com leitor de
   ecrã e para os motores de busca. */
.b-linha { display: block; overflow: hidden; }
.b-linha > span {
  display: block;
  transform: translateY(105%);
  animation: bLinhaSobe var(--dur-lenta) var(--ease-saida) forwards;
}
.b-linha:nth-of-type(1) > span { animation-delay: 260ms; }
.b-linha:nth-of-type(2) > span { animation-delay: 360ms; }
.b-linha:nth-of-type(3) > span { animation-delay: 460ms; }
.b-linha:nth-of-type(4) > span { animation-delay: 560ms; }
@keyframes bLinhaSobe { to { transform: none; } }

/* Indicador de scroll: um traço que desce em ciclo, a dizer que a
   página continua. Desaparece assim que se começa a percorrer. */
.b-scroll-dica {
  position: absolute;
  bottom: var(--sp-6);
  left: 50%;
  translate: -50% 0;
  z-index: 2;
  width: var(--bw);
  height: var(--sp-8);
  background: linear-gradient(var(--gold), transparent);
  opacity: 0;
  animation: bDica 2600ms var(--ease-saida) 1400ms infinite;
  transition: opacity var(--dur-media) var(--ease-saida);
}
.b-scroll-dica[data-fora='true'] { opacity: 0 !important; animation: none; }
@keyframes bDica {
  0% { opacity: 0; transform: scaleY(0); transform-origin: top; }
  40% { opacity: 1; transform: scaleY(1); transform-origin: top; }
  70% { opacity: 1; transform: scaleY(1); transform-origin: bottom; }
  100% { opacity: 0; transform: scaleY(0); transform-origin: bottom; }
}

/* ============================================================
   5. DESLOCAMENTO POR SCROLL (parallax)
   O JavaScript escreve --scroll-y em cada elemento marcado com
   data-parallax; o CSS decide o que fazer com esse número. Manter
   a matemática do lado do CSS permite variar o efeito por elemento
   sem tocar no JavaScript.
   ============================================================ */
[data-parallax] { will-change: transform; }
[data-parallax='lento'] { transform: translate3d(0, calc(var(--scroll-y, 0) * 0.12px), 0); }
[data-parallax='medio'] { transform: translate3d(0, calc(var(--scroll-y, 0) * 0.22px), 0); }

/* Barra fina no topo a marcar quanto falta ler. Custa quase nada e
   dá a quem lê a noção de comprimento da página. */
.b-progresso {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: var(--bw-focus);
  z-index: 60;
  background: var(--gold);
  transform: scaleX(var(--lido, 0));
  transform-origin: left center;
  pointer-events: none;
}

/* ============================================================
   6. NÚMEROS QUE CONTAM
   O valor final vive no HTML (é o que fica se o JavaScript falhar);
   o JavaScript limita-se a contar até lá quando a secção aparece.
   As casas ficam com largura fixa para o número não abanar.
   ============================================================ */
.b-conta {
  /* Algarismos de largura fixa: sem isto, «7» é mais estreito do que
     «0» e o número abana enquanto conta. */
  font-variant-numeric: tabular-nums;
  /* E isto resolve o resto: enquanto o contador sobe de 0 a 51, passa
     de um algarismo para dois, e o parágrafo à volta refluía a cada
     mudança. Medido em 0,018 de deslocamento acumulado — pouco, mas
     é deslocamento gratuito, e o Google conta-o. Reservar a largura
     de dois algarismos e alinhar à direita mantém tudo quieto. */
  display: inline-block;
  min-width: 2ch;
  text-align: right;
}

/* ============================================================
   7. LIGAÇÕES E BOTÕES COM VIDA
   ============================================================ */
/* Sublinhado que se desenha do centro para fora. */
.b-link-anim {
  position: relative;
  text-decoration: none;
}
.b-link-anim::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.15em;
  height: var(--bw);
  background: currentColor;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-media) var(--ease-saida);
}
.b-link-anim:hover::after,
.b-link-anim:focus-visible::after { transform: scaleX(1); }

/* Uma seta que avança quando se passa por cima do botão. */
.btn .b-seta { transition: transform var(--dur-media) var(--ease-saida); }
.btn:hover .b-seta { transform: translateX(var(--sp-2)); }

/* Imagens que aproximam devagar dentro do próprio recorte. */
.b-moldura { overflow: hidden; position: relative; }
.b-moldura img,
.b-moldura svg {
  display: block;
  width: 100%;
  transition: transform var(--dur-lenta) var(--ease-saida);
}
[data-hoverable]:hover .b-moldura img,
[data-hoverable]:hover .b-moldura svg,
.b-moldura:hover img,
.b-moldura:hover svg { transform: scale(1.035); }

/* ============================================================
   8. MOVIMENTO REDUZIDO — a cláusula que anula tudo o resto.
   Não é uma cortesia: quem activou esta preferência costuma ter
   uma razão médica para o fazer.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .b-reveal,
  .b-reveal--esq,
  .b-reveal--dir { opacity: 1 !important; transform: none !important; transition: none !important; }
  .b-mascara > .b-mascara__linha,
  .b-linha > span { transform: none !important; animation: none !important; transition: none !important; }
  .b-filete { transform: scaleX(1) !important; transition: none !important; }
  .b-entrada > * { opacity: 1 !important; transform: none !important; animation: none !important; }
  .b-hero__luz { opacity: 1 !important; animation: none !important; }
  .b-cursor { display: none !important; }
  .b-scroll-dica { display: none !important; }
  [data-parallax] { transform: none !important; }
  [data-hoverable]:hover { transform: none !important; }
  .b-moldura img, .b-moldura svg { transition: none !important; }
}
