/* ===========================================================================
   CORREDOR — protótipo de galeria em fluxo de profundidade
   ---------------------------------------------------------------------------
   MECANISMO EM UMA FRASE: as imagens existem num espaço com profundidade e
   caminham continuamente na direção do observador; ao passar por ele, cada uma
   se dissolve e renasce no fundo com outro projeto. Ninguém nasce do gesto —
   o gesto MIRA e ACELERA um fluxo que já estava acontecendo.

   AS LEIS DA CASA, RESPEITADAS ASSIM:
   · motion.css decide COMO: todas as TRANSIÇÕES discretas daqui (o crossfade
     do upgrade de nitidez, a legenda, a cortina do intro) usam os tokens
     --m-dur-* / --m-ease-*, copiados verbatim de src/styles/motion.css.
   · o JS decide QUANDO — e aqui ele decide também ONDE, que é coisa diferente
     de "quando". A posição em z é ESTADO CONTÍNUO, não transição: tem
     velocidade (px/s), não duração nem easing. É por isso que não existe
     keyframe nenhum governando o voo.
   · E há um motivo técnico duro para isso, medido no dossiê: o Chrome NÃO
     re-rasteriza uma camada quando a escala muda por CSS animation / Web
     Animations — ele reaproveita um bitmap velho, e a imagem perde 17-24% de
     detalhe em voo. Escrevendo transform por quadro no JS ele re-rasteriza e a
     imagem fica nítida. Num portfólio de direção de arte, isso não é detalhe.

   POR QUE PERSPECTIVE + FILHOS DIRETOS, E NÃO preserve-3d:
   O <header> real tem overflow:hidden, e overflow != visible ACHATA
   transform-style:preserve-3d em silêncio (o getComputedStyle mente e diz
   "preserve-3d" mesmo achatado). Mas isso só morde 3D ANINHADO. Perspective no
   pai + filhos DIRETOS em translate3d funciona perfeito sob overflow:hidden.
   Portanto: .palco tem a perspectiva e o clip; cada folha é filha DIRETA dele.
   Nada de cenário, nada de preserve-3d, nada de depender da classe-hash
   .framer-15ylwyw-container (que é um hash do Framer e pode sumir num
   re-export).

   CONSEQUÊNCIA DISSO, e é o único truque manual do arquivo: com
   transform-style flat, o navegador NÃO ordena os irmãos por profundidade —
   ele pinta na ordem do documento. Então a oclusão (quem tapa quem) é
   resolvida à mão, escrevendo z-index por ordem de z. Ver ordenarZ().
   =========================================================================== */

/* ---------------------------------------------------------------------------
   O PALCO. Reproduz as condições medidas do hero real:
   1440x900 no desktop, overflow hidden, e o gradiente do <header> como
   background PRÓPRIO (não como camada extra) — porque no site ele é o
   background do header e portanto pinta ABAIXO de todos os filhos.

   O gradiente não é enfeite: ele é a razão de várias decisões aqui. A rampa
   vertical vai de ~243 de luminância no topo a 0 no rodapé. Ou seja: o topo do
   hero é quase BRANCO e o rodapé é preto puro. Isso proíbe usar escurecimento
   como pista de profundidade (o fundo troca de polaridade no meio do caminho)
   e obriga o campo a viver numa banda vertical onde as duas polaridades de
   imagem sobrevivem — daí o parâmetro "centro Y".
   --------------------------------------------------------------------------- */
#palco{
  position:absolute; inset:0;
  overflow:hidden;                 /* igual ao header real */
  perspective: var(--p, 1060px);
  perspective-origin: 50% var(--cy, 50%);
  background: linear-gradient(#fff -5%, #00000017 100%);
}
#palco.sem-gradiente{ background:#000; }

/* ---------------------------------------------------------------------------
   A FOLHA. Ele disse "ARQUIVOS se movimentando", não partículas — então cada
   peça é uma prancha: proporção REAL da imagem (sem crop), um leve giro em Y
   para ganhar plano próprio, e uma inclinação mínima em Z para não parecer
   um azulejo alinhado. Nada de sombra, nada de borda, nada de moldura: a
   espacialidade tem de sair da própria imagem.
   --------------------------------------------------------------------------- */
.folha{
  position:absolute;
  left:50%; top:var(--cy, 50%);    /* centro do box no ponto de fuga */
  display:block;
  will-change: transform, opacity;
  backface-visibility:hidden;
  transform-style:flat;            /* explícito: cada folha é um plano só */
  text-decoration:none;
  outline:none;
  -webkit-user-drag:none;
}
.folha img{
  position:absolute; inset:0;
  width:100%; height:100%;
  display:block;
  /* nenhum object-fit: a caixa JÁ está na proporção real do arquivo.
     Hoje o rastro força tudo em aspect-ratio 4/5 com background-size:cover,
     o que corta 40 das 74 imagens do acervo (as 16:9 e as 1.62). */
}
/* a segunda camada é o upgrade de nitidez (-960) que entra por cima da -640
   quando a folha chega perto. Crossfade com token, porque ISSO é transição. */
.folha img.hi{
  opacity:0;
  transition: opacity var(--m-dur-2) var(--m-ease);
}
.folha:focus-visible{
  outline:1px solid rgba(255,255,255,.7);
  outline-offset:6px;
}

/* ---------------------------------------------------------------------------
   LEGENDA. O nome do projeto NÃO fica preso na folha: uma folha que passa
   perto está acelerando em tela e o texto vira borrão ilegível. A legenda
   mora num slot fixo no rodapé esquerdo — sóbrio, tipográfico, parado.
   --------------------------------------------------------------------------- */
#legenda{
  position:absolute; left:32px; bottom:30px; z-index:40;
  font-size:11px; letter-spacing:.14em; text-transform:uppercase;
  color:#fff; mix-blend-mode:difference;
  opacity:0; transform:translateY(4px);
  transition: opacity var(--m-dur-1) var(--m-ease),
              transform var(--m-dur-1) var(--m-ease);
  pointer-events:none;
}
#legenda.on{ opacity:1; transform:translateY(0); }
/* Duas linhas: o nome pesa, as tags recuam. mix-blend-mode: difference no pai
   resolve as duas polaridades do gradiente do hero (topo quase branco, rodapé
   preto) sem precisar de caixa, sombra ou scrim atrás do texto. */
#legenda{ display:flex; flex-direction:column; gap:3px; }
.leg-nome{ font-size:11px; letter-spacing:.14em; }
.leg-tags{ font-size:9.5px; letter-spacing:.1em; opacity:.62; }

/* A folha é um link: o cursor tem de dizer isso. Só as folhas da frente
   recebem pointer-events (o JS decide, ver atualizar()) — no fundo elas são
   pequenas e rápidas, e clicar ali seria loteria. */
.folha[href]{ cursor:pointer; }

/* --- moldura de referência 1440x900 (só para calibrar) -------------------- */
#moldura{
  position:absolute; left:50%; top:50%; width:1440px; height:900px;
  transform:translate(-50%,-50%);
  border:1px solid rgba(255,0,80,.35);
  pointer-events:none; z-index:35; display:none;
}
#moldura.on{ display:block; }


