/* ---------------------------------------------------------------------------
   motion.css — the site's motion foundation. Loaded by EVERY page, Framer-
   exported and store alike, so both pipelines animate with one vocabulary.

   Structure (read this before adding anything):

   1. TOKENS      all durations/easings live here as custom properties. New
                  animation work uses tokens, never literal ms/bezier values —
                  that's what keeps the site feeling like one piece.
   2. NAV         state classes for the hide/show nav. JS (motion.js) only
                  flips `m-nav-hidden` on <html>; every visual consequence is
                  CSS, so restyling the gesture never touches logic.
   3. REVEAL      scroll-in for generated pages ([data-mreveal]). Framer pages
                  keep their own [data-reveal] path in runtime.js — untouched.
   4. TRANSITIONS cross-document view transitions. Pure CSS; pages opt in by
                  carrying this stylesheet. Elements gain continuity by getting
                  a `view-transition-name` — see store.mjs for the card→product
                  morph, and motion.js for the nav.

   The easing values are not invented: they were read out of Framer's own
   published bundles (see src/client/runtime.js) so new motion matches the
   original site's physics.
   --------------------------------------------------------------------------- */

:root {
  /* durations */
  --m-dur-1: 200ms;   /* micro: hovers, presses, small fades          */
  --m-dur-2: 420ms;   /* structural: nav slide, panel moves           */
  --m-dur-3: 800ms;   /* entrances: reveals — Framer's own 0.8s       */
  --m-dur-curtain: 1000ms; /* a folha da cortina saindo de uma vez      */
  --m-dur-acc: 880ms; /* acordeão do case no scroll: mais longo que o painel */

  /* easings — measured from Framer's bundles */
  --m-ease:        cubic-bezier(.44, 0, .56, 1);      /* standard tween    */
  --m-ease-exit:   cubic-bezier(.32, -.01, .15, .98); /* intro/exit curve  */
  --m-ease-spring: cubic-bezier(.34, 1.26, .64, 1);   /* hover spring ≈ bounce .2 */
  --m-ease-acc:    cubic-bezier(.4, 0, .15, 1);       /* sai devagar e pousa */

  /* reveal geometry */
  --m-rise: 24px;

  /* nav thresholds are JS constants (motion.js), not CSS — they're inputs,
     not styles. Only the *response* lives here. */
}

/* --- 2. nav ---------------------------------------------------------------- */

/* The wrapper is tagged data-site-nav — at build time on store pages, by
   motion.js on Framer pages (it finds the pinned container at runtime, so a
   re-export from Framer can't silently orphan a hardcoded selector).

   `translate` (not `transform`) on purpose: Framer centres fixed elements with
   transform, and the individual property composes with it instead of clobbering. */
/* The attribute is doubled on purpose. Framer styles this element through
   `.framer-raLMF .framer-5ozm90-container` — two classes — so a single
   attribute selector loses and the z-index below silently did nothing.
   Doubling matches that specificity and wins on order, which beats !important:
   a later rule can still override this one normally. */
[data-site-nav][data-site-nav] {
  transition:
    translate var(--m-dur-2) var(--m-ease),
    opacity   var(--m-dur-2) var(--m-ease);

  /* Above everything. Framer gave the nav z-index 8, but the home <header>
     opens its own stacking context at z-index 7 one level up — so the pointer
     trail living inside that header painted straight over the menu. The nav is
     the one element that must never be covered; pinning it here also protects
     it from whatever overlay comes next. */
  z-index: 50;
}

/* -100% clears the element itself; the extra 80px clears Framer's `top: 50px`
   offset too, so it leaves the viewport instead of resting just off the edge. */
html.m-nav-hidden [data-site-nav][data-site-nav] {
  translate: 0 calc(-100% - 80px);
  opacity: 0;
  pointer-events: none;
}

/* Case pages keep FEL/WORK/ABOUT + accordion on screen for the mosaic scroll.
   copy.css repeats this so it wins even if motion.css loads first. */
html.m-nav-hidden body[data-project-page] [data-site-nav][data-site-nav] {
  translate: none;
  opacity: 1;
  pointer-events: auto;
}

