/* ===== SWITZER =====
   Arquivo variável local, copiado de Switzer_Complete/Fonts/WEB/fonts/ para
   assets/fonts/: publicar o pacote inteiro levaria OTF, TTF e dezoito pesos
   para o servidor sem necessidade. Uma declaração só, com o eixo limitado a
   300–500, porque o teto do site é Medium. O navegador nunca recebe permissão
   para pedir mais que isso, e font-synthesis desligado impede negrito falso.
   Nada é baixado da internet. */
@font-face{
  font-family:"Switzer";
  src:url("../assets/fonts/Switzer-Variable.woff2") format("woff2-variations"),
      url("../assets/fonts/Switzer-Variable.woff2") format("woff2");
  font-style:normal;
  font-weight:var(--w-light) 500;
  font-display:swap;
}

:root{
  /* ===== PALETA: escura, monocromática, levemente quente =====
     Toda cor do site sai daqui. Os triplets existem para compor alpha
     sem espalhar valores literais pelo arquivo. */
  --ink:#0D0A0A;          /* fundo principal */
  --ink-2:#261B1D;        /* fundo suave e superfícies */
  --ink-3:#403638;        /* superfícies elevadas */
  --paper:#F2EEEE;        /* texto principal, branco quente */
  --muted:#B7ACAC;        /* texto secundário */
  --muted-2:#8A7E7E;      /* texto terciário */
  --accent:#A29595;       /* rótulos e detalhes */
  --cream:#C9BDBD;        /* realce quente discreto */
  /* O nome da hero tem cor própria e única: marrom-claro, o mesmo na intro e na
     capa. A continuidade do elemento depende de a cor nunca mudar no caminho.
     9,9:1 de contraste sobre o fundo, bem acima do mínimo. */
  --color-hero-name:#C2B3B3;
  --ink-rgb:13,10,10;
  --ink-2-rgb:38,27,29;
  --ink-3-rgb:64,54,56;
  --paper-rgb:242,238,238;
  --warm-rgb:115,104,104;
  --status-available:#9EAD8B;
  --status-available-on-light:#5F7A4A;
  --status-unavailable:#D9797D;
  --status-unavailable-on-light:#9A353B;
  /* Opacidade da borda separada da cor: o CMS controla as duas de forma
     independente, sem exigir rgba() manual. --line e --line-strong continuam
     existindo com o mesmo resultado de sempre, só que compostas a partir de
     um número editável em vez de um valor cravado. */
  --border-opacity:.20;
  --border-opacity-strong:.36;
  --line:rgba(var(--warm-rgb),var(--border-opacity));
  --line-strong:rgba(var(--warm-rgb),var(--border-opacity-strong));
  --glass:rgba(38,27,29,.55);
  /* Material fixo dos controles flutuantes. É escuro o bastante para manter
     contraste sobre imagens e sobre o FAQ claro, mas continua translúcido e
     pertencendo à paleta quente do site. */
  --floating-surface:rgba(var(--ink-2-rgb),.82);
  --floating-surface-hover:rgba(var(--ink-3-rgb),.78);
  --floating-line:rgba(var(--paper-rgb),.14);
  --floating-line-strong:rgba(var(--paper-rgb),.24);
  --floating-shadow:rgba(var(--ink-rgb),.38);
  /* ===== ARREDONDAMENTO =====
     Quatro famílias, e só estas: card (grade de projetos), image (capa e
     galeria dos cases), button (controles de raio discreto) e field (campos de
     formulário). Detalhes pequenos de interface (miniatura do menu, ícone de
     copiar) continuam com o número próprio deles: são cápsulas de navegação,
     não o "arredondamento de imagem" que o painel expõe. */
  --radius-card:16px;
  --radius-image:16px;
  --radius-button:12px;
  --radius-field:12px;
  /* Uma família só no site inteiro. O token --mono continua existindo porque é
     usado em dezenas de regras, mas aponta para a mesma Switzer: o que dava
     textura de rótulo técnico agora vem do tracking e do tamanho, não de uma
     segunda família. */
  --font:"Switzer","Helvetica Neue",Arial,sans-serif;
  --mono:var(--font);
  /* ===== PESOS: o teto do site é 500 =====
     Medium para destaque, Regular para intermediário, Light para texto corrido. */
  --w-medium:500;
  --w-regular:400;
  --w-light:300;
  /* ===== CONTAINER FLUIDO ===== */
  --page-gutter:clamp(20px,3vw,56px);
  --content-max:1680px;
  --gutter:var(--page-gutter);
  /* ===== INTERAÇÃO ===== */
  --dur-hover:420ms;
  --ease-hover:cubic-bezier(.16,1,.3,1);
  /* ===== SISTEMA DE MOVIMENTO =====
     Início responsivo, final desacelerado, distâncias curtas.
     Nada de ease-in-out genérico, nada linear, nada elástico. */
  --ease-soft:cubic-bezier(.16,1,.3,1);
  --ease-smooth:cubic-bezier(.22,1,.36,1);
  --ease-out-expo:var(--ease-soft);      /* aliases legados */
  --ease-out-smooth:var(--ease-smooth);
  --dur-micro:350ms;
  --dur-fast:500ms;
  --dur-default:700ms;
  --dur-slow:1000ms;
  --dur-reveal:1200ms;
  --dur-ambient:1600ms;
  /* Entrada dos blocos. Alinhada à cauda do lerp .04 da rolagem: o conteúdo
     chega no mesmo tempo em que a página assenta, e o conjunto lê como um
     movimento só. Não use isto em hover, que continua em --dur-micro. */
  --dur-enter:1500ms;
  --grain-opacity:.10;
  --grain-size:220px;
  --grain-url:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.72' numOctaves='3' stitchTiles='stitch' result='n'/%3E%3CfeColorMatrix in='n' type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
}
*{box-sizing:border-box;margin:0;padding:0}
/* impede o navegador de fabricar negrito ou itálico que a fonte não tem */
html,body{font-synthesis:none;-webkit-font-synthesis:none}
/* clip em vez de hidden: contém o sangramento lateral sem criar um
   contêiner de rolagem nem inflar o scrollWidth do documento.
   scrollbar-gutter reserva a calha sempre: durante a intro o body trava a
   rolagem, a barra sumia e a página inteira alargava uns 15px, deslocando o
   nome e a frase no exato momento da passagem para a capa. */
html{scroll-behavior:smooth;overflow-x:clip;scrollbar-gutter:stable}
body{font-family:var(--font);background:var(--ink);color:var(--paper);line-height:1.6;font-weight:var(--w-regular);
  -webkit-font-smoothing:antialiased;overflow-x:clip;position:relative}
body.locked{overflow:hidden;height:100dvh}
/* Enter nos campos multilinha do CMS vira uma quebra editorial real. O texto
   continua escapado e espaços comuns continuam colapsando normalmente. */
[data-pt][data-en]{white-space:pre-line}
/* Proteção automática contra palavras órfãs. Quebras digitadas no CMS
   continuam soberanas porque são quebras forçadas preservadas por pre-line. */
p[data-pt][data-en]{text-wrap:pretty}
.wi-title,.about-title,.help-title,.faq-title,.contact-big,
.card-foot h3,.hi-title,.faq-q span,.case-hero h1,.case-nav a>span:last-child{text-wrap:balance}

/* Nenhum filter:blur() no site. Gradiente radial já é suave por natureza;
   o blur só custava pintura. Os stops foram alargados para compensar. */
/* will-change promove estas camadas fixas para o compositor. Sob rolagem
   dirigida por JS (window.scrollTo por quadro), tudo que é position:fixed é
   repintado a cada quadro junto com o conteúdo; promovidas, elas viram
   texturas prontas na GPU e só ficam paradas. É o que separa uma rolagem
   suave em JS de uma que engasga, e o que o Lenis do midu.design depende
   para parecer fluido. Não espalhe will-change: em excesso ele consome
   memória de vídeo e piora. Aqui são só as três camadas de fundo fixas. */
/* ===== ROLAGEM SUAVE POR TRANSFORM =====
   O JS envolve main+footer no .smooth-holder e dá ao body a altura do conteúdo,
   para a barra de rolagem nativa continuar existindo. A roda nunca é
   interceptada: a rolagem acontece no compositor, e o holder apenas persegue a
   posição com translate3d, que é composto na GPU e não repinta.
   Só vale com a classe .smooth no <html>, que o JS adiciona: sem JS, ou com
   movimento reduzido, a página rola normalmente e nada disso se aplica. */
.smooth .smooth-holder{position:fixed;top:0;left:0;width:100%;
  will-change:transform;backface-visibility:hidden}
.smooth body{overflow:visible}

.glow{position:fixed;inset:0;z-index:-2;pointer-events:none;overflow:hidden;
  will-change:transform}
/* 273px contra os 420px anteriores: 35% menor. O centro subiu de .13 para .156
   (20% mais intenso) e os stops fecharam de 42%/70% para 34%/62%, o que
   concentra a luz em vez de dispersá-la. A borda continua sem contorno visível,
   porque o gradiente termina em transparente. Tons de #C2B3B3 e #736868, os
   mesmos marrons do site. Move por translate3d, nunca por top/left. */
.cursor-glow{position:fixed;top:0;left:0;width:273px;height:273px;margin:-136px 0 0 -136px;
  border-radius:50%;pointer-events:none;z-index:-1;opacity:0;
  background:radial-gradient(circle,rgba(194,179,179,.156),rgba(115,104,104,.07) 34%,transparent 62%);
  transform:translate3d(var(--mx,50vw),var(--my,50vh),0);
  transition:transform .55s var(--ease-out-smooth),opacity var(--dur-default) var(--ease-out-smooth)}
.cursor-glow.on{opacity:1}
@media(prefers-reduced-motion:reduce),(pointer:coarse){.cursor-glow{display:none}}

/* ===== CURSOR INTERATIVO =====
   Um glow compacto e exatamente três rastros, todos reaproveitados: nenhum nó
   é criado durante o movimento. A camada inteira é pointer-events:none, então
   nada aqui bloqueia clique, hover, arraste ou seleção.
   O .cursor-glow acima continua existindo: ele é a luz ambiente do fundo
   (z-index:-1, 273px), coisa diferente do cursor. O que mudou é que os dois
   passaram a ser movidos pelo mesmo listener e pelo mesmo laço.
   Cores derivadas da paleta, sem valor solto: --cream para o núcleo quente,
   --paper-rgb para halo e borda. */
:root{
  --cursor-core:var(--cream);
  --cursor-glow-soft:rgba(var(--paper-rgb),.16);
  --cursor-border:rgba(var(--paper-rgb),.34);
}
/* z-index 100: ACIMA do overlay do menu (95) e do véu (90). Em 70 o cursor
   ficava atrás do menu aberto e sumia — e como html.has-custom-cursor já
   esconde o ponteiro do sistema, não sobrava cursor nenhum sobre o menu.
   Continua pointer-events:none, então estar por cima não bloqueia clique. */
.cur-layer{position:fixed;inset:0;z-index:100;pointer-events:none;
  contain:layout style size}
/* O FAQ inverte a superfície para clara. O cursor acompanha essa superfície
   sem mudar sua lógica nem reativar o ponteiro nativo: só os tokens visuais da
   camada são invertidos enquanto o alvo está dentro do FAQ. */
.cur-layer.is-on-light{
  --cursor-core:var(--ink-2);
  --cursor-glow-soft:rgba(var(--ink-rgb),.18);
  --cursor-border:rgba(var(--ink-rgb),.34)}
/* Várias classes de rótulo declaram display próprio e venceriam o [hidden] da
   folha do navegador. A regra explícita garante remoção real da caixa. */
.about-k[hidden],.caps-k[hidden],.help-k[hidden],.mail-k[hidden],
.case-hero .eyebrow[hidden]{display:none}
.cur-dot,.cur-trail,.cur-label{position:absolute;top:0;left:0;pointer-events:none;
  will-change:transform}
/* núcleo: 16px, some por padrão até o ponteiro se mover pela primeira vez */
.cur-dot{width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;
  background:radial-gradient(circle,var(--cursor-core),var(--cursor-glow-soft) 62%,transparent 72%);
  box-shadow:0 0 12px 2px var(--cursor-glow-soft);
  opacity:0;transition:opacity var(--dur-micro) var(--ease-soft),
                       width var(--dur-micro) var(--ease-soft),
                       height var(--dur-micro) var(--ease-soft),
                       margin var(--dur-micro) var(--ease-soft)}
.cur-layer.on .cur-dot{opacity:1}
/* sobre controle: menor e mais preciso. Sobre card: vira halo atrás do rótulo. */
.cur-layer.is-control .cur-dot{width:9px;height:9px;margin:-4.5px 0 0 -4.5px}
.cur-layer.is-card .cur-dot{width:64px;height:64px;margin:-32px 0 0 -32px;opacity:.5}

/* três rastros fixos, cada um menor e mais apagado que o anterior */
.cur-trail{border-radius:50%;background:var(--cursor-glow-soft);opacity:0;
  transition:opacity 320ms var(--ease-soft)}
.cur-trail:nth-child(1){width:11px;height:11px;margin:-5.5px 0 0 -5.5px}
.cur-trail:nth-child(2){width:8px;height:8px;margin:-4px 0 0 -4px}
.cur-trail:nth-child(3){width:5px;height:5px;margin:-2.5px 0 0 -2.5px}
.cur-layer.fast .cur-trail:nth-child(1){opacity:.42}
.cur-layer.fast .cur-trail:nth-child(2){opacity:.26}
.cur-layer.fast .cur-trail:nth-child(3){opacity:.14}
/* sobre card ou controle o rastro sai: ali o cursor precisa ser preciso */
.cur-layer.is-card .cur-trail,.cur-layer.is-control .cur-trail{opacity:0}

/* rótulo "Ver projeto": centrado no mesmo ponto do núcleo, então o cursor
   parece crescer e revelar o texto por dentro, sem animar largura. */
.cur-label{transform-origin:50% 50%;white-space:nowrap;
  font-family:var(--mono);font-size:.7rem;letter-spacing:.025em;color:var(--paper);
  padding:.6rem 1.05rem;border-radius:var(--radius-button,12px);
  background:rgba(var(--ink-rgb),.55);border:1px solid var(--cursor-border);
  backdrop-filter:blur(10px) saturate(140%);-webkit-backdrop-filter:blur(10px) saturate(140%);
  opacity:0;visibility:hidden;
  transition:opacity var(--dur-micro) var(--ease-soft),
             transform var(--dur-micro) var(--ease-soft),
             visibility var(--dur-micro)}
.cur-layer.is-card .cur-label{opacity:1;visibility:visible}

/* O cursor nativo só desaparece depois que o JS confirma que a camada foi
   criada e o dispositivo é compatível — a classe é adicionada por último, no
   fim da inicialização. Se o JS falhar em qualquer ponto antes disso, a classe
   nunca entra e o ponteiro do sistema continua lá.
   Campos de texto e áreas editáveis mantêm o cursor nativo: o I-beam informa
   onde o texto será inserido, e um glow não substitui isso. */
html.has-custom-cursor,
html.has-custom-cursor a,
html.has-custom-cursor button,
html.has-custom-cursor [role="button"],
html.has-custom-cursor summary{cursor:none}
html.has-custom-cursor input,
html.has-custom-cursor textarea,
html.has-custom-cursor select,
html.has-custom-cursor [contenteditable="true"],
html.has-custom-cursor label{cursor:auto}

/* Sem ponteiro fino, sem hover ou sob movimento reduzido a camada nem é criada
   pelo JS. Estas regras são a rede de segurança para o caso de o HTML vir de
   um cache antigo com a camada já no documento. */
@media(prefers-reduced-motion:reduce),(pointer:coarse),(hover:none){
  .cur-layer{display:none}
  html.has-custom-cursor,html.has-custom-cursor a,html.has-custom-cursor button{cursor:auto}
}
.orb{position:absolute;border-radius:50%}
.orb.a{width:88vw;height:88vw;top:-40vw;right:-30vw;background:radial-gradient(circle,rgba(89,79,79,.20),rgba(89,79,79,.06) 44%,transparent 72%)}
.orb.b{width:76vw;height:76vw;bottom:-36vw;left:-30vw;background:radial-gradient(circle,rgba(64,54,56,.22),rgba(64,54,56,.07) 46%,transparent 74%)}

/* Sem mix-blend-mode aqui. Camada fixa em tela cheia com blend força o navegador
   a remisturar todo o viewport a cada quadro da rolagem, e era o que fazia o
   scroll agarrar. Blend segue nos elementos pequenos, onde a área é limitada.
   A opacidade compensa a troca: ajuste só este número se o grão pedir. */
.grain{position:fixed;inset:-24%;z-index:72;pointer-events:none;
  opacity:var(--grain-current-opacity,var(--grain-opacity));background-image:var(--grain-url);
  background-size:var(--grain-size) var(--grain-size);will-change:transform,opacity;
  animation:grain-vivo 1450ms steps(2,end) infinite;
  transition:opacity var(--dur-micro) var(--ease-soft)}
.grain.is-off{opacity:0;animation-play-state:paused}
@keyframes grain-vivo{
  0%,100%{transform:translate3d(-2%,1%,0)}
  24%{transform:translate3d(1.5%,-2%,0)}
  49%{transform:translate3d(2%,1.5%,0)}
  74%{transform:translate3d(-1.5%,-1%,0)}
}
@media(prefers-reduced-motion:reduce){.grain{animation:none}}
/* ===== DESFOQUE DE BORDA INFERIOR =====
   Faixa fixa no rodapé da janela que borra o que passa por baixo e some para
   cima por máscara. Acompanha a página inteira e dá a sensação de o conteúdo
   se dissolver em vez de ser cortado pela borda da tela.
   É o único backdrop-filter em área larga do site: fica no rodapé, tem altura
   contida e não recebe transform, então não é reavaliado a cada quadro.
   Desligado em movimento reduzido e onde não há suporte a máscara. */