/* --- cortina do intro (simulação do initIntro real) ----------------------
   O initIntro cobre o hero com uma chapa marrom OPACA do primeiro pixel até
   ~1800ms, e só limpa de verdade em ~2700ms. Consequência de projeto: uma
   galeria que arranque vazia e vá enchendo anima 1,7s no escuro e o visitante
   recebe um palco pela metade. Por isso o corredor nasce JÁ POVOADO em toda a
   profundidade — quando a cortina sobe, o acervo já está em movimento. */
#cortina{
  position:absolute; inset:0; z-index:45; background:#2b1b0d;
  transform-origin: 50% 0%;
  pointer-events:none;
}
#cortina.sobe{
  transform: scaleY(0);
  transition: transform 1000ms var(--m-ease-exit);
}
#cortina.fora{ display:none; }

/* --- HUD ------------------------------------------------------------------ */
#hud{
  position:fixed; right:18px; bottom:18px; z-index:60;
  width:294px; max-height:82vh;
  font-size:10px; line-height:1.5; letter-spacing:.02em;
  background:rgba(8,8,8,.82);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,.13);
  color:rgba(255,255,255,.82);
}
#hud > summary{
  cursor:pointer; padding:8px 11px; list-style:none;
  text-transform:uppercase; letter-spacing:.16em; font-size:9px;
  color:rgba(255,255,255,.5);
  display:flex; justify-content:space-between; align-items:center;
}
#hud > summary::-webkit-details-marker{ display:none; }
#hud[open] > summary{ border-bottom:1px solid rgba(255,255,255,.1); }
#hud .corpo{ padding:6px 11px 11px; overflow-y:auto; max-height:calc(82vh - 34px); }
#hud fieldset{
  border:0; border-top:1px solid rgba(255,255,255,.08);
  margin:9px 0 0; padding:8px 0 0;
}
#hud legend{
  padding:0 0 3px; font-size:8px; letter-spacing:.2em;
  color:rgba(255,255,255,.38); text-transform:uppercase;
}
#hud .lin{ display:grid; grid-template-columns: 1fr 46px; gap:6px; align-items:center; margin:3px 0; }
#hud .lin > label{ color:rgba(255,255,255,.62); }
#hud .lin > b{ text-align:right; font-weight:400; color:#fff; font-variant-numeric:tabular-nums; }
#hud input[type=range]{
  grid-column:1 / -1; width:100%; height:12px; margin:0 0 2px;
  -webkit-appearance:none; appearance:none; background:transparent; cursor:ew-resize;
}
#hud input[type=range]::-webkit-slider-runnable-track{ height:1px; background:rgba(255,255,255,.26); }
#hud input[type=range]::-webkit-slider-thumb{
  -webkit-appearance:none; width:9px; height:9px; margin-top:-4px;
  border-radius:50%; background:#fff;
}
#hud .chk{ display:flex; align-items:center; gap:6px; margin:4px 0; color:rgba(255,255,255,.62); cursor:pointer; }
#hud .chk input{ accent-color:#fff; width:11px; height:11px; margin:0; }
#hud .btns{ display:flex; gap:5px; margin-top:9px; flex-wrap:wrap; }
#hud button{
  flex:1 1 auto; font:inherit; font-size:9px; letter-spacing:.1em; text-transform:uppercase;
  padding:5px 6px; color:rgba(255,255,255,.8); background:transparent;
  border:1px solid rgba(255,255,255,.2); cursor:pointer;
}
#hud button:hover{ background:rgba(255,255,255,.09); color:#fff; }
#hud .medidor{
  display:flex; justify-content:space-between; font-variant-numeric:tabular-nums;
  color:rgba(255,255,255,.44); padding-top:7px; margin-top:4px;
  border-top:1px solid rgba(255,255,255,.08);
}
#hud .nota{ color:rgba(255,255,255,.34); padding-top:6px; }

/* ---------------------------------------------------------------------------
   REDUCED MOTION. O hero real é VAZIO — zero texto, zero <img>, zero <video>
   próprios, só o gradiente. Então em reduced-motion o efeito não pode
   DESLIGAR: sobraria uma tela preta. Ele tem de PARAR.
   Aqui isso sai de graça: como o z é estado escrito por rAF, basta não iniciar
   o loop. O campo é montado com as folhas distribuídas em profundidade, com a
   perspectiva intacta, e fica assim — uma composição estática de arquivos em
   corredor. As imagens, que são o conteúdo, continuam lá e continuam
   clicáveis. Tira-se o movimento e fica a composição, não o vazio.
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  .folha img.hi{ transition:none; }
  #legenda{ transition:none; }
  #cortina{ display:none; }
}

/* ===========================================================================
   O SCROLL — hero cravado, conteúdo sobrepondo, acervo adormecendo

   REFERÊNCIA: studionari.co.uk, medido. Hero fixo e o conteúdo subindo por
   cima, com o scroll mexendo em BLUR e OPACITY — nunca em escala.

   POR QUE NÃO ZOOM, que era o pedido original: o corredor já faz 4,5× de
   escala sozinho (medido: 0,284 → 1,288). Somar zoom por scroll no mesmo
   `scale` não soma — SOBRESCREVE: a última animação da lista ganha e o loop
   autônomo congela. Testado. Não é degradação, é o fluxo morrendo. E mesmo
   compondo com animation-composition:add, dois motores no mesmo eixo z é
   exatamente a competição que se quer evitar. O cotton.design usa zoom, mas
   de 1,0 a 1,1 — dez por cento, num hero que não tem movimento próprio.
   Num eixo que já varia 450%, 10% é invisível e o que dá pra ver, briga.

   Blur e opacity são eixos livres. Não encostam no corredor.
   =========================================================================== */

/* Marcados pelo JS com classes próprias, nunca pelas classes-hash do Framer.
   Sem JS nada disso existe e a home rola como sempre rolou. */
/* A classe é repetida de propósito. O Framer estiliza o hero com
   `.framer-raLMF .framer-btwl28` — duas classes, especificidade (0,2,0), que
   ganha de uma `.fel-hero` sozinha (0,1,0) e mantinha o position:relative.
   Repetir empata em (0,2,0), e como corredor.css é o último <link> do <head>
   (depois do <style> inline do Framer), o empate resolve a meu favor.
   É isso ou !important — e !important eu não teria como desfazer depois. */
.fel-hero.fel-hero{
  position: sticky;
  top: 0;
  /* z-index fica o 7 do Framer: o backdrop-filter fixo do topo (z-index 5)
     precisa continuar ABAIXO do hero, senão desfoca o corredor para sempre. */
}

/* O conteúdo sobe por cima. 10 > 7, e os fixed foram pulados na marcação.
   Mesma repetição, mesmo motivo. */
.fel-sobre-hero.fel-sobre-hero{
  position: relative;
  z-index: 10;
}