/* A FOTO DO ABOUT.
   ---------------------------------------------------------------------------
   O retrato é 4:5 (1639×2048). O Framer abre uma janela de 418px de altura
   com `object-position: right center` — em qualquer tela larga isso come
   o topo da cabeça. Pedido (14/09): o rosto não pode estar cortado em
   nenhuma resolução.

   A janela passa a ter a mesma razão do arquivo, ancorada no topo-direita
   (ele está à direita do quadro). Cover sobre 4:5 em 4:5 não recorta o
   rosto; recortaria o torso, e o torso não é o assunto. O parallax de
   `translateY` saiu: ele empurrava a cara para fora da janela no scroll. */
[data-framer-name="DSC04005-2-min"] {
  overflow: hidden !important;
  height: auto !important;
  aspect-ratio: 4 / 5 !important;
}
[data-framer-name="DSC04005-2-min"] > div {
  overflow: hidden !important;
}
[data-framer-name="DSC04005-2-min"] img {
  /* !important: o Framer grava object-position/height no style inline.
     A janela agora é 4:5, igual ao arquivo — `right center` enquadra o
     rosto. `100% 0%` mostrava a parede cinza no teto do first block. */
  object-fit: cover !important;
  object-position: right center !important;
  width: 100% !important;
  height: 100% !important;
  transform: none !important;
  animation: none !important;
}

/* --- 3. reveal ------------------------------------------------------------- */

/* Armed by JS only for elements that start below the fold — content is never
   hidden by default, so no-JS and reduced-motion users see everything, always.
   Removing the attribute plays the entrance. Same arming pattern as runtime.js. */
[data-mreveal] {
  transition:
    opacity   var(--m-dur-3) var(--m-ease),
    transform var(--m-dur-3) var(--m-ease);
}

[data-mreveal][data-mreveal-armed] {
  opacity: 0;
  transform: translateY(var(--m-rise));
}

/* --- 4. cross-document view transitions ------------------------------------ */

/* Same-origin navigations crossfade instead of flashing white. Elements that
   share a view-transition-name morph between pages:
     · a project card's image morphs into the case hero (`proj-<slug>`, named
       by motion.js after layout so duplicate breakpoint cards don't abort)
     · a store card's image morphs into the product hero (store.mjs, vt-<id>)
   A barra FEL/WORK/ABOUT NÃO morphs: as caixas não combinam (ver grupo
   `site-nav` abaixo). Browsers without support simply navigate. */
@view-transition { navigation: auto; }

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--m-dur-2);
  animation-timing-function: var(--m-ease);
}

/* A barra pintada ganha `site-nav` no pageswap/pagereveal (boot-vt.js).
   O grupo NÃO interpola: home é vidro no meio do hero, about/projects são
   sticky à esquerda, curriculum é mix-blend. Morphar essas caixas é o samba.
   Esconder o grupo tira os chips do `root` — o conteúdo crossfada sozinho,
   o morph `proj-*` (card→hero) segue, e os chips reaparecem no sítio certo. */
::view-transition-group(site-nav),
::view-transition-group(fel-cam),
::view-transition-group(fel-cam-mira),
::view-transition-group(fel-hud) {
  animation: none;
  opacity: 0;
}
::view-transition-old(site-nav),
::view-transition-new(site-nav),
::view-transition-old(fel-cam),
::view-transition-new(fel-cam),
::view-transition-old(fel-cam-mira),
::view-transition-new(fel-cam-mira),
::view-transition-old(fel-hud),
::view-transition-new(fel-hud) {
  animation: none;
  mix-blend-mode: normal;
}

/* Enquanto a cortina cobre, a barra não pode vazar por cima (z-index 50). */
html:not(.fel-intro-done) [data-site-nav],
html:not(.fel-intro-done) .cv-nav,
html:not(.fel-intro-done) .fel-cam,
html:not(.fel-intro-done) .fel-cam-mira,
html:not(.fel-intro-done) #hud {
  visibility: hidden;
}

/* Sessão que já viu a cortina: o overlay não pode pintar um quadro marrom. */
html.fel-intro-done #fel-loader {
  display: none !important;
}