.edge-blur{position:fixed;left:0;right:0;bottom:0;height:clamp(4.5rem,9vh,8rem);
  z-index:40;pointer-events:none;
  backdrop-filter:blur(9px);-webkit-backdrop-filter:blur(9px);
  -webkit-mask-image:linear-gradient(to top,#000 0%,rgba(0,0,0,.65) 42%,transparent 100%);
          mask-image:linear-gradient(to top,#000 0%,rgba(0,0,0,.65) 42%,transparent 100%)}
@media(prefers-reduced-motion:reduce){.edge-blur{display:none}}
@supports not ((mask-image:linear-gradient(#000,transparent)) or
               (-webkit-mask-image:linear-gradient(#000,transparent))){
  .edge-blur{display:none}
}

a{color:inherit;text-decoration:none}

/* ===== INTRO =====
   Não existe um segundo nome. O véu cobre a página e o próprio .hero-name
   sobe acima dele, então é literalmente o mesmo elemento que atravessa as
   duas etapas: nenhum crossfade entre cópias, nenhum salto de posição. */
/* a saída do véu é longa de propósito: em 1s a passagem para a capa lia como
   corte. 2,7s dá tempo de o nome trocar de cor e os secundários entrarem por
   cima da cauda do fundido, e não de o véu simplesmente sumir.
   A duração vem do JS junto com o resto da linha do tempo: é o mesmo fator de
   escala da intro, e assim não existe número de intro escrito fora do arquivo
   que manda nela. O fallback só cobre a janela antes de o script rodar. */
.intro-veil{position:fixed;inset:0;z-index:90;background:var(--ink);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity var(--intro-veil,2700ms) var(--ease-soft),
             visibility var(--intro-veil,2700ms)}
.js body.intro-mode .intro-veil{opacity:1;visibility:visible}
/* o véu começa a sair junto com o assentamento do nome, não depois dele */
.js body.intro-mode.hero-in .intro-veil{opacity:0}
.js body.intro-mode:not(.hero-in) .intro-veil{transition:none}
body.intro-mode{overflow:hidden}
/* a hero inteira sobe acima do véu durante a intro: só o nome está visível
   (os secundários ainda estão em opacity 0), e quando eles entram já ficam
   por cima do véu que se dissolve. A classe sai no fim e tudo volta ao
   empilhamento normal, sem passar por cima do header. */
.js body.intro-mode .hero{position:relative;z-index:95}
/* Com a rolagem suave, a .hero vive dentro do .smooth-holder, que é fixed e
   cria contexto de empilhamento próprio: o z-index dela deixa de alcançar o
   véu e a intro vira uma tela preta. Quem precisa subir é o holder. Só a hero
   ocupa a primeira dobra, então nada mais aparece por cima do véu. */
.js body.intro-mode .smooth-holder{z-index:95}

/* ===== TRANSFORMAÇÃO DO NOME NA INTRO =====
   Uma única transformação contínua que ocupa a intro inteira: começa 20% maior
   e quase apagado, e só chega a escala 1 com opacidade 1 nos últimos 5%.
   Nada de varredura por máscara nem gradiente no texto: a revelação é opacidade
   e escala, e a cor não muda em momento algum.

   A curva mora nos próprios keyframes, e a interpolação entre eles é linear de
   propósito. Um easing forte de saída (como o .16,1,.3,1 do projeto) resolveria
   quase todo o movimento nos primeiros instantes e deixaria o nome parado
   esperando o fim da intro, que é exatamente o que se quer evitar aqui.

   A duração vem de --intro-dur, que o JS escreve a partir de INTRO_MS: os dois
   não podem divergir, senão o nome termina antes ou depois do véu sair.

   Ao final a animação segura o estado com forwards. Quando .intro-mode sai, a
   regra deixa de valer e o nome cai em scale(1)/opacity 1, que é exatamente
   onde a animação parou: mesmo elemento, mesma posição, sem salto e sem
   crossfade com uma segunda cópia. */
/* Estado de partida sem animação. O véu já cobre a página desde o primeiro
   quadro, mas o nome fica acima dele, então ele precisa nascer já apagado e
   desfocado, exatamente como o quadro 0% da animação: sem isso apareceria
   nítido por um instante antes de a animação assumir. */
.js body.intro-mode .hero-name{transform-origin:50% 50%;
  opacity:.1;filter:blur(8px);
  transform:translateY(var(--nome-dy,0px)) scale(1.12)}
/* A animação só entra com .name-in, que o JS adiciona depois de medir o
   deslocamento. Disparar junto com .intro-mode fazia a animação capturar um
   --nome-dy ainda calculado com a fonte de sistema, e o nome começava fora do
   centro. Trocar o var depois não corrige uma animação já iniciada. */
.js body.intro-mode.name-in .hero-name{
  animation:nome-intro var(--intro-dur,2000ms) linear forwards}
/* Escala inicial de 1.12 como padrão, e não 1.2. Abaixo de ~1277px a fonte do
   nome cresce com a viewport (10.4vw) e o texto já ocupa 83% da largura da
   janela: em 1.2 ele chega a 99,8% e sai da tela pela esquerda. Medido a 1000px,
   dava -6px. Com 1.12 a ocupação fica em 93,2% em qualquer largura dessa faixa.
   Acima de 1360px a fonte está travada no teto de 8.3rem, o texto para de
   crescer junto com a tela e aí 1.2 cabe com folga: é o desktop de verdade.

   --nome-dy é a distância entre o centro do nome na composição final e o centro
   exato da viewport, medida pelo JS. A animação parte desse deslocamento e
   termina em zero: o nome começa centrado na tela e sobe até seu lugar na capa,
   junto com a escala, numa transformação só. Sem a medição, o valor mudaria a
   cada proporção de tela e o nome não ficaria centrado na intro.

   O blur existe só nos primeiros ~280ms, para o texto não surgir do nada. É a
   única exceção à regra de não usar filter:blur() no site: dura um instante,
   afeta um elemento e some antes de a rolagem existir. */
/* ETAPA 1, 0 a 800ms: o nome fica absolutamente parado no centro, em escala
   1.2, e só a luz o atravessa. A revelação é clip-path na .hn-line, uma faixa
   contínua da esquerda para a direita, sem digitação e sem letra por letra.
   Opacidade e blur correm junto, no mesmo intervalo.
   Duas animações em elementos diferentes, não duas cópias do nome: a máscara
   vive na linha interna, a escala e o deslocamento no .hero-name. */
@keyframes revela-nome{
  0%  {clip-path:inset(0 100% 0 0)}
  100%{clip-path:inset(0 0 0 0)}
}
@keyframes acende-nome{
  0%  {opacity:.10; filter:blur(8px)}
  50% {opacity:.58; filter:blur(3px)}
  100%{opacity:1;   filter:blur(0)}
}
.js body.intro-mode.name-in .hero-name .hn-line{
  animation:revela-nome var(--intro-reveal,800ms) var(--ease-smooth) forwards}
.js body.intro-mode.name-in .hero-name{
  animation:acende-nome var(--intro-reveal,800ms) var(--ease-smooth) forwards,
            assenta-nome var(--intro-move,1200ms) var(--ease-soft) var(--intro-reveal,800ms) forwards}

/* ETAPA 2, 800 a 2000ms: escala e posição caminham juntas, num movimento só.
   Sem novo fade, sem novo blur, sem parada entre as duas etapas: esta animação
   começa exatamente quando a primeira termina, pelo delay. */
@keyframes assenta-nome{
  from{transform:translateY(var(--nome-dy,0px)) scale(1.12)}
  to  {transform:translateY(0) scale(1)}
}
@media(min-width:1360px){
  .js body.intro-mode .hero-name{transform:translateY(var(--nome-dy,0px)) scale(1.2)}
  .js body.intro-mode.name-in .hero-name{animation-name:acende-nome,assenta-nome-amplo}
}
@keyframes assenta-nome-amplo{
  from{transform:translateY(var(--nome-dy,0px)) scale(1.2)}
  to  {transform:translateY(0) scale(1)}
}
/* estado de partida da linha: fechada, para não piscar inteira antes da animação */
.js body.intro-mode .hero-name .hn-line{transform:none;clip-path:inset(0 100% 0 0)}
/* a máscara do nome não pode recortar: o clip-path já cuida da revelação */
.js body.intro-mode .hero-name .hn-mask{overflow:visible}

/* O fundo ambiente entra discreto e ganha corpo conforme o nome assenta, para
   a passagem para a capa não ter degrau de textura. */
.hero-liquid{opacity:1;transition:opacity var(--dur-ambient) var(--ease-soft)}
.js body.intro-mode:not(.hero-in) .hero-liquid{opacity:.5}

@media(prefers-reduced-motion:reduce){
  /* Nada de esperar 2s: fundido de 260ms, sem escala, sem blur, sem percurso.
     A linha nasce aberta, senão o nome ficaria recortado para sempre. */
  .js body.intro-mode .hero-name,
  .js body.intro-mode.name-in .hero-name{filter:none;transform:none;
    animation:nome-intro-simples 260ms linear forwards}
  .js body.intro-mode .hero-name .hn-line,
  .js body.intro-mode.name-in .hero-name .hn-line{animation:none;clip-path:none}
  @keyframes nome-intro-simples{from{opacity:.35}to{opacity:1}}
}

.eyebrow{font-family:var(--mono);font-size:.72rem;letter-spacing:.05em;color:var(--accent);font-weight:var(--w-medium);
  display:inline-flex;align-items:center;gap:.6em}
.eyebrow::before{content:"";width:1.6em;height:1px;background:var(--accent);opacity:.5}
/* Container fluido: ocupa quase toda a largura com gutters consistentes e só
   trava em ultrawide. O limite estreito de leitura fica nos textos, não no
   wrapper inteiro, que era o que deixava blocos pequenos perdidos no centro. */
.wrap{width:calc(100% - var(--page-gutter) * 2);max-width:var(--content-max);
  margin-inline:auto;padding-inline:0}

header{position:fixed;top:0;left:0;right:0;z-index:50;will-change:transform;
  transition:transform var(--dur-default) var(--ease-out-smooth)}
/* Durante a assinatura inicial só o nome fica acima do véu. Quando os
   elementos secundários da hero começam a entrar (`hero-sec`), o cabeçalho
   entra no mesmo instante e também sobe acima do véu. Antes ele permanecia
   em z-index:50, atrás do véu (90) e do holder (95), e só aparecia bem depois
   durante o fundido final. A entrada é imediata: quando os controles da hero
   aparecem, o cabeçalho já está 100% visível, sem um segundo fade atrasado. */
.js body.intro-mode header{opacity:0}
.js body.intro-mode.hero-sec header{z-index:96;opacity:1}
.js body.intro-mode.hero-sec .overlay{z-index:99}
.nav{margin:1.1rem var(--gutter);display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:1.4rem}
/* ===== CONTRASTE DO HEADER SOBRE QUALQUER FUNDO =====
   Uma vinheta de largura cheia (header::before) chegou a existir aqui, mas
   pintava uma faixa escura visível mesmo sobre o fundo escuro padrão do site,
   lendo como uma sombra indevida. drop-shadow resolve o mesmo problema (marca,
   pílula de Menu/Disponibilidade e seletor de idioma sem contraste garantido
   contra o que rola por baixo) sem pintar retângulo nenhum: escurece só o
   contorno de cada glifo e ícone, invisível sobre o próprio fundo escuro do
   site e só aparece quando o fundo por baixo realmente precisa dele. filter,
   não text-shadow, porque também precisa cobrir os ícones SVG (seta do
   idioma, ponto do Menu).
   A primeira versão (dois drop-shadow, um deles com 7px de raio e opacidade
   .55) ficou forte e espalhada demais, visível mesmo onde não precisava. A
   segunda, só um drop-shadow bem fraco (opacidade .4), deixou de segurar a
   letra sobre um fundo bem claro (testado com uma imagem quase branca atrás
   do header). Raio curto (2px, sem segunda camada "espalhada") com opacidade
   média (.6) é o meio-termo: continua imperceptível sobre o fundo escuro
   padrão do site, mas ainda define a borda da letra o bastante sobre um
   fundo muito claro. */
/* ===== TEMA DO HEADER: UM ESTADO SÓ =====
   Antes, o contraste sobre fundo claro era resolvido por uma regra .on-light
   que listava só quatro elementos (.brand, .menu-btn, .avail, .lp-toggle).
   Todo o resto — ponto de disponibilidade, botão Contato e seu ícone, fundo e
   borda da pílula, divisor, bandeira, seta, hover e foco — continuava com cor
   de tema escuro e sumia sobre o FAQ claro.
   Agora existe um conjunto de variáveis locais do header. Todo componente lê
   daqui; trocar de tema é trocar estas variáveis num lugar só, e nenhum
   componente pode ficar para trás por esquecimento. */
header{
  --hd-fg:var(--paper);                          /* texto principal */
  --hd-fg-soft:var(--muted);                     /* texto secundário */
  --hd-fg-dim:var(--muted-2);                    /* ícones fracos */
  --hd-surface:rgba(var(--paper-rgb),.05);       /* material da pílula */
  --hd-surface-hover:rgba(var(--paper-rgb),.09);
  --hd-line:var(--line);
  --hd-line-strong:var(--line-strong);
  --hd-div:rgba(var(--paper-rgb),.14);           /* divisor interno */
  --hd-ring:rgba(var(--paper-rgb),.16);          /* anel da bandeira */
  --hd-cta-bg:var(--paper);                      /* botão Contato */
  --hd-cta-fg:var(--ink);
  --hd-halo:rgba(var(--ink-rgb),.6);             /* drop-shadow do glifo */
  --hd-dot:var(--status-available);               /* ponto de disponibilidade */
}
/* Sobre fundo claro (FAQ, capa de projeto clara): inverte o conjunto inteiro.
   O ponto de disponibilidade escurece porque o verde claro original sumia
   sobre #F2EEEE; o botão Contato inverte para não virar branco sobre claro. */
header[data-theme="light"]{
  --hd-fg:var(--ink);
  --hd-fg-soft:var(--ink-3);
  --hd-fg-dim:var(--ink-3);
  --hd-surface:rgba(var(--ink-rgb),.06);
  --hd-surface-hover:rgba(var(--ink-rgb),.11);
  --hd-line:rgba(var(--ink-rgb),.20);
  --hd-line-strong:rgba(var(--ink-rgb),.34);
  --hd-div:rgba(var(--ink-rgb),.20);
  --hd-ring:rgba(var(--ink-rgb),.24);
  --hd-cta-bg:var(--ink);
  --hd-cta-fg:var(--paper);
  --hd-halo:rgba(255,255,255,.55);
  --hd-dot:var(--status-available-on-light);
}
body[data-availability="unavailable"] header{--hd-dot:var(--status-unavailable)}
body[data-availability="unavailable"] header[data-theme="light"]{--hd-dot:var(--status-unavailable-on-light)}
/* A troca precisa ser suave e simultânea em todo o topo. Só cor, borda e
   sombra entram na transição: nada de layout, então não existe salto. */
header .brand,header .menu-btn,header .avail,header .lp-toggle,
header .nav-cta,header .ctrl-group,header .ctrl-div,
header .lp-arrow,header .lp-flag,header .menu-btn i{
  transition:color var(--dur-default) var(--ease-soft),
             background-color var(--dur-default) var(--ease-soft),
             border-color var(--dur-default) var(--ease-soft),
             box-shadow var(--dur-default) var(--ease-soft),
             filter var(--dur-default) var(--ease-soft),
             background-image var(--dur-default) var(--ease-soft)}
/* drop-shadow em vez de text-shadow porque também precisa cobrir SVG (seta do
   idioma, ponto do Menu). Raio curto e opacidade média: imperceptível sobre o
   fundo escuro padrão, mas ainda define a borda do glifo sobre fundo claro. */
.brand,.menu-btn,.avail,.lp-toggle{
  filter:drop-shadow(0 1px 2px var(--hd-halo))}

/* Sobre um card marcado --light no CMS (ver content/projects/index.json,
   coverLight), o header troca para texto escuro em vez de contar só com o
   drop-shadow: mais limpo que forçar sombra escura sobre texto que já é
   escuro. js/main.js (medirClaros/pintarClaros) liga esta classe pela mesma
   lógica de posição por scroll que o resto do arquivo usa, porque
   IntersectionObserver não reavalia com a rolagem suave por transform. */
/* As regras .on-light que existiam aqui (só .brand, .menu-btn, .avail e
   .lp-toggle) foram substituídas pelas variáveis --hd-* acima, que todo o
   header consome. */
/* ===== CAIXA COMPARTILHADA: MENU + DISPONIBILIDADE =====
   Um contorno só, um raio só, um material só. O fundo e a borda vivem no
   container; o botão e o status ficam sem contorno próprio, senão o conjunto
   voltaria a ler como dois pills encostados. */
.nav .ctrl-group{justify-self:center;display:flex;align-items:center;
  padding:.22rem;border-radius:var(--radius-button,12px);
  background:var(--floating-surface);border:1px solid var(--floating-line);
  box-shadow:0 12px 34px var(--floating-shadow);
  backdrop-filter:blur(18px) saturate(145%);-webkit-backdrop-filter:blur(18px) saturate(145%)}
/* O cabeçalho continua atrás do painel translúcido durante a abertura. Sem
   ocultar a pílula original, os dois textos aparecem sobrepostos até o fade
   terminar. Ela segue na área de hit para o hover, mas deixa de ser pintada. */
body.menu-open header .ctrl-group{opacity:0}
@media(hover:hover) and (pointer:fine){
  .nav .ctrl-group:hover{border-color:var(--floating-line-strong)}
}
/* divisor interno discreto, altura parcial */
.ctrl-div{width:1px;height:1.1rem;flex:none;margin-inline:.3rem;
  background:var(--floating-line)}
.ctrl-div[hidden],.avail[hidden],.menu-panel-div[hidden],.menu-panel-avail[hidden]{display:none!important}
.nav .nav-right{justify-self:end}
.brand{font-weight:var(--w-medium);letter-spacing:-.01em;font-size:1rem;color:var(--hd-fg)}
.brand span{color:var(--hd-fg-soft)}
.nav-right{display:flex;align-items:center;gap:1rem}
/* Sem contorno nem material próprios: quem os carrega é o .ctrl-group. O Menu
   é o elemento principal, então fica em Medium, com contraste cheio e 44px de
   altura mínima para toque. */
.menu-btn{display:inline-flex;align-items:center;gap:.6em;cursor:pointer;font-family:var(--font);
  font-size:.88rem;font-weight:var(--w-medium);color:var(--hd-fg);
  padding:.62rem 1.05rem;min-height:44px;border-radius:var(--radius-button,12px);
  background:transparent;border:none}
.ctrl-group .menu-btn{color:var(--paper);filter:none}
@media(hover:hover) and (pointer:fine){
  .menu-btn:hover{background:var(--floating-surface-hover)}
  .menu-btn:hover i{background-image:radial-gradient(circle,var(--paper) 1.4px,transparent 1.5px)}
}
.menu-btn i{width:9px;height:9px;flex:none;
  background-image:radial-gradient(circle,var(--hd-fg-soft) 1.4px,transparent 1.5px);
  background-size:4.5px 4.5px}
.ctrl-group .menu-btn i{background-image:radial-gradient(circle,var(--muted) 1.4px,transparent 1.5px)}

.nav-cta{position:relative;display:inline-flex;align-items:center;gap:.55em;cursor:pointer;
  font-family:var(--font);font-size:.88rem;font-weight:var(--w-medium);color:var(--hd-cta-fg);
  padding:.6rem 1.1rem;border-radius:var(--radius-button,12px);background:var(--hd-cta-bg);border:none;
  transition:transform var(--dur-micro) var(--ease-soft),
             background-color var(--dur-micro) var(--ease-soft)}
.nav-cta svg{width:14px;height:14px;flex:none;
  transition:transform var(--dur-fast) var(--ease-soft)}
.nav-cta .nc-label{display:inline-block;
  transition:opacity var(--dur-micro) var(--ease-soft),transform var(--dur-fast) var(--ease-soft)}
/* No hover o rótulo some e o ícone caminha até o centro. O texto continua no
   fluxo, só invisível, então a largura do botão não muda: zero layout shift.
   --nc-shift é medido pelo JS, porque o rótulo muda de largura com o idioma. */
@media(hover:hover){
  .nav-cta:hover{transform:translateY(-2px)}
  .nav-cta:hover .nc-label{opacity:0;transform:translateX(-6px)}
  .nav-cta:hover svg{transform:translateX(var(--nc-shift,0px)) scale(1.12)}}
@media(max-width:480px){.nav-cta .nc-label{display:none}}

/* ===== HERO: o nome é o centro exato da tela =====
   Só o nome fica no fluxo. Tag, carimbo e "role para explorar" são
   absolutos, então não empurram o nome do centro do viewport, que é o que
   permite ele atravessar a intro sem se mover um pixel. */
.hero{position:relative;min-height:100svh;display:grid;place-items:center;
  padding-block:0;text-align:center}

/* Coluna única e centrada: carimbo, cargo, nome e chamada de rolagem ficam
   juntos, em vez de espalhados pelos cantos da tela.
   Os secundários seguem no fluxo mesmo durante a intro, só invisíveis: como
   reservam espaço, o nome ocupa exatamente a mesma posição nas duas etapas e
   a continuidade da intro não quebra. Se algum deles virar absoluto de novo,
   o nome salta na passagem. */
/* ===== FUNDO DE VIDRO LÍQUIDO (só CSS) =====
   Quatro massas de cor em marrom escuro que derivam, giram e respiram em
   ciclos longos e primos entre si, para o laço nunca bater no mesmo desenho.
   Nada de filter:blur(): a maciez vem de stops muito largos, que é o que o
   projeto já usa nas orbes. Só transform anima, então a GPU compõe e a thread
   principal não repinta nada.
   A vinheta central puxa o miolo de volta para o fundo do site: sem ela o
   marrom sobe atrás do nome e os rótulos pequenos perdem contraste. */
/* O vidro líquido é só da capa. Chegou a servir projetos e sobre também, mas
   essas duas seções voltaram ao plano: a mesma cor lisa do rodapé e de
   "o que eu faço", sem massa animada por baixo. A classe ficou genérica
   (.liquid-bg) porque é reutilizável, não porque está em uso em mais de um
   lugar agora. */
.hero{isolation:isolate;position:relative}
.liquid-bg{position:absolute;top:0;bottom:0;left:50%;z-index:0;
  width:var(--vw,100vw);margin-left:calc(var(--vw,100vw) / -2);
  overflow:hidden;pointer-events:none}
.hero-stack{position:relative;z-index:1}

/* Vídeo de fundo da capa: cobre a mesma caixa das massas de vidro líquido e
   fica atrás delas no DOM, então a vinheta (.lq-veil), sempre por último,
   continua por cima protegendo o contraste. Com vídeo ativo (.has-video),
   as massas somem para não brigar visualmente com a imagem em movimento. */
.hero-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.liquid-bg.has-video .lq{display:none}

/* Poster da capa: fica ATRÁS do vídeo e do iframe, então aparece antes de
   qualquer um deles carregar, e continua sendo o que se vê se o Vimeo estiver
   bloqueado ou sob movimento reduzido. Pintado por variável, definida pelo JS,
   para o caminho da imagem não precisar entrar em innerHTML. */
.liquid-bg.has-poster::before{content:"";position:absolute;inset:0;z-index:0;
  background-image:var(--hero-poster);background-size:cover;background-position:50% 50%}

/* O iframe do Vimeo cobre a capa como um object-fit:cover faria. Um iframe não
   tem object-fit, então o recorte vem de dimensionar por aspecto: o player é
   16/9 e precisa transbordar no eixo mais curto para não deixar barra preta.
   min-width/min-height em vw e vh cobrem os dois casos, e o translate recentra
   o excesso.

   opacity entra em cena depois que o iframe carrega (.is-pronto, posta pelo JS
   no evento load). Antes disso ele fica invisível, e o que se vê é o poster ou
   o fundo animado. Isso resolve um caso concreto: quando o vídeo não existe,
   é privado ou o Vimeo está fora do ar, o player renderiza a própria tela de
   erro ("Desculpe, este vídeo não existe") — que apareceria por cima da capa,
   competindo com o nome. Invisível por padrão, um vídeo que não carrega
   simplesmente não aparece, e a capa continua íntegra. */
.hero-vimeo{position:absolute;top:50%;left:50%;z-index:1;border:0;
  width:100vw;height:56.25vw;min-width:177.78vh;min-height:100vh;
  transform:translate(-50%,-50%);
  pointer-events:none;
  opacity:0;transition:opacity var(--dur-slow) var(--ease-soft)}
.hero-vimeo.is-pronto{opacity:1}
/* decorativo: não captura clique nem foco, e o conteúdo da capa segue acima */
.liquid-bg .lq-veil{z-index:2}
@media(prefers-reduced-motion:reduce){
  /* rede de segurança: o JS já não cria o iframe neste modo */
  .hero-vimeo{display:none}
}

.lq{position:absolute;display:block;border-radius:50%}
.lq1{width:76vw;height:76vw;top:-26vw;left:-16vw;
  background:radial-gradient(circle,rgba(92,62,48,.50),rgba(74,50,40,.22) 38%,rgba(58,40,34,.07) 62%,transparent 78%);
  animation:lq-a 34s ease-in-out infinite}
.lq2{width:64vw;height:64vw;bottom:-24vw;right:-14vw;
  background:radial-gradient(circle,rgba(78,52,42,.46),rgba(62,42,36,.20) 40%,rgba(48,34,30,.06) 64%,transparent 80%);
  animation:lq-b 41s ease-in-out infinite}
.lq3{width:52vw;height:52vw;top:22%;right:6%;
  background:radial-gradient(circle,rgba(112,78,58,.30),rgba(84,58,46,.12) 42%,transparent 72%);
  animation:lq-c 29s ease-in-out infinite}
.lq4{width:58vw;height:58vw;bottom:4%;left:12%;
  background:radial-gradient(circle,rgba(64,46,42,.34),rgba(48,34,32,.13) 44%,transparent 74%);
  animation:lq-d 47s ease-in-out infinite}

/* A vinheta protege o contraste do texto sobre a névoa, puxando o miolo de
   volta para o fundo do site. */
.lq-veil{position:absolute;inset:0;display:block;
  background:radial-gradient(ellipse 62% 52% at 50% 50%,
    rgba(var(--ink-rgb),.86) 0%,rgba(var(--ink-rgb),.62) 38%,
    rgba(var(--ink-rgb),.22) 66%,transparent 88%)}

@keyframes lq-a{
  0%,100%{transform:translate3d(0,0,0) scale(1)}
  33%{transform:translate3d(7vw,5vh,0) scale(1.13)}
  66%{transform:translate3d(-4vw,9vh,0) scale(.95)}
}
@keyframes lq-b{
  0%,100%{transform:translate3d(0,0,0) scale(1.04)}
  40%{transform:translate3d(-8vw,-6vh,0) scale(.92)}
  72%{transform:translate3d(5vw,-3vh,0) scale(1.12)}
}
@keyframes lq-c{
  0%,100%{transform:translate3d(0,0,0) scale(.96)}
  45%{transform:translate3d(-6vw,7vh,0) scale(1.16)}
  75%{transform:translate3d(3vw,-5vh,0) scale(1.02)}
}
@keyframes lq-d{
  0%,100%{transform:translate3d(0,0,0) scale(1.08)}
  38%{transform:translate3d(9vw,-7vh,0) scale(.94)}
  70%{transform:translate3d(-5vw,-2vh,0) scale(1.15)}
}
/* Fundo fora do campo de visão: o JS marca e a animação para. Não é
   display:none nem visibility, para a seção não ter de repintar o fundo do zero
   quando voltar; a camada continua composta, só deixa de se mover. */
.liquid-bg.parado .lq{animation-play-state:paused}
@media(prefers-reduced-motion:reduce){
  .lq{animation:none!important}
}

.hero-stack{display:flex;flex-direction:column;align-items:center;text-align:center}

/* fit-content: a largura do wrapper é a largura real do nome, e a linha de
   metadados abaixo herda essa medida. É o que ancora profissão e carimbo às
   bordas do texto em vez das bordas da página. */
.hero-name-wrap{width:fit-content;max-width:100%;margin-inline:auto}
/* Grid com minmax(0,1fr) e não flex: assim a contribuição da linha para a
   largura do wrapper é a de min-content (a maior palavra), e não a soma dos
   dois textos inteiros. Quem define a medida do wrapper continua sendo o nome,
   que é a âncora do alinhamento. Com flex, uma localização mais longa alargava
   o wrapper e o nome parava de tocar as bordas. */
.hero-meta{width:100%;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  align-items:start;gap:1.4rem;margin-top:clamp(.9rem,1.8vh,1.5rem)}
.hero-tag{justify-self:start}
.hero-when{justify-self:end;align-items:flex-end}
.hero-tag{font-family:var(--mono);font-size:.74rem;letter-spacing:.03em;color:var(--accent);
  text-align:left}
/* localização e carimbo formam um bloco de duas linhas, ancorado na borda
   direita do nome */
.hero-when{font-family:var(--mono);font-size:.72rem;letter-spacing:.015em;color:var(--muted-2);
  text-align:right;display:flex;flex-direction:column;gap:.3rem}
.hero-when b{color:var(--muted);font-weight:var(--w-regular)}
.hero-when #stamp{margin-left:0}

/* Segunda camada da capa: presença real, ainda claramente abaixo do nome. */
/* O respiro entre o nome e a frase é o que separa as duas camadas da capa.
   Como o .hero-stack é centrado, aumentar esta margem também empurra o nome
   para cima, que é exatamente o efeito desejado: mais ar embaixo, nome mais
   alto. O --nome-dy é remedido pelo JS, então a intro continua nascendo no
   centro exato mesmo com a composição deslocada. */
.hero-claim{max-width:min(40ch,900px);margin:clamp(3.2rem,7vh,5.6rem) auto 0;
  font-size:clamp(1.3rem,2.1vw,2.3rem);font-weight:var(--w-medium);line-height:1.28;
  letter-spacing:-.02em;color:var(--cream)}

/* ===== TAG DE DISPONIBILIDADE =====
   Vive ao lado do botão Menu, no bloco central do topo: assim não consome uma
   faixa própria na hero. Só o ponto se move; o fundo da tag fica parado. */
/* Status, não botão: peso Regular, corpo menor e nenhum cursor de clique. Não
   deve competir com o Menu nem parecer acionável, mas precisa continuar
   legível sozinho: era --muted-2 com opacity:.82 por cima, contraste baixo
   demais para se sustentar sobre a vinheta do header num fundo muito claro.
   --muted sem opacity extra já basta para diferenciar do Menu (esse sim em
   --paper, peso Medium). */
.avail{display:inline-flex;align-items:center;gap:.55em;
  font-size:.7rem;font-weight:var(--w-regular);letter-spacing:.02em;color:var(--hd-fg-soft);
  padding:.3rem .85rem .3rem .2rem;white-space:nowrap;
  background:none;border:none}
.ctrl-group .avail{color:var(--muted);filter:none}
.avail-dot{width:7px;height:7px;flex:none;border-radius:50%;background:var(--hd-dot,var(--status-available));
  animation:pulso-disp 1600ms var(--ease-smooth) infinite}
body[data-availability="available"] .ctrl-group .avail-dot{background:var(--status-available)}
body[data-availability="unavailable"] .ctrl-group .avail-dot{background:var(--status-unavailable)}
body[data-availability="unavailable"] .menu-panel-avail .avail-dot{background:var(--status-unavailable)}
@keyframes pulso-disp{
  0%,100%{opacity:.5;transform:scale(.9)}
  50%{opacity:1;transform:scale(1.15)}
}
@media(prefers-reduced-motion:reduce){
  .avail-dot{animation:none;opacity:1;transform:none}
}
/* A caixa compartilhada sobrevive ao estreito: em vez de sumir, o status
   encurta para "Disponível" / "Available", que o JS troca pelo mesmo sistema de
   idioma. Só abaixo de 560px, onde nem isso cabe, resta o ponto com rótulo
   acessível, e o Menu nunca é escondido. */
@media(max-width:900px){
  .avail{font-size:.66rem;padding-right:.7rem}
  .ctrl-div{margin-inline:.22rem}
}
/* ===== HEADER NO CELULAR =====
   O grid "1fr auto 1fr" centraliza a pílula na JANELA, e para isso as duas
   colunas laterais precisam ter a mesma largura. A partir do momento em que a
   marca e o seletor de idioma não cabem nessa metade, o grid não encolhe nada:
   ele estoura. Era isso que jogava o seletor 26px para fora da tela a 375px e
   quebrava "Pedro de Trindade." em duas linhas.
   No estreito o centro exato deixa de ser um requisito: as três peças passam a
   se distribuir pelo espaço que existe, cada uma com o tamanho que tem. */
@media(max-width:640px){
  .nav{grid-template-columns:auto auto auto;justify-content:space-between;gap:.5rem}
  .nav .ctrl-group{justify-self:center}
  .brand{white-space:nowrap;font-size:.9rem}
  .menu-btn{font-size:.82rem;padding:.62rem .8rem;gap:.45em}
  .lp-toggle{font-size:.76rem;padding:.45rem .6rem;gap:.4em}
  .lp-flag{width:15px;height:15px}
  .nav-right{gap:.5rem}
  /* O "Contato" do header é o que empurrava o seletor de idioma para fora da
     tela. No estreito ele é a peça mais fácil de dispensar: o mesmo contato
     está no menu, que fica a um toque, e na seção do fim da página. O
     interruptor showContactButton do CMS continua mandando onde o botão
     aparece — isto aqui só o retira de onde ele não cabe. */
  .nav-cta{display:none}
}
/* Abaixo disso nem as versões encolhidas somam menos que a largura da tela.
   O que sai é o rótulo do status, que já tem versão curta e nome acessível, e
   o código do idioma, cuja bandeira continua dizendo a mesma coisa. O Menu e a
   marca nunca saem: são a identidade e a única navegação. */
@media(max-width:400px){
  .nav{gap:.35rem}
  .brand{font-size:.82rem}
  .menu-btn{font-size:.78rem;padding:.62rem .65rem}
  .lp-code{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
}
/* Rede de segurança, não regra de rotina: com a variante curta ativa a caixa
   inteira mede ~165px a 360px, então ela cabe em qualquer telefone real. Só
   abaixo disso o rótulo sai da tela e fica apenas para leitor de tela. */
/* 560px e não 340px: a 340 a regra chegava tarde demais, quando o seletor de
   idioma já tinha saído da tela havia muito. Abaixo de 560 o rótulo do status
   é a peça que menos custa perder, porque o ponto pulsante continua ali e o
   nome acessível continua sendo lido. */
@media(max-width:560px){
  .avail span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
  .avail{padding:.3rem .55rem .3rem .2rem}
}

/* No estreito a linha de metadados não cabe lado a lado: vira duas linhas,
   profissão acima e carimbo abaixo, cada um mantendo sua borda. */
@media(max-width:640px){
  .hero-meta{grid-template-columns:1fr;justify-items:center;gap:.65rem;
    width:min(100%,32rem);margin-top:1.15rem}
  .hero-tag{justify-self:stretch;text-align:center;padding-bottom:.65rem;
    border-bottom:1px solid var(--line)}
  .hero-when{justify-self:center;align-items:center;text-align:center;gap:.18rem}
  /* No desktop o nome é, de longe, o elemento mais largo da capa. No celular
     ele encolhe com a viewport e a frase passava a ultrapassá-lo, o que
     desmanchava a coluna. --nome-larg é publicado pelo JS a cada remedição;
     sem JS o 100% de reserva devolve o comportamento antigo. */
  .hero-claim{max-width:var(--nome-larg,100%)}
}

/* Peso 600 é o teto. A presença vem da escala e do tracking, não do peso.
   Nome sempre em uma linha só: o tamanho é puramente proporcional à largura
   da viewport, então a relação entre texto e tela nunca muda. O coeficiente
   em vw foi calibrado medindo o texto real contra a área útil; mexer no
   letter-spacing ou no peso exige recalibrar, senão volta a quebrar. */
.hero-name{font-size:clamp(1.5rem,10.4vw,8.3rem);font-weight:var(--w-medium);
  line-height:.9;letter-spacing:-.055em;margin:0;white-space:nowrap;
  color:var(--color-hero-name)}
.hn-mask{display:block;overflow:hidden;padding-bottom:.1em;margin-bottom:-.1em}
/* O nome cresce até 1.2 na intro. Com overflow:hidden a máscara cortaria as
   laterais e o topo durante a redução, então aqui ela não recorta: o nome não
   sobe de dentro dela, nada precisa ser escondido. */
.hero-name .hn-mask{overflow:visible}
.hn-line{display:block;white-space:nowrap}
.js .hn-mask.reveal .hn-line{transform:translateY(105%)}
.hn-line i{font-style:normal;color:inherit;opacity:.62}

@media(max-width:760px){
  .hero-tag{white-space:nowrap}
  .hero-when{white-space:normal}
}

section{padding-block:clamp(4rem,8vw,7rem)}

/* ===== CAPÍTULO =====
   Cada seção principal se comporta como um capítulo: entramos no espaço dela
   antes de o conteúdo aparecer. A profundidade vem de padding e min-height, não
   de divs vazias nem de espaçador. Seções que já são naturalmente maiores que a
   viewport ignoram o min-height, porque o conteúdo manda.
   No estreito o respiro cai bastante: a mesma medida do desktop viraria tela
   vazia no celular. */
/* O #about não entra aqui: ele é uma faixa sangrada e quem carrega o respiro
   é o .about-break, senão o padding dobraria.
   #work fica de fora do grupo reduzido abaixo de propósito: é a seção mais
   longa da página e a mais fotografada nas revisões, então mantém o respiro
   original enquanto as outras encolhem. */
/* Espaçamento por seção, com três camadas de dispositivo cada.
   js/content.js resolve a herança (global -> seção, desktop -> tablet ->
   celular) e escreve, para cada seção e lado, três variáveis já resolvidas:
   --{seção}-pad-{lado}-desktop, -tablet e -mobile. O CSS aqui não decide
   herança nenhuma, só lê o número certo por faixa de largura — quem decide
   "esse valor herda do anterior" é o JS (e o painel, que é quem mostra o
   selo "herdando"). Os literais depois da vírgula em var(...) são o que o
   site já usava antes de existir CMS: se o JSON faltar ou o script falhar,
   a aparência atual continua exatamente igual.
   Os dois breakpoints (900px e 640px) já existiam no projeto para outra
   finalidade (empilhamento do cabeçalho do hero); reaproveitados aqui em vez
   de criar um terceiro ponto de corte novo.
   #work fica de fora do grupo de min-height:100svh de propósito: é a seção
   mais longa da página e a mais fotografada nas revisões, então não encolhe
   com as demais. */
#work{padding-block:var(--work-pad-top-desktop,clamp(5.5rem,20vh,13rem)) var(--work-pad-bottom-desktop,clamp(5rem,17vh,11rem))}
/* help, faq e contact têm conteúdo curto (uma lista, um accordion, um e-mail)
   dentro de um min-height quase do tamanho da tela: era esse min-height, não
   o padding, que sobrava como vazio. 112svh estourava a tela por 12%; 100svh
   cabe nela exatamente, que é o pedido. O padding também caiu, para quando o
   conteúdo cresce (accordion aberto, texto mais longo em inglês) e passa a
   mandar mais do que o min-height. */
#help{padding-block:var(--help-pad-top-desktop,clamp(3.5rem,9vh,6rem)) var(--help-pad-bottom-desktop,clamp(3rem,7vh,5rem))}
#faq{padding-block:var(--faq-pad-top-desktop,clamp(3.5rem,9vh,6rem)) var(--faq-pad-bottom-desktop,clamp(3rem,7vh,5rem))}
#contact{padding-block:min(var(--contact-pad-top-desktop,3.5rem),6svh) min(var(--contact-pad-bottom-desktop,3rem),5svh)}
/* Fundo opcional por seção. Work, Help e Contact vivem dentro da `.wrap`, por
   isso o pseudo-elemento sangra até a largura útil da janela. Sem override do
   CMS, todos preservam o fundo histórico. */
.hero,#work,#help,#contact{position:relative;isolation:isolate}
.hero::before,#work::before,#help::before,#contact::before{content:"";position:absolute;
  inset-block:0;left:50%;width:var(--vw,100vw);margin-left:calc(var(--vw,100vw) / -2);
  pointer-events:none;z-index:-1}
.hero::before{background:var(--hero-section-bg,transparent)}
#work::before{background:var(--work-section-bg,transparent)}
#help::before{background:var(--help-section-bg,transparent)}
#contact::before{background:var(--contact-section-bg,transparent)}
/* Imagem opcional do CMS. A camada fica acima da cor da seção e abaixo de
   todo conteúdo; a opacidade permite preservar contraste sem editar o arquivo
   original. O sangramento usa a mesma largura útil medida pelo site. */
.cms-section-surface{position:relative;isolation:isolate}
.cms-section-surface.has-section-image::after{content:"";position:absolute;inset-block:0;
  left:50%;width:var(--vw,100vw);margin-left:calc(var(--vw,100vw) / -2);z-index:-1;
  pointer-events:none;background-image:var(--section-image);background-size:cover;
  background-position:var(--section-image-position,50% 50%);background-repeat:no-repeat;
  opacity:var(--section-image-opacity,1)}
.hero.has-section-image .liquid-bg{display:none}
/* #contact saiu do grupo min-height:100svh: centralizado numa caixa quase do
   tamanho da tela, o vazio entre o fim do conteúdo (redes sociais) e a linha
   do rodapé ficava grande demais em telas largas. Sem min-height, a seção
   cresce só pelo próprio conteúdo mais o padding acima, igual a #work. */
@media(min-width:900px){
  #help,#faq{min-height:100svh;display:flex;flex-direction:column;
    justify-content:center}
  /* Contato + footer formam o último capítulo e, juntos, ocupam exatamente uma
     viewport. O JS mede a altura real do footer (inclusive idioma e CMS) e
     publica --footer-block-height; o contato completa somente o espaço restante. */
  #contact{display:flex;flex-direction:column;justify-content:center;
    min-height:calc(100svh - var(--footer-block-height,22rem))}
}
/* faixa "tablet": 640px a 899px */
@media(max-width:899px) and (min-width:640px){
  #work{padding-block:var(--work-pad-top-tablet,8.75rem) var(--work-pad-bottom-tablet,8.75rem)}
  #help{padding-block:var(--help-pad-top-tablet,6.5rem) var(--help-pad-bottom-tablet,6.5rem)}
  #faq{padding-block:var(--faq-pad-top-tablet,6.5rem) var(--faq-pad-bottom-tablet,6.5rem)}
  #contact{padding-block:var(--contact-pad-top-tablet,6.5rem) var(--contact-pad-bottom-tablet,6.5rem)}
}
/* faixa "celular": abaixo de 640px. Sem override próprio, cada seção herda o
   valor da faixa tablet (mesmo literal de fallback), então nada muda até o
   painel gravar um número específico para o celular. */
@media(max-width:639px){
  #work{padding-block:var(--work-pad-top-mobile,8.75rem) var(--work-pad-bottom-mobile,8.75rem)}
  #help{padding-block:var(--help-pad-top-mobile,6.5rem) var(--help-pad-bottom-mobile,6.5rem)}
  #faq{padding-block:var(--faq-pad-top-mobile,6.5rem) var(--faq-pad-bottom-mobile,6.5rem)}
  #contact{padding-block:var(--contact-pad-top-mobile,6.5rem) var(--contact-pad-bottom-mobile,6.5rem)}
}
.sec-head{display:flex;align-items:baseline;justify-content:space-between;gap:2rem;
  border-top:var(--work-divider-width,1px) solid var(--line);padding-top:1.4rem;margin-bottom:3rem}
.sec-head h2{font-size:clamp(1.4rem,2.4vw,2rem);font-weight:var(--w-medium);letter-spacing:-.02em}
.sec-head .count{font-family:var(--mono);font-size:.78rem;color:var(--muted-2)}

/* WORK CARDS */
/* mesmo esquema de três camadas do espaçamento de seção, resolvido pelo JS */
.cards{display:grid;grid-template-columns:1fr;gap:var(--grid-gap-mobile,var(--grid-gap-desktop,1.6rem))}
@media(min-width:640px) and (max-width:899px){.cards{gap:var(--grid-gap-tablet,var(--grid-gap-desktop,1.6rem))}}
@media(min-width:860px){.cards{grid-template-columns:1fr 1fr;gap:var(--grid-gap-desktop,1.6rem)}}
.card{position:relative;display:block;aspect-ratio:4/3.4;border-radius:var(--radius-card,16px);overflow:hidden;
  border:1px solid var(--line-strong);background:var(--ink-2);
  transition:border-color var(--dur-hover) var(--ease-hover),
             box-shadow var(--dur-hover) var(--ease-hover)}
/* Névoa interna só nas bordas: o miolo da imagem continua nítido. É gradiente
   radial com centro transparente, não filtro sobre o card inteiro, que custaria
   repaint caro. Só a opacidade anima, e nada bloqueia o clique. */
.card::before{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  border-radius:inherit;opacity:0;
  background:radial-gradient(118% 100% at 50% 50%,transparent 54%,rgba(89,79,79,.26) 84%,rgba(115,104,104,.36) 100%);
  transition:opacity var(--dur-hover) var(--ease-hover)}
@media(hover:hover) and (pointer:fine){
  /* a caixa externa não muda de tamanho nem de posição: mover o card
     deslocaria a grade inteira. O que muda é o interior. */
  .card:hover{border-color:rgba(var(--paper-rgb),.26);
    box-shadow:0 0 0 1px rgba(194,179,179,.13),0 0 36px -10px rgba(194,179,179,.20)}
  .card:hover::before{opacity:1}
  .card:hover .card-foot h3{color:var(--paper)}
  .card:hover .card-foot .yr,.card:hover .card-foot .sub{color:var(--muted)}
}
/* no touch o glow e a névoa saem: custam pintura e ninguém os vê */
@media(hover:none),(prefers-reduced-motion:reduce){
  .card::before{display:none}
}
/* preview art. .scene é sempre o gradiente placeholder (p1-p4); a foto real
   entra como <img> por dentro dele, com onerror removendo a própria tag se o
   arquivo não existir ainda. Assim quem for colocar a foto só precisa salvar
   o arquivo com o nome certo na pasta assets/projetos/case-0X/ — nada de
   editar HTML ou CSS, e nada quebra enquanto a foto não chega. */
.scene{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  transform:scale(1.01);transition:transform 820ms var(--ease-hover)}
.scene>img,.scene>picture,.scene>picture>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
@media(hover:hover) and (pointer:fine){.card:hover .scene{transform:scale(1.15)}}
@media(prefers-reduced-motion:reduce){.card:hover .scene{transform:scale(1.01)}}
/* Sem grão aqui: por cima de uma foto real ele lia como sujeira na imagem, não
   como textura do site. O degradê continua, é o que garante contraste do
   texto do card sobre qualquer foto. */
.card::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(var(--ink-rgb),.42) 0%,transparent 34%,transparent 52%,rgba(var(--ink-rgb),.82) 100%)}
.thumb{position:relative;aspect-ratio:4/3;border-radius:var(--radius-image,16px);overflow:hidden;
  border:1px solid var(--line-strong);background:var(--ink-2)}
.card-tags{position:absolute;z-index:2;top:1.1rem;right:1.1rem;display:flex;flex-wrap:wrap;justify-content:flex-end;gap:.5rem}
.card-tags span{font-family:var(--mono);font-size:.66rem;letter-spacing:.02em;color:var(--paper);
  background:rgba(var(--paper-rgb),.10);border:1px solid rgba(var(--paper-rgb),.20);
  backdrop-filter:blur(12px) saturate(140%);-webkit-backdrop-filter:blur(12px) saturate(140%);
  padding:.42rem .8rem;border-radius:var(--radius-button,12px);white-space:nowrap}
.card-foot{position:absolute;z-index:2;left:1.6rem;right:1.6rem;bottom:1.5rem;
  display:flex;align-items:flex-end;justify-content:space-between;gap:1rem}
.card-foot h3{font-size:clamp(1.3rem,2.1vw,1.75rem);font-weight:var(--w-medium);letter-spacing:-.02em;
  line-height:1.1;transition:color var(--dur-hover) var(--ease-hover)}
.card-foot .sub{font-size:.72rem;color:var(--muted-2);margin-top:.5rem;letter-spacing:.015em;
  transition:color var(--dur-hover) var(--ease-hover)}
.card-foot .yr{font-size:.74rem;color:var(--muted-2);white-space:nowrap;
  transition:color var(--dur-hover) var(--ease-hover)}
.scene.p1{background:radial-gradient(circle at 50% 46%,rgba(201,189,189,.26),transparent 17%),radial-gradient(circle at 50% 46%,transparent 20%,rgba(115,104,104,.14) 30%,transparent 44%),radial-gradient(circle at 50% 46%,transparent 46%,rgba(89,79,79,.12) 58%,transparent 72%),radial-gradient(circle at 50% 46%,#2A1E20,#100C0D 78%)}
.scene.p2{background:radial-gradient(circle at 40% 50%,rgba(226,220,220,.30),rgba(226,220,220,0) 40%),radial-gradient(circle at 59% 50%,rgba(201,189,189,.26),rgba(201,189,189,0) 40%),radial-gradient(circle at 49% 59%,rgba(242,238,238,.13),transparent 46%),linear-gradient(180deg,#403638,#1E1618)}
.scene.p3{background:radial-gradient(circle at 64% 36%,rgba(201,189,189,.24),transparent 46%),radial-gradient(circle at 38% 70%,rgba(115,104,104,.18),transparent 50%),radial-gradient(circle at 50% 50%,#251A1C,#100C0D 80%)}
.scene.p4{background:radial-gradient(circle at 43% 48%,transparent 19%,rgba(184,172,172,.18) 27%,transparent 40%),radial-gradient(circle at 57% 52%,rgba(160,148,148,.22),transparent 36%),radial-gradient(circle at 50% 50%,#251A1C,#100C0D 80%)}

/* .card-cue foi removido: o rótulo "Ver projeto" agora vive dentro do próprio
   cursor (.cur-label), movido pelo mesmo laço. O elemento antigo era posicionado
   por left/top — propriedades de layout — e dependia de listeners presos aos
   .card existentes no carregamento, que a grade reconstruída pelo CMS
   invalidava sem aviso. */

#about{padding-block:0}
/* Borda limpa contra o fundo escuro: sem máscara, sem névoa, sem orbes
   desfocadas. A troca de seção se resolve por contraste e ritmo vertical. */
/* sangramento total descontando a barra de rolagem: 100vw a inclui, e era
   isso que empurrava 15px de overflow e deixava a faixa curta à direita */
/* Sem fundo claro: a seção se separa por elevação mínima de tom e por duas
   hairlines, não por uma troca de cor. */
/* Sem background aqui: a seção fica no --ink plano do body, a mesma cor de
   projetos, do rodapé e de "o que eu faço". Já teve um gradiente próprio e já
   teve o vidro líquido da capa; as duas vezes destoou do resto do site, e a
   separação já é feita pelas hairlines acima e pelo ritmo vertical. */
.about-break{overflow:hidden;isolation:isolate;
  --bleed:var(--vw,100vw);
  width:var(--bleed);position:relative;left:50%;
  margin-left:calc(var(--bleed) / -2);margin-right:calc(var(--bleed) / -2);
  padding:var(--about-pad-top-desktop,clamp(5rem,10vh,8rem)) var(--gutter) var(--about-pad-bottom-desktop,clamp(4.5rem,8vh,6.5rem));
  background:var(--about-section-bg,transparent);
  border-top:var(--about-divider-width,1px) solid var(--line);
  border-bottom:var(--about-divider-width,1px) solid var(--line)}
/* Precisa vir depois da regra base acima: mesma especificidade (uma classe
   só), então quem vier depois no arquivo é quem manda. As duas faixas
   responsivas de .about-break moravam antes da regra base e nunca
   venciam a cascata — o padding de celular nunca mudava de verdade. */
@media(max-width:899px) and (min-width:640px){
  .about-break{padding-block:var(--about-pad-top-tablet,7rem) var(--about-pad-bottom-tablet,7rem)}
}
@media(max-width:639px){
  .about-break{padding-block:var(--about-pad-top-mobile,7rem) var(--about-pad-bottom-mobile,7rem)}
}
.about-inner{position:relative;z-index:1;max-width:var(--content-max);margin-inline:auto}
/* grid editorial de 12 colunas: retrato em 5, conteúdo em 6, com uma coluna de
   respiro entre eles, usando a largura toda do container */
.about-grid{display:grid;grid-template-columns:1fr;gap:2.3rem}
.about-details .caps{margin-top:0}
/* Tablet já usa a composição editorial de duas colunas, com retrato menor.
   No celular a ordem do HTML manda: cabeçalho, dois textos, foto, detalhes. */
@media(min-width:640px){
  .about-grid{grid-template-columns:repeat(12,minmax(0,1fr));
    column-gap:clamp(1rem,2.2vw,2rem);row-gap:1.8rem;align-items:start}
  .about-photo{grid-column:1 / span 4;grid-row:1 / span 3}
  .about-head{grid-column:6 / span 7;grid-row:1}
  .about-text{grid-column:6 / span 7;grid-row:2}
  .about-details{grid-column:6 / span 7;grid-row:3}
}
@media(min-width:900px){
  .about-grid{column-gap:clamp(1.4rem,2.4vw,3rem)}
  .about-photo{grid-column:1 / span 5}
  .about-head,.about-text,.about-details{grid-column:7 / span 6}
}
.about-photo{margin:0}
.portrait{position:relative;aspect-ratio:4/5;border-radius:var(--radius-image,16px);overflow:hidden;
  border:1px solid var(--line);display:flex;align-items:center;justify-content:center;
  background:radial-gradient(circle at 50% 38%,#33262A,#150F10 78%)}
.portrait::after{content:"";position:absolute;inset:0;background:var(--grain-url);opacity:.3;
  mix-blend-mode:soft-light;pointer-events:none}
/* absoluto para cobrir o placeholder que fica por baixo como rede */
.portrait-img{position:absolute;inset:0;z-index:1;width:100%;height:100%;
  object-fit:cover;object-position:50% 22%}
.portrait-hint{position:relative;font-family:var(--mono);font-size:.7rem;letter-spacing:.015em;color:var(--muted-2);
  background:rgba(var(--paper-rgb),.05);border:1px solid var(--line);border-radius:var(--radius-button,12px);padding:.5rem 1.1rem}
/* reveal por máscara: a imagem descobre de baixo para cima e assenta a escala.
   1250ms, o tempo de imagem: mais longo que texto, sem parecer preguiçoso. */
.js .mask-reveal{clip-path:inset(100% 0 0 0);transform:scale(1.025)}
.js .mask-reveal.in{clip-path:inset(0 0 0 0);transform:none;
  transition:clip-path 980ms var(--ease-soft),transform 980ms var(--ease-soft)}

.about-k{display:block;font-family:var(--mono);font-size:.72rem;letter-spacing:.03em;
  color:var(--muted-2);margin-bottom:1.4rem}
/* Mesma escala e mesmo tracking de .help-title: as duas são o título da
   seção, e ler uma menor que a outra tiraria "Sobre" do mesmo nível de
   hierarquia que "O que eu faço". */
.about-title{font-size:clamp(1.9rem,4.4vw,3.4rem);font-weight:var(--w-medium);
  letter-spacing:-.035em;line-height:1.02;color:var(--paper)}
.about-head .about-k{margin-bottom:1rem}
.about-lead{font-size:clamp(1rem,1.25vw,1.1rem);font-weight:var(--w-light);
  color:var(--muted);max-width:58ch;letter-spacing:-.005em;line-height:1.68}

.caps{margin-top:2.8rem}
.caps-k{display:block;font-family:var(--mono);font-size:.7rem;letter-spacing:.03em;color:var(--muted-2);
  padding-bottom:.9rem;border-bottom:1px solid var(--line)}
.caps-grid{list-style:none;display:grid;grid-template-columns:1fr;gap:0}
@media(min-width:560px){.caps-grid{grid-template-columns:1fr 1fr;column-gap:2.4rem}}
.caps-grid li{font-size:.95rem;font-weight:var(--w-regular);color:var(--paper);
  padding:.85rem 0;border-bottom:1px solid var(--line)}
.js .caps-grid li{opacity:0;transform:translateY(12px);
  transition:opacity var(--dur-default) var(--ease-soft),transform var(--dur-default) var(--ease-soft)}
.js .caps-grid.in li{opacity:1;transform:none}

/* LinkedIn e "Vamos conversar" lado a lado; quebram em coluna no estreito */
.about-actions{display:flex;flex-wrap:wrap;align-items:center;gap:.9rem;margin-top:2.4rem}
.about-actions .ghost-cta{margin-top:0}

.ghost-cta{display:inline-flex;align-items:center;gap:.6em;margin-top:2.4rem;cursor:pointer;
  font-family:var(--font);font-size:.92rem;font-weight:var(--w-medium);color:var(--paper);
  background:transparent;border:1px solid var(--line-strong);border-radius:var(--radius-button,12px);padding:.85rem 1.6rem;
  transition:transform var(--dur-micro) var(--ease-soft),border-color var(--dur-micro) var(--ease-soft),
             background-color var(--dur-micro) var(--ease-soft)}
.ghost-cta::after{content:"→";transition:transform var(--dur-micro) var(--ease-soft)}
/* o do LinkedIn troca a seta pelo ícone da marca, à esquerda do rótulo */
.ghost-cta.linkedin::after{content:none}
.ghost-cta.linkedin svg{width:16px;height:16px;flex:none;color:var(--muted);
  transition:color var(--dur-micro) var(--ease-soft)}
.ghost-cta.linkedin:hover svg{color:var(--cream)}
.ghost-cta:hover{transform:translateY(-2px);border-color:var(--accent);background:rgba(var(--paper-rgb),.05)}
.ghost-cta:hover::after{transform:translateX(4px)}

/* Três ações no Sobre, três pesos. Nenhuma cor nova e nenhum botão chapado:
   a hierarquia sai só de contraste de borda e de texto, como no resto do
   site. .cta-primary é "Vamos conversar", .cta-resume é o currículo e
   .cta-quiet é o LinkedIn. */
.ghost-cta.cta-primary{border-color:var(--accent);background:rgba(var(--paper-rgb),.06)}
.ghost-cta.cta-primary:hover{background:rgba(var(--paper-rgb),.1)}
.ghost-cta.cta-quiet{color:var(--muted);border-color:var(--line)}
.ghost-cta.cta-quiet:hover{color:var(--paper)}
/* seta para baixo: é download, não navegação para frente */
.ghost-cta.cta-resume::after{content:"↓"}
.ghost-cta.cta-resume:hover::after{transform:translateY(3px)}
/* display:inline-flex ganha do [hidden] da folha do navegador, então o botão
   do currículo continuaria visível mesmo oculto. Mesmo detalhe que já tinha
   escondido o painel do CMS atrás da tela de "Sem acesso". */
.ghost-cta[hidden]{display:none}

/* ===== O QUE EU FAÇO =====
   Linhas horizontais amplas, não cards: índice e título à esquerda, descrição
   e entregáveis à direita, divisor crescendo da esquerda em cada frente.
   Sem elevação, sem sombra, sem superfície fechada. */
/* introdução em 4 colunas e accordion em 7, lado a lado no desktop grande:
   aproveita a largura sem esticar o texto além do limite de leitura */
@media(min-width:1100px){
  #help .wrap-grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
    column-gap:clamp(1.4rem,2.4vw,3rem);align-items:start}
  .help-head{grid-column:1 / span 4;position:sticky;top:clamp(6rem,12vh,9rem);margin-bottom:0}
  .help-list{grid-column:6 / span 7}
}
.help-head{max-width:44ch;margin-bottom:clamp(3rem,7vh,5.5rem)}
.help-k{display:block;font-family:var(--mono);font-size:.72rem;letter-spacing:.03em;
  color:var(--accent);margin-bottom:1.2rem}
.help-title{font-size:clamp(1.9rem,4.4vw,3.4rem);font-weight:var(--w-medium);letter-spacing:-.035em;
  line-height:1.06;text-wrap:balance}
.help-lead{margin-top:1.6rem;font-size:clamp(.98rem,1.2vw,1.1rem);color:var(--muted);
  line-height:1.6;max-width:52ch}

.help-list{display:flex;flex-direction:column}
.help-item{position:relative;
  transition:background-color var(--dur-hover) var(--ease-hover)}
.help-item h3{margin:0;font-size:inherit;font-weight:inherit}
/* a linha inteira abre: o ícone é indicação, não o único alvo */
.hi-q{width:100%;display:grid;grid-template-columns:2.6rem minmax(0,1fr) auto;
  align-items:center;gap:clamp(1rem,2vw,1.8rem);
  min-height:56px;padding:clamp(1.4rem,3vh,2.1rem) 0;cursor:pointer;
  background:none;border:none;text-align:left;font-family:var(--font);color:var(--paper)}
.help-item .hi-num{font-size:.76rem;letter-spacing:.04em;color:var(--muted-2);
  transition:color var(--dur-hover) var(--ease-hover)}
.hi-title{font-size:clamp(1.25rem,2.2vw,1.9rem);font-weight:var(--w-medium);
  letter-spacing:-.028em;line-height:1.12;
  transition:transform var(--dur-hover) var(--ease-hover)}
/* o mais vira fechar por rotação, sem trocar de glifo */
.hi-ico{position:relative;width:15px;height:15px;flex:none;
  transition:transform 420ms var(--ease-soft)}
.hi-ico::before,.hi-ico::after{content:"";position:absolute;inset:50% 0 auto 0;
  height:1px;background:var(--muted);transition:background-color var(--dur-hover) var(--ease-hover)}
.hi-ico::after{inset:0 auto 0 50%;width:1px;height:100%}
.hi-q[aria-expanded="true"] .hi-ico{transform:rotate(45deg)}
.hi-a{overflow:hidden}
.hi-a>*{grid-column:2 / -1}
@media(min-width:640px){
  .hi-a{padding-left:calc(2.6rem + clamp(1rem,2vw,1.8rem))}
}
.hi-q:focus-visible{outline:2px solid var(--accent);outline-offset:4px}
/* a linha cresce da esquerda, é o gesto que abre cada frente */
.hi-line,.stat-line{display:block;height:1px;background:var(--line-strong);transform-origin:left}
.help-item .hi-line{position:absolute;top:0;left:0;right:0;height:var(--help-divider-width,1px)}
.js .hi-line,.js .stat-line{transform:scaleX(0)}
.js .in .hi-line,.js .hi-line.in,.js .in .stat-line,.js .stat-line.in{transform:scaleX(1);
  transition:transform 820ms var(--ease-soft)}
.help-item p{font-size:.98rem;font-weight:var(--w-light);color:var(--muted);
  line-height:1.66;max-width:62ch;letter-spacing:-.005em;padding-bottom:1.1rem}
/* tags discretas, sem pill grande e sem fundo claro */
.hi-tags{list-style:none;display:flex;flex-wrap:wrap;gap:.5rem;
  padding-bottom:clamp(1.4rem,3vh,2.2rem)}
.hi-tags li{font-size:.7rem;letter-spacing:.02em;color:var(--muted-2);
  padding:.32rem .7rem;border-radius:var(--radius-button,12px);border:1px solid var(--line)}
/* hover só destaca; abrir é por clique, toque ou teclado */
@media(hover:hover) and (pointer:fine){
  .help-item:hover{background:rgba(var(--paper-rgb),.022)}
  .help-item:hover .hi-title{transform:translateX(3px)}
  .help-item:hover .hi-num{color:var(--muted)}
  .help-item:hover .hi-ico::before,.help-item:hover .hi-ico::after{background:var(--paper)}
  .help-item:hover .hi-line{background:rgba(var(--paper-rgb),.34)}
}
.help-item .hi-line{transition:background-color var(--dur-hover) var(--ease-hover),
  transform 850ms var(--ease-soft)}
/* item aberto mantém o destaque, sem depender do cursor continuar em cima */
.help-item:has(.hi-q[aria-expanded="true"]){background:rgba(var(--paper-rgb),.028)}
.help-item:has(.hi-q[aria-expanded="true"]) .hi-line{background:rgba(var(--paper-rgb),.34)}

/* ===== FAQ =====
   A única superfície clara do site. Sangra de ponta a ponta descontando a
   barra de rolagem e reescreve os tokens de cor localmente, para os
   componentes internos herdarem o contraste invertido sem precisar de regra
   própria. A troca de escuro para claro é seca: sem gradiente, sem faixa
   intermediária, sem blur. */
.faq{--bleed:var(--vw,100vw);
  width:var(--bleed);position:relative;left:50%;margin-left:calc(var(--bleed) / -2);
  background:var(--faq-section-bg,#F2EEEE);color:#0D0A0A;
  padding-inline:var(--gutter)}
/* título numa coluna lateral e as perguntas ocupando a maior parte da largura:
   o accordion deixa de ser uma coluna estreita no meio da tela */
.faq-inner{max-width:var(--content-max);margin-inline:auto}
@media(min-width:1000px){
  .faq-inner{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
    column-gap:clamp(1.4rem,2.4vw,3rem);align-items:start}
  .faq-title{grid-column:1 / span 3;position:sticky;top:clamp(6rem,12vh,9rem);margin-bottom:0}
  .faq-list{grid-column:5 / span 8}
}
.faq-title{font-size:clamp(2rem,5vw,3.6rem);font-weight:var(--w-medium);letter-spacing:-.04em;
  line-height:1.02;margin-bottom:clamp(2.4rem,5vh,4rem)}
.faq-list{display:flex;flex-direction:column}
.faq-item{border-bottom:var(--faq-divider-width,1px) solid rgba(38,27,29,.22)}
.faq-item:first-child{border-top:var(--faq-divider-width,1px) solid rgba(38,27,29,.22)}
.faq-item h3{margin:0;font-size:inherit;font-weight:inherit}
/* a linha inteira é o alvo: área de toque confortável, não só o ícone */
.faq-q{width:100%;display:flex;align-items:flex-start;justify-content:space-between;
  gap:1.6rem;min-height:56px;padding:clamp(1.1rem,2.4vh,1.6rem) 0;cursor:pointer;
  background:none;border:none;text-align:left;font-family:var(--font);
  font-size:clamp(1rem,1.55vw,1.22rem);font-weight:var(--w-regular);letter-spacing:-.012em;
  line-height:1.35;color:#0D0A0A;
  transition:opacity var(--dur-micro) var(--ease-soft)}
@media(hover:hover){.faq-q:hover{opacity:.62}}
/* o mais vira fechar por rotação, sem trocar de glifo */
.faq-ico{position:relative;width:16px;height:16px;flex:none;margin-top:.28em;
  transition:transform 420ms var(--ease-soft)}
.faq-ico::before,.faq-ico::after{content:"";position:absolute;inset:50% 0 auto 0;
  height:1px;background:#403638}
.faq-ico::after{inset:0 auto 0 50%;width:1px;height:100%}
.faq-q[aria-expanded="true"] .faq-ico{transform:rotate(45deg)}
.faq-a{overflow:hidden}
.faq-a p{padding-bottom:clamp(1.2rem,2.6vh,1.8rem);max-width:64ch;
  font-size:clamp(.95rem,1.15vw,1.05rem);line-height:1.62;color:#403638}
/* altura animada pelo JS a partir da medida real, sem max-height chutado */
.js .faq-a,.js .hi-a{height:0;opacity:0;
  transition:height 600ms var(--ease-soft),opacity 600ms var(--ease-soft)}
.js .faq-a.open,.js .hi-a.open{opacity:1}
.js .faq-a p,.js .hi-a p,.js .hi-a .hi-tags{transform:translateY(-6px);
  transition:transform 600ms var(--ease-soft)}
.js .faq-a.open p,.js .hi-a.open p{transform:none}
.js .hi-a.open .hi-tags{transform:none;transition-delay:60ms}
.faq-q:focus-visible{outline:2px solid #0D0A0A;outline-offset:3px}
@media(prefers-reduced-motion:reduce){
  .js .faq-a,.js .faq-a p,.faq-ico{transition-duration:1ms}
}

/* ===== NÚMEROS =====
   Sem cabeçalho: os números falam sozinhos e o título anterior repetia o que a
   trajetória já diz. A seção abre direto na grade. */
.stats{display:grid;grid-template-columns:1fr;gap:2.8rem}
@media(min-width:640px){.stats{grid-template-columns:1fr 1fr;gap:2.6rem}}
@media(min-width:1000px){.stats{grid-template-columns:repeat(3,1fr);gap:clamp(2rem,3.5vw,3.2rem)}}
.stat-v{display:flex;align-items:baseline;font-size:clamp(3.4rem,7.4vw,5.6rem);font-weight:var(--w-medium);
  letter-spacing:-.05em;line-height:1;font-variant-numeric:tabular-nums}
.stat-v em{font-style:normal;color:var(--muted-2);font-size:.62em;margin-left:.06em}
.stat-line{margin-top:1.6rem}
.stat h3{margin-top:1.4rem;font-size:1.02rem;font-weight:var(--w-medium);letter-spacing:-.015em}
.stat p{margin-top:.7rem;font-size:.9rem;font-weight:var(--w-light);color:var(--muted-2);line-height:1.6;max-width:32ch}

/* ===== CTA FINAL ===== */
.contact{text-align:center}
/* Causa real do desalinhamento: as duas linhas viviam dentro de .hn-mask, que
   tem overflow:hidden e um par padding-bottom/margin-bottom de .1em pensado
   para o nome da hero. Nesse corpo (até 8rem) o recorte comia as descidas do
   "j" de "juntos" e o par de margens negativas encavalava as linhas, o que
   lia como uma delas fora do lugar. Aqui a máscara não precisa recortar: a
   entrada é por opacidade e deslocamento, não por linha subindo de dentro. */
.contact-big{font-size:clamp(2.8rem,min(10vw,13svh),8rem);font-weight:var(--w-medium);line-height:.94;
  letter-spacing:-.055em;margin-bottom:clamp(1.75rem,4svh,3.6rem)}
.contact-big .hn-mask{overflow:visible;padding-bottom:0;margin-bottom:0}
.contact-big .hn-line{display:block}
.contact-big .em{color:var(--muted-2)}
/* O bloco utilitário desce sem mudar o fluxo ou a altura do encerramento. Em
   Full HD o avanço chega a 3.75rem; em desktops baixos ele cai rapidamente
   para preservar distâncias equivalentes dos dois lados e o encaixe exato de
   contato + footer. */
@media(min-width:900px){.contact-details{transform:translateY(clamp(1rem,calc(13.8svh - 5.55rem),3.75rem))}}
.mail-k{font-family:var(--mono);font-size:.76rem;letter-spacing:.02em;color:var(--muted-2)}
/* justify-content:center, não só o inline-flex: o #contact virou um flex
   column para centralizar em telas curtas (min-height:100svh), e um flex item
   com display:inline-flex é blocado para display:flex e esticado à largura
   total do container. Sem isso o e-mail ficava colado à esquerda dentro dessa
   largura, exatamente o mesmo problema que .contact .links já resolve com
   justify-content:center. */
.mail-row{margin-top:1rem;display:inline-flex;justify-content:center;align-items:center;
  gap:.8rem;position:relative}
.mail-link{font-size:clamp(1.05rem,2.4vw,1.7rem);font-weight:var(--w-regular);letter-spacing:-.02em;
  color:var(--paper);border-bottom:1px solid var(--line-strong);padding-bottom:.12em;
  transition:border-color var(--dur-micro) var(--ease-soft),color var(--dur-micro) var(--ease-soft)}
.mail-link:hover{border-color:var(--accent)}
.copy-btn{position:relative;flex:none;width:2.6rem;height:2.6rem;display:grid;place-items:center;
  cursor:pointer;border-radius:var(--radius-field,12px);color:var(--muted);
  background:rgba(var(--paper-rgb),.05);border:1px solid var(--line);
  transition:transform var(--dur-micro) var(--ease-soft),color var(--dur-micro) var(--ease-soft),
             border-color var(--dur-micro) var(--ease-soft),background-color var(--dur-micro) var(--ease-soft)}
.copy-btn:hover{transform:translateY(-2px);color:var(--paper);border-color:var(--line-strong)}
.copy-btn svg{position:absolute;width:17px;height:17px;
  transition:opacity var(--dur-micro) var(--ease-soft),transform var(--dur-micro) var(--ease-soft)}
.copy-btn .ico-done{opacity:0;transform:scale(.7)}
.copy-btn.done{color:var(--paper);border-color:var(--accent)}
.copy-btn.done .ico-copy{opacity:0;transform:scale(.7)}
.copy-btn.done .ico-done{opacity:1;transform:none}
/* o aviso é absoluto: aparecer não pode empurrar o e-mail de lugar */
.copy-flash{position:absolute;left:50%;translate:-50% 0;top:calc(100% + .9rem);white-space:nowrap;
  font-family:var(--mono);font-size:.72rem;letter-spacing:.02em;color:var(--muted);
  opacity:0;transform:translateY(-4px);pointer-events:none;
  transition:opacity var(--dur-micro) var(--ease-soft),transform var(--dur-micro) var(--ease-soft)}
.copy-flash.on{opacity:1;transform:none}
.contact .links{justify-content:center}


.contact .links{margin-top:clamp(1.5rem,3svh,2.5rem);display:flex;flex-wrap:wrap;gap:1.4rem;font-family:var(--mono);font-size:.82rem}
.contact .links a{padding:.7rem 1.1rem;border:1px solid var(--line-strong);border-radius:var(--radius-field,12px);color:var(--muted);
  display:inline-flex;gap:.5em;align-items:center;
  transition:color var(--dur-micro) var(--ease-out-smooth),
             border-color var(--dur-micro) var(--ease-out-smooth),
             background-color var(--dur-micro) var(--ease-out-smooth),
             transform var(--dur-micro) var(--ease-out-smooth)}
.contact .links a:hover{transform:translateY(-2px)}
.contact .links a:hover{color:var(--paper);border-color:var(--accent);background:var(--glass)}
.ico{width:15px;height:15px;flex:none}

footer{border-top:var(--contact-divider-width,1px) solid var(--line);
  padding-top:min(var(--contact-pad-bottom-desktop,3rem),5svh);
  padding-bottom:0;
  font-size:.74rem;color:var(--muted-2);
  display:flex;flex-direction:column;gap:1.15rem}
@media(max-width:899px) and (min-width:640px){
  footer{padding-top:var(--contact-pad-bottom-tablet,6.5rem)}
}
@media(max-width:639px){footer{padding-top:var(--contact-pad-bottom-mobile,6.5rem)}}
/* Botão e disclaimer empilhados, os dois centrados. O disclaimer vivia
   sozinho depois do marquee, exatamente na faixa que .edge-blur (o desfoque
   fixo do rodapé da janela) borra sem parar: texto pequeno perdendo nitidez
   permanentemente é o pior lugar para uma informação de crédito. Subir para
   este bloco tira o texto de debaixo do desfoque, e o marquee, decorativo e já
   com baixa opacidade, vira o verdadeiro fim visual da página. */
/* O respiro entre o botão e o crédito era 1rem fixo, e a linha do copyright
   ficava colada nele. Cresce com a largura em vez de trocar por três valores
   em media query: clamp resolve desktop, tablet e celular numa declaração só,
   sem breakpoint novo. 1,5rem no celular, 2,4rem no desktop largo — folga
   suficiente para separar os dois blocos, sem abrir um vão que empurre o
   marquee para longe. */
.foot-row{display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(1.25rem,3svh,2.4rem)}
.foot-note{font-size:.82rem;font-weight:var(--w-light);line-height:1.5;
  color:var(--muted-2);opacity:.78;text-align:center}

/* ===== MARQUEE DE ENCERRAMENTO =====
   Dois grupos idênticos lado a lado; a faixa anda -50% e volta ao início, que
   é exatamente onde o segundo grupo começa. Por isso o laço não tem salto nem
   pausa. Sangra de ponta a ponta descontando a barra de rolagem, e o overflow
   fica contido aqui dentro, sem gerar rolagem horizontal na página.
   Só transform anima: é composto na GPU e não repinta. */
.marquee{--bleed:var(--vw,100vw);
  width:var(--bleed);position:relative;left:50%;margin-left:calc(var(--bleed) / -2);
  overflow:hidden;pointer-events:none;user-select:none;
  padding-block:clamp(.4rem,1.4vh,1.2rem) 0}
.marquee-track{display:flex;width:max-content;
  animation:marquee 32s linear infinite}
.mq-group{display:flex;align-items:center;gap:clamp(1.2rem,2.6vw,3rem);
  padding-right:clamp(1.2rem,2.6vw,3rem)}
.mq-group b{font-weight:var(--w-medium);font-size:clamp(3rem,min(9vw,13svh),8rem);line-height:1.06;
  letter-spacing:-.045em;color:var(--color-hero-name);opacity:.16;white-space:nowrap}
.mq-group i{font-style:normal;font-size:clamp(1.4rem,3.4vw,3rem);
  color:var(--accent);opacity:.22}
@keyframes marquee{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
@media(prefers-reduced-motion:reduce){
  .marquee-track{animation:none}
}

/* ===== VOLTAR AO TOPO =====
   Controle utilitário, abaixo dos CTAs na hierarquia. A caixa não muda no
   hover; só contraste, borda e um deslocamento curto da seta respondem. */
.to-top{display:inline-flex;align-items:center;justify-content:center;gap:.55em;cursor:pointer;
  min-height:42px;font-family:var(--font);font-size:.78rem;font-weight:var(--w-regular);color:var(--muted-2);
  background:transparent;border:1px solid var(--line);border-radius:var(--radius-button,12px);
  padding:.55rem .95rem;
  transition:color var(--dur-micro) var(--ease-soft),
             border-color var(--dur-micro) var(--ease-soft),background-color var(--dur-micro) var(--ease-soft)}
.to-top svg{width:13px;height:13px;flex:none;
  transition:transform var(--dur-micro) var(--ease-soft)}
.to-top:hover{color:var(--muted);border-color:var(--line-strong);background:rgba(var(--paper-rgb),.025)}
.to-top:hover svg{transform:translateY(-2px)}
@media(prefers-reduced-motion:reduce){
  .to-top,.to-top svg{transition:none}
  .to-top:hover{transform:none}
}

/* ===== INDICADOR DE PRÓXIMA SEÇÃO =====
   Um componente só, injetado pelo JS no fim de cada seção a partir da tabela
   PROXIMA. No desktop encosta à direita, sem sobrepor conteúdo; no estreito
   volta ao fluxo, centrado no fim da seção. Editorial, nunca botão. */
.next-hint{display:flex;flex-direction:column;gap:.3rem;width:max-content;
  margin-top:clamp(3rem,6vw,5rem);
  font-family:var(--mono);font-size:.72rem;letter-spacing:.02em;color:var(--muted-2);
  transition:color var(--dur-micro) var(--ease-soft)}
@media(min-width:900px){.next-hint{margin-left:auto;text-align:right;align-items:flex-end}}
.next-hint:hover{color:var(--muted)}
.nh-name{font-family:var(--font);font-size:1rem;font-weight:var(--w-medium);color:var(--paper);
  letter-spacing:-.01em}
.nh-arrow{font-size:.9rem;color:var(--accent);animation:nh-flutua 3.4s ease-in-out infinite}
.next-hint:hover .nh-arrow{color:var(--cream)}
@keyframes nh-flutua{0%,100%{transform:translateY(0)}50%{transform:translateY(3px)}}
/* entra deslizando da direita, no mesmo easing do resto */
.js .next-hint.reveal{--rv-dur:950ms;opacity:0;transform:translateX(10px);
  transition:opacity 950ms var(--ease-soft),transform 950ms var(--ease-soft);
  transition-delay:var(--d,0ms)}
.js .next-hint.reveal.in{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){.nh-arrow{animation:none}}

/* A hero centra o nome porque só o .hero-stack está no fluxo. O indicador dela
   é absoluto de propósito: entrar no fluxo deslocaria o nome do centro e
   quebraria a continuidade com a intro. Ele fica centrado com o próprio nome,
   não preso a uma borda: left:50%+translateX(-50%) em vez do deslocamento
   contra a largura da hero que ele usava antes. */
.hero .next-hint{position:absolute;margin:0;left:50%;transform:translateX(-50%);
  text-align:center;align-items:center;
  bottom:clamp(1.4rem,4vh,3rem)}
.js .hero .next-hint.reveal{transform:translateX(-50%) translateY(10px)}
.js .hero .next-hint.reveal.in{transform:translateX(-50%)}
@media(max-width:640px){
  .hero .next-hint{bottom:clamp(1rem,3vh,2rem);font-size:.68rem}
  .hero .nh-name{font-size:.9rem}
}

/* ===== ENTRADA DOS BLOCOS =====
   Duração por natureza do elemento, não um número único: quanto maior a peça,
   mais longo o movimento. Todas começam a responder de imediato, sem atraso
   antes do movimento, e terminam com calma. O easing é sempre o --ease-soft
   (.16,1,.3,1), que sai rápido e desacelera longamente.
   Deslocamentos curtos de propósito: nada de 60px, nada de blur, nada elástico.
   Só esconde se o JS estiver vivo para revelar de volta. */
/* Meio-termo: as durações caíram cerca de 25% em relação à versão anterior, e
   os deslocamentos subiram cerca de 20%. O conteúdo começa antes e conclui mais
   rápido, sem voltar à brusquidão da primeira versão. */
.js .reveal{opacity:0;transform:translateY(var(--rv-y,16px));
  transition:opacity var(--rv-dur,750ms) var(--ease-soft),
             transform var(--rv-dur,750ms) var(--ease-soft);
  transition-delay:var(--d,0ms)}
.js .reveal.in{opacity:1;transform:none}

/* rótulos e elementos pequenos: 10px, 620ms (antes 12px, 800ms) */
.js .sec-head.reveal,.js .caps-k.reveal,.js .mail-k.reveal,
.js .about-k.reveal,.js .help-k.reveal,.js .eyebrow.reveal{--rv-y:10px;--rv-dur:620ms}
/* parágrafos: 19px, 760ms (antes 16px, 1000ms) */
.js .about-lead.reveal,.js .about-sub.reveal,.js .help-lead.reveal,
.js .wi-context.reveal,.js .wi-aside.reveal{--rv-y:19px;--rv-dur:760ms}
/* títulos: 22px, 860ms (antes 22px, 1180ms) */
.js .help-title.reveal,.js .wi-title.reveal,.js .faq-title.reveal,
.js .about-title.reveal{--rv-y:22px;--rv-dur:860ms}
/* previews grandes: 31px, 1050ms (antes 26px, 1320ms) */
.js .card.reveal{--rv-y:31px;--rv-dur:1050ms;transform:translateY(31px) scale(.975)}
.js .card.reveal.in{transform:none}
/* imagens e blocos de mídia */
.js .mask-reveal{--rv-dur:980ms}
/* linhas e divisores dos itens de atuação */
.js .help-item.reveal{--rv-y:14px;--rv-dur:700ms}

/* ===== ENTRADA DE DESTAQUE: PALAVRA POR PALAVRA E BLUR SUAVE =====
   Opt-in por data-reveal, nunca por seletor genérico de heading: quem entra
   nesses dois modos é escolhido um a um no HTML.
     data-reveal="words" -> título curto, revelado palavra a palavra
     data-reveal="soft"  -> texto principal de seção, blur no bloco inteiro
   O blur é a única exceção à regra de "nenhum filter:blur no site" (ver
   CLAUDE.md). Ele foi banido por custo de pintura em camada fixa de tela
   cheia, que repinta todo quadro; aqui é o oposto: alguns elementos curtos,
   só durante a transição de entrada, terminando em blur(0). Nada fica
   borrado em repouso e nada disso está no caminho da rolagem. */
.js [data-reveal="words"] .rv-w{display:inline-block;opacity:0;
  filter:blur(10px);transform:translateY(16px);
  transition:opacity var(--rv-w-dur,1248ms) var(--ease-soft),
             filter var(--rv-w-dur,1248ms) var(--ease-soft),
             transform var(--rv-w-dur,1248ms) var(--ease-soft)}
.js [data-reveal="words"].in .rv-w{opacity:1;filter:blur(0);transform:none}
/* Duas animações concorrentes seriam o bloco e as palavras entrando juntos.
   Quem tem palavra por palavra abre mão da entrada de bloco: o container fica
   parado e visível, e só as palavras animam. */
.js .reveal[data-reveal="words"]{opacity:1;transform:none;transition:none}

/* Blur no bloco, para o texto principal que NÃO é palavra por palavra. Entra
   bem mais devagar que o padrão (1568ms contra 750ms), que é o "um pouco mais
   lento que os secundários" pedido, já na escala 1,6x. */
.js .reveal[data-reveal="soft"]{--rv-y:18px;--rv-dur:1568ms;filter:blur(8px);
  transition:opacity var(--rv-dur) var(--ease-soft),
             transform var(--rv-dur) var(--ease-soft),
             filter var(--rv-dur) var(--ease-soft);
  transition-delay:var(--d,0ms)}
.js .reveal[data-reveal="soft"].in{filter:blur(0)}

/* máscara de linha: só o transform interno anima, o pai não some */
.js .hn-mask.reveal{opacity:1;transform:none}
.js .hn-mask.reveal .hn-line{transition:transform var(--rv-dur,1150ms) var(--ease-soft);
  transition-delay:var(--d,0ms)}
.js .hn-mask.reveal.in .hn-line{transform:none}

/* scrub: palavras clareiam conforme a leitura avança */
.scrub{color:var(--muted-2)}
.scrub .w{color:var(--muted-2);transition:color var(--dur-micro) var(--ease-out-smooth)}
.scrub .w.lit{color:var(--paper)}

/* Movimento reduzido: conteúdo imediatamente visível, nada preso em estado
   inicial invisível, sem scroll suave artificial (o JS também não o instala). */
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important}
  .js .reveal,.js .reveal.in{opacity:1!important;transform:none!important}
  /* entrada de destaque: sem blur, sem deslocamento e sem sequência. O texto
     já nasce legível — o JS também não chega a dividir em palavras neste modo,
     mas a regra fica como rede caso ele divida (troca de idioma, por exemplo). */
  .js [data-reveal="words"] .rv-w{opacity:1!important;filter:none!important;
    transform:none!important;transition-delay:0ms!important}
  .js .reveal[data-reveal="soft"],
  .js .reveal[data-reveal="soft"].in{filter:none!important}
  .scrub .w{color:inherit}
  .cursor-glow,.cur-layer{display:none}
  /* nada preso em estado inicial: intro, máscaras, linhas e capacidades */
  .intro-veil{display:none}
  .js .h-step,.js .hn-line,.js .caps-grid li{opacity:1!important;transform:none!important}
  .js .mask-reveal{clip-path:none!important;transform:none!important}
  .hi-line,.stat-line{transform:scaleX(1)!important}
  /* menu simplificado: aparece sem escalonar nem deslizar */
  .overlay.dropdown .menu-item{
    opacity:1!important;transform:none!important;transition-delay:0ms!important}}

/* ===== ENTRADA DA HERO =====
   Encadeia com a saída da intro. As duas linhas do nome sobem por dentro
   das máscaras; tag, carimbo e "role para explorar" vêm em sequência curta. */
.js .h-step{opacity:0;transform:translateY(18px);
  transition:opacity var(--dur-slow) var(--ease-soft),transform var(--dur-slow) var(--ease-soft)}
/* Entram em 82% da transformação do nome, não no fim dela: o overlap evita a
   pausa entre intro e capa. Cedo demais e competem com a escala do nome. */
.js body.hero-sec .h-step,
.js body.hero-in .h-step,.js .hero-in .h-step{opacity:1;transform:none}
/* o nome sobe assim que a intro comeca; os secundarios entram depois,
   por cima da cauda do movimento do nome */
.js body.name-in .hero-name .hn-line,
.js body.hero-in .hero-name .hn-line{transform:none;
  transition:transform 1476ms var(--ease-soft)}
/* escalonamento dos secundários: 144ms entre eles. Entram sobre a cauda da
   subida do nome, nunca desde o começo da intro.
   Durações e atrasos acompanham a mesma escala de 20% da intro: manter os
   valores anteriores faria os secundários entrarem rápido demais contra o
   movimento lento do nome, e a sequência perderia a costura. */
/* Com .js na frente: sem isso estas três perdiam para .js .h-step, que declara
   a transição inteira com --dur-slow, e as durações escalonadas nunca chegavam
   a valer. Os atrasos logo abaixo valiam, porque empatam em especificidade e
   vêm depois; era só a duração que se perdia, e por isso passou despercebido. */
.js .h-step.hero-tag{transition-duration:1476ms}
.js .h-step.hero-when{transition-duration:1584ms}
.js .h-step.hero-claim{transition-duration:1728ms}
.hero-sec .hero-tag,.hero-in .hero-tag{transition-delay:0ms}
.hero-sec .hero-when,.hero-in .hero-when{transition-delay:144ms}
.hero-sec .hero-claim,.hero-in .hero-claim{transition-delay:288ms}

/* O indicador da hero não pode depender do observador de rolagem: ele fica no
   rodapé da primeira dobra e só entraria na conta depois de um scroll. Aqui ele
   é comandado pela própria intro, junto com os outros secundários. */
.js body.intro-mode:not(.hero-sec) .hero .next-hint{opacity:0;transform:translateX(-50%) translateY(10px)}
.js body.hero-sec .hero .next-hint,
.js body.hero-in .hero .next-hint{opacity:1;transform:translateX(-50%);
  transition:opacity 1620ms var(--ease-soft) 432ms,transform 1620ms var(--ease-soft) 432ms}

/* ===== ACCESSIBILITY: focus & skip link =====
   Sem border-radius aqui: era ele que quadrava o botão de idioma em foco.
   O contorno já acompanha o raio próprio de cada elemento. */
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible{
  outline:2px solid var(--accent);outline-offset:3px}
.skip-link{position:fixed;z-index:200;top:1rem;left:1rem;
  background:var(--paper);color:var(--ink);font-family:var(--font);font-size:.9rem;font-weight:var(--w-medium);
  padding:.8rem 1.4rem;border-radius:var(--radius-button,12px);
  transform:translateY(-140%);transition:transform var(--dur-micro) var(--ease-out-smooth);pointer-events:none}
.skip-link:focus-visible{transform:none;pointer-events:auto;outline:2px solid var(--ink);outline-offset:3px}

/* ===== PAGE TRANSITION ===== */
.veil{position:fixed;inset:0;z-index:90;pointer-events:none;background:var(--ink);
  opacity:0;transition:opacity var(--dur-default) var(--ease-out-smooth)}
.veil.on{opacity:1;pointer-events:all}
main,footer{opacity:0;transform:translateY(10px);transition:opacity var(--dur-slow) var(--ease-out-smooth),transform var(--dur-slow) var(--ease-out-smooth)}
body.ready main,body.ready footer{opacity:1;transform:none}

/* ===== OVERLAYS (menu / contact) ===== */
.overlay{position:fixed;inset:0;z-index:95;display:flex;align-items:flex-start;justify-content:center;
  padding:1.4rem var(--gutter);overflow-y:auto;
  background:rgba(13,10,10,.66);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  opacity:0;visibility:hidden;transition:opacity var(--dur-default) var(--ease-out-smooth),visibility var(--dur-default)}
.overlay.open{opacity:1;visibility:visible}
.overlay-sheet{width:100%;max-width:620px;margin-block:auto;border-radius:var(--radius-card,16px);overflow:hidden;
  background:rgba(38,27,29,.86);border:1px solid var(--line-strong);
  backdrop-filter:blur(26px) saturate(150%);-webkit-backdrop-filter:blur(26px) saturate(150%);
  box-shadow:0 40px 90px rgba(0,0,0,.55);
  transform:translateY(18px) scale(.985);opacity:.6;transition:transform var(--dur-default) var(--ease-out-smooth),opacity var(--dur-default) var(--ease-out-smooth)}
.overlay.open .overlay-sheet{transform:none;opacity:1}

/* ===== MENU COMO DROPDOWN (só onde existe cursor) =====
   Ancorado logo abaixo do header, sem véu escuro e sem travar a rolagem:
   é um painel, não um modal. */
.overlay.dropdown{background:transparent;backdrop-filter:none;-webkit-backdrop-filter:none;
  pointer-events:none;align-items:flex-start;overflow:visible;padding-block:0;
  opacity:1;transition:visibility 0s linear var(--dur-micro)}
.overlay.dropdown.open{transition-delay:0s}
.overlay.dropdown .overlay-sheet{pointer-events:auto;margin-block:0;margin-top:4.4rem;max-width:500px;
  transform:translateY(-7px) scale(.975);opacity:0;transform-origin:50% 0;
  box-shadow:0 30px 70px rgba(0,0,0,.5);
  transition:transform var(--dur-micro) var(--ease-soft),opacity var(--dur-micro) var(--ease-soft)}
.overlay.dropdown.open .overlay-sheet{transform:none;opacity:1}
/* ponte invisível cobrindo o vão entre o botão e o painel: sem ela o cursor
   sai da região interativa no meio do caminho e o menu pisca */
.overlay.dropdown::before{content:"";position:absolute;top:2.9rem;left:50%;translate:-50% 0;
  width:min(500px,92vw);height:2rem;pointer-events:auto}
/* conteúdo interno entra escalonado, e sai junto para o fechamento ser curto */
.overlay.dropdown .menu-item{
  opacity:1;transform:none;
  transition:background-color var(--dur-micro) var(--ease-soft),padding-left var(--dur-micro) var(--ease-soft)}
.overlay.dropdown.open .menu-item{opacity:1;transform:none}
/* O painel de contato usa o MESMO material do menu: os tokens --floating-*,
   que também vestem a pílula do header. Antes ele era o único painel flutuante
   do site com superfície própria (rgba(38,27,29,.86) + --line-strong), e por
   isso lia como um componente de outro sistema. Um material só para toda a UI
   flutuante mantém menu, header e contato reconhecíveis como a mesma coisa. */
#contact-panel .overlay-sheet{background:var(--floating-surface);
  border-color:var(--floating-line);
  backdrop-filter:blur(18px) saturate(145%);-webkit-backdrop-filter:blur(18px) saturate(145%);
  box-shadow:0 24px 64px var(--floating-shadow)}
#contact-panel .overlay-top{border-bottom-color:var(--floating-line)}
#contact-panel .cform-direct{border-top-color:var(--floating-line)}
#contact-panel .cform input,#contact-panel .cform textarea,
#contact-panel .cform-direct-mail{border-color:var(--floating-line-strong)}

.overlay-top{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1.3rem 1.6rem;border-bottom:1px solid var(--line)}
.overlay-note{font-family:var(--mono);font-size:.7rem;color:var(--muted-2);letter-spacing:.02em}
.overlay-x{background:none;border:none;color:var(--muted);cursor:pointer;font-size:.95rem;padding:.3rem;transition:color var(--dur-micro) var(--ease-out-smooth)}
.overlay-x:hover{color:var(--paper)}

/* O painel aberto nasce da mesma pílula translúcida do header. A primeira
   faixa repete Menu + disponibilidade, sem X e sem trocar de material. */
#menu{
  --menu-fg:var(--paper);--menu-muted:var(--muted);--menu-line:var(--floating-line);
  --menu-line-strong:var(--floating-line-strong);--menu-surface:var(--floating-surface);
  --menu-hover:rgba(var(--paper-rgb),.06);--menu-icon-surface:rgba(var(--paper-rgb),.055)
}
#menu .overlay-sheet{max-width:350px}
#menu:not(.dropdown) .overlay-sheet{margin-block:clamp(.7rem,3vh,1.8rem) auto}
#menu.dropdown .overlay-sheet{margin-top:1.1rem;max-width:350px;max-height:calc(100svh - 2.2rem)}
#menu.dropdown::before{display:none}
#menu .overlay-sheet{background:var(--menu-surface);border-color:var(--menu-line);
  backdrop-filter:blur(18px) saturate(145%);-webkit-backdrop-filter:blur(18px) saturate(145%);
  box-shadow:0 24px 64px var(--floating-shadow);border-radius:var(--radius-card,16px)}
.menu-panel-head{min-height:55px;display:flex;align-items:center;padding:.22rem .75rem;
  border-bottom:1px solid var(--menu-line);color:var(--menu-fg)}
.menu-panel-title{display:inline-flex;align-items:center;gap:.58rem;padding:.62rem .3rem;
  font-size:.88rem;font-weight:var(--w-medium)}
.menu-panel-title>i{width:9px;height:9px;flex:none;
  background-image:radial-gradient(circle,var(--menu-muted) 1.4px,transparent 1.5px);
  background-size:4.5px 4.5px}
.menu-panel-div{width:1px;height:1.1rem;flex:none;margin-inline:.65rem .55rem;background:var(--menu-line-strong)}
.menu-panel-avail{min-width:0;display:inline-flex;align-items:center;gap:.48rem;
  color:var(--menu-muted);font-size:.68rem;letter-spacing:.01em;white-space:nowrap}
.menu-panel-avail>span{overflow:hidden;text-overflow:ellipsis}
body[data-availability="hidden"] .menu-panel-head{justify-content:flex-start}
#menu .menu-item{gap:.63rem;padding:.58rem .91rem;
  font-size:clamp(.84rem,1.68vw,1.16rem)}
#menu .menu-item:hover{padding-left:1.2rem}
#menu .menu-ico{width:25px;height:25px;border-radius:7px}
#menu .menu-ico svg{width:12px;height:12px}

/* ===== OFFSET DAS ÂNCORAS =====
   Um número só, num lugar só, em vez de espalhar cálculo de offset por
   handler. O header é fixo e cobriria o título da seção logo após a
   navegação; scroll-margin-top resolve isso tanto para a rolagem por JS
   quanto para o salto nativo (movimento reduzido, ?nosmooth=1, ou hash
   carregado direto na URL).
   #top fica de fora: o topo da página não pode ter recuo, senão o link
   "Início" pararia acima do início real do documento. */
:root{--anchor-offset:clamp(5rem,11vh,7.5rem)}
#work,#about,#help,#faq,#contact{scroll-margin-top:var(--anchor-offset)}

/* ===== MENU COM SEIS ITENS =====
   A lista cresceu de 3 para 6. No desktop o painel continua com a altura do
   conteúdo; no estreito ele precisa poder rolar por dentro, senão o último
   item (Contato) fica fora da viewport e inalcançável.
   O .overlay já tem overflow-y:auto; o que faltava era o teto de altura no
   sheet e o respiro do safe-area em celular com barra de gestos. */
.overlay-sheet{max-height:calc(100svh - 2.8rem);display:flex;flex-direction:column}
.overlay-sheet .menu-list{overflow-y:auto;overscroll-behavior:contain;
  padding-bottom:env(safe-area-inset-bottom,0px)}
/* o topo com o botão fechar nunca rola junto: continua alcançável */
.overlay-sheet .overlay-top{flex:none}
@media(max-width:640px){
  /* itens um pouco mais compactos para caberem seis sem virar rolagem já de
     cara, mas ainda com altura confortável para toque */
  .menu-item{font-size:clamp(1.25rem,6vw,1.6rem);padding:.95rem 1.3rem}
  .menu-ico{width:36px;height:36px}
  #menu .menu-item{font-size:clamp(.9rem,4.2vw,1.05rem);padding:.58rem .86rem}
  #menu .menu-item:hover{padding-left:1.05rem}
  .menu-panel-avail>span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}
  .menu-panel-div{margin-inline:auto .55rem}
}

/* Item da seção em que o visitante está. Não comunica só por cor: ganha um
   traço à esquerda e peso, e carrega aria-current no HTML. */
.menu-item[aria-current="location"]{background:var(--menu-hover,rgba(var(--paper-rgb),.05))}
.menu-item[aria-current="location"] .menu-ico{
  color:var(--menu-fg,var(--paper));border-color:var(--menu-line-strong,var(--line-strong));background:var(--menu-hover,rgba(var(--paper-rgb),.1))}
.menu-item[aria-current="location"]::before{content:"";position:absolute;left:0;top:50%;
  width:3px;height:1.5em;border-radius:0 3px 3px 0;background:var(--accent);
  transform:translateY(-50%)}
.menu-item{position:relative}

.menu-list{display:flex;flex-direction:column}
/* Ícone à esquerda e texto em seguida: o olho encontra o mesmo ponto de
   partida em todas as linhas. Antes o item era justify-between, com uma caixa
   de 104x64 na ponta direita que lia como thumbnail de projeto. */
.menu-item{display:flex;align-items:center;justify-content:flex-start;gap:1.1rem;
  padding:1.1rem 1.6rem;border-bottom:1px solid var(--menu-line,var(--line));color:var(--menu-fg,var(--paper));
  font-size:clamp(1.4rem,3vw,2rem);font-weight:var(--w-medium);letter-spacing:-.02em;
  transition:background-color var(--dur-micro) var(--ease-out-smooth),padding-left var(--dur-default) var(--ease-out-smooth)}
.menu-item:last-child{border-bottom:none}
.menu-item:hover{background:var(--menu-hover,rgba(var(--paper-rgb),.05));padding-left:2.1rem}
/* Caixa do ícone: material discreto, não thumbnail. Tamanho fixo para as
   linhas ficarem alinhadas mesmo com rótulos de larguras diferentes. */
.menu-ico{flex:none;display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:12px;color:var(--menu-muted,var(--muted));
  background:var(--menu-icon-surface,rgba(var(--paper-rgb),.05));border:1px solid var(--menu-line,var(--line));
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);
  transition:color var(--dur-micro) var(--ease-out-smooth),
             background-color var(--dur-micro) var(--ease-out-smooth),
             border-color var(--dur-micro) var(--ease-out-smooth)}
.menu-ico svg{width:19px;height:19px;display:block}
.menu-item:hover .menu-ico,.menu-item:focus-visible .menu-ico{
  color:var(--menu-fg,var(--paper));background:var(--menu-hover,rgba(var(--paper-rgb),.1));border-color:var(--menu-line-strong,var(--line-strong))}
.menu-item[aria-current="page"] .menu-ico{color:var(--menu-fg,var(--paper));border-color:var(--menu-line-strong,var(--line-strong))}

.cform{padding:1.8rem 1.6rem 2rem}
.cform h2{font-size:clamp(1.5rem,3.4vw,2.2rem);font-weight:var(--w-medium);letter-spacing:-.025em;line-height:1.1;
  max-width:18ch;margin-bottom:1.8rem}
.cform form{display:flex;flex-direction:column;gap:1.1rem}
.cform label{display:flex;flex-direction:column;gap:.5rem}
.cform .k{font-family:var(--mono);font-size:.7rem;color:var(--accent);letter-spacing:.02em}
.cform input,.cform textarea{font-family:var(--font);font-size:.95rem;color:var(--paper);
  background:rgba(var(--paper-rgb),.05);border:1px solid var(--line-strong);border-radius:var(--radius-field,12px);
  padding:.85rem 1rem;resize:vertical;transition:border-color var(--dur-micro) var(--ease-out-smooth),background-color var(--dur-micro) var(--ease-out-smooth)}
.cform input:focus,.cform textarea:focus{outline:none;border-color:var(--accent);background:rgba(var(--paper-rgb),.08)}
.cform-submit{font-family:var(--font);font-size:.95rem;font-weight:var(--w-medium);color:var(--ink);cursor:pointer;
  background:var(--paper);border:none;border-radius:var(--radius-button,12px);padding:.95rem 1.5rem;margin-top:.4rem;
  transition:transform var(--dur-micro) var(--ease-out-smooth),opacity var(--dur-micro) var(--ease-out-smooth)}
.cform-submit:hover{transform:translateY(-2px);opacity:.9}
/* Armadilha anti-robô. Fora da tela em vez de display:none ou visibility:
   hidden, que os robôs mais espertos já sabem reconhecer e pular. Continua
   sem participar do layout (position:absolute) e sem ser anunciada, porque o
   HTML a marca com aria-hidden. */
.cform-trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* Retorno do envio. Nasce escondido e só aparece quando há o que dizer, para
   não reservar espaço vazio embaixo do botão. */
.cform-status{font-family:var(--mono);font-size:.76rem;line-height:1.5;text-align:center;
  margin-top:.2rem;color:var(--muted)}
.cform-status[data-tom="erro"]{color:var(--cream)}

/* ===== E-MAIL DIRETO =====
   Era uma linha de nota de rodapé em --muted-2, do mesmo tamanho de um
   crédito. Como é a segunda via real de contato (e a única para quem prefere
   o próprio cliente de e-mail), passou a ler como alternativa de verdade: uma
   hairline separa do formulário, o rótulo continua discreto e o endereço
   ganha corpo, cor de texto e a mesma moldura dos CTAs do site. */
.cform-direct{display:flex;flex-direction:column;align-items:center;gap:.6rem;
  margin-top:1.4rem;padding-top:1.4rem;border-top:1px solid var(--line)}
.cform-direct-k{font-family:var(--mono);font-size:.72rem;letter-spacing:.02em;color:var(--muted-2)}
.cform-direct-mail{display:inline-flex;align-items:center;gap:.55em;
  font-size:.95rem;font-weight:var(--w-medium);letter-spacing:-.005em;color:var(--paper);
  padding:.7rem 1.2rem;border:1px solid var(--line-strong);border-radius:var(--radius-button,100px);
  transition:transform var(--dur-micro) var(--ease-soft),
             border-color var(--dur-micro) var(--ease-soft),
             background-color var(--dur-micro) var(--ease-soft)}
.cform-direct-mail::before{content:"↗";font-size:.85em;color:var(--muted)}
.cform-direct-mail:hover{transform:translateY(-2px);border-color:var(--accent);
  background:rgba(var(--paper-rgb),.05)}
.cform-direct-mail:hover::before{color:var(--cream)}

/* ===== CASE PAGE ===== */
.case-hero{min-height:auto;padding-top:9rem;padding-bottom:1rem}
.case-hero .eyebrow{margin-bottom:1.2rem}
.case-hero h1{font-size:clamp(2rem,4.6vw,3.6rem);font-weight:var(--w-medium);line-height:1.08;letter-spacing:-.02em;max-width:20ch;margin-bottom:1.6rem}
.case-hero .sub{max-width:56ch;font-size:clamp(1rem,1.3vw,1.1rem);color:var(--muted);font-weight:var(--w-light)}
/* .case-meta e não .hero-meta: o nome colidia com a linha de metadados da home,
   e esta regra, por vir depois no arquivo, desmontava o grid de lá. */
.case-meta{display:flex;flex-wrap:wrap;gap:1.6rem 3rem;margin-top:2.2rem;
  border-top:1px solid var(--line);padding-top:1.6rem;
  font-family:var(--mono);font-size:.76rem;color:var(--muted-2)}
.case-meta div span{display:block;color:var(--accent);margin-bottom:.35rem;letter-spacing:.035em}
.case-meta div p{color:var(--paper);font-family:var(--font);font-size:.92rem}

/* ===== ESPAÇAMENTO POR BLOCO (páginas de projeto) =====
   Cada bloco (a capa, cada seção de texto, a galeria) recebe --block-mt-* e
   --block-mb-* como propriedade CSS embutida NO PRÓPRIO ELEMENTO, escritas
   por js/content-render.js a partir de content/projects/<slug>.json. É o
   mesmo esquema de três níveis do espaçamento de seção (ver mais acima,
   --help-pad-top-desktop etc.), só que declarado inline em vez de em :root,
   porque aqui quem varia é o bloco individual, não a página inteira.
   Sem configuração própria, todos os blocos e o grid de mídia partem da
   mesma régua de 18px. Isso mantém alinhamento lateral e vertical previsível
   mesmo quando galerias têm quantidades diferentes de imagens. */
.case-cover{aspect-ratio:16/9;border-radius:var(--radius-image,16px);overflow:hidden;position:relative;
  border:1px solid var(--line-strong);background:var(--ink-2);
  margin-top:var(--block-mt-desktop,18px);margin-bottom:var(--block-mb-desktop,0px)}
.case-cover .scene{filter:none;transform:none}

/* .case-body não usa mais gap: precisava de um valor só para todos os
   filhos, e cada .case-section (bloco de texto) agora carrega o próprio
   espaço antes/depois. O grid em si só empilha; quem afasta um bloco do
   outro é a margem de cada um. */
.case-body{display:grid}
.case-section{margin-top:var(--block-mt-desktop,0px);margin-bottom:var(--block-mb-desktop,0px)}
.case-section .k{font-family:var(--mono);font-size:.72rem;color:var(--accent);letter-spacing:.02em;
  border-top:1px solid var(--line);padding-top:1.2rem;margin-bottom:1rem}
.case-section p{font-size:1.02rem;color:var(--muted);max-width:64ch}
.case-section p+p{margin-top:1.1rem}

.case-gallery{display:grid;grid-template-columns:1fr;
  gap:var(--block-gap-desktop,18px);
  margin-top:var(--block-mt-desktop,18px);margin-bottom:var(--block-mb-desktop,0px)}
@media(min-width:640px){.case-gallery{grid-template-columns:1fr 1fr}}
.case-gallery .thumb{aspect-ratio:4/3}
@media(max-width:899px) and (min-width:640px){
  .case-cover{margin-top:var(--block-mt-tablet,var(--block-mt-desktop,18px));
    margin-bottom:var(--block-mb-tablet,var(--block-mb-desktop,0px))}
  .case-section{margin-top:var(--block-mt-tablet,var(--block-mt-desktop,0px));
    margin-bottom:var(--block-mb-tablet,var(--block-mb-desktop,0px))}
  .case-gallery{margin-top:var(--block-mt-tablet,var(--block-mt-desktop,18px));
    margin-bottom:var(--block-mb-tablet,var(--block-mb-desktop,0px));
    gap:var(--block-gap-tablet,var(--block-gap-desktop,18px))}
}
@media(max-width:639px){
  .case-cover{margin-top:var(--block-mt-mobile,var(--block-mt-tablet,var(--block-mt-desktop,18px)));
    margin-bottom:var(--block-mb-mobile,var(--block-mb-tablet,var(--block-mb-desktop,0px)))}
  .case-section{margin-top:var(--block-mt-mobile,var(--block-mt-tablet,var(--block-mt-desktop,0px)));
    margin-bottom:var(--block-mb-mobile,var(--block-mb-tablet,var(--block-mb-desktop,0px)))}
  .case-gallery{margin-top:var(--block-mt-mobile,var(--block-mt-tablet,var(--block-mt-desktop,18px)));
    margin-bottom:var(--block-mb-mobile,var(--block-mb-tablet,var(--block-mb-desktop,0px)));
    gap:var(--block-gap-mobile,var(--block-gap-tablet,var(--block-gap-desktop,18px)))}
}

/* ===== BLOCOS LIVRES DA PÁGINA DE PROJETO =====
   Além de texto e galeria, um case pode intercalar imagem, citação e vídeo na
   ordem que quiser. Todos herdam o mesmo esquema de espaçamento acima, com os
   mesmos três níveis, então nenhum tipo novo traz régua própria.
   .case-blocks só existe como âncora para o JS: quem empilha e afasta continua
   sendo a margem de cada bloco. É grid pelo mesmo motivo que .case-body: em
   fluxo normal as margens de blocos vizinhos colapsariam, e o espaço entre
   dois blocos deixaria de ser o que foi configurado. */
.case-blocks{display:grid}
/* Os wrappers são semânticos, mas não são capítulos da página. Sem esta
   neutralização eles herdavam 112px em cada lado da regra global `section`,
   um espaço invisível aos controles de margem do CMS. */
.case-blocks>.case-block-group{padding-block:0;min-height:0}

.case-figure{margin:var(--block-mt-desktop,18px) 0 var(--block-mb-desktop,0px)}
.case-figure .scene{aspect-ratio:16/9;border-radius:var(--radius-image,16px);overflow:hidden;
  position:relative;border:1px solid var(--line-strong);background:var(--ink-2)}
.case-figure img{display:block;width:100%;height:100%;object-fit:cover}
/* Proporção livre: a imagem manda na altura, em vez de ser recortada em 16/9.
   É o que serve peça vertical, captura de tela e GIF, que raramente cabem
   num quadro fixo sem perder o assunto. */
.case-figure.is-auto .scene{aspect-ratio:auto}
.case-figure.is-auto img{height:auto}
/* Sangramento de ponta a ponta pela mesma régua do resto do site: --vw medido
   pelo JS, nunca calc(100vw - barra). Ver a seção própria no CLAUDE.md. */
.case-figure.is-full{--bleed:var(--vw,100vw);
  width:var(--bleed);position:relative;left:50%;
  margin-left:calc(var(--bleed) / -2);margin-right:calc(var(--bleed) / -2)}
.case-figure.is-full .scene{border-radius:0;border-left:none;border-right:none}
.case-figure figcaption{margin-top:.9rem;font-family:var(--mono);font-size:.72rem;
  letter-spacing:.02em;color:var(--muted-2);max-width:64ch}
.case-figure.is-full figcaption{padding-inline:var(--gutter)}

/* Sem aspas decorativas nem barra lateral: o que separa a citação do corpo é
   a escala e a cor, como no resto do site. */
.case-quote{margin:var(--block-mt-desktop,18px) 0 var(--block-mb-desktop,0px);max-width:44ch}
.case-quote p{font-size:clamp(1.25rem,2vw,1.75rem);font-weight:var(--w-light);
  line-height:1.36;letter-spacing:-.02em;color:var(--cream)}
.case-quote cite{display:block;margin-top:1.1rem;font-style:normal;
  font-family:var(--mono);font-size:.72rem;letter-spacing:.02em;color:var(--muted-2)}

.case-video{margin:var(--block-mt-desktop,18px) 0 var(--block-mb-desktop,0px)}
.case-video .scene{aspect-ratio:16/9;border-radius:var(--radius-image,16px);overflow:hidden;
  position:relative;border:1px solid var(--line-strong);background:var(--ink-2)}
.case-video video,.case-video iframe{display:block;width:100%;height:100%;border:0;object-fit:cover}
.case-video figcaption{margin-top:.9rem;font-family:var(--mono);font-size:.72rem;
  letter-spacing:.02em;color:var(--muted-2);max-width:64ch}

@media(max-width:899px) and (min-width:640px){
  .case-figure,.case-quote,.case-video{
    margin-top:var(--block-mt-tablet,var(--block-mt-desktop,18px));
    margin-bottom:var(--block-mb-tablet,var(--block-mb-desktop,0px))}
}
@media(max-width:639px){
  .case-figure,.case-quote,.case-video{
    margin-top:var(--block-mt-mobile,var(--block-mt-tablet,var(--block-mt-desktop,18px)));
    margin-bottom:var(--block-mb-mobile,var(--block-mb-tablet,var(--block-mb-desktop,0px)))}
}

.case-nav{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;
  border-top:0;padding-top:0;font-family:var(--font)}
.case-nav[hidden]{display:none}
.case-nav a{position:relative;min-height:112px;display:flex;flex-direction:column;
  justify-content:center;gap:.55rem;padding:1.15rem 1.25rem 1.15rem 3.8rem;
  color:var(--paper);background:rgba(var(--paper-rgb),.035);
  border:1px solid var(--line);border-radius:var(--radius-image,16px);
  transition:transform var(--dur-micro) var(--ease-soft),
             color var(--dur-micro) var(--ease-soft),
             background-color var(--dur-micro) var(--ease-soft),
             border-color var(--dur-micro) var(--ease-soft)}
.case-nav a::before{content:"←";position:absolute;left:1.15rem;top:50%;translate:0 -50%;
  width:2rem;height:2rem;display:grid;place-items:center;border-radius:var(--radius-field,12px);
  color:var(--muted);background:rgba(var(--paper-rgb),.045);border:1px solid var(--line);
  transition:transform var(--dur-micro) var(--ease-soft),color var(--dur-micro) var(--ease-soft)}
.case-nav a:hover{transform:translateY(-2px);color:var(--paper);
  background:rgba(var(--paper-rgb),.065);border-color:var(--line-strong)}
.case-nav a:hover::before{transform:translateX(-3px);color:var(--paper)}
.case-nav a.next{text-align:right;align-items:flex-end;padding:1.15rem 3.8rem 1.15rem 1.25rem}
.case-nav a.next::before{content:"→";left:auto;right:1.15rem}
.case-nav a.next:hover::before{transform:translateX(3px)}
.case-nav a>span:last-child{font-size:clamp(1rem,1.7vw,1.3rem);font-weight:var(--w-medium);
  line-height:1.2;letter-spacing:-.02em;width:100%;max-width:none}
.case-nav .lbl{font-family:var(--mono);font-size:.68rem;font-weight:var(--w-regular);
  color:var(--muted-2);letter-spacing:.025em}
@media(max-width:639px){
  .case-nav{grid-template-columns:1fr}
  .case-nav a{min-height:92px}
}

/* ===== ENTRADAS POR BLOCO (coreografia via .in do observador) =====
   Índice e título vêm de 18px em 1050ms; descrição e entregáveis de 14px em
   900ms. Depois do divisor, que é quem abre cada frente. */
.js .help-item .hi-num,.js .help-item h3,
.js .stat .stat-v,.js .stat h3,.js .stat p{
  opacity:0;transform:translateY(18px);
  transition:opacity 1050ms var(--ease-soft),transform 1050ms var(--ease-soft)}
.js .help-item p,.js .help-item .hi-tags{
  opacity:0;transform:translateY(14px);
  transition:opacity 900ms var(--ease-soft),transform 900ms var(--ease-soft)}
.js .help-item.in .hi-num,.js .help-item.in h3,
.js .help-item.in p,.js .help-item.in .hi-tags,
.js .stat.in .stat-v,.js .stat.in h3,.js .stat.in p{opacity:1;transform:none}

/* ===== SELETOR DE IDIOMA =====
   Fechado: bandeira, código e seta. Aberto: dropdown compacto com os nomes
   completos e um check discreto no idioma atual. */
.lang-pick{position:relative}
.lp-toggle{display:inline-flex;align-items:center;gap:.5em;cursor:pointer;
  font-family:var(--font);font-size:.82rem;font-weight:var(--w-medium);color:var(--muted);
  padding:.5rem .75rem;border-radius:var(--radius-button,12px);
  background:var(--floating-surface);border:1px solid var(--floating-line);
  box-shadow:0 12px 34px var(--floating-shadow);filter:none;
  backdrop-filter:blur(18px) saturate(145%);-webkit-backdrop-filter:blur(18px) saturate(145%)}
.lp-toggle:hover{color:var(--paper);border-color:var(--floating-line-strong);
  background:var(--floating-surface-hover)}
.lp-flag{display:block;width:17px;height:17px;flex:none;border-radius:50%;overflow:hidden;
  box-shadow:0 0 0 1px var(--floating-line)}
.lp-flag svg{display:block;width:100%;height:100%;border-radius:50%}
.lp-code{letter-spacing:.02em}
.lp-arrow{width:12px;height:12px;flex:none;color:var(--muted-2);
  transition:transform var(--dur-micro) var(--ease-soft)}
.lang-pick.open .lp-arrow{transform:rotate(180deg)}

.lp-menu{position:absolute;top:calc(100% + .6rem);right:0;z-index:60;
  list-style:none;min-width:12.5rem;padding:.4rem;border-radius:var(--radius-card,16px);
  background:var(--floating-surface);border:1px solid var(--floating-line);
  backdrop-filter:blur(18px) saturate(145%);-webkit-backdrop-filter:blur(18px) saturate(145%);
  box-shadow:0 22px 50px var(--floating-shadow);
  opacity:0;transform:translateY(-6px) scale(.98);pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease-soft),transform var(--dur-fast) var(--ease-soft)}
.lang-pick.open .lp-menu{opacity:1;transform:none;pointer-events:auto}
.lp-menu button{display:flex;align-items:center;gap:.7em;width:100%;cursor:pointer;
  padding:.6rem .7rem;border:none;border-radius:var(--radius-field,12px);background:transparent;
  color:var(--muted);text-align:left;font-family:var(--font);
  opacity:0;transform:translateY(-4px);
  transition:opacity var(--dur-fast) var(--ease-soft),transform var(--dur-fast) var(--ease-soft),
             background-color var(--dur-micro) var(--ease-soft),color var(--dur-micro) var(--ease-soft)}
.lang-pick.open .lp-menu li:nth-child(1) button{opacity:1;transform:none;transition-delay:45ms}
.lang-pick.open .lp-menu li:nth-child(2) button{opacity:1;transform:none;transition-delay:90ms}
.lp-menu button:hover{background:var(--floating-surface-hover);color:var(--paper)}
.lp-menu button[aria-selected="true"]{color:var(--paper)}
.lp-txt{display:flex;flex-direction:column;line-height:1.2;flex:1}
.lp-txt b{font-size:.9rem;font-weight:var(--w-medium)}
.lp-txt small{font-family:var(--mono);font-size:.64rem;letter-spacing:.02em;color:var(--muted-2);margin-top:.15rem}
.lp-check{width:14px;height:14px;flex:none;opacity:0;color:var(--accent);
  transition:opacity var(--dur-micro) var(--ease-soft)}
.lp-menu button[aria-selected="true"] .lp-check{opacity:1}

/* ===== ABERTURA EDITORIAL DOS PROJETOS =====
   Frase grande ocupando duas ou três linhas, e o contexto abaixo em duas
   colunas: o de esquerda explica, o de direita convida. Sem card, sem moldura,
   muito espaço negativo. O peso vai até 600, teto da fonte carregada. */
.work-intro{margin-bottom:clamp(3.4rem,7vw,6rem)}
.wi-title{font-size:clamp(1.85rem,4.4vw,3.5rem);font-weight:var(--w-medium);line-height:1.1;
  letter-spacing:-.035em;max-width:19ch}
.wi-foot{display:grid;grid-template-columns:1fr;gap:1.6rem;
  margin-top:clamp(2.2rem,4.5vw,3.6rem)}
@media(min-width:860px){
  .wi-foot{grid-template-columns:minmax(0,1fr) minmax(0,1fr);
    gap:clamp(2rem,5vw,5rem);align-items:start}
}
.wi-context{font-size:clamp(.98rem,1.2vw,1.08rem);color:var(--muted);max-width:46ch}
.wi-aside{font-size:.92rem;color:var(--muted-2);max-width:38ch}
@media(min-width:860px){.wi-aside{justify-self:end;text-align:right}}

/* ===== PREVIEWS GRANDES =====
   Duas colunas que juntas dominam a viewport, com gap curto. Sangra além do
   .wrap descontando a barra de rolagem, como a faixa do Sobre. */
.work-bleed{--bleed:var(--vw,100vw);
  width:var(--bleed);position:relative;left:50%;
  margin-left:calc(var(--bleed) / -2);margin-right:calc(var(--bleed) / -2);
  padding-inline:clamp(1rem,2vw,2rem);gap:clamp(1rem,1.4vw,1.5rem)}
@media(min-width:900px){.work-bleed{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(min-width:900px){
  /* preenchem quase a viewport inteira, como no midu.design: a prévia é o
     assunto da seção, não uma miniatura ao lado do texto */
  .work-bleed .card{aspect-ratio:auto;height:clamp(38rem,88vh,58rem)}
  /* Tamanhos já previstos em projects/index.json. O normal preserva a grade
     atual; as variações só alteram span e altura dentro da mesma linguagem. */
  .work-bleed .card--largo{grid-column:span 2;height:clamp(30rem,72vh,48rem)}
  .work-bleed .card--alto{height:clamp(46rem,105vh,68rem)}
  .work-bleed .card--grande{grid-column:span 2;height:clamp(44rem,98vh,64rem)}
  .work-bleed .card--featured{grid-column:span 2}
  .work-bleed .card--largura-completa{grid-column:1/-1;height:clamp(38rem,88vh,58rem)}}
@media(max-width:899px){
  .work-bleed .card{aspect-ratio:4/4.4}}
.work-bleed .card-foot{left:clamp(1.4rem,2.2vw,2.4rem);right:clamp(1.4rem,2.2vw,2.4rem);
  bottom:clamp(1.4rem,2vw,2.2rem)}
.work-bleed .card-foot h3{font-size:clamp(1.35rem,1.9vw,2rem)}
.work-bleed .card-tags{top:clamp(1.2rem,1.8vw,1.8rem);right:clamp(1.2rem,1.8vw,1.8rem)}
/* o conteúdo do card entra depois da imagem, em sequência curta */
.js .card .card-foot,.js .card .card-tags{opacity:0;transform:translateY(14px);
  transition:opacity var(--dur-default) var(--ease-soft),transform var(--dur-default) var(--ease-soft)}
.js .card.in .card-foot{opacity:1;transform:none;transition-delay:220ms}
.js .card.in .card-tags{opacity:1;transform:none;transition-delay:300ms}
/* --muted-2 deixava este parágrafo perto do limite de leitura confortável:
   ele é texto corrido longo, não um rótulo de apoio. Subiu para --muted, um
   degrau acima, que ainda o mantém abaixo do parágrafo principal (--paper) e
   preserva a hierarquia entre os dois. */
.about-sub{margin-top:1.3rem;font-size:.98rem;font-weight:var(--w-light);color:var(--muted);
  max-width:56ch;line-height:1.68;letter-spacing:-.005em}