/* A faixa de blur progressivo do topo tem de ficar acima do conteúdo, senão o
   conteúdo cruza o topo sem desfocar e o vidro que ele já tinha some. 20 é
   acima do conteúdo (10) e bem abaixo da nav (50), que precisa ficar nítida
   por cima de tudo. */
.fel-vidro-topo.fel-vidro-topo{
  z-index: 20;
}

/* O DESFOQUE.
   Fora do @supports não existe animação nenhuma — o hero fica nítido e apenas
   cravado, que é um estado íntegro. Isso NÃO é conservadorismo: uma
   animation-timeline que o navegador não entende computa para `auto`, e aí a
   animação vira time-based e dispara sozinha no load. Sem o gate, quem abre a
   página num navegador sem suporte vê o hero desfocar inteiro de uma vez, no
   primeiro segundo, e ficar assim. Medido.

   E a ordem importa: `animation` (shorthand) ANTES de `animation-timeline`.
   Invertido, o shorthand reseta a timeline para `auto` e cai no mesmo bug. */
@supports (animation-timeline: scroll()) and (animation-range: 0% 100%){
  .fel-hero #palco{
    animation: fel-adormece linear both;
    animation-timeline: scroll(root);
    animation-range: 0 78vh;   /* mesmo fim da rampa da atenção no JS */
  }
}

/* ESCURECER, não só desaparecer. `opacity` sobre o preto do body também
   escurece, mas por subtração — a imagem some. `brightness` escurece por
   redução de luz e preserva a estrutura: o acervo continua legível como
   massa e forma, só que na penumbra. É a diferença entre apagar a luz e
   tirar a foto da parede.

   A opacidade fica bem mais alta que antes (.72 contra .38): quem carrega o
   escurecimento agora é o brightness, e acumular os dois sumia com tudo.

   O gradiente do hero escurece junto — é background do #palco e portanto
   entra no mesmo filter. Proposital: o topo quase branco virando penumbra é
   o que faz a PÁGINA escurecer, não só as imagens. */
@keyframes fel-adormece{
  from{ filter: blur(0px)  brightness(1);   opacity: 1;   }
  to  { filter: blur(20px) brightness(.28); opacity: .72; }
}

/* A legenda não desfoca junto: ela some antes, e rápido. Texto borrado é o
   tipo de coisa que lê como defeito de renderização, não como intenção. */
@supports (animation-timeline: scroll()){
  .fel-hero #legenda{
    animation: fel-legenda-sai linear both;
    animation-timeline: scroll(root);
    animation-range: 0 22vh;
  }
}
@keyframes fel-legenda-sai{
  from{ opacity: 1; }
  to  { opacity: 0; }
}

/* Reduced-motion: o hero continua cravado e o conteúdo continua sobrepondo —
   isso é LAYOUT, não movimento, e tirar deixaria a página pior. O que sai é a
   rampa de desfoque, trocada por um estado único e estável. */
@media (prefers-reduced-motion: reduce){
  .fel-hero #palco,
  .fel-hero #legenda{ animation: none; }
}

/* ===========================================================================
   A NAV — sobe e cola, em vez de sumir

   MEDIDO EM PRODUÇÃO (by-fel.com, scrollY 0 / 300 / 800): a nav fica
   `position: fixed; top: 50px; opacity: 1` nos TRÊS pontos. Ela nunca some.
   O esconder-ao-rolar é do motion.js — uma adição do projeto, não um
   comportamento do site original. Aqui ele é desfeito na home.

   O escopo é `:has(.fel-hero)`, ou seja SÓ a home: /loja e /projects continuam
   com a máquina de estados do motion.js intacta. Mexer no `.m-nav-hidden`
   global mudaria a nav do site inteiro por causa de um pedido sobre o hero.

   Ela sobe 34px (de 50 para 16) e cola. O movimento é curto de propósito: a
   nav é chrome, não é o assunto — ela precisa sair do caminho do acervo e
   assumir a posição de barra, sem virar um evento.
   =========================================================================== */
html:has(.fel-hero) [data-site-nav][data-site-nav]{
  transition:
    translate var(--m-dur-2) var(--m-ease),
    opacity   var(--m-dur-2) var(--m-ease);
}

/* O estado "escondido" do motion.js vira, aqui, o estado "colado no topo".
   Mesma classe, mesma máquina de estados, resposta diferente — que é
   exatamente o que o cabeçalho do motion.css promete: "flips m-nav-hidden on
   <html>; every visual consequence is CSS, so restyling the gesture never
   touches logic." Estou usando o gancho como ele foi desenhado. */
html.m-nav-hidden:has(.fel-hero) [data-site-nav][data-site-nav]{
  /* A distância vem medida do JS (ver corredor.js, NAV_TOPO_ALVO). O fallback
     0px é o que vale se a medição não acontecer: a nav fica onde estava, em
     vez de saltar para um número inventado. */
  translate: 0 calc(-1 * var(--fel-nav-subida, 0px));
  opacity: 1;
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce){
  html:has(.fel-hero) [data-site-nav][data-site-nav]{ transition: none; }
}

/* ---------------------------------------------------------------------------
   MODO B — a nav sobe JUNTO com o escurecimento (default, toggle no HUD)

   Diferença real entre os dois, e é de natureza, não de número:

   · MODO A (.fel-nav-junto ausente) — a subida é um EVENTO. O motion.js
     decide (220px de página + 160px de rolagem comprometida) e a nav faz um
     movimento único de 420ms. Ela reage ao seu gesto: rolar pra cima a traz
     de volta na hora, independente de onde você está.

   · MODO B (.fel-nav-junto) — a subida é um ESTADO. Presa à mesma timeline do
     escurecimento, a nav está sempre exatamente onde o scroll manda. Não há
     decisão nem histerese: 40vh de rolagem = nav no topo, sempre igual, e
     voltar meio caminho devolve a nav ao meio do caminho.

   A rampa é 40vh contra os 78vh do escurecimento: a nav chega ao topo bem
   antes do acervo apagar. Ela precisa estar assentada como barra ANTES de o
   fundo virar penumbra, senão as duas coisas terminam no mesmo instante e o
   olho não sabe qual causou qual.
   --------------------------------------------------------------------------- */
@supports (animation-timeline: scroll()) and (animation-range: 0% 100%){
  html.fel-nav-junto:has(.fel-hero) [data-site-nav][data-site-nav]{
    animation: fel-nav-sobe linear both;
    animation-timeline: scroll(root);
    animation-range: 0 40vh;
    /* a transition do modo A brigaria com a timeline: cada quadro do scroll
       viraria um alvo de 420ms e a nav ficaria arrastando atrás do dedo. */
    transition: none;
  }
  /* No modo B a máquina de estados do motion.js não manda na posição — só a
     timeline manda. Sem isso, um gesto pra cima dispararia o translate do
     modo A por cima do da timeline. */
  html.fel-nav-junto.m-nav-hidden:has(.fel-hero) [data-site-nav][data-site-nav]{
    translate: none;
  }
}