/* --- reduced motion --------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  [data-site-nav],
  [data-mreveal] {
    transition: none;
  }
  [data-mreveal][data-mreveal-armed] {
    opacity: 1;
    transform: none;
  }
  [data-framer-name="DSC04005-2-min"] img {
    animation: none;
    height: 100%;
    transform: none;
  }
  @view-transition { navigation: none; }
}

/* --- smooth anchors ---------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* --- /projects loader + row entrance -------------------------------------- */

/* Synthetic DESIGN by FEL curtain. Injected only on /projects (no nQ9AK).
   First-paint brown lives in the boot <style> in transform.mjs; this is the
   sequence: words rise, curtain scaleY(0) from the top, same tokens as home. */
#fel-loader {
  position: fixed;
  inset: 0;
  z-index: 80;
  pointer-events: auto;
  overflow: hidden;              /* a folha é mais alta que a tela; ver abaixo */
  background: var(--c-brown, #2b1b0d);
}
#fel-loader.is-ready {
  background: transparent;
}

/* ===========================================================================
   A FOLHA — a cortina e o texto numa peça só, que sai junta.

   O DEFEITO QUE ISTO CONSERTA (Felipe, 11/09): "o background está
   desaparecendo antes do texto". Era verdade e a conta explica: a cortina
   fazia `scaleY(0)` a partir do topo, varrendo a altura inteira da tela
   (~900px em 1s), enquanto as palavras andavam `translateY(-110%)` dentro
   do próprio clip — 110% de uma palavra de 79px são 87px. Mesmo 1000ms,
   dois percursos com uma ordem de grandeza de diferença: o marrom já tinha
   ido embora e o texto ainda estava pendurado sobre a página revelada.

   O CONSERTO É UMA MÁSCARA, que é como ele descreveu: "como se o background
   tivesse uma máscara ali levando o texto". Cortina e palavras passam a
   morar na mesma folha, e quem sai é a folha — um `translateY` só. O texto
   não tem mais percurso próprio; ele é carregado.

   E O DEGRADÊ VEM DE GRAÇA NO MESMO GESTO. A máscara é um gradiente com a
   borda de baixo esfumada, então a linha que revela a página não é um corte
   seco: é uma dissolvência de 30vh subindo. É o pedido do degradê/blur, e
   ele sai da mesma peça em vez de virar um elemento a mais.

   POR QUE 130vh E NÃO 100. A parte esfumada da máscara precisa ficar FORA
   da tela enquanto a folha está parada — senão a página apareceria por
   baixo do marrom antes de a animação começar. Com 130vh de folha e o opaco
   indo até 76,9% (= exatamente 100vh), a tela em repouso vê só o opaco, e
   os 30vh de degradê esperam abaixo da dobra.

   `transform` e nada mais: é a única propriedade que o compositor anima sem
   repintar, e isto ocupa a tela inteira. Animar `mask-position` daria o
   mesmo desenho repintando 1s de tela cheia. */