@keyframes fel-nav-sobe{
  from{ translate: 0 0; }
  to  { translate: 0 calc(-1 * var(--fel-nav-subida, 0px)); }
}

@media (prefers-reduced-motion: reduce){
  html.fel-nav-junto:has(.fel-hero) [data-site-nav][data-site-nav]{ animation: none; }
}

/* ===========================================================================
   O ENCAIXE DO SCROLL — chegar nas seções, sem sequestrar a rolagem

   `proximity`, NUNCA `mandatory`, e a razão é medida: a faixa dos projetos
   tem 1415px contra uma viewport de 900 — 515px a mais do que cabe. Sob
   `mandatory` o navegador é obrigado a pousar num ponto de encaixe, então
   parar no meio dessa faixa fica IMPOSSÍVEL: o scroll é puxado para o topo
   dela ou para a próxima parada, e o miolo (que é onde as imagens dos
   projetos vivem) vira território inalcançável. `proximity` só encaixa se
   você já parou perto — quem quiser ficar no meio, fica.

   Isso também é o que mantém a rolagem NATIVA. Não existe aqui nenhum
   sequestro de wheel, nenhuma interpolação em JS, nenhum smooth-scroll
   virtual: a física da rolagem continua a do sistema operacional, com a
   inércia do trackpad que o usuário já conhece. O único acréscimo é um imã
   fraco no fim do gesto.

   scroll-padding: 0 porque o hero é sticky e ocupa o topo — não há barra
   fixa comendo altura de onde a seção deveria começar.
   =========================================================================== */
html:has(.fel-hero){
  scroll-snap-type: y proximity;
  scroll-padding-top: 0;
}

/* O marcador é o ponto de encaixe: 1px no topo da seção, sem altura de
   layout, invisível, fora do alcance de ponteiro e de leitor de tela. A
   seção em si NÃO recebe scroll-snap-align — ver a nota em corredor.js sobre
   o encaixe fantasma no fim de seções altas. */
.fel-marca{
  position: absolute;
  top: 0; left: 0;
  width: 1px; height: 1px;
  margin: 0; padding: 0;
  pointer-events: none;
  opacity: 0;
  scroll-snap-align: start;
}

/* Teclado e âncoras ganham a mesma suavidade do encaixe. Isto afeta APENAS
   rolagem programática (setas, PageDown, links de âncora) — a roda e o
   trackpad continuam nativos, como devem. */
@media (prefers-reduced-motion: no-preference){
  html:has(.fel-hero){ scroll-behavior: smooth; }
}

/* Reduced-motion desliga o encaixe inteiro. Um scroll que se move sozinho no
   fim do gesto é exatamente o tipo de movimento involuntário que a
   preferência existe para evitar — e sem ele a página continua perfeitamente
   utilizável, só sem o imã. */
@media (prefers-reduced-motion: reduce){
  html:has(.fel-hero){ scroll-snap-type: none; scroll-behavior: auto; }
}

/* ===========================================================================
   AS FILEIRAS DE PROJETO — perspectiva do recolhimento e grade mais justa

   .fel-fileira é marcada pelo initCardHover (runtime.js) nas mesmas fileiras
   que ele anima — nenhum seletor de hash do Framer.
   =========================================================================== */

/* A perspectiva foi REMOVIDA (26/08). Com rotateY nos cards ela dava
   profundidade real, mas distorcia as imagens — e o recolhimento agora é
   translação pura, então não há rotação para ancorar e `perspective` só
   custaria uma camada de composição sem efeito nenhum. */

/* A GRADE MAIS JUSTA — só na home. O Framer resolve cada fileira com
   justify-content: space-between numa coluna de 1410px, e o vão de 18px é
   RESIDUAL: (1410 − 5×268) / 4 = 17,5 — ninguém escolheu 18, ele sobrou.
   Centro + gap fixo troca o vão residual por um vão escolhido; os cards não
   mudam de tamanho, a grade inteira só fica 11px mais recuada das bordas em
   cada lado. O initCardHover mede as posições DEPOIS do CSS aplicar
   (cacheRest roda no boot), então o recolhimento pousa nos lugares novos.
   Especificidade: html:has(.fel-hero) + classe = (0,2,1), ganha do par de
   classes do Framer (0,2,0) sem !important. */
html:has(.fel-hero) .fel-fileira{
  justify-content: center;
  gap: 12px;
}

/* O MESMO VÃO NA VERTICAL (01/09). A regra acima escolheu 12px entre os cards
   de uma fileira, mas o vão ENTRE fileiras continuou nos 18px do Framer — e
   18 também é residual ali, pelo mesmo motivo: é a sobra da coluna, não uma
   escolha. O olho lê a grade como um campo só, então dois vãos diferentes
   aparecem como desalinho (medido: 12 na horizontal contra 18 na vertical).

   Mexer no `row-gap` do container é seguro e cirúrgico: medidos os 17 itens
   dele, os únicos VISÍVEIS são as 8 fileiras de projeto e um marcador de 1px
   (.fel-marca) — os outros 8 são variantes de breakpoint em display:none, que
   não geram vão. Nenhuma outra seção da home divide este container.

   Não mexo no recuo das bordas (26px): ele não é arbitrário — são os 15px de
   padding da coluna mais os 11px que o `justify-content: center` acima deixa
   de cada lado, como o comentário anterior já previu. Margem maior que o vão
   é o que faz a grade ler como bloco em vez de vazar na borda. */
html:has(.fel-hero) .fel-sobre-hero{
  row-gap: 12px;
}

/* ===========================================================================
   O APAGAMENTO ATÉ O PRETO — segunda fase, e a razão de ela existir

   `brightness()` MULTIPLICA. O gradiente do hero começa em #fff, então
   brightness(.28) × opacity(.72) = 51 — cinza #333, não preto. O rodapé do
   site é #000 opaco. Onde um encosta no outro nascia uma linha dura de cor,
   e ela ficava visível por ~1800px de rolagem, porque a primeira rampa
   termina em 78vh e o resto da página inteira seguia cinza.

   Uma camada preta com opacity 0 → 1 chega a #000 EXATO, que é a mesma cor
   do rodapé: a emenda deixa de existir em vez de ficar disfarçada.

   Por que não simplesmente brightness(0): a primeira fase é o que dá a
   PENUMBRA — o acervo ainda legível como massa atrás do texto — e ela
   precisa terminar cedo (78vh). O apagamento até o preto termina tarde.
   São tempos diferentes, então são camadas diferentes.

   RECALIBRADO (26/08, pedido do Felipe): "no pixel do texto centrado, o
   site já deve estar 90% escuro." O pixel dele é scroll = 100vh — onde o
   snap pousa a viewport do texto. A rampa única antiga (78vh → rodapé, ~2481px)
   estava em ~11% nesse ponto: quase toda a escuridão chegava tarde demais.

   Virou DUAS fases, duas camadas:
   · ::after — 0 → 0,9 entre 78vh e 100vh. O mergulho. Termina exatamente no
     pixel pedido, com o valor pedido.
   · ::before — 0 → 1 entre 100vh e o rodapé (medido). Os 10% finais, lentos,
     garantem o preto PURO na chegada do rodapé — a razão original desta
     camada (a emenda invisível com o #000 do footer) continua valendo.
   As duas se compõem: no fim, o ::before sozinho já é opaco.
   =========================================================================== */
.fel-hero::after,
.fel-hero::before{
  content: '';
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  /* acima do palco (auto) e da legenda (40), dentro do hero — que é z-index 7,
     portanto tudo isto continua abaixo do conteúdo (10) e do vidro (20). */
  z-index: 41;
}

@supports (animation-timeline: scroll()) and (animation-range: 0% 100%){
  .fel-hero::after{
    animation: fel-apaga linear both;
    animation-timeline: scroll(root);
    /* do fim da penumbra ao pixel do texto: o grosso do apagamento. */
    animation-range: 78vh 100vh;
  }
  .fel-hero::before{
    animation: fel-apaga-resto linear both;
    animation-timeline: scroll(root);
    /* do pixel do texto até o rodapé entrar na tela — fim MEDIDO em
       corredor.js (apagaFim); 260vh é só o fallback se a medição faltar. */
    animation-range: 100vh var(--fel-apaga-fim, 260vh);
  }
}

@keyframes fel-apaga{
  from{ opacity: 0; }
  to  { opacity: .9; }
}
@keyframes fel-apaga-resto{
  from{ opacity: 0; }
  to  { opacity: 1; }
}

/* Reduced-motion: só `animation: none`, e a opacity volta sozinha para o 0 da
   regra base (linha 530) — que é o estado INICIAL da rampa.

   Tinha `opacity: 1` aqui, que é o estado FINAL: duas chapas #000 opacas em
   z-index 41 cobrindo o corredor em qualquer posição de scroll, inclusive no
   topo. Ou seja, a home nascia PRETA para quem liga "reduzir movimento" — o
   oposto exato do que o comentário de REDUCED MOTION (linha 207) estabelece:
   "o efeito não pode DESLIGAR: sobraria uma tela preta. Ele tem de PARAR...
   Tira-se o movimento e fica a composição, não o vazio."
   O bloco irmão do #palco/#legenda (linha 323) já usava o critério certo. */
@media (prefers-reduced-motion: reduce){
  .fel-hero::after,
  .fel-hero::before{ animation: none; }
}

/* ===========================================================================
   O VIDRO — não mora aqui, e essa é a correção

   Pedido (28/08): "quero que os botoes de nav e hover tenha o mesmo aspecto
   do botão do meu site atual, o mesmo frost effect."

   A resposta é que ele JÁ ERA aquele. A receita do ar está capturada
   verbatim em framer-runtime.css, keyed na classe de variante que o Framer
   usa como discriminador — blur(60px), branco a 20% em repouso, preto opaco
   no hover, e as variantes DARK e LIGHT para os outros contextos.

   O QUE ESCONDIA isso era um override daqui: uma classe `.fel-glass`
   triplicada (0,3,0) que o motion.js pregava nas pílulas da nav e que
   repintava tudo com um vidro líquido de três camadas, borda-gradiente e
   refração por filtro SVG. Só que o motion.js marca apenas os links DENTRO
   da nav — então READ MORE e SEE ALL PROJECTS, que são o mesmo componente
   (.framer-1x0lp31), seguiram com o frost do ar. O site tinha dois vidros
   diferentes para o mesmo botão, e o que divergia era justamente a nav.

   Removido o override, a nav volta sozinha para a captura do ar e os três
   passam a concordar. Nada aqui redeclara a receita: uma fonte só, em
   framer-runtime.css, que é o arquivo cujo contrato é guardar o que foi
   medido. O badge dos projetos também vive lá, na variante LIGHT.
   =========================================================================== */

/* ===========================================================================
   O MOTOR DE ROLAGEM (ver corredor.js, "1½. O MOTOR") — o que sai da frente

   Quando o motor assume (html.fel-motor, posto pelo JS quando não é touch e
   não é reduced-motion), duas coisas nativas PRECISAM sair:

   · scroll-snap-type — o proximity nativo ajusta a posição por conta própria
     depois de cada operação de scroll, e o motor escreve scrollTop a cada
     quadro: os dois brigariam pelo mesmo valor. O ímã continua existindo,
     reimplementado dentro do motor sobre as mesmas .fel-marca, com o mesmo
     raio de 96px que os testes do proximity validaram.
   · scroll-behavior: smooth — transformaria cada write do motor numa
     animação própria do navegador, e a página escorregaria atrás do dedo.

   Sem o motor (touch, reduced-motion), a classe não existe e o comportamento
   antigo — snap nativo + smooth para teclado — segue intacto. As duas regras
   abaixo vêm DEPOIS de html:has(.fel-hero) no arquivo de propósito: mesma
   especificidade, ordem decide.
   =========================================================================== */
html.fel-motor,
html.fel-motor:has(.fel-hero){
  scroll-snap-type: none;
  scroll-behavior: auto;
}

/* ===========================================================================
   O RODAPÉ QUE SOBE — sai do fluxo e passa POR CIMA dos cards

   Pedido (28/08): "quero que o footer do site surja da base numa animação em
   que ele sobrepõe os cards dos projetos, e que ele fique tela cheia, e
   quando a gente chegar no footer, o nav pode desaparecer."

   COMO: o rodapé se descola do fluxo (fixed, colado na base, 100svh) e sobe
   de translateY(100%) até 0 ao longo dos ÚLTIMOS 100svh de rolagem. Por ser
   fixed com z-index acima do conteúdo, ele PASSA POR CIMA dos cards em vez
   de empurrá-los — que é a sobreposição pedida. O bloco que ele deixou vira
   espaçador de 100svh, então a altura do documento não muda: medido, 4282px
   antes e 4282px depois. Sem isso a página encolheria 900px e o fim do
   percurso mudaria de lugar.

   POR QUE QUEM ANIMA É O `html`, e não o rodapé: mesmo motivo que já aparece
   duas vezes neste arquivo — um elemento `fixed` não é descendente do
   scroller para efeito de scroll-driven animation, e a timeline devolve
   progress **null**, em silêncio. Então o `html` anima um número só
   (--rodape-t) e o rodapé CONSOME esse número no transform. @property é o
   que torna o número interpolável; sem registrar o tipo ele saltaria de 0
   para 1 em vez de percorrer.

   ESPECIFICIDADE TRIPLA (0,3,0): o Framer estiliza o bloco com duas classes-
   hash (0,2,0). Medido: com classe simples o `position` NÃO passava — só o
   `transform` — e o rodapé era empurrado 900px para baixo DENTRO do fluxo,
   crescendo o documento em 899px. O sintoma é silencioso; a causa é cascata.

   TUDO DENTRO DO @supports: sem scroll-driven animations o rodapé fica no
   fluxo normal, como sempre foi. Nada de rodapé fixo preso em translateY
   sem nada para animá-lo — que seria um rodapé permanentemente fora da tela.
   =========================================================================== */