.fel-loader-folha {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 130vh;
  -webkit-mask-image: linear-gradient(to bottom, #000 0 76.9%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0 76.9%, transparent 100%);
  will-change: transform;
}
.fel-loader-curtain {
  position: absolute;
  inset: 0;
  background: var(--c-brown, #2b1b0d);
}
/* CENTRALIZADA E NO CORPO ORIGINAL — e vale registrar a ida e a volta.

   Em 09/09 o Felipe pediu a assinatura "de uma borda à outra", e ela foi: o
   corpo passou a 14,2vw (calibrado por medição — "DESIGN by FEL" em Maxeville
   Construct mede 645,2px a cada 100px de corpo) com `space-between` para as
   pontas encostarem nas bordas.

   Em 10/09 ele desfez: "quero que o design by FEL volte a ter o tamanho
   anterior, menor e centralizado". Fica o que era antes — clamp(36px, 10vw,
   79px) e `center` — e fica também esta nota, porque a medição continua
   válida se um dia a ideia voltar: 14,2vw enche a linha, e o `space-between`
   é o que faz "borda a borda" ser exato em vez de aproximado. */
/* Altura de UMA tela, e não `inset: 0`: dentro de uma folha de 130vh o
   `align-items: center` centralizaria a assinatura em 65vh — abaixo do meio
   da tela. */
.fel-loader-words {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  z-index: 2;
  overflow: hidden;
  pointer-events: none;
}
/* A frase de conceito precisa de margem lateral: ela quebra em linhas e
   encostar na borda deixaria a leitura apertada. A assinatura não precisa —
   ela é uma linha só, e já está centralizada. */
#fel-loader[data-conceito] .fel-loader-words { padding: 0 5vw; }
.fel-loader-clip {
  display: block;
  overflow: hidden;
  line-height: 1;
}
/* O clip da frase tem de caber várias linhas — com line-height:1 herdado ele
   cortaria as descidas da última. */
#fel-loader[data-conceito] .fel-loader-clip { line-height: inherit; padding-bottom: .08em; }
.fel-loader-word {
  display: block;
  font-family: 'Maxeville Construct', 'Maxeville Regular', sans-serif;
  font-size: clamp(36px, 10vw, 79px);
  color: #fff;
  line-height: 1;
  white-space: nowrap;
  transform: translateY(100%);
}

/* A FRASE DE CONCEITO — corpo fixo e centralizada (escolha do Felipe entre as
   três que eu propus). As frases vão de 17 caracteres ("Feeding the world") a
   68 ("An investment company opening space for real people in the ETF world"),
   e corpo fixo significa que a CURTA respira e a LONGA quebra em mais linhas —
   o ritmo tipográfico é o mesmo em todo projeto, que é o oposto de escalar
   cada uma para encher a tela.

   Construct e não Tekst apesar de ser uma frase: a regra da casa é por
   TAMANHO (Tekst abaixo de 18 pt), e aqui o corpo passa de 28px em qualquer
   tela. É a voz de abertura, não texto corrido. */
.fel-loader-frase {
  font-size: clamp(26px, 4.6vw, 58px);
  line-height: 1.12;
  white-space: normal;            /* desfaz o nowrap das palavras da assinatura */
  max-width: 19ch;
  text-align: center;
  text-wrap: balance;             /* linhas de comprimento parecido, não uma órfã */
}
#fel-loader.is-arrive .fel-loader-word {
  transform: translateY(0);
  transition: transform var(--m-dur-3) var(--m-ease);
}
#fel-loader.is-arrive .fel-loader-clip:nth-child(2) .fel-loader-word {
  transition-delay: 110ms;
}
#fel-loader.is-arrive .fel-loader-clip:nth-child(3) .fel-loader-word {
  transition-delay: 220ms;
}
/* A SAÍDA É UMA SÓ. As palavras não têm mais fuga própria e a cortina não
   encolhe mais: quem se move é a folha, e ela leva os dois. Era a existência
   de dois percursos diferentes que fazia o texto sobreviver ao fundo. */
#fel-loader.is-leave .fel-loader-folha {
  transform: translateY(-100%);
  transition: transform var(--m-dur-curtain) var(--m-ease-exit);
}

html:not(.fel-intro-done) body[data-projects-page] {
  overflow: hidden;
}

/* Rows hold still under the curtain, then ease in with the reveal vocabulary. */
body[data-projects-page] a[data-project-name] {
  transition:
    opacity var(--m-dur-3) var(--m-ease) var(--fel-row-delay, 0ms),
    transform var(--m-dur-3) var(--m-ease) var(--fel-row-delay, 0ms);
}
body[data-projects-page] a[data-project-name][data-fel-row-armed] {
  opacity: 0;
  transform: translateY(var(--m-rise));
}

@media (prefers-reduced-motion: reduce) {
  #fel-loader {
    display: none !important;
  }
  html:not(.fel-intro-done) body[data-projects-page] {
    overflow: auto;
  }
  body[data-projects-page] a[data-project-name],
  body[data-projects-page] a[data-project-name][data-fel-row-armed] {
    transition: none;
    opacity: 1;
    transform: none;
  }
}

/* A INTRO DO FRAMER, NA HOME, VOLTOU A SER A DELE.

   Houve aqui um bloco que a punha de borda a borda: largura e altura soltas
   nos dois níveis de invólucro e o corpo em 14,2vw. Saiu em 10/09 a pedido do
   Felipe ("menor e centralizado"), e sair inteiro é o ponto — soltar só parte
   das medidas travadas do Framer foi o que produziu, em sequência, as
   palavras atropeladas e depois a faixa cortada no meio das letras. Sem
   override nenhum, a cortina volta a ser exatamente a que o export desenhou. */