@property --rodape-t{
  syntax: '<number>';
  inherits: true;
  initial-value: 0;   /* 0 = escondido embaixo; 1 = tela cheia */
}

@supports (animation-timeline: scroll()) and (animation-range: 0% 100%){
  html:has(.fel-rodape){
    animation: fel-rodape-t linear both;
    animation-timeline: scroll(root);
    /* os últimos 100svh do percurso — exatamente o espaço que o rodapé
       deixou no fluxo, então a subida termina quando a rolagem termina. */
    animation-range: calc(100% - 100svh) 100%;
  }

  .fel-rodape-espaco.fel-rodape-espaco{
    height: 100svh;   /* o buraco que o rodapé deixou ao virar fixed */
  }

  .fel-rodape.fel-rodape.fel-rodape{
    position: fixed;
    left: 0; right: 0; bottom: 0; top: auto;
    width: auto;
    height: 100svh;            /* tela cheia, como pedido */
    z-index: 55;               /* ACIMA DA NAV, que o motion.css fixa em 50 com
                                  a nota "must never be covered". Aqui ela é
                                  coberta de propósito: é o pedido. Abaixo do
                                  HUD (60), que é ferramenta e não página. */
    transform: translateY(calc((1 - var(--rodape-t, 0)) * 100%));
    will-change: transform;
  }

  /* A NAV SOME quando o rodapé chega. Sai no primeiro quarto da subida (o
     ×4), para não ficar boiando sobre o rodapé durante a travessia.

     SEM clamp(): a primeira versão era `clamp(0, calc(1 - var(...) * 4), 1)` e
     não pintava nada — medido, a nav ficava em opacity 1 o percurso inteiro.
     Com var() dentro, a expressão vira inválida em tempo de VALOR COMPUTADO,
     e nesse caso a propriedade não cai para a regra anterior: cai para o
     valor inicial, que em opacity é 1. Falha silenciosa, sem erro no console.
     E o clamp era supérfluo desde o começo — opacity já satura em [0,1]
     sozinha, então basta a conta.

     OS DOIS SELETORES existem por especificidade, não por capricho. O estado
     "colada no topo" da nav (linha 355) é
       html.m-nav-hidden:has(.fel-hero) [data-site-nav][data-site-nav]
     que dá (0,4,1) e declara `opacity: 1` — e é justamente o estado em que a
     nav está quando o rodapé chega. Com um seletor só, de (0,3,1), a conta
     abaixo perdia e a nav ficava opaca o percurso inteiro. Medido: opacidade
     1 em t=0, t=0,13 e t=1. O segundo seletor empata em (0,4,1) e ganha por
     vir depois no arquivo — sem !important, que aqui seria esconder a causa.

     Sem pointer-events: com o rodapé em z-index 55 e a nav em 50, quando ela
     está invisível o rodapé já está por cima e é ele quem recebe o clique. */
  html:has(.fel-rodape) [data-site-nav][data-site-nav],
  html.m-nav-hidden:has(.fel-rodape) [data-site-nav][data-site-nav]{
    opacity: calc(1 - var(--rodape-t, 0) * 4);
  }
}

@keyframes fel-rodape-t{
  from{ --rodape-t: 0; }
  to  { --rodape-t: 1; }
}

/* Reduced-motion: sem subida. O rodapé volta para o fluxo e o espaçador
   deixa de existir — a página fica como sempre foi, e a nav não desaparece.
   Tirar o movimento não pode tirar o conteúdo. */
@media (prefers-reduced-motion: reduce){
  html:has(.fel-rodape){ animation: none; }
  .fel-rodape.fel-rodape.fel-rodape{
    position: relative;
    transform: none;
    height: auto;
  }
  .fel-rodape-espaco.fel-rodape-espaco{ height: auto; }
  html:has(.fel-rodape) [data-site-nav][data-site-nav],
  html.m-nav-hidden:has(.fel-rodape) [data-site-nav][data-site-nav]{ opacity: 1; }
}

/* ===========================================================================
   OS CARDS DAS FILEIRAS — todos exatamente do mesmo tamanho

   Pedido (28/08): "corrija o tamanho dos cards. eles devem ter exatamente o
   mesmo tamanho, e quando alinhados ao centro, as imagens por trás com
   exatamente os mesmos tamanhos."

   O QUE ESTAVA ACONTECENDO: o Framer dá a cada card uma altura derivada da
   PROPORÇÃO NATIVA do vídeo dentro dele. A largura é a mesma para todos (a
   coluna da grade, 268px em 1440), mas a altura não — medido na home:
     fileira 0  vídeo 720/442  → 160
     fileira 1  vídeo 720/404  → 165
     fileira 5  vídeo 720/450  → 130
     fileira 5, a CAPA central, vídeo 1280/658 → 136
   Ou seja, alturas diferentes entre fileiras E dentro da mesma fileira: a
   capa (o card que fica centralizado, com translate(-50%,-50%)) tinha 136
   contra 130 dos outros quatro. Quando o hover junta tudo no centro, era
   exatamente esse desencontro que aparecia — as imagens por trás sobrando
   alguns pixels em relação à da frente.

   A CORREÇÃO é uma proporção única para o frame. A mídia dentro já é
   `width:100%; height:100%; object-fit:cover`, então ela RECORTA para caber
   em vez de distorcer — nenhuma imagem estica.

   67/40 = 268x160, que é a proporção dominante hoje (5 das 8 fileiras). A
   escolha é essa para o ajuste ser o menor possível: as fileiras que já
   estavam em 160 não mudam nada.

   `height: auto` junto porque o Framer declara altura explícita no frame, e
   altura explícita + largura vencem aspect-ratio — sem zerar a altura, a
   proporção não pinta. Classe tripla (0,3,0) pela mesma razão de sempre: o
   Framer estiliza com duas classes-hash.
   =========================================================================== */
.fel-card.fel-card.fel-card{
  aspect-ratio: 67 / 40;
  height: auto;
}

/* E o MIOLO tem de preencher o card. Uniformizar só o frame não bastava: a
   CAPA (o card que fica centralizado) tem um wrapper próprio dentro dele com
   `aspect-ratio: 1.97037/1` e `position: absolute` — medido, o frame ia para
   160 e esse wrapper continuava em 136, então a imagem da frente ficava menor
   que as de trás justamente no estado convergido, que é onde isso aparece.

   O `:has()` mantém a regra cirúrgica: só wrappers que de fato carregam mídia
   são esticados. Sem ele, qualquer filho do card (rótulo, sobreposição)
   também seria forçado a 100% de altura. */
.fel-card.fel-card.fel-card > *:has(video, img),
.fel-card.fel-card.fel-card > video,
.fel-card.fel-card.fel-card > img{
  aspect-ratio: auto;
  height: 100%;
}

/* ===========================================================================
   A COLUNA DO NOME DO PROJETO — 100px mais estreita

   Pedido (28/08): "quero estreitar a coluna com o nome do projeto. Acho que
   podemos tirar uns 100 px."

   O Framer declara `width: 30%` nessa coluna. Em 1440 a fileira mede 1430 de
   conteúdo, então 30% = 429px — e 23% = 329px, exatamente os 100 pedidos. Fica
   em PORCENTAGEM, e não `calc(30% - 100px)`, porque a coluna precisa encolher
   junto com a janela: um desconto fixo de 100px viraria 130px de coluna em
   768, e as fichas ("BRAND STRATEGY", "MARKET ANALYSIS") empilhariam uma por
   linha. A área dos cards é `flex: 1`, então ela absorve os 100px sozinha.

   A ESPECIFICIDADE AQUI É O PONTO DELICADO, e é por isso que a classe é dupla:
   ela precisa ficar ENTRE duas regras do Framer.
     · a base `.framer-1ikri2k{width:30%}` é (0,1,0) — esta regra tem de vencer;
     · mas nos breakpoints empilhados o Framer manda
       `.framer-NLj4w.framer-v-1luua83 .framer-1ikri2k{width:100%}`, que é
       (0,3,0) — e essa tem de continuar vencendo, ou o mobile perderia o
       empilhamento e ficaria com uma coluna estreita no lugar de largura
       cheia.
   Classe dupla dá (0,2,0): passa por cima da base e passa por baixo das
   variantes. Triplicar aqui quebraria o mobile.
   =========================================================================== */
:root{
  /* A LARGURA DA COLUNA DE NOME, em UM lugar só.

     Ela governa duas coisas que precisam concordar: a coluna de cada fileira
     e a coluna equivalente na barra do topo (a que reserva o espaço à
     esquerda do "SELECTED WORK").      O Framer dava 30% às duas. Uma passagem estreitou para 0.23 (~100px a
     menos) e as tags passaram a quebrar em 3 linhas, empurrando a fileira.
     30% é o número do export e o que o hover do site ao vivo usa.

     Sem unidade para poder ser usada em conta: a fileira multiplica por 100%,
     a barra multiplica pela largura de conteúdo. */
  --fel-col-nome: 0.30;
}

.fel-coluna-nome.fel-coluna-nome{
  width: calc(var(--fel-col-nome) * 100%);
}

/* A linha de hover mora em [data-framer-name="TEXT"], que o Framer já
   declara como coluna space-between (nome+tags no topo, frase embaixo).
   O SSR não traz a frase — o React do Framer a injetava no hover.
   runtime.js (initProjectsHover) recoloca o <p.fel-hover-line>. */
.fel-hover-line{
  margin: 0;
  padding: 0 2px 2px;
  font-family: 'Maxeville Regular', 'Maxeville Construct', sans-serif;
  font-size: 14px;
  line-height: 1.3;
  color: #fff;
  max-width: 34ch;
}

@media (hover: hover) and (min-width: 810px){
  .fel-hover-line{
    opacity: 0;
    transition: opacity var(--m-dur-1, 200ms) var(--m-ease, ease);
  }
  .fel-fileira:hover .fel-hover-line,
  .fel-fileira:focus-visible .fel-hover-line{
    opacity: 1;
  }
}

/* Touch e mobile empilhado: a frase fica visível, sem depender de hover. */
@media (hover: none), (max-width: 809.98px){
  .fel-hover-line{ opacity: 1; }
}

/* A COLUNA DA BARRA DO TOPO. A conta não é a mesma porcentagem simples: a
   fileira tem 5px de padding e mede os 23% sobre o conteúdo, a barra não tem
   padding e mede sobre a largura cheia. Reproduzir a fórmula da fileira —
   padding + 23% do conteúdo — faz as duas caírem no MESMO pixel, em vez de
   ficarem a 2-3px uma da outra. */
/* SÓ ACIMA DE 810px, que é o breakpoint do PRÓPRIO Framer — o export declara
   `@media (min-width:810px)` e `@media (max-width:809.98px)`, então o número
   não é chute meu, é o dele.

   Abaixo disso o Framer empilha o layout: a coluna da FILEIRA volta a 100% por
   uma regra de variante (0,3,0) que a minha, dupla (0,2,0), perde de propósito.
   A da barra não tinha adversário equivalente e continuava em 30% mesmo
   empilhada — medido em 768: fileiras já empilhadas e a barra ainda a 23%,
   espremendo a nav contra o "SELECTED WORK". Envolver na mesma media query dá
   à barra a saída de mobile que a fileira já tinha. */
@media (min-width: 810px){
  .fel-cab-coluna.fel-cab-coluna{
    width: calc(5px + (100% - 10px) * var(--fel-col-nome));
    flex: 0 0 auto;
  }
}

/* ===========================================================================
   A TIRA DE /projects — corre para a esquerda no hover, com borda chapada

   Pedido (28/08). O JS (runtime.js, initTiras) pendura as imagens extras na
   área que o Framer já usa para os cards e persegue um alvo em `scrollLeft`.
   Aqui ficam só as coisas que são de CSS.

   1. NOWRAP. A área nasce `flex-wrap: wrap` — com mais imagens do que cabe,
      elas quebrariam para uma segunda linha em vez de sair pela direita. É a
      única declaração que realmente destrava o efeito.

   2. BORDA CHAPADA. Houve aqui um `mask-image` esfumando 96px em cada ponta;
      saiu a pedido (28/08: "não quero esse esfumaçado, deixe 100% flat"). O
      corte agora é seco, feito pelo `overflow: hidden` da própria área.

   3. ROLAGEM MANUAL EM REDUCED-MOTION. Sem a corrida, as imagens extras
      ficariam inalcançáveis — conteúdo escondido atrás de um movimento que a
      pessoa desligou. Então a área vira rolável no eixo X. Tirar o movimento
      não pode tirar o acesso.
   =========================================================================== */
.fel-tira.fel-tira{
  flex-wrap: nowrap;
  scrollbar-width: none;

  /* A ÁREA É O CONTAINER DE MEDIDA. Os cards precisam de uma largura relativa
     à área (três cabem na janela), mas eles são filhos da PISTA, cuja largura
     vem do conteúdo — vários milhares de px.

     A primeira tentativa foi um custom property com porcentagem declarado
     aqui. Não funciona, e o motivo vale registrar: porcentagem em custom
     property resolve ONDE É USADA, não onde é declarada. O `100%` ia resolver
     contra a pista, que por sua vez depende dos filhos — referência circular.
     Medido: pista de 373px e cards de 28x17.

     `container-type: inline-size` resolve de verdade: transforma a área em
     container de consulta, e a unidade `cqi` na regra dos itens passa a medir
     1% da largura DELA, independente de onde o filho esteja. */
  container-type: inline-size;

  /* SEM MÁSCARA (pedido 28/08: "não quero esse esfumaçado, deixe 100% flat").
     Tinha aqui um `mask-image` que apagava 96px em cada ponta. A borda agora é
     corte seco — o `overflow: hidden` da própria área faz o recorte.

     `scroll-behavior: auto` NÃO é redundante, é blindagem. O JS escreve
     scrollLeft a cada quadro; se algum dia esta área herdar ou receber
     `scroll-behavior: smooth`, cada uma dessas escritas viraria uma animação
     própria do navegador e a tira arrastaria atrás do ponteiro. É exatamente o
     bug que já custou caro no motor de rolagem (ver "O MOTOR DE ROLAGEM"),
     e uma linha aqui impede a reincidência. */
  scroll-behavior: auto;
}
.fel-tira.fel-tira::-webkit-scrollbar{ display: none; }

/* A PISTA — o que de fato se move.

   Mover a ÁREA seria mover o próprio recorte, então o conteúdo ganha um
   elemento só para isso. O gap vem da área para cá junto com os cards.

   `transform` e não `scrollLeft`: o scrollLeft arredonda (medido — 137,5
   guardou 138), e a 220px/s isso vira passos de 4/3/4/3px por quadro. O
   transform aceita fração; `will-change` avisa o compositor. */
.fel-tira-pista{
  display: flex;
  gap: 0 8px;
  align-items: center;
  width: max-content;        /* larga como o conteúdo, para haver o que correr */
  will-change: transform;
}

@media (hover: hover){
  /* O arraste precisa se anunciar: sem cursor de mão ninguém descobre que a
     tira pega. */
  .fel-tira-pista{ cursor: grab; }
  .fel-tira-pista.arrastando{ cursor: grabbing; }
  /* Arrastar não pode selecionar as imagens no caminho. */
  .fel-tira-pista.arrastando, .fel-tira-pista.arrastando *{ user-select: none; }

  /* Máquina híbrida — notebook com tela sensível e mouse — resolve
     `(hover: hover)` como VERDADEIRO, então recebe o motor de arraste mas
     ficava sem `touch-action` declarado e sem a rolagem nativa de reserva do
     bloco (hover: none). Com o padrão `auto` o navegador pode assumir o gesto
     como pan e cancelar o ponteiro no meio. `pan-y` devolve o eixo horizontal
     ao JS e mantém a rolagem VERTICAL da página com o navegador — que é a
     regra da casa. Nunca `none`: isso sequestraria a rolagem no toque. */
  .fel-tira-pista{ touch-action: pan-y; }

  /* Nem virar drag-and-drop nativo. O JS já desarma o link (ver runtime.js,
     "O ARRASTE NATIVO DO LINK"), e isto fecha o cerco pelas imagens: uma
     <img> ou <video> arrastável dentro da tira reintroduz o mesmo sequestro
     de pointermove por outra porta. */
  .fel-tira-pista, .fel-tira-pista *{ -webkit-user-drag: none; }

  /* A COR DA FILEIRA NO HOVER, de volta — e só aqui.

     O preenchimento #1a1a1a existia e foi removido porque "lia como uma placa
     cinza quando as miniaturas se recolhiam ao centro" (ver framer-runtime.css,
     "Project rows"). Aquele motivo era da CONVERGÊNCIA, e em /projects ela não
     acontece mais: lá as imagens correm na tira e a fileira permanece cheia.
     Sem o recolhimento não há placa cinza — há uma faixa inteira acendendo,
     que é o retorno que a fileira precisa dar ao ponteiro.

     O `:has(.fel-coluna-nome)` é o que separa as duas páginas: só as fileiras
     de /projects têm coluna de nome. Na home, onde o recolhimento continua, o
     fundo segue preto e a razão original segue de pé. */
  .fel-fileira:has(.fel-coluna-nome){
    transition: background-color var(--m-dur-2, 420ms) var(--m-ease, ease);
  }
  .fel-fileira:has(.fel-coluna-nome):hover{
    background-color: #1a1a1a;
  }
}

/* TOUCH: rolagem NATIVA, sem motor. O momentum do sistema é melhor que
   qualquer inércia escrita à mão, e `pan-y` deixa a rolagem vertical da página
   passar intacta — sequestrar o scroll no toque é a linha que a casa não
   cruza. */
@media (hover: none){
  .fel-tira.fel-tira{
    overflow-x: auto;
    touch-action: pan-y pinch-zoom;
    -webkit-overflow-scrolling: touch;
  }
}

/* A LARGURA DE TODOS OS ITENS DA TIRA, originais e injetados.

   Sem isto a tira sai torta: `.fel-card` dá a PROPORÇÃO (67/40) e a altura,
   mas não a largura — e um <img> sem largura declarada, como item flex
   `0 0 auto`, assume a largura INTRÍNSECA do arquivo. Medido: as injetadas
   saíam em 640×382 (o tamanho do derivado -640) enquanto os cards originais,
   que tinham largura de porcentagem calculada para uma linha que agora não
   quebra mais, colapsavam para 0×0.

   `(100% - 16px) / 3` reproduz exatamente o que a fileira já mostrava: três
   cards com os dois gaps de 8px da área. Ou seja, a janela continua com a
   mesma leitura de antes — o que muda é que agora existe fila esperando fora
   dela. Em porcentagem, não em px, para acompanhar a janela.

   `flex: 0 0` (não encolher) é o que destrava a corrida: com shrink ligado o
   flex espremeria os 13 itens para caber nos 1101px, que é o oposto do efeito. */
/* Classe DOBRADA (0,2,0), não simples. O seletor `.fel-tira-pista > *` dá
   (0,1,0) e EMPATA com as regras que o Framer escreve para cada container de
   card — e o <style> do export vem depois do nosso <link>, então no empate ele
   ganha. Medido quando isto era simples: as <img> injetadas ficavam certas
   (não têm regra do Framer) e os 3 cards ORIGINAIS de cada fileira caíam para
   flex-basis 0 e 1x1 na tela — 45 itens quebrados em 189. */
.fel-tira-pista.fel-tira-pista > *{
  /* 100cqi = a largura da ÁREA (ver container-type acima), não da pista.
     `(100cqi - 16px)/3` reproduz exatamente os três cards com os dois gaps de
     8px que a fileira já mostrava — o que muda é que agora existe fila
     esperando fora do recorte. */
  flex: 0 0 calc((100cqi - 16px) / 3);
  /* min-width: 0 NÃO é adorno — sem ele a base acima é ignorada nas <img>.
     Item flex nasce com `min-width: auto`, que num elemento SUBSTITUÍDO
     resolve para a largura INTRÍNSECA do arquivo. A largura usada vira
     max(base, min-width), então as injetadas ficavam em 640px (o derivado
     -640) enquanto os <div> originais, que não são substituídos, obedeciam a
     base e ficavam em 362. Medido: 362x216 e 640x382 lado a lado na mesma
     tira. Zerar o mínimo devolve o controle à base. */
  min-width: 0;
}
.fel-tira-img.fel-tira-img{
  object-fit: cover;
  display: block;
}

@media (prefers-reduced-motion: reduce){
  /* Sem corrida e sem arraste com inércia — mas o conteúdo continua
     alcançável, agora pela rolagem nativa. Tirar o movimento não pode tirar o
     acesso. O JS nem instala o motor neste caso, então não há transform
     concorrendo com a rolagem. */
  .fel-tira.fel-tira{ overflow-x: auto; }
}
