/* ============================================================
   RÉGIS REGI · portfólio minimalista
   Preto absoluto. O trabalho vira índice tipográfico gigante.
   Um único acento violeta. Movimento: um "corte" por bloco.
   Efeito assinatura: o nome entra letra a letra na abertura.
   ============================================================ */

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font-family: var(--body); font-size: 1rem; line-height: 1.6;
  -webkit-font-smoothing: antialiased; overflow-x: hidden;
}
h1, h2, h3 { margin: 0; font-weight: 400; }
p { margin: 0; }
a { color: inherit; }
ul { margin: 0; padding: 0; list-style: none; }
dl, dt, dd { margin: 0; }
::selection { background: var(--accent); color: #000; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.skip {
  position: absolute; left: -999px; top: 0;
  background: var(--accent); color: #000;
  font-family: var(--mono); font-size: .8rem; padding: .5rem .9rem; z-index: 50;
}
.skip:focus { left: 8px; top: 8px; }

.mono {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--dim);
}
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

.grain {
  position: fixed; inset: 0; z-index: 40; pointer-events: none; opacity: .03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---------- rail de cena ---------- */
.rail {
  position: fixed; left: clamp(.75rem, 2vw, 1.6rem); top: 50%;
  transform: translateY(-50%); z-index: 30;
  display: flex; flex-direction: column; gap: .9rem;
}
.rail a {
  font-family: var(--mono); font-size: .64rem; letter-spacing: .1em;
  color: var(--dim); text-decoration: none; opacity: .5;
  transition: opacity .3s var(--ease), color .3s var(--ease);
}
.rail a::before {
  content: ""; display: inline-block; width: 14px; height: 1px;
  margin-right: .5rem; vertical-align: middle; background: currentColor;
  transition: width .3s var(--ease);
}
.rail a.on { opacity: 1; color: var(--accent); }
.rail a.on::before { width: 26px; }
/* playhead do rail: timecode que corre com a rolagem, 3px = 1 quadro a 24fps,
   pintado por js/site.js. O rail diz QUAL cena; o timecode dá a posição
   contínua na linha do tempo da página. Textura numérica no limiar da
   percepção periférica — opacity .45 de propósito, um ponto abaixo do resto
   do rail. Só aparece com .tc-vivo, classe que o MÓDULO põe no html ao
   armar de verdade — presença de JS não basta: boot em tela estreita ou
   com menos-movimento deixaria o span congelado em 00:00:00:00, peso
   morto. Falha sancionada é "timecode ausente", nunca "timecode parado".
   O reduce-motion o desliga no bloco do fim do arquivo. */
.rail-tc {
  display: none; font-size: .6rem; letter-spacing: .08em; color: var(--dim);
  font-variant-numeric: tabular-nums; margin-top: .5rem; opacity: .45;
}
.tc-vivo .rail-tc { display: block; }
@media (max-width: 860px) { .rail { display: none; } }

/* ---------- topo ---------- */
.top {
  position: fixed; top: 0; left: 0; right: 0; z-index: 30;
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.4rem var(--edge);
  /* barra sólida atrás do cabeçalho (pedido do dono): antes o texto ficava
     legível por mix-blend-mode: difference, mas SOBRE o conteúdo, e ao
     rolar tudo se sobrepunha. Preto quase cheio + blur: em cima do fundo
     preto a barra desaparece (como deve), e quando pôster ou texto passa
     por baixo ela segura a leitura com vidro escuro, sem inversão. Quem
     não tem backdrop-filter fica com o rgba .88, que já cobre. */
  background: rgba(0, 0, 0, .88);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
}
.top .wm {
  font-family: var(--mono); font-weight: 700; font-size: .8rem;
  letter-spacing: .24em; text-transform: uppercase; color: #fff; text-decoration: none;
  /* o nome é assinatura: nunca quebra em duas linhas, nem na tela mais
     estreita — abaixo o letter-spacing cede antes de a palavra ceder */
  white-space: nowrap;
}
.top .cv {
  font-family: var(--mono); font-weight: 700; font-size: .72rem;
  letter-spacing: .12em; text-transform: uppercase; color: #fff;
  text-decoration: none; border-bottom: 1px solid #fff; padding-bottom: 2px;
}
/* seletor de idioma: as duas bandeiras sempre na tela, a da página atual
   acesa e com o fio de acento embaixo, a outra em meia-luz esperando o
   clique. Chip de raio 2px com fio interno branco a 18%: unifica as duas
   na mesma moldura, define a borda da verde e tira o ar de figurinha
   colada da inglesa, que é quase toda branca. Altura em clamp para ficar
   proporcional ao mono do cabeçalho, não maior que ele. */
/* dois grupos no topo: à esquerda o nome com o LinkedIn colado nele (o
   ícone pertence à assinatura, não à barra de ações), à direita idioma e
   CV. O gap da esquerda é menor de propósito — o ícone é aposto do nome,
   não item de lista. */
.top-start { display: flex; align-items: center; gap: clamp(.65rem, 1.8vw, 1rem); }
.top-end { display: flex; align-items: center; gap: clamp(1rem, 3vw, 2rem); }
.top .lang { display: flex; align-items: center; gap: .5rem; margin: 0; }
.top .lang-bt {
  display: inline-block; vertical-align: middle; position: relative;
  padding: .4rem .15rem; line-height: 0; text-decoration: none;
  transition: transform .25s var(--ease);
}
.top .lang-bt .flag {
  display: block; width: auto; height: clamp(13px, 1.5vw, 16px);
  border-radius: 2px; overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .18);
  /* em repouso as duas ficam apagadas em cinza — cor de terceiro não
     compete com a paleta da casa; a cor cheia é recompensa do hover.
     Quem diz qual idioma está no ar é o fio de acento embaixo, não a
     saturação: por isso ele é obrigatório aqui. */
  filter: grayscale(1) opacity(.5);
  transition: filter .3s var(--ease);
}
/* a do idioma atual fica um degrau acima, ainda em cinza */
.top .lang-bt.is-on .flag { filter: grayscale(1) opacity(.8); }
/* fio de acento sob a bandeira da página atual: o mesmo gesto de estado
   ativo que o rail e a sala de projeção usam */
.top .lang-bt.is-on::after {
  content: ""; position: absolute; left: .15rem; right: .15rem; bottom: .12rem;
  height: 1px; background: var(--accent);
}
@media (hover: hover) and (pointer: fine) {
  /* cor cheia no hover, nas duas: a bandeira acende ao ser mirada */
  .top .lang-bt:hover .flag { filter: none; }
  .top a.lang-bt:hover { transform: translateY(-1px); }
}
.top .lang-bt:focus-visible .flag { filter: none; }
.top a.lang-bt:focus-visible { outline-offset: 4px; }
/* sem hover não há como revelar a cor: no toque as duas já entram
   coloridas, a inativa um pouco mais discreta */
@media (hover: none) {
  .top .lang-bt .flag { filter: opacity(.6); }
  .top .lang-bt.is-on .flag { filter: none; }
}
/* LinkedIn: marca de terceiro entra em BRANCO monocromático, a mesma regra
   que normaliza as logos dos clientes — o azul da marca sobre o preto
   quebraria a unidade e daria ar de botão de rede social colado. Altura um
   fio acima da bandeira para compensar o peso ótico do glifo vazado. */
.top .social {
  /* inline-block e não block: assim o ícone senta na MESMA LINHA do nome
     mesmo que o .top-start perca o display:flex (CSS velho em cache, por
     exemplo). O flex do grupo é refinamento, não requisito — sem ele o
     vertical-align segura o alinhamento. */
  display: inline-block; vertical-align: middle;
  padding: .4rem .15rem; line-height: 0; color: #fff;
  opacity: .7; transition: opacity .25s var(--ease), transform .25s var(--ease);
}
.top .social svg {
  display: block; width: auto; height: clamp(14px, 1.6vw, 17px); fill: currentColor;
}
@media (hover: hover) and (pointer: fine) {
  .top .social:hover { opacity: 1; transform: translateY(-1px); }
}
.top .social:focus-visible { opacity: 1; outline-offset: 4px; }

/* tela estreita: nome, LinkedIn, bandeiras e CV numa linha só. O que cede
   é o tracking e o respiro entre grupos, nunca a posição do ícone — ele é
   aposto do nome e acompanha o nome onde quer que ele esteja. */
@media (max-width: 430px) {
  .top { padding-left: 1rem; padding-right: 1rem; }
  .top .wm { letter-spacing: .12em; font-size: .72rem; }
  .top .cv { letter-spacing: .06em; font-size: .66rem; }
  .top-start { gap: .45rem; }
  .top-end { gap: .8rem; }
  .top .lang { gap: .3rem; }
}

/* alvo de toque maior no celular, e press seco como o resto da casa */
@media (hover: none) {
  .top .lang-bt, .top .social { padding: .6rem .35rem; -webkit-tap-highlight-color: transparent; }
  .top a.lang-bt:active .flag { filter: none; transition-duration: 0s; }
  .top .social:active { opacity: 1; transition-duration: 0s; }
}

/* ---------- hero ---------- */
/* MEDIDO: com o nome em uma linha o conteúdo do hero encolheu ~175px, e
   como o bloco era centrado num quadro de 100svh o vazio absorveu tudo —
   233px mortos em cima e 273px embaixo num 1440x900, com os quatro
   elementos amontoados no meio. Duas mudanças:

   1. O quadro cai para 84svh. Continua sendo tela cheia na leitura, mas
      deixa a faixa "Exibido em" espiar por baixo — o vazio vira convite a
      rolar em vez de margem morta.
   2. Sai o justify-content: center. A sobrelinha ancora no topo, o nome
      flutua no terço superior e o rodapé (lede + "role") ancora embaixo,
      pelas duas margens automáticas. É a estrutura de cartaz: o conteúdo
      usa o quadro em vez de se encolher no meio dele.

   Resultado medido: a maior folga cai de 273px para 148px num 1440x900, e
   fica entre 109 e 192px de 1280x720 a 1920x1080. */
.hero {
  min-height: 84svh; display: flex; flex-direction: column; justify-content: flex-start;
  padding: clamp(4.5rem, 9vh, 6rem) var(--edge) clamp(2.2rem, 4.5vh, 3rem);
}

.hero-eyebrow {
  display: flex; gap: 1.5rem; flex-wrap: wrap;
  /* a folga cresce com a LARGURA tambem, nao so com a altura: o nome escala em
     15vw e o agudo do E sobe .247em acima da caixa alta, entao em tela larga e
     baixa (1280x720, 1920x1080) a margem so em vh deixava o acento encostar
     nesta linha. Medido: folga passou de -11.4px para +11.5px em 1280x720. */
  margin-bottom: max(clamp(2rem, 5vh, 3.2rem), clamp(1.6rem, 4.6vw, 4.6rem));
}
.hero-name {
  /* flow-root: impede que o margin-top negativo de .ln (abaixo) colapse para fora
     e arraste o h1 inteiro para cima. Sem isso o nome sobe .28em e desalinha o hero. */
  display: flow-root;
  font-family: var(--display); font-size: clamp(4rem, 15vw, 15rem);
  line-height: .9; letter-spacing: -.01em; text-transform: uppercase; margin-left: -.05em;
  /* --fator é a largura de "RÉGIS REGI" em em, MEDIDA no Anton por canvas
     (3.895 a 100px); --alvo é a fração da largura útil que o nome ocupa.
     Só valem acima de 701px, onde o nome vira uma linha só. */
  --fator: 3.97; --alvo: .62;
}

/* efeito assinatura: cada linha é uma janela; as letras sobem de dentro dela.
   O JS quebra as linhas em spans .ch com atraso escalonado. */
/* O ACENTO. Anton com line-height .86 corta tudo que passa da caixa alta, e o agudo
   do E sobe .247em acima dela (medido: tinta do E ascende 211px contra 165px do R a
   192px de corpo). Resultado: o nome lia "REGIS", decapitado.
   O padding empurra a JANELA de clip .28em para cima e a margem negativa devolve o
   espaco ao fluxo, entao o acento aparece e o layout nao anda um pixel. A borda de
   BAIXO da janela nao muda, que e a que segura as letras escondidas do reveal.
   Nao usar clip-path aqui: ele cria contexto de empilhamento e mexeria no
   mix-blend-mode das fatias do glitch. Nao usar overflow-clip-margin: abre os quatro
   lados e vaza o reveal por baixo. */
.hero-name .ln {
  display: block; overflow: clip;
  padding-top: .28em; margin-top: -.28em;
}
.hero-name .ch { display: inline-block; }

/* ---------- o nome em UMA linha, centrado (desktop) ----------
   No celular seguem duas linhas na grade à esquerda: em coluna estreita
   não há ar para ler um eixo central, e a condensada em uma linha ficaria
   pequena demais. Acima de 701px o nome vira objeto centrado — ~45% da
   tela, ar generoso dos dois lados — enquanto sobrelinha, frentes e
   rodapé do hero continuam na grade. É a gramática de cartaz: o objeto
   central só ganha destaque porque o resto obedece à margem.

   Os dois .ln viram inline-block: continuam sendo as janelas de recorte
   (o reveal letra a letra e as fatias do glitch moram neles), mas passam
   a dividir a mesma linha, e o nó de texto entre os spans volta a ser
   desenhado — é ele que separa as palavras. */
@media (min-width: 701px) {
  .hero-name {
    font-size: min(calc((100vw - 2 * var(--edge)) * var(--alvo) / var(--fator)), 15rem);
    text-align: center;
    /* o -.05em é compensação óptica de alinhamento à ESQUERDA; num eixo
       central ele só entorta o conjunto. */
    margin-left: 0;
    /* cinto: se a calibragem errar por 1px o nome NÃO pode quebrar em
       duas — sangra 1px invisível em vez de derrubar o REGI. */
    white-space: nowrap;
  }
  .hero-name .ln { display: inline-block; }
}

.hero-name.split .ch {
  /* 130% e nao 115%: a 115% a PONTA do agudo ainda entrava 13px dentro da
     janela de recorte (medido), e ficava uma lasca branca parada embaixo
     da linha antes do É subir. O agudo mora .24em acima da caixa alta, e
     era essa sobra que voltava para dentro. O mínimo medido é 127.9%;
     130% deixava 2,2px de folga e o antialiasing ainda arranhava a borda,
     então 136% — a viagem cresce 6% de uma linha, invisível na curva.
     Isso já acontecia antes de o acento ser destampado — só não dava para
     ver, porque o topo estava cortado de qualquer jeito. */
  transform: translateY(136%);
  transition: transform .9s var(--ease);
  transition-delay: var(--d, 0s);
}
.hero-name.split.go .ch { transform: none; }

/* competências logo abaixo do nome, com as cores de frente */
.hero-roles {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem;
  margin-top: clamp(1.2rem, 3vh, 2rem);
  font-family: var(--mono); font-size: clamp(.7rem, 1vw, .82rem);
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink);
}
.hero-roles .dot {
  display: inline-block; width: 10px; height: 10px; border-radius: 2px;
  margin-right: .5rem; vertical-align: baseline;
}
.dot-coord { background: var(--coord); }
.dot-roteiro { background: var(--roteiro); }
.dot-edicao { background: var(--edicao); }
.dot-curadoria { background: var(--curadoria); }
.hero-roles .sep { color: var(--dim); opacity: .5; }

.hero-foot {
  display: flex; justify-content: space-between; align-items: flex-end;
  gap: 2rem; flex-wrap: wrap; margin-top: clamp(1.2rem, 3vh, 2.2rem);
  border-top: 1px solid var(--line); padding-top: 1.4rem;
}
.hero-foot p { max-width: 46ch; font-size: clamp(.95rem, 1.3vw, 1.1rem); color: var(--ink); }
.hero-scroll { white-space: nowrap; }

/* as duas âncoras do hero moram AQUI e não junto de .hero: a regra de
   .hero-foot acima define margin-top e, vindo depois, vencia a âncora —
   o rodapé colava no lede e a folga toda ia para cima do nome (medido:
   203px contra os 148px previstos). Mesma especificidade, quem decide é
   a ordem. */
.hero-name { margin-top: auto; }
.hero-foot { margin-top: auto; }

/* ---------- glitch do nome: "corte de fita" ----------
   Duas fatias do nome saltam na lateral em amarelo (roteiro) e violeta
   (curadoria), como emenda mal alinhada na ilha de edição. As cópias
   nascem de ::before/::after com o texto em data-txt, então não duplicam
   markup nem atrapalham o efeito assinatura (letra a letra) que já mora
   em .ln/.ch.

   Calibragem atual, a pedido do dono: mais perceptível, constante e
   levemente exagerada. Três decisões fazem isso sem virar ruído:
   1. DUAS rajadas por ciclo em cada fatia (mais um tique curto no fim da
      violeta), intercaladas no tempo — o maior silêncio cai a ~350ms e o
      olho lê o defeito como permanente, não como piscada rara.
   2. A faixa de recorte MUDA a cada quadro (inset diferente por keyframe):
      fatia que salta sempre no mesmo lugar lê como carimbo; mudando de
      altura, lê como a emenda varrendo a fita.
   3. Pico de deslocamento em .065em — acima dos .045em antigos, abaixo do
      limiar de .08em medido no navegador em que a fatia sai do bloco do
      nome e vira letra fantasma. O leve escorregão vertical (.012em) é a
      emenda desencaixando também na altura, não só no eixo do tempo.
   linear e não var(--ease): entre quadros a 3% de distância (~45ms) a
   curva da casa viraria rampa; o corte tem que ser mecânico. */
.hero-name .ln { position: relative; }
.hero-name .ln::before, .hero-name .ln::after {
  content: attr(data-txt); position: absolute; left: 0; width: 100%;
  /* top acompanha o padding-top de .ln: absoluto resolve contra a caixa de padding,
     entao top:0 jogaria as fatias .28em acima do texto real. */
  top: .28em;
  pointer-events: none; mix-blend-mode: screen;
}
.hero-name .ln::before {
  color: var(--roteiro);
  animation: corte-fita-a 1.5s linear infinite;
  clip-path: inset(42% 0 38% 0);
}
.hero-name .ln::after {
  color: var(--curadoria);
  animation: corte-fita-b 1.5s linear infinite;
  clip-path: inset(68% 0 12% 0);
}
@keyframes corte-fita-a {
  0%, 8% { transform: none; opacity: 0; clip-path: inset(42% 0 38% 0); }
  9%     { transform: translate(-.065em, .012em); opacity: 1; clip-path: inset(34% 0 48% 0); }
  12%    { transform: translateX(.032em); opacity: 1; clip-path: inset(45% 0 35% 0); }
  15%, 52% { transform: none; opacity: 0; clip-path: inset(42% 0 38% 0); }
  53%    { transform: translateX(.05em); opacity: 1; clip-path: inset(40% 0 42% 0); }
  56%    { transform: translate(-.028em, -.01em); opacity: 1; clip-path: inset(48% 0 30% 0); }
  59%, 100% { transform: none; opacity: 0; clip-path: inset(42% 0 38% 0); }
}
@keyframes corte-fita-b {
  0%, 10% { transform: none; opacity: 0; clip-path: inset(68% 0 12% 0); }
  11%    { transform: translateX(.055em); opacity: 1; clip-path: inset(64% 0 16% 0); }
  14%    { transform: translate(-.03em, .012em); opacity: 1; clip-path: inset(72% 0 8% 0); }
  17%, 55% { transform: none; opacity: 0; clip-path: inset(68% 0 12% 0); }
  56%    { transform: translate(-.06em, -.01em); opacity: 1; clip-path: inset(60% 0 20% 0); }
  59%    { transform: translateX(.028em); opacity: 1; clip-path: inset(70% 0 10% 0); }
  62%, 84% { transform: none; opacity: 0; clip-path: inset(68% 0 12% 0); }
  85%    { transform: translateX(.035em); opacity: 1; clip-path: inset(76% 0 6% 0); }
  88%, 100% { transform: none; opacity: 0; clip-path: inset(68% 0 12% 0); }
}

/* ---------- faixa de marcas: rótulo fixo ----------
   Rolagem infinita sem JavaScript: a lista de marcas existe duas vezes
   dentro de .trilho e a faixa anda -50%; o ponto de emenda cai onde a
   segunda cópia começa, então o laço não salta. O rótulo fica fixo à
   esquerda para responder "que lista é essa" sem precisar de legenda
   separada. */
.marcas { display: flex; align-items: stretch; margin-top: clamp(1.4rem, 3.5vh, 2.4rem); }
.marcas-rot {
  flex: 0 0 auto; display: flex; align-items: center;
  padding: 0 clamp(1rem, 2.5vw, 1.8rem) 0 var(--edge);
  border-top: 1px solid var(--line); border-right: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: .62rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--accent); white-space: nowrap;
}
.marcas-faixa {
  flex: 1 1 auto; overflow: hidden; padding: .85rem 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.marcas-trilho { display: flex; width: max-content; animation: marcas-correr 46s linear infinite; }
/* .presa = dedo pousado na fita (classe posta por js/site.js): o mesmo pausar
   do hover, para quem navega por toque conseguir ler as credenciais. O :hover
   fica atrás de (hover:hover) como o das linhas de Trabalho, e pelo mesmo
   motivo: no toque o hover emulado GRUDA depois que o dedo solta, e a fita
   ficaria presa anulando exatamente o pointerup/pointercancel do JS. */
.marcas-faixa:focus-within .marcas-trilho,
.marcas-faixa.presa .marcas-trilho { animation-play-state: paused; }
@media (hover: hover) and (pointer: fine) {
  .marcas-faixa:hover .marcas-trilho { animation-play-state: paused; }
}
.marcas-trilho ul {
  display: flex; align-items: center; gap: clamp(1.6rem, 3.5vw, 3rem);
  padding-right: clamp(1.6rem, 3.5vw, 3rem); margin: 0; list-style: none;
}
.marcas-trilho li {
  font-family: var(--mono); font-weight: 700; font-size: clamp(.66rem, 1.05vw, .78rem);
  letter-spacing: .2em; text-transform: uppercase; color: var(--dim);
  white-space: nowrap; transition: color .3s var(--ease);
}
/* cada canal agora é link para a subseção mais relevante (js/site.js abre a
   linha e rola). O texto herda o desenho do item; o hover sobe de --ink para
   o acento porque a faixa deixou de ser lista decorativa e virou navegação —
   acento em interativo é uso sancionado do token. */
.marcas-trilho a { color: inherit; text-decoration: none; }
.marcas-trilho li:hover { color: var(--accent); }
.marcas-trilho a:focus-visible { outline-offset: 5px; }
.marcas-trilho li::after { content: "·"; margin-left: clamp(1.6rem, 3.5vw, 3rem); color: var(--line); }
.marcas-trilho li:last-child::after { content: none; }
@keyframes marcas-correr { to { transform: translateX(-50%); } }
@media (max-width: 640px) {
  .marcas-rot { display: none; }
  .marcas-faixa { border-left: 1px solid var(--line); }
}

/* ---------- estatísticas ---------- */
.stats-band { padding: 0 var(--edge) clamp(1.3rem, 3vh, 2.2rem); }
.stats { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--line); }
.stat { display: flex; flex-direction: column; padding: clamp(1.4rem, 3vw, 2.4rem) 1rem clamp(.4rem, 2vw, 1rem) 0; }
.stat + .stat { padding-left: clamp(1rem, 2vw, 2rem); border-left: 1px solid var(--line); }
.stat dd { font-family: var(--display); font-size: clamp(2.6rem, 6vw, 4.6rem); line-height: 1; color: var(--ink); order: -1; font-variant-numeric: tabular-nums; }
.stat dt { font-family: var(--mono); font-size: .64rem; letter-spacing: .14em; text-transform: uppercase; color: var(--dim); margin-top: .7rem; }
@media (max-width: 720px) {
  .stats { grid-template-columns: 1fr 1fr; }
  .stat:nth-child(odd) { border-left: none; padding-left: 0; }
  .stat:nth-child(n+3) { border-top: 1px solid var(--line); }
}

/* ---------- blocos ---------- */
section { padding: clamp(2.6rem, 5vh, 3.9rem) var(--edge); }
.head { display: flex; align-items: baseline; gap: 1.2rem; margin-bottom: clamp(1.3rem, 2.6vh, 1.8rem); }
.head .n { font-family: var(--mono); font-size: .8rem; color: var(--accent); }
.head h2 {
  font-family: var(--display); font-size: clamp(2.2rem, 6vw, 5rem);
  line-height: 1; text-transform: uppercase; letter-spacing: -.01em;
}

/* o esconde-e-revela é condicionado a .js, classe que um script inline no
   <head> põe no <html> antes da primeira pintura. Sem isso, quem chega com o
   JS bloqueado ou quebrado recebia seção invisível — e a conta piorou quando
   o cabeçalho das seções e o contato, que guarda o e-mail, entraram no mesmo
   mecanismo. Assim o pior caso vira "site inteiro de pé, sem entrada", que é
   a falha certa. Inline e no head: script diferido esconderia depois de já
   ter pintado, e o lampejo seria pior que o problema. */
.js .cut > * { opacity: 0; transform: translateY(24px); transition: opacity .8s var(--ease), transform .8s var(--ease); }
.cut.in > * { opacity: 1; transform: none; }

/* ---------- sobre ---------- */
.about { display: grid; gap: clamp(1.8rem, 3.4vw, 3rem); }
.about .rest { color: var(--dim); font-size: clamp(.85rem, 1.1vw, .95rem); max-width: 68ch; line-height: 1.6; }

/* ---------- sobre: a tese e as três frentes ----------
   A tese vem do curriculo ("três competências que raramente andam
   juntas") e vira o lede em display; embaixo, uma coluna por frente,
   cada uma assinada pelo MESMO ponto de cor que legenda as frentes sob
   o nome — a cor aqui é índice, não decoração. */
.sobre-lede {
  font-family: var(--display); font-size: clamp(1.9rem, 4.4vw, 3.8rem);
  line-height: 1.06; text-transform: uppercase; letter-spacing: -.01em;
  max-width: 24ch;
}
.sobre-lede em { font-style: normal; color: var(--accent); }
.sobre-tres {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.4rem, 3vw, 2.8rem);
}
.sobre-rotulo {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--dim); margin-bottom: .85rem;
}
.sobre-rotulo .dot {
  display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  margin-right: .55rem; vertical-align: baseline;
}
.sobre-bloco > p:last-child { font-size: clamp(.9rem, 1.15vw, 1rem); line-height: 1.6; color: var(--ink); }
.sobre-bloco i { font-style: italic; }
@media (max-width: 860px) {
  .sobre-tres { grid-template-columns: 1fr; gap: 1.6rem; }
}

/* ---------- bloco de carreira ----------
   O currículo desenhado: eixo de anos na horizontal, três raias por frente,
   onze barras ocupando o período real de cada trabalho. Lê-se de uma vez
   que ele fazia roteiro enquanto já finalizava, e que a curadoria correu
   por baixo de tudo desde 2018.

   ÍNDICE DE MÊS, a única conta do bloco: i = (ano - 2018) * 12 + (mes - 1),
   jan/2018 = 0, --ate EXCLUSIVO. Eixo de 108 meses = 9 anos. Um índice
   errado NÃO gera erro, só põe a barra no lugar errado — conferir sempre
   contra a tabela no comentário do HTML, nos DOIS idiomas.

   REGRA DE MANUTENÇÃO: a linha de meta tem no MÁXIMO 14 caracteres. É ela,
   e não o nome, que governa a largura do rótulo — mono tem avanço fixo de
   .6em em qualquer fallback, Anton não tem. Passar disso encosta um rótulo
   no outro na largura mínima do regime desktop (861px). */
.gantt {
  margin-top: clamp(3rem, 7vh, 5rem);
  --meses: 108;
  --anos: 9;
  --passo: clamp(3rem, 3.6vh, 3.4rem);   /* altura de uma linha plotada */
  --hoje: 103;                           /* ago/2026 — o JS reescreve no boot */
  --fios: repeating-linear-gradient(to right,
          var(--line) 0 1px, transparent 1px calc(100% / var(--anos)));
}

.g-titulo {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--dim);
  margin-bottom: clamp(1.2rem, 2.6vh, 1.7rem);
}

/* eixo: nove células de 1fr com fio à esquerda — exatamente as fronteiras
   que --fios repete atrás das raias, porque 108 / 9 = 12 meses exatos. */
.g-eixo {
  list-style: none; margin: 0; padding: 0;   /* o reset da casa zera ul, não ol */
  display: grid; grid-template-columns: repeat(var(--anos), 1fr);
  font-family: var(--mono); font-size: clamp(.5rem, .62vw, .58rem);
  letter-spacing: .12em; color: var(--dim); font-variant-numeric: tabular-nums;
}
.g-eixo li { border-left: 1px solid var(--line); padding: 0 0 .4rem .3rem; }

/* o plot é o bloco contentor do playhead. NÃO remover este position:
   relative achando que é inútil: sem ele o fio de HOJE resolve contra
   outro ancestral e migra de lugar. */
.g-plot { position: relative; padding-top: 1rem; }

.g-hoje {
  position: absolute; top: 1rem; bottom: 0; width: 1px;
  left: calc(var(--hoje) / var(--meses) * 100%);
  background: var(--accent); opacity: .45; pointer-events: none;
}
.g-hoje b {
  position: absolute; bottom: 100%; left: 0; margin-bottom: 3px;
  transform: translateX(-50%);
  font-family: var(--mono); font-weight: 400; font-size: .5rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--accent);
}

.g-raia + .g-raia { margin-top: clamp(1.2rem, 2.6vh, 1.8rem); }
/* --fio-raia alimenta o fio lateral do celular; no desktop não é usado */
.g-raia:has(.dot-coord)     { --fio-raia: color-mix(in srgb, var(--coord) 55%, transparent); }
.g-raia:has(.dot-roteiro)   { --fio-raia: color-mix(in srgb, var(--roteiro) 55%, transparent); }
.g-raia:has(.dot-edicao)    { --fio-raia: color-mix(in srgb, var(--edicao) 55%, transparent); }
/* o ponto do item segue a frente da BARRA, nao a da raia: a Maracutaia e
   curadoria dentro da raia de edicao, e o violeta dela precisa aparecer. */
.g-item:has(.g-barra[data-frente="curadoria"]) { --fio-raia: var(--curadoria); }
.g-raia-nome {
  display: flex; align-items: center; gap: .5rem;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--dim); margin-bottom: .6rem;
}
/* .dot-coord e irmãs só dão a COR; a dimensão mora em .hero-roles .dot e
   .sobre-rotulo .dot, ambas escopadas. Sem esta regra o quadrado nasce 0x0. */
.g-raia-nome .dot { width: 9px; height: 9px; border-radius: 2px; flex: 0 0 auto; }

.g-linhas {
  list-style: none; margin: 0; padding: 0;
  position: relative; height: calc(var(--linhas) * var(--passo));
  background-image: var(--fios);
}

.g-item {
  position: absolute; left: 0; right: 0;
  top: calc(var(--r) * var(--passo)); height: var(--passo);
}

/* o link cobre a linha inteira para que rótulo E barra sejam um alvo só — e
   é por isso que ele NÃO pode receber ponteiro: itens da mesma linha se
   sobrepõem por construção, e com pointer-events:auto o último cobriria os
   anteriores e roubaria os cliques do a[data-alvo]. Falha silenciosa. Os
   dois filhos VISÍVEIS reativam o ponteiro; o :hover continua funcionando
   porque propaga do filho atingido para os ancestrais. */
.g-link { position: absolute; inset: 0; pointer-events: none;
          text-decoration: none; color: inherit; }
.g-rot, .g-barra { pointer-events: auto; }

/* rótulo: banda de CIMA, ancorado no início da barra, largura livre para
   transbordar à direita sobre o preto. Ele nunca desce até a banda da barra
   — é essa separação vertical que torna o transbordo inofensivo: o rótulo
   passa por cima da barra do vizinho sem disputar um pixel de clique. */
.g-rot {
  position: absolute; top: 0;
  left: calc(var(--de) / var(--meses) * 100%);
  white-space: nowrap;
}
.g-nome {
  display: block; font-family: var(--display); font-weight: 400;
  font-size: clamp(.8rem, 1.15vw, 1.1rem); letter-spacing: .02em;
  text-transform: uppercase; line-height: 1.05; color: var(--ink);
  transition: color .25s var(--ease);
}
.g-meta {
  display: block; margin-top: .18rem;
  font-family: var(--mono); font-size: clamp(.5rem, .62vw, .58rem);
  letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* o período por extenso: recortado no desktop (o eixo dá o ano, o painel dá
   a data exata), visível no celular. Classe PRÓPRIA, nunca a
   .visually-hidden global — des-esconder um utilitário compartilhado por
   consulta de mídia é armadilha. */
.g-datas { position: absolute; width: 1px; height: 1px;
           overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* a faixa é o trilho de largura total e o bloco contentor da barra nos DOIS
   regimes: absoluta no desktop, relativa em fluxo no celular. É ela que faz
   uma tabela só de --de/--ate servir as duas formas sem recalcular nada. */
.g-faixa { position: absolute; left: 0; right: 0; bottom: 0; height: 9px; }

.g-barra {
  position: absolute; top: 0; bottom: 0;
  left:  calc(var(--de) / var(--meses) * 100%);
  width: calc((var(--ate) - var(--de)) / var(--meses) * 100%);
  border-radius: 1px; opacity: .78; transform-origin: 0 50%;
  transition: opacity .25s var(--ease);
}
.g-barra[data-frente="coord"]   { background-color: var(--coord); }
.g-barra[data-frente="roteiro"] { background-color: var(--roteiro); }
.g-barra[data-frente="edicao"]  { background-color: var(--edicao); }
/* curadoria É literalmente o acento (#a48bff). Numa composição onde violeta
   é interface — foco, hover, o fio de HOJE — uma barra violeta maciça
   quebraria a doutrina da casa. A hachura devolve a barra à condição de
   índice: é textura, não estado. E funciona sem cor. */
.g-barra[data-frente="curadoria"] {
  background-color: var(--curadoria);
  background-image: repeating-linear-gradient(135deg, #000 0 1px, transparent 1px 4px);
}
/* em curso: dissolve na ponta em vez de fingir uma data de saída. */
.g-aberta {
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
          mask-image: linear-gradient(90deg, #000 calc(100% - 26px), transparent);
}

.g-link:hover .g-nome, .g-link:focus-visible .g-nome { color: var(--accent); }
.g-link:hover .g-barra { opacity: 1; }
/* o link tem a largura do plot inteiro: um anel nele seria uma faixa de
   ponta a ponta. O anel vai nos dois filhos, que são a área visível. */
.g-link:focus-visible { outline: none; }
.g-link:focus-visible .g-rot,
.g-link:focus-visible .g-barra { outline: 2px solid var(--accent); outline-offset: 3px; }

.g-formacao {
  margin-top: clamp(1.4rem, 3vh, 2rem); padding-top: .9rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: clamp(.55rem, .68vw, .62rem);
  letter-spacing: .12em; text-transform: uppercase; color: var(--dim);
  line-height: 1.7;
}
.g-formacao b { color: var(--ink); font-weight: 400; }

/* um corte por bloco: as barras se escrevem da esquerda para a direita na
   ordem em que a carreira aconteceu. O atraso sai do PRÓPRIO dado — --de é
   o mês de início — então não há segunda ordenação para manter à mão. Só
   transform: não multiplica com o fade-up do .cut do pai. Sob .cut.in, sem
   JS nada anima e as barras nascem inteiras. */
@keyframes g-wipe { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.gantt.cut.in .g-barra {
  animation: g-wipe .5s var(--ease) both;
  animation-delay: calc(var(--de) * 4ms);
}

/* CELULAR: o bloco de carreira SAI.
   Decisao do dono, e ela se sustenta: as onze entradas repetem, com menos
   contexto, o que o indice de TRABALHO logo abaixo ja diz — mesma empresa,
   mesmo cargo, mesmo periodo, e la ainda abre o painel inteiro. O que o
   gantt acrescenta e a SIMULTANEIDADE entre as frentes, e isso precisa de
   largura: numa coluna estreita as barras viram tracos de 15px e a leitura
   some. Sem a comparacao, sobra repeticao ocupando 670px de rolagem.

   Fica so a linha de FORMACAO, que nao e redundante: UFF e AIC nao
   aparecem em nenhum outro lugar do site. Por isso o corte e por peca e
   nao um "display: none" no .gantt inteiro — jogar fora o unico conteudo
   exclusivo do bloco para poupar codigo seria trocar informacao por
   conveniencia. O grafico continua inteiro no desktop. */
@media (max-width: 860px) {
  .g-titulo, .g-eixo, .g-plot { display: none; }
  /* sem o grafico acima, a formacao deixa de ser rodape e vira a propria
     linha: perde o fio de cima e encosta no texto do Sobre. */
  .gantt { margin-top: clamp(1.6rem, 4vh, 2.4rem); }
  .g-formacao { margin-top: 0; padding-top: 0; border-top: none; }
}

/* ---------- caixa da playlist (Maracutaia) ----------
   Referência: o bloco do currículo, que não é um botão — é um convite com
   três camadas: o losango ◈, o lema da rádio e o rótulo espaçado
   "PLAYLIST NO YOUTUBE". A pílula branca de antes dizia só "ver a
   playlist" e não carregava nada da identidade da rádio.
   O violeta aqui é estado de interface (a borda acende no hover), não
   decoração: em repouso a caixa é fio da casa. */
.playlist {
  grid-column: 1 / -1; justify-self: stretch;
  display: grid; grid-template-columns: auto 1fr auto;
  align-items: center; gap: 0 clamp(.9rem, 2vw, 1.3rem);
  /* a caixa é a chamada principal daquele painel, e é também a peça que
     fecha a diferença de altura entre texto e arte na Maracutaia: com
     esta altura as duas colunas terminam a 13px uma da outra, contra os
     137px de antes. */
  padding: clamp(1.4rem, 3.4vh, 2rem) clamp(1.2rem, 2.8vw, 1.6rem);
  border: 1px solid color-mix(in srgb, var(--accent) 42%, transparent);
  background-color: color-mix(in srgb, var(--accent) 9%, transparent);
  border-radius: 4px;
  text-decoration: none; color: inherit; position: relative; overflow: hidden;
  transition: border-color .25s var(--ease), background-color .25s var(--ease);
}
/* varredura de luz que atravessa uma vez no hover, como a agulha passando
   pelo sulco. Fica sob o conteúdo e não recebe ponteiro. */
.playlist::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 35%,
              color-mix(in srgb, var(--accent) 14%, transparent) 50%, transparent 65%);
  transform: translateX(-100%);
}
.playlist:hover, .playlist:focus-visible {
  border-color: var(--accent);
  background-color: color-mix(in srgb, var(--accent) 17%, transparent);
}
.playlist:hover::before, .playlist:focus-visible::before {
  transition: transform 1.1s var(--ease); transform: translateX(100%);
}
.playlist:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.playlist-marca {
  font-size: clamp(.85rem, 1.6vw, 1.05rem); color: var(--accent); line-height: 1;
  transition: transform .35s var(--ease);
}
.playlist:hover .playlist-marca { transform: rotate(90deg); }
.playlist-corpo { display: grid; gap: .62rem; min-width: 0; }
.playlist-lema {
  font-family: var(--body); font-size: clamp(.9rem, 1.25vw, 1.05rem);
  line-height: 1.45; color: var(--ink);
}
.playlist-rotulo {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .3em;
  text-transform: uppercase; color: color-mix(in srgb, var(--accent) 88%, transparent);
  transition: color .25s var(--ease);
}
.playlist:hover .playlist-rotulo { color: var(--ink); }
.playlist-seta {
  font-family: var(--mono); font-size: 1rem; color: var(--accent); line-height: 1;
  transition: transform .25s var(--ease), color .25s var(--ease);
}
.playlist:hover .playlist-seta { color: var(--accent); transform: translate(2px, -2px); }
@media (max-width: 640px) {
  .playlist { grid-template-columns: auto 1fr; }
  .playlist-seta { display: none; }   /* a seta é enfeite; no toque, some */
}

/* ---------- televisão (Porta dos Fundos) ----------
   O favicon do site é uma TV de tubo com antenas fora do ar. Aqui o mesmo
   desenho vira moldura do vídeo, agora ligada: o gabinete em violeta, a
   antena em V no topo, a luz de tensão acesa ao lado da legenda.
   Antena e varredura são pseudo/spans vazios com aria-hidden — o alvo de
   clique continua sendo o <a> com data-embed, intocado. */
.tv { position: relative; padding-top: clamp(18px, 3vw, 26px); }

/* antena: duas hastes em V, desenhadas com bordas de um quadrado girado.
   Nasce atrás do gabinete e aparece só acima dele. */
.tv-antena {
  position: absolute; left: 50%; top: 0;
  width: clamp(52px, 9vw, 82px); height: clamp(18px, 3vw, 26px);
  transform: translateX(-50%); pointer-events: none;
}
.tv-antena::before, .tv-antena::after {
  content: ""; position: absolute; bottom: 0; left: 50%;
  width: 1px; height: 100%;
  background: color-mix(in srgb, var(--accent) 70%, transparent);
  transform-origin: 50% 100%;
}
.tv-antena::before { transform: rotate(-27deg); }
.tv-antena::after  { transform: rotate(27deg); }

.tv-tubo {
  margin: 0; position: relative;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 10px;
  padding: clamp(.5rem, 1.1vw, .7rem);
  background: color-mix(in srgb, var(--accent) 5%, #000);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6) inset,
              0 18px 46px -28px color-mix(in srgb, var(--accent) 60%, transparent);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.tv:hover .tv-tubo {
  border-color: color-mix(in srgb, var(--accent) 80%, transparent);
  box-shadow: 0 0 0 1px rgba(0,0,0,.6) inset,
              0 22px 54px -26px color-mix(in srgb, var(--accent) 85%, transparent);
}
.tv-tubo a { display: block; position: relative; border-radius: 5px; overflow: hidden; text-decoration: none; }
.tv-tubo a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.tv-tubo img {
  display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover;
  border-radius: 5px; transition: transform .5s var(--ease), filter .3s var(--ease);
}
.tv:hover .tv-tubo img { transform: scale(1.02); filter: saturate(1.06); }

/* linhas de varredura: a textura do tubo. Fixas, sem animação — o
   movimento desta subseção já é a esteira de produção logo abaixo, e a
   casa dá um corte por bloco. */
.tv-varre {
  position: absolute; inset: 0; pointer-events: none; border-radius: 5px;
  /* duas camadas: um escurecido radial no meio, para o triângulo de play
     ler sobre qualquer quadro (o frame desta esquete tem arte vermelha
     saturada bem no centro, e o play branco sumia nela); e as linhas de
     varredura do tubo por cima. */
  background:
    radial-gradient(ellipse 34% 46% at 50% 50%, rgba(0,0,0,.55), transparent 72%),
    repeating-linear-gradient(180deg, rgba(0,0,0,.16) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply; opacity: .62;
}
/* botão de play: só o triângulo, sem disco. Cresce no hover. */
.tv-play {
  position: absolute; left: 50%; top: 50%; width: 0; height: 0;
  transform: translate(-40%, -50%);
  border-top: 11px solid transparent; border-bottom: 11px solid transparent;
  border-left: 18px solid var(--ink);
  filter: drop-shadow(0 2px 14px rgba(0,0,0,.85));
  transition: transform .25s var(--ease), border-left-color .25s var(--ease);
  pointer-events: none;
}
.tv:hover .tv-play { transform: translate(-40%, -50%) scale(1.15); border-left-color: var(--accent); }

.tv-tubo figcaption {
  display: flex; align-items: center; gap: .5rem;
  margin-top: .6rem; padding: 0 .15rem;
  font-family: var(--mono); font-size: .58rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim);
}
/* luz de tensão: o único ponto que pulsa, devagar, como LED de aparelho */
.tv-luz {
  width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto;
  background: var(--accent);
  box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 70%, transparent);
  animation: tv-tensao 2.6s ease-in-out infinite;
}
@keyframes tv-tensao { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.tv-canal { margin-left: auto; color: color-mix(in srgb, var(--accent) 75%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .tv-luz { animation: none; }
  .tv:hover .tv-tubo img, .tv:hover .tv-play { transform: none; }
  .playlist::before { display: none; }
  .playlist:hover .playlist-marca, .playlist:hover .playlist-seta { transform: none; }
}

/* ---------- índice de trabalho ---------- */
/* o índice recua da margem para os números não competirem com o rail de
   cenas, que fica fixo à esquerda. A consulta é o complemento exato da que
   esconde o rail, senão larguras fracionárias entre 860 e 861 ficariam com
   rail visível e lista sem recuo, que é justamente a colisão a evitar. */
.work { border-top: 1px solid var(--line); }
@media not all and (max-width: 860px) {
  .work { margin-left: clamp(2rem, 5vw, 5.5rem); }
}
.row {
  display: grid; grid-template-columns: 2.6rem 1fr auto 1.6rem; align-items: baseline;
  gap: .8rem 1.2rem; padding: clamp(.85rem, 1.6vh, 1.15rem) 0;
  border-bottom: 1px solid var(--line); cursor: pointer; position: relative;
  /* quem chega por link da faixa "Exibido em" rola até aqui: a margem
     impede a linha de parar embaixo do topo fixo */
  scroll-margin-top: clamp(3.8rem, 8vh, 5rem);
}
/* ---------- a marca na linha fechada ----------
   As logos saíram do corpo do painel e vieram para o índice, a pedido do
   dono: a marca costuma identificar a empresa antes do nome. Uma quinta
   coluna de largura fixa entre o número e o nome — toda linha reserva o
   MESMO retângulo, então a coluna do nome começa sempre no mesmo x, e é
   isso que segura o ritmo da pilha de onze.

   --otica é a média geométrica entre normalizar por ÁREA da caixa e por
   TINTA (pixels realmente pintados, medidos nos arquivos). Só área punia
   logo de traço fino: Mulheres que Escrevem tem 18,5% de tinta, A Fábrica
   23,4% e Paramount+ 28,0% — as três chegavam encolhidas justamente por
   serem as mais vazadas do conjunto. Só tinta super-corrige na direção
   oposta e transforma os wordmarks em tiras de 14px. A média resolve as
   duas pontas.

   A marca é aria-hidden e o img leva alt vazio: o nome ao lado já diz a
   empresa, e anunciar duas vezes é eco no leitor de tela. */
.row {
  grid-template-columns: 2.6rem clamp(96px, 10vw, 132px) 1fr auto 1.6rem;
  align-items: center;
}
.row-marca {
  width: clamp(96px, 10vw, 132px); height: clamp(32px, 3.4vw, 44px);
  line-height: 0;
}
.row-marca img {
  display: block;
  width: calc(100% * var(--otica, 1)); height: calc(100% * var(--otica, 1));
  object-fit: contain; object-position: left center;
  opacity: .82; transition: opacity .3s var(--ease);
}
.row:hover .row-marca img, .row:focus-within .row-marca img,
.row.open .row-marca img { opacity: 1; }

.row-idx { font-family: var(--mono); font-size: .72rem; color: var(--dim); }
.row-name {
  /* o próprio nome é o botão que expande */
  all: unset;
  font-family: var(--display); font-size: clamp(1.6rem, 4.5vw, 3.4rem);
  line-height: .95; text-transform: uppercase; letter-spacing: -.01em; color: var(--ink);
  cursor: pointer;
  transition: color .3s var(--ease), transform .4s var(--ease);
}
.row-name:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.row-meta {
  font-family: var(--mono); font-size: .7rem; letter-spacing: .06em;
  text-transform: uppercase; color: var(--dim); text-align: right; white-space: nowrap;
}
/* afordância de expandir: + vira × quando aberto */
.row-plus {
  justify-self: end; font-family: var(--mono); font-size: 1.1rem; line-height: 1;
  color: var(--dim); transition: color .3s var(--ease), transform .4s var(--ease);
}
.row-plus::before { content: "+"; }
/* o "+" deixa de ser metadado sussurrado (mono pequeno, --dim) e passa a
   pesar como segunda massa tipográfica da linha — controle secundário com
   o mesmo rigor do nome, não ícone decorativo. Feedback real: no celular
   ninguém percebia que a linha abre; isto é permanente e 100% CSS, então
   funciona em toda linha, sempre, sem depender de pegar quem visita no
   momento certo de scroll. :not(.row-ext):not(.row-go) porque essas duas
   classes reusam .row-plus só pelo grid/posição (a seta de saída em
   Escrita) e precisam continuar leves, não competir com o nome. Vem antes
   de .row.open .row-plus de propósito: mesma especificidade, e o estado
   aberto (cor accent) precisa vencer no empate. */
.row-plus:not(.row-ext):not(.row-go) {
  font-weight: 700; font-size: clamp(1.3rem, 3vw, 1.9rem); color: var(--ink);
}
.row.open .row-plus { color: var(--accent); transform: rotate(45deg); }
.row-ext::before { content: "↗"; font-size: .9rem; }
.row-go::before { content: "→"; font-size: .9rem; }
.row.open .row-ext { transform: none; }
/* fio de estado sob a linha. Cresce por transform: scaleX e não por width:
   width é propriedade de layout e o fio cresce em 11 linhas ao mesmo tempo
   durante a varredura de entrada; scaleX roda no compositor e desenha o
   mesmo traço. transform-origin: left para crescer da esquerda, como antes. */
.row::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px; width: 100%;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform .45s var(--ease);
}
/* :hover escopado a dispositivo com hover de verdade: sem isto, um toque no
   celular deixa a linha "presa" na cor de hover até o usuário tocar em outro
   lugar da tela, porque touch não tem estado de :hover, só simula ao tocar e
   não sai sozinho. :focus-within continua fora do escopo, teclado não deve
   depender de hover. */
@media (hover: hover) and (pointer: fine) {
  .row:hover .row-name { color: var(--accent); transform: translateX(.4rem); }
  .row:hover .row-plus { color: var(--accent); }
  .row:hover::after { transform: scaleX(1); }
  /* faixa de acento atravessando a linha a cada passagem do mouse, para dizer
     "isto abre" antes do clique. Vive no ::before para não brigar com o
     ::after, que é o fio de estado (cresce e FICA enquanto o mouse está em
     cima). Só nas linhas que expandem: .row-out são as de Escrita, que levam
     para fora e já se anunciam com sublinhado e seta, então varrer nelas
     prometeria uma expansão que não existe.
     Faixa feita com background-position, não com transform: um segmento de
     45% da largura corre de -45% a 145%, o que atravessa a linha inteira sem
     depender de conta de translate em cima de largura variável. */
  .row:not(.row-out)::before {
    content: ""; position: absolute; left: 0; bottom: -1px; height: 1px;
    width: 100%; pointer-events: none; opacity: 0;
    background-image: linear-gradient(90deg, transparent, var(--accent), transparent);
    background-size: 45% 100%; background-repeat: no-repeat;
  }
  /* curva própria, não var(--ease): a curva da casa é fortemente frontal
     (serve para coisas que ASSENTAM) e medida aqui levava a faixa a 85% do
     trajeto em 150ms, virando um flash em vez de uma passagem. Esta é
     simétrica, então a faixa atravessa em velocidade legível. */
  .row:not(.row-out):hover::before {
    animation: passa-faixa .85s cubic-bezier(.45, .05, .55, .95);
  }
}
@keyframes passa-faixa {
  from { background-position-x: -45%; opacity: 1; }
  to   { background-position-x: 145%; opacity: 1; }
}

/* ---------- corte seco no toque ----------
   Toda a linguagem de hover vive atrás de (hover:hover)/(pointer:fine) — o
   que estava certo, e deixava o toque mudo. Aqui o dedo recebe os MESMOS
   valores do hover, em gramática de corte: acende em 0s no press e apaga na
   transition de repouso ao soltar. É mudança de estado, não animação, então
   fica de pé com prefers-reduced-motion de propósito — feedback de toque é
   função, não ornamento. O tap-highlight nativo sai só de quem ganhou
   resposta própria, nunca do site inteiro. O :active destravado no Safari
   do iPhone por um listener de touchstart em js/site.js. */
@media (hover: none) {
  .row, .row-hit, .sheet a, .peca-cta, .end a.mail, .end-links a {
    -webkit-tap-highlight-color: transparent;
  }
  .row:active .row-name, .row:active .row-plus { color: var(--accent); transition-duration: 0s; }
  .marcas-trilho li:active { color: var(--accent); transition-duration: 0s; }
  .row:active::after { transform: scaleX(1); transition-duration: 0s; }
  .row-out .row-hit:active .row-name { text-decoration-color: var(--accent); transition-duration: 0s; }
  .sheet a:active img { box-shadow: inset 0 0 0 2px var(--accent); transition-duration: 0s; }
  .peca-cta:active { background: #fff; transition-duration: 0s; }
  .end a.mail:active { border-color: var(--accent); transition-duration: 0s; }
  .end-links a:active { color: var(--ink); transition-duration: 0s; }
}
.row:focus-within .row-name { color: var(--accent); transform: translateX(.4rem); }
.row:focus-within::after { transform: scaleX(1); }
.row.open .row-name { color: var(--accent); }

/* ---------- varredura de entrada: mostra a interação em vez de anunciá-la ----------
   Ao entrar em cena, a lista de Trabalho recebe uma passagem única de cima
   pra baixo que reproduz, linha a linha, os MESMOS valores que .row:hover já
   usa (cor accent, translateX, o fio crescendo) — não inventa vocabulário
   novo, é o estado de interação real acontecendo sem precisar de dedo ou
   mouse, a tradução do sinal de hover de desktop pro toque. Reaproveita o
   IntersectionObserver que já existe para .cut em js/site.js (fire-once,
   nenhum observer novo). Escopado à seção de Trabalho para não repetir em
   Escrita, que já tem sublinhado e seta sempre visíveis, não precisa do
   reforço. :is(#trabalho, #work) porque o id da seção muda por idioma
   (#trabalho no pt-BR, #work no en) — index.html e en/index.html carregam
   este mesmo arquivo de CSS, então a regra precisa cobrir os dois nomes.
   Duração 380ms, passo de 65ms entre linhas: a proporção (~5,8×) é o que
   faz o efeito ler como luz corrida contínua, não como piscadas em sequência
   tipo onda de estádio — não alterar o passo sem alterar a duração junto. */
:is(#trabalho, #work) .work.cut.in .row:nth-child(1)  { --sweep-i: 0; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(2)  { --sweep-i: 1; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(3)  { --sweep-i: 2; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(4)  { --sweep-i: 3; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(5)  { --sweep-i: 4; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(6)  { --sweep-i: 5; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(7)  { --sweep-i: 6; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(8)  { --sweep-i: 7; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(9)  { --sweep-i: 8; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(10) { --sweep-i: 9; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(11) { --sweep-i: 10; }
:is(#trabalho, #work) .work.cut.in .row:nth-child(n+12) { --sweep-i: 11; }

:is(#trabalho, #work) .work.cut.in .row-name {
  animation: varre-nome .38s var(--ease);
  animation-delay: calc(var(--sweep-i, 0) * 65ms + .26s);
}
:is(#trabalho, #work) .work.cut.in .row-plus {
  animation: varre-plus .38s var(--ease);
  animation-delay: calc(var(--sweep-i, 0) * 65ms + .26s);
}
:is(#trabalho, #work) .work.cut.in .row::after {
  animation: varre-fio .38s var(--ease);
  animation-delay: calc(var(--sweep-i, 0) * 65ms + .26s);
}
@keyframes varre-nome { 50% { color: var(--accent); transform: translateX(.4rem); } }
@keyframes varre-plus { 50% { color: var(--accent); } }
@keyframes varre-fio  { 50% { transform: scaleX(1); } }

/* ---------- linhas que são um link inteiro (escrita) ----------
   O <a> assume o grid e o respiro da linha, então a área clicável cobre a
   faixa toda, não só o nome. A linha continua sendo o <li>, que mantém o
   fio de baixo, o position: relative e a varredura do ::after.
   Este bloco precisa vir ANTES do @media de telas estreitas: .row-hit e
   .row têm a mesma especificidade, então quem estiver por último vence, e
   o grid de 3 colunas do mobile seria anulado se o bloco viesse depois. */
.row-out { display: block; padding: 0; }
/* sinopse sob cada publicação. Fica FORA do <a> de propósito: dentro, o nome
   acessível do link viraria o parágrafo inteiro. Medium e Academia.edu
   proíbem enquadramento por cabeçalho (frame-ancestors 'self' num, 403 no
   outro), então leitor embutido não existe — o que dá para fazer é chegar no
   clique já sabendo do que se trata. */
.row-sinopse {
  margin: -.2rem 0 clamp(.9rem, 2vh, 1.25rem);
  padding-left: calc(2.6rem + 1.2rem);
  max-width: 70ch; font-size: .92rem; line-height: 1.6; color: var(--dim);
}
.row-sinopse em { font-style: normal; color: #b3adbd; }
@media (max-width: 860px) { .row-sinopse { padding-left: 0; font-size: .88rem; } }
.row-hit {
  display: grid; grid-template-columns: 2.6rem 1fr auto 1.6rem; align-items: baseline;
  gap: .8rem 1.2rem; padding: clamp(.85rem, 1.6vh, 1.15rem) 0;
  color: inherit; text-decoration: none;
}
.row-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* afordância de link: aqui o nome é sublinhado, o que separa à primeira
   vista "isso abre fora" de "isso expande um painel" (+ nas outras linhas).
   A espessura é em em para acompanhar o clamp do corpo do Anton. */
.row-out .row-name {
  text-decoration: underline; text-decoration-thickness: .045em;
  text-underline-offset: .1em; text-decoration-color: var(--dim);
  transition: color .3s var(--ease), transform .4s var(--ease),
              text-decoration-color .3s var(--ease);
}
.row-out:hover .row-name, .row-out:focus-within .row-name { text-decoration-color: var(--accent); }
/* a seta acende e sobe na diagonal no hover, gesto de "sai daqui". Em
   repouso fica em --dim como o +, porque o token reserva o acento para
   foco, hover e estado ativo. Quem separa link de painel é o sublinhado. */
.row-out:hover .row-ext, .row-out:focus-within .row-ext {
  color: var(--accent); transform: translate(.15rem, -.15rem);
}

/* em telas estreitas a meta desce para baixo do nome, senão a linha estoura.
   3ª coluna alargada de 1.6rem pra 2.2rem: dá respiro pro "+" maior (Peso
   Assentado); a seta de Escrita (.row-ext/.row-go) fica só com mais espaço
   sobrando à direita, sem quebrar, porque continua com justify-self: end. */
@media (max-width: 640px) {
  /* a marca sobe para a primeira linha, ao lado do número: em coluna
     estreita ela não cabe como coluna própria — medido, a do nome esmagava
     para 13px — e é a marca que o dono quer ver primeiro. O nome desce uma
     linha e mantém a largura inteira. */
  .row, .row-hit { grid-template-columns: 2.6rem 1fr 2.2rem; }
  .row-idx { grid-column: 1; grid-row: 1; }
  .row-marca { grid-column: 2; grid-row: 1; }
  .row-name { grid-column: 2; grid-row: 2; }
  .row-plus { grid-column: 3; grid-row: 1; }
  .row-meta { grid-column: 2; grid-row: 3; text-align: left; white-space: normal; }
  .panel { grid-column: 1 / -1; grid-row: 4; }
}
.panel { grid-column: 1 / -1; display: grid; grid-template-rows: 0fr; transition: grid-template-rows .5s var(--ease); }
.panel > div { overflow: hidden; }
.row.open .panel { grid-template-rows: 1fr; }
/* a medida de 70ch é do TEXTO, não do painel: assim o contact sheet pode
   usar a largura inteira da linha e o pôster cresce junto com a tela */
.panel-inner { padding: 1.4rem 0 .4rem; display: grid; gap: 1rem; }
.panel-inner p, .panel-inner .tagline { max-width: 70ch; }
.panel-inner p { color: var(--dim); font-size: .98rem; line-height: 1.6; }
.panel-inner strong { color: var(--ink); font-weight: 600; }
.tagline { color: var(--accent); font-family: var(--mono); font-size: .68rem; letter-spacing: .14em; text-transform: uppercase; }

/* ---------- contact sheet: pôsteres das produções ----------
   Ficam DENTRO do painel, então o índice segue sendo tipografia pura e a
   imagem só aparece quando a linha é aberta. Entram em cor cheia: o pôster
   é o trabalho, e escondê-lo atrás de um hover custava mais do que rendia.
   O que segura a página é o painel fechado, não a dessaturação.

   A fileira é centrada. Por isso auto-fit e não contagem fixa de coluna:
   auto-fit COLAPSA a trilha vazia, e sem isso o painel de 2 pôsteres
   ficaria encostado à esquerda enquanto o de 6 preenche tudo.

   A fileira é centrada, e por isso flex e não grid: o justify-content do
   grid centra as TRILHAS, então numa última fileira incompleta o pôster
   sobrava encostado à esquerda. O flex centra fileira parcial também.

   A célula tem base 130px e cresce até 185px. Com base pequena o painel de 6
   (DOT Cine, o maior) cabe em fileira única a partir de ~1060px de tela; mais
   estreito que isso ele quebra, mas cada fileira sai centrada. */
.sheet {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start;
  gap: clamp(.7rem, 1.6vw, 1.15rem); padding-top: .5rem;
  /* width: 100% é obrigatório junto do margin-inline: auto. O .sheet é item
     de grid do .panel-inner; a margem automática tira o stretch e o bloco
     encolhe para o conteúdo, deixando a fileira estreita fora do lugar. */
  width: 100%; max-width: 1240px; margin-inline: auto;
}
.sheet figure { flex: 1 1 130px; max-width: 185px; margin: 0; }
/* pôster largo: único caso em que o material não é pôster 2:3, é um mockup
   16:9 de página inteira (ver assets/posters/README.md, painel Maracutaia.fm).
   Mesma moldura, mesmo hover do resto do contact sheet, só a proporção e a
   largura mudam; continua dentro de .sheet de propósito, para o JS que liga
   os pôsteres à sala de projeção (.sheet a[href]) alcançar sem mudança. */
.sheet figure.wide { flex-basis: 100%; max-width: 100%; }
.sheet figure.wide img { aspect-ratio: 16 / 9; }

.sheet a { display: block; text-decoration: none; }
.sheet img {
  width: 100%; aspect-ratio: 2 / 3; object-fit: cover; display: block;
  background: var(--line);
  /* fio interno de 1px: pôster de borda escura não dissolve no fundo preto */
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .1);
  transition: box-shadow .35s var(--ease), filter .35s var(--ease);
}
/* em cor desde o repouso, o hover precisa de outro sinal: o fio interno
   acende no acento e a imagem clareia de leve */
.sheet a:hover img, .sheet a:focus-visible img {
  box-shadow: inset 0 0 0 2px var(--accent); filter: brightness(1.08);
}
.sheet a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.sheet figcaption {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--dim); margin-top: .55rem; line-height: 1.4;
  /* a fileira é centrada, a legenda acompanha */
  text-align: center;
  /* reserva duas linhas: título comprido não desalinha o pé da fileira */
  min-height: 2.8em;
}
/* pôster sem material público não é link: fica um pouco recuado */
.sheet figure.mute img { opacity: .82; }

/* ---------- peça de painel: cada subseção é uma peça viva ----------
   Todo painel de Trabalho abre numa peça desenhada, não num parágrafo com
   imagem embaixo: algarismo do índice em fantasma, título em Anton, marca do
   cliente, lide, ficha em mono e o material — pôsteres, fotos, marcas ou uma
   textura própria.

   É HTML e CSS, não JPG. O pôster exportado nascia em 1920px e entrava no
   painel a ~330px num celular: tudo escalava por 0,17, o mono de 13px virava
   2px e a ficha sumia. Vivo, o mesmo desenho reflui, indexa na busca e fica
   nítido em qualquer densidade.

   Os hyperlinks continuam nos pôsteres e nos botões, e js/site.js alcança
   .peca a[href] junto com .sheet a[href]: quem tem vídeo abre na sala de
   projeção, quem não tem sai marcado com data-embed="off". */
.peca {
  position: relative; overflow: hidden; isolation: isolate;
  /* sem borda de caixa por decisão do dono: a peça se sustenta pelos
     próprios chãos (chapa, textura, faixa) e pelo fio de acento do topo,
     que segue sendo o sinal de "abriu" */
  padding: clamp(1.35rem, 3.2vw, 2.9rem);
  display: grid; gap: clamp(1rem, 2vw, 1.6rem);
  width: 100%; max-width: 1240px; margin-inline: auto;
  /* minmax(0,1fr) e não 1fr: com min-width:auto, as fileiras internas de
     auto-fit somam a largura mínima das colunas e esticam o bloco para fora
     da tela em vez de quebrar. */
  grid-template-columns: minmax(0, 1fr);
}
/* min-width: 0 tem que descer até os netos: quem estoura a largura são as
   fileiras internas de auto-fit (marcas, frentes, pôsteres), e elas
   moram dentro de .peca-visual, não como filhas diretas da peça */
.peca > *, .peca-corpo, .peca-texto, .peca-visual,
.peca-corpo > *, .peca-texto > *, .peca-visual > * { min-width: 0; }

/* algarismo do índice, fantasma, atrás do cabeçalho: repete a numeração das
   linhas de Trabalho e dá profundidade sem custar cor */
.peca-indice {
  position: absolute; top: .04em; right: clamp(.7rem, 2.4vw, 2rem); z-index: -1;
  font-family: var(--display); font-size: clamp(4.4rem, 10vw, 9rem);
  line-height: .78; letter-spacing: -.05em; color: #141120;
  pointer-events: none;
}

.peca-topo {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .26em;
  text-transform: uppercase; color: var(--dim);
}
.peca-topo b { color: var(--ink); font-weight: 400; }

.peca h3 {
  font-family: var(--display); font-weight: 400;
  /* 1.14 e não .95: o título é caixa alta com acento (AGÊNCIA, PADRÃO) e o
     Anton desenha o acento ACIMA da altura de caixa — em .95 o circunflexo
     da segunda linha batia na vírgula da primeira. 1.06 resolvia a colisão
     mas ainda ficava apertado a olho (pedido do dono); 1.14 dá ao acento o
     respiro de peça premium sem dissolver o peso de cartaz do bloco. */
  font-size: clamp(1.95rem, 4.6vw, 3.5rem); line-height: 1.14;
  letter-spacing: -.03em; text-transform: uppercase; color: var(--ink);
  max-width: 18ch; margin-top: .35rem;
}
.peca h3 em { font-style: normal; color: var(--accent); }

/* cabeça da peça, padrão dos ONZE painéis: título primeiro, marca depois —
   cartela de crédito na ordem de leitura (primeiro o que fiz, depois para
   quem). Par ancorado à esquerda de propósito: o algarismo fantasma
   continua dono do canto direito, sem marca por cima dele. Centro
   vertical alinhado entre o título de duas linhas e a marca; em tela
   estreita o wrap derruba a marca para BAIXO do título, e a ordem de
   leitura fica intacta. Wordmarks (Porta, Dafina, Maracutaia, DOT,
   A Fábrica) usam a altura base; emblemas mais altos que largos
   (Mellin, MQE, Grupo Sal, Paramount+, Descomplica) usam .alta, para
   pesarem o mesmo na fileira de painéis. */
.peca-cabeca { margin-top: .35rem; }
.peca-cabeca h3 { margin-top: 0; }

/* corpo: no largo, texto de um lado e material do outro */
.peca-corpo { display: grid; gap: clamp(1.1rem, 2.4vw, 2.2rem); }
.peca-texto { display: grid; gap: clamp(.75rem, 1.4vw, 1.15rem); align-content: start; }
.peca-visual { display: grid; gap: clamp(.7rem, 1.4vw, 1.1rem); align-content: start; }
@media (min-width: 860px) {
  /* MEDIDO: com .92/1.08 o texto acabava 137px (Maracutaia) e 147px (Porta)
     antes do fim da arte — a coluna de imagem era a mais larga e mandava na
     altura do bloco. Invertida a hierarquia: o texto passa a ser a coluna
     larga e a arte encolhe, e as duas fecham quase juntas (+29 e -23 num
     1280, contra 137/147). O ponto foi achado varrendo a proporção no
     navegador; passar de 1.24 zera a Maracutaia mas joga o Porta para -49,
     porque os dois painéis têm volumes de texto diferentes. */
  .peca--lado .peca-corpo { grid-template-columns: minmax(0, 1.24fr) minmax(0, .76fr); align-items: start; }
  /* teto na arte: sem ele, acima de ~1400px a coluna de imagem volta a
     crescer enquanto o texto trava no corpo máximo e em 40ch, e a
     diferença reabre (medido: 45px num 1600). Com 360px a diferença fica
     em 18px de 1440 a 1920, e em 1280 o teto nem chega a morder, porque
     ali a coluna já mede 355px. */
  .peca--lado .peca-visual { max-width: 360px; }
}


/* .panel-inner .peca-lede e não só .peca-lede: a regra genérica
   ".panel-inner p" (bem acima, no bloco do painel) tem especificidade 0-1-1
   e vencia a de classe única, então TANTO o corpo QUANTO a cor deste
   parágrafo vinham de lá — o lede saía em --dim, mais apagado do que o
   desenho pedia, e qualquer ajuste de tamanho aqui era inerte. */
.panel-inner .peca-lede {
  max-width: 52ch; font-size: clamp(1.03rem, 1.36vw, 1.22rem);
  line-height: 1.6; color: #b3adbd;
}
/* nos dois painéis de coluna dupla a medida fecha em 40ch: com a coluna de
   texto agora larga, 52ch deixaria a linha comprida demais para ler e ainda
   por cima encurtaria o bloco, que é o defeito que se está corrigindo. */
/* 42ch, achado varrendo a medida no navegador: em 40ch o texto passa da
   arte, em 45ch fica curto demais e a arte volta a sobrar. Em 41-43ch o
   resultado é o mesmo, então 42 fica no meio da faixa estável e aguenta
   variação de fonte sem sair do lugar. */
.panel-inner .peca--lado .peca-lede { max-width: 42ch; }
.peca-lede strong { color: var(--ink); font-weight: 600; }

.peca-ficha {
  display: flex; flex-wrap: wrap; gap: .35rem 1.05rem; list-style: none;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim);
}
.peca-ficha b { color: var(--ink); font-weight: 400; }

.peca-cta {
  justify-self: start; display: inline-flex; align-items: center; gap: .7rem;
  padding: .78rem 1.45rem; border-radius: 999px;
  background: var(--ink); color: #07060c; text-decoration: none;
  font-size: .82rem; font-weight: 600; white-space: nowrap;
  transition: transform .2s var(--ease), background-color .2s var(--ease);
}
.peca-cta:hover, .peca-cta:focus-visible { transform: translateY(-2px); background: #fff; }
.peca-cta:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.peca-rodape {
  border-top: 1px solid var(--line); padding-top: clamp(.7rem, 1.4vw, 1rem);
  font-family: var(--mono); font-size: .58rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--dim);
  display: flex; flex-wrap: wrap; gap: .4rem 1.2rem; justify-content: space-between;
}
.peca-rodape .acento { color: var(--accent); }

/* ---- material 1: os pôsteres das produções, com o link no player ----
   Reaproveita o .sheet do contact sheet para o JS da sala de projeção não
   precisar de seletor novo; só a caixa muda, porque dentro da peça a fileira
   alinha à esquerda em vez de centralizar. */
.peca .sheet { justify-content: center; max-width: none; padding-top: 0; margin: 0; }
.peca .sheet figure { flex: 1 1 104px; max-width: 158px; }
.peca .sheet figcaption { text-align: center; min-height: 0; font-size: .55rem; }
/* o pôster largo (16:9) dentro de peça ocupa a coluna inteira: sem isto o
   teto de 158px do contact sheet venceria o .wide por vir depois no arquivo */
.peca .sheet figure.wide { flex-basis: 100%; max-width: 100%; }

/* ---- material 2b: módulos da Maracutaia ----
   As fotos dos sets recortadas nas formas da própria marca — quadrado,
   círculo, arco e meia-cana — alternadas com um módulo do padrão gráfico e um
   módulo violeta com o símbolo. É a peça que existia como imagem 16:9, agora
   viva: a grade era o que dava identidade a ela, não o texto. */
.peca-modulos {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.45rem, 1vw, .85rem); list-style: none;
}
.peca-modulos li { display: grid; gap: .5rem; min-width: 0; }
.peca-modulos .mod { position: relative; aspect-ratio: 1 / 1; overflow: hidden; background: #07060c; }
.peca-modulos .mod > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.peca-modulos .circ { border-radius: 50%; }
.peca-modulos .arco { border-radius: 50% 50% 0 0; }
.peca-modulos .dee { border-radius: 0 50% 50% 0; }
/* o padrão é preto sobre branco no arquivo: inverte para virar branco sobre
   preto sem precisar de uma segunda versão do asset */
.peca-modulos .padrao > img { filter: invert(1); }
.peca-modulos .violeta { background: var(--accent); display: grid; place-items: center; }
.peca-modulos .violeta > img { position: static; width: 42%; height: auto; object-fit: contain; }
.peca-modulos span {
  font-family: var(--mono); font-size: .55rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim);
}
.peca-modulos b { color: var(--accent); font-weight: 400; }
@media (max-width: 640px) {
  .peca-modulos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ---- material 3: fileira de marcas atendidas ----
   Os arquivos vêm em proporções muito diferentes (C6 é um wordmark 5:1,
   Praya e BSC são emblemas mais altos que largos), então cada logo pousa
   num PALCO de altura fixa: a caixa é a mesma nas três células, a logo
   centra dentro dela e o rótulo cai sempre na mesma linha — sem o topo
   serrilhado que a fileira tinha quando cada célula media pela própria
   logo. max-width e max-height JUNTOS escalam preservando a proporção;
   o esquema antigo (height fixa + max-width) achatava o wordmark do C6
   quando a célula apertava. .alta dá mais teto aos emblemas, que precisam
   de mais altura para pesar o mesmo que um wordmark; o palco não muda.
   3 colunas fixas também no celular: vira uma régua de marcas numa linha
   só, em vez de 2+1 com órfã desalinhada. */
.peca-marcas {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.8rem, 2vw, 1.8rem); list-style: none; width: 100%;
}
.peca-marcas li {
  display: grid; grid-template-rows: clamp(58px, 7vw, 92px) auto;
  gap: .7rem; justify-items: center; align-items: center; min-width: 0;
}
.peca-marcas img {
  display: block; width: auto; height: auto;
  max-width: 100%; max-height: clamp(30px, 3.6vw, 46px);
}
.peca-marcas li.alta img { max-height: clamp(52px, 6.4vw, 84px); }
.peca-marcas span {
  font-family: var(--mono); font-size: .55rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--dim);
  text-align: center; line-height: 1.5;
  /* start e não o center herdado do li: com a fileira de rótulos esticada,
     o center faria rótulo de 2 linhas (Brasil Surfe Clube no celular)
     começar mais alto que os de 1 linha — todos partem da mesma linha */
  align-self: start;
}
/* quando a coluna visual é SÓ a fileira de marcas (caso do Mellin), ela
   centra na vertical ao lado do texto: bloco curto grudado no topo deixava
   o resto da coluna como vazio morto. Navegador sem :has() fica com o
   alinhamento de topo de sempre — degradação limpa. */
@media (min-width: 860px) {
  .peca--lado .peca-visual:has(> .peca-marcas:only-child) {
    align-self: stretch; align-content: center;
  }
}

/* ---- material 4: três frentes com algarismo fantasma ---- */
.peca-frentes {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: clamp(1.2rem, 2.6vw, 2.2rem); list-style: none;
}
.peca-frentes li { position: relative; display: grid; gap: .65rem; align-content: start; min-width: 0; }
/* o algarismo fica à direita: à esquerda batia atrás da marca do cliente */
.peca-frentes .num {
  position: absolute; top: -.34em; right: 0; z-index: -1;
  font-family: var(--display); font-size: clamp(3.6rem, 6.4vw, 5.6rem);
  line-height: 1; letter-spacing: -.04em; color: #1b1726;
}
.peca-frentes img { display: block; width: auto; max-width: 100%; height: clamp(28px, 3.4vw, 44px); }
.peca-frentes .fio { height: 1px; background: var(--line); }
.peca-frentes p { font-size: clamp(.8rem, .95vw, .9rem); line-height: 1.55; color: #9d97a8; }
.peca-frentes b {
  font-family: var(--mono); font-size: .55rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink); font-weight: 400;
}

/* ---- material 5: cronômetro de dez casas ---- */
.peca-crono { display: flex; gap: clamp(3px, .5vw, 8px); align-items: flex-end; list-style: none; }
.peca-crono li { flex: 1; display: grid; gap: .45rem; }
.peca-crono i { display: block; height: clamp(34px, 5vw, 68px); background: #231e31; }
.peca-crono span { font-family: var(--mono); font-size: .5rem; letter-spacing: .08em; color: #4a4455; }
.peca-crono li:last-child i { background: var(--accent); }
.peca-crono li:last-child span { color: var(--accent); }

/* ---- material 6: onda de áudio ----
   Barras em pílula (raio total), vocabulário de player de áudio moderno.
   O trecho .acesa é o estado ESTÁTICO — sem JS, com menos-movimento ou
   antes de abrir, a onda mostra um trecho aceso fixo. Com o painel aberto,
   o playhead varre (onda-toca, abaixo) e pinta por cima dele. */
.peca-onda { display: flex; gap: 3px; align-items: center; height: clamp(54px, 6.4vw, 96px); }
.peca-onda i { flex: 1; display: block; background: #2f2942; border-radius: 99px; }
.peca-onda i.acesa { background: var(--accent); }

/* ---- material 7: fluxo semanal (Porta dos Fundos) ----
   O pipeline da entrega semanal como trilho de cinco etapas: roteiro,
   gravação, edição, pós, no ar. Um pulso de acento atravessa o trilho
   acendendo nó, fio e rótulo em sequência — e atravessa TRÊS vezes por
   ciclo, com uma respirada depois da terceira: são as três esquetes da
   semana, o ritmo que o painel inteiro descreve. Ciclo de 18s com
   travessias em 0%, 30.6% e 61.1% (5,5s cada) e 5s de silêncio no fim;
   o mesmo keyframe serve todos os nós porque quem estagia é o delay
   (.9s por etapa, índice --e posto no LI por nth-child e herdado por
   nó, fio e rótulo). Estado parado: trilho desenhado, "no ar" aceso —
   é o que fica para menos-movimento e para quem chega sem JS. */
.peca-fluxo {
  list-style: none; display: grid; grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: .5rem; margin-top: .6rem;
  /* width explícita: colunas minmax(0,1fr) têm base zero, e sem isto a
     grade colapsa para a largura do conteúdo em vez de abrir o trilho */
  width: 100%;
}
.peca-fluxo li { position: relative; display: grid; gap: .6rem; justify-items: start; min-width: 0; }
.peca-fluxo li:nth-child(2) { --e: 1; }
.peca-fluxo li:nth-child(3) { --e: 2; }
.peca-fluxo li:nth-child(4) { --e: 3; }
.peca-fluxo li:nth-child(5) { --e: 4; }
/* nó: mesmo quadradinho das frentes de atuação da hero */
.peca-fluxo .no {
  display: block; width: 9px; height: 9px; border-radius: 2px;
  background: #2f2942;
}
/* trilho entre um nó e o próximo */
.peca-fluxo li:not(:last-child)::before {
  content: ""; position: absolute; left: 17px; right: 7px; top: 4px; height: 1px;
  background: var(--line);
}
/* fio de acento que varre o trilho quando o pulso passa */
.peca-fluxo li:not(:last-child)::after {
  content: ""; position: absolute; left: 17px; right: 7px; top: 4px; height: 1px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
}
.peca-fluxo span:not(.no) {
  font-family: var(--mono); font-size: .55rem; letter-spacing: .14em;
  text-transform: uppercase; color: #4a4455;
}
/* estado parado: a chegada fica marcada — composição estática completa */
.peca-fluxo li:last-child span:not(.no) { color: var(--accent); }

@keyframes fluxo-no {
  0%, 30.55%, 61.11% { background: var(--accent); transform: scale(1.55); }
  2.8%, 33.35%, 63.91% { background: var(--accent); transform: none; }
  4.4%, 34.95%, 65.51%, 100% { background: #2f2942; transform: none; }
}
@keyframes fluxo-fio {
  0%, 30.55%, 61.11% { transform: scaleX(0); opacity: 1; }
  4.9%, 35.45%, 66.01% { transform: scaleX(1); opacity: 1; }
  6%, 36.55%, 67.11%, 100% { transform: scaleX(1); opacity: 0; }
}
@keyframes fluxo-rotulo {
  0%, 30.55%, 61.11% { color: var(--ink); }
  4.4%, 34.95%, 65.51%, 100% { color: #4a4455; }
}
/* a chegada ("no ar") segura o acento mais tempo: é o ponto do fluxo */
@keyframes fluxo-final {
  0%, 30.55%, 61.11% { color: var(--accent); }
  9%, 39.55%, 70.11%, 100% { color: #4a4455; }
}
.row.open .peca-fluxo .no {
  animation: fluxo-no 18s linear calc(.8s + var(--e, 0) * .9s) infinite;
}
.row.open .peca-fluxo li:not(:last-child)::after {
  animation: fluxo-fio 18s linear calc(.8s + var(--e, 0) * .9s) infinite;
}
.row.open .peca-fluxo span:not(.no) {
  animation: fluxo-rotulo 18s linear calc(.8s + var(--e, 0) * .9s) infinite;
}
.row.open .peca-fluxo li:last-child span:not(.no) {
  animation-name: fluxo-final;
}

/* ---- material 8: entregas em andamento (Mellin) ----
   As três marcas como três ENTREGAS correndo em paralelo, cada uma com a
   régua de status da pós rodando embaixo, no ciclo natural da ilha:
   INGEST, CONVERSÃO, ORGANIZAÇÃO, CONFORM, V1, APROVAÇÃO, ONLINE — em corte seco, palavra substituindo palavra, nada
   desliza. As réguas ficam DEFASADAS de um terço de ciclo entre si: três
   campanhas, cada uma num estágio, que é a história do painel. Quando a
   régua bate em ONLINE a logo acende e o fio de acento cresce embaixo —
   a logo é o payoff do ciclo, não decoração.
   Ciclo de 12s: seis passes de 1,4s + 3,6s de ONLINE no ar. Delays
   NEGATIVOS de propósito: o ciclo já está rodando quando o painel abre —
   ninguém liga uma ilha, a operação já estava em andamento. Fase por
   aritmética (--f da entrega, --k da palavra), sem JS. Estado parado
   (fechado, sem JS ou com menos-movimento): ONLINE aceso e logo cheia,
   a composição final. */
.peca-entregas { width: 100%; display: grid; gap: clamp(.9rem, 2vw, 1.4rem); }
.peca-entregas-cab {
  display: flex; justify-content: space-between; gap: 1rem;
  font-family: var(--mono); font-size: .55rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--dim);
  padding-bottom: .55rem; border-bottom: 1px solid var(--line);
}
.peca-entregas-cab b { color: var(--accent); font-weight: 400; }
.peca-entregas ul {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(.8rem, 2.4vw, 2rem); list-style: none;
}
.entrega { position: relative; display: grid; gap: .8rem; justify-items: center; min-width: 0; }
/* palco da logo: altura fixa, logo centrada, proporção intocada */
.entrega-palco {
  display: grid; place-items: center; width: 100%;
  height: clamp(64px, 8vw, 104px);
}
/* a logo é MÁSCARA, não imagem: o arquivo branco-sobre-transparente vira
   recorte, a base pinta branco cheio e o ::after é a coluna de acento que
   SOBE POR DENTRO do desenho — um sexto a cada estágio completado, tudo
   roxo no Online. A subida é na curva da casa e o topo da coluna tem um
   respiro de gradiente (menisco), para ler como líquido enchendo, não
   como barra dura. mask-size: contain segura a proporção mesmo se a caixa
   não bater com o aspect-ratio inline. transform-only: nada de animar
   altura. O estado parado (fechado, sem JS, menos-movimento) é a
   composição final: tudo no ar, tudo acento. */
.entrega-logo {
  position: relative; overflow: hidden;
  height: clamp(30px, 3.8vw, 48px); max-width: 88%;
  background: #fff;
  /* longhands e não o shorthand, e o --logo inline usa caminho ABSOLUTO
     de raiz (/assets/...): url() relativa dentro de custom property é
     resolvida pelo Chrome contra a base da folha que consome o var — ou
     seja, /css/ — e a máscara apontava para /css/assets/..., 404, logo
     invisível. Absoluto não tem base para errar. */
  -webkit-mask-image: var(--logo); mask-image: var(--logo);
  -webkit-mask-size: contain; mask-size: contain;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.entrega.alta .entrega-logo { height: clamp(56px, 7vw, 92px); }
.entrega-logo::after {
  content: ""; position: absolute; inset: -10% 0 0 0;
  background: linear-gradient(to top, var(--accent) 88%, rgba(164, 139, 255, 0) 100%);
  background-size: 100% 116%; background-repeat: no-repeat;
  background-position: 50% 50%;
  transform: translateY(0);
}
/* fio do payoff: cresce sob o palco quando a entrega fica no ar */
.entrega::after {
  content: ""; position: absolute; left: 12%; right: 12%;
  top: calc(clamp(64px, 8vw, 104px) + .1rem); height: 1px;
  background: var(--accent); transform: scaleX(1); transform-origin: left;
  opacity: .35;
}
/* régua de status: as seis palavras empilhadas na mesma célula; quem fala
   é uma por vez, por opacidade em corte */
.entrega-fila { display: grid; justify-items: center; }
.entrega-fila b {
  grid-area: 1 / 1; font-weight: 400;
  font-family: var(--mono); font-size: .55rem; letter-spacing: .2em;
  text-transform: uppercase; color: var(--dim); white-space: nowrap;
  opacity: 0;
}
.entrega-fila b.final { color: var(--accent); opacity: 1; }

/* palavra comum: viva por 1,4s do ciclo de 10,5s (13,33%), corte nas bordas */
@keyframes entrega-passa {
  0% { opacity: 1; }
  11.63% { opacity: 1; }
  11.67% { opacity: 0; }
  100% { opacity: 0; }
}
/* ONLINE: viva por 3,5s (33,33%) */
@keyframes entrega-final {
  0% { opacity: 1; }
  29.96% { opacity: 1; }
  30% { opacity: 0; }
  100% { opacity: 0; }
}
/* a coluna de acento sobe SEM PARAR: o líquido enche durante cada estágio
   e cruza o nível exato (um sexto por processo) no instante em que a
   palavra vira — ease-in-out por trecho, então o movimento desacelera de
   leve a cada fronteira, marca o processo e segue, nunca morre. A escada
   de segura-e-salta anterior lia dura (pedido do dono). Cheio do Online
   ao fim do ciclo; o retorno ao vazio é corte, gramática da casa. */
@keyframes entrega-enche {
  0%      { transform: translateY(100%); animation-timing-function: ease-in-out; }
  11.67%  { transform: translateY(83.4%); animation-timing-function: ease-in-out; }
  23.33%  { transform: translateY(66.7%); animation-timing-function: ease-in-out; }
  35%     { transform: translateY(50%); animation-timing-function: ease-in-out; }
  46.67%  { transform: translateY(33.4%); animation-timing-function: ease-in-out; }
  58.33%  { transform: translateY(16.7%); animation-timing-function: ease-in-out; }
  70%     { transform: translateY(0%); }
  99.96%  { transform: translateY(0%); }
  100%    { transform: translateY(100%); }
}
/* a respiração do menisco: a borda de gradiente oscila devagar dentro da
   coluna, defasada por carta (--f), como superfície de líquido que não
   está perfeitamente parada. Anima background-position, paint barato em
   elemento pequeno; o transform continua inteiro para o nível. */
@keyframes entrega-respira {
  from { background-position: 50% 14%; }
  to   { background-position: 50% 86%; }
}
@keyframes entrega-fio {
  0% { transform: scaleX(0); opacity: 1; }
  69.93% { transform: scaleX(0); opacity: 1; }
  70.9% { transform: scaleX(1); opacity: 1; }
  99.96% { transform: scaleX(1); opacity: .55; }
  100% { transform: scaleX(0); opacity: 1; }
}
.row.open .entrega-fila b {
  animation: entrega-passa 12s linear calc(var(--f, 0s) + var(--k, 0) * 1.4s - 12s) infinite;
}
.row.open .entrega-fila b.final {
  animation: entrega-final 12s linear calc(var(--f, 0s) + 8.4s - 12s) infinite;
}
.row.open .entrega-logo::after {
  animation:
    entrega-enche 12s linear calc(var(--f, 0s) - 12s) infinite,
    entrega-respira 3.4s ease-in-out calc(var(--f, 0s) - 12s) infinite alternate;
}
.row.open .entrega::after {
  animation: entrega-fio 12s linear calc(var(--f, 0s) - 12s) infinite;
}

/* a FAIXA DE MÁQUINA: chão preto próprio para o trilho do fluxo, no pé
   da peça — sangria total, fio de separação, o pulso corre em superfície
   limpa. Classe própria (.peca-maquina) para qualquer peça usar; as
   margens negativas são o padding do cartão, mesmo clamp. */
.peca-maquina {
  margin: .4rem calc(-1 * clamp(1.35rem, 3.2vw, 2.9rem))
          calc(-1 * clamp(1.35rem, 3.2vw, 2.9rem));
  padding: clamp(1rem, 2.2vw, 1.6rem) clamp(1.35rem, 3.2vw, 2.9rem);
  background: var(--bg);
  border-top: 1px solid var(--line);
}
.peca-maquina .peca-fluxo { margin-top: 0; }

/* ---------- animação das peças ----------
   A peça só existe depois que a linha abre, então a entrada é one-shot,
   presa em .row.open: nada anima em repouso, nada fica em laço, e o texto
   nunca se move enquanto está sendo lido. Fechar e reabrir a linha tira e
   repõe a classe, então a entrada roda de novo sem JS nenhum.

   Só opacity e transform, que rodam no compositor — nenhuma propriedade de
   layout entra em keyframe aqui. Duração de 450 a 600ms com o ease da casa;
   o escalonamento é curto de propósito (40 a 50ms por item), o suficiente
   para o olho perceber ordem sem esperar pela peça.

   ORDEM DE POUSO, que é o ponto: o que se lê chega primeiro e o ornamento
   chega por último. Texto e botão assentam por volta de 780ms, o material
   por volta de 930ms, cronômetro e onda fecham perto de 1s. Nunca o
   contrário — parágrafo ainda entrando embaixo de foto já parada lê como
   página lenta.

   Como o .panel usa transition de grid-template-rows (500ms), a entrada
   corre junto com a abertura: quando a caixa termina de crescer, o miolo do
   texto já pousou.

   --d é o atraso de cada elemento e mora no PAI (figure, li), não na
   declaração de animação: custom property herda, então a foto lá dentro
   reaproveita o atraso do próprio quadro sem repetir a tabela de nth-child. */
@keyframes peca-entra {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
@keyframes peca-indice {
  from { opacity: 0; transform: translateY(-6px) scale(1.06); }
  to { opacity: 1; transform: none; }
}
/* o fio de acento atravessa o topo da peça: é o MESMO fio que cresce sob a
   linha de Trabalho no hover, agora dizendo "abriu". Vocabulário reusado,
   não inventado. Termina em opacity .5 para virar moldura, não sinal aceso. */
@keyframes peca-fio {
  from { transform: scaleX(0); opacity: 1; }
  to { transform: scaleX(1); opacity: .5; }
}
/* a foto assenta dentro da moldura parada: 1,06 → 1 enquanto o quadro surge.
   Dura mais que a entrada do quadro de propósito, o resto do movimento é
   quase imperceptível e é ele que dá peso de material impresso. */
@keyframes peca-foto {
  from { transform: scale(1.06); }
  to { transform: none; }
}
/* o cronômetro cresce do pé, uma casa de cada vez: a animação É o conceito
   da peça, dez segundos correndo */
@keyframes peca-crono {
  from { opacity: .35; transform: scaleY(.1); }
  to { opacity: 1; transform: none; }
}
/* a onda sobe do eixo, do começo para o fim, como leitura de forma de onda */
@keyframes peca-onda {
  from { opacity: .3; transform: scaleY(.08); }
  to { opacity: 1; transform: none; }
}

.peca::after {
  content: ""; position: absolute; left: 0; top: 0; height: 1px; width: 100%;
  background: linear-gradient(90deg, var(--accent), transparent);
  transform: scaleX(0); transform-origin: left; pointer-events: none;
}
.row.open .peca::after { animation: peca-fio .9s var(--ease) both; }

.row.open .peca-topo { animation: peca-entra .45s var(--ease) both; }
.row.open .peca h3 { animation: peca-entra .5s var(--ease) .04s both; }
.row.open .peca-indice { animation: peca-indice .8s var(--ease) .08s both; }

.row.open .peca-texto > * { animation: peca-entra .5s var(--ease) var(--d, .1s) both; }
.row.open .peca-texto > *:nth-child(2) { --d: .145s; }
.row.open .peca-texto > *:nth-child(3) { --d: .19s; }
.row.open .peca-texto > *:nth-child(4) { --d: .235s; }
.row.open .peca-texto > *:nth-child(5) { --d: .28s; }
.row.open .peca-rodape { animation: peca-entra .45s var(--ease) .3s both; }

/* material: cada item entra depois do texto, em fila. O :nth-child(n+7)
   trava a fila: sem ele o sétimo pôster em diante cai no atraso zero e
   entra junto com o primeiro, quebrando a leitura de fileira. */
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega) {
  --d: calc(.18s + var(--i, 0) * 50ms);
  animation: peca-entra .5s var(--ease) var(--d) both;
}
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega):nth-child(2) { --i: 1; }
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega):nth-child(3) { --i: 2; }
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega):nth-child(4) { --i: 3; }
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega):nth-child(5) { --i: 4; }
.row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega):nth-child(n+6) { --i: 5; }

/* fill BACKWARDS e não both: com both a animação segura transform: none no
   fim e passa na frente do :hover, que morreria em silêncio. Backwards só
   vale antes e durante, e o último keyframe já é o valor de repouso, então
   não há salto quando ela sai. */
.row.open .peca .sheet a img,
.row.open .peca-modulos .mod > img {
  animation: peca-foto .6s var(--ease) var(--d, 0s) backwards;
}

/* o --i mora no LI e desce por herança para a casa E para o algarismo:
   os dois precisam do mesmo índice, a casa para a entrada e o par
   casa+algarismo para a contagem regressiva abaixo */
.row.open .peca-crono i {
  transform-origin: bottom;
  animation:
    peca-crono .45s var(--ease) calc(.2s + var(--i, 0) * 40ms) both,
    crono-conta 10s linear calc(.8s + var(--i, 0) * 1s) infinite;
}
.row.open .peca-crono li:nth-child(2)  { --i: 1; }
.row.open .peca-crono li:nth-child(3)  { --i: 2; }
.row.open .peca-crono li:nth-child(4)  { --i: 3; }
.row.open .peca-crono li:nth-child(5)  { --i: 4; }
.row.open .peca-crono li:nth-child(6)  { --i: 5; }
.row.open .peca-crono li:nth-child(7)  { --i: 6; }
.row.open .peca-crono li:nth-child(8)  { --i: 7; }
.row.open .peca-crono li:nth-child(9)  { --i: 8; }
.row.open .peca-crono li:nth-child(10) { --i: 9; }
/* contagem regressiva de verdade: com o painel aberto, cada casa acende
   por exatamente 1 segundo, da 10 até a 01, em ciclo de 10s — o formato
   do programa rodando na peça. Mesmo keyframe para todas as casas; quem
   conta é o delay (1s por casa). O algarismo acompanha a própria casa.
   linear com corte em 10%/12%: relógio não desliza, salta. Enquanto a
   animação roda ela pinta por cima do accent estático da última casa;
   com menos-movimento tudo volta ao quadro parado de antes. */
@keyframes crono-conta {
  0%   { background: var(--accent); }
  10%  { background: var(--accent); }
  12%  { background: #231e31; }
  100% { background: #231e31; }
}
@keyframes crono-num {
  0%   { color: var(--accent); }
  10%  { color: var(--accent); }
  12%  { color: #4a4455; }
  100% { color: #4a4455; }
}
.row.open .peca-crono li span {
  animation: crono-num 10s linear calc(.8s + var(--i, 0) * 1s) infinite;
}

/* aqui o --i vem do HTML, uma por barra: com 46 barras, uma tabela de
   nth-child custaria mais que o atributo inline.

   Depois de pousar, a onda CONTINUA respirando (pedido do dono: onda
   sonora não fica estática): cada barra oscila em scaleY como medidor de
   áudio caindo do pico e voltando. Só para baixo (escala ≤ 1) — acima
   de 1 a barra de 96% estouraria a régua. A fase de partida é igual para
   todas, mas a DURAÇÃO varia por barra (--i), então elas dessincronizam
   sozinhas ciclo a ciclo, como escuta de verdade — sem random, loop
   determinístico e compositor-only. O delay do laço é o fim exato da
   varredura de entrada daquela barra, e sem fill-mode ele não pisa na
   animação de pouso enquanto espera: pouso termina em escala 1, laço
   começa em escala 1, emenda invisível. Vem por último na lista de
   propósito — animação posterior vence o transform enquanto ativa. */
.row.open .peca-onda i {
  transform-origin: center;
  animation:
    peca-onda .45s var(--ease) calc(.2s + var(--i, 0) * 7ms) both,
    onda-viva calc(1.1s + var(--i, 0) * 29ms) ease-in-out calc(.65s + var(--i, 0) * 7ms) infinite alternate,
    onda-toca 7s linear calc(.65s + var(--i, 0) * .152s) infinite;
}
@keyframes onda-viva {
  from { transform: scaleY(1); }
  to   { transform: scaleY(.58); }
}
/* playhead: um trecho aceso ATRAVESSA a onda da esquerda para a direita,
   como reprodução em andamento — o mesmo conceito do timecode do rail.
   Cada barra acende quando a "agulha" passa: mesmo keyframe para todas,
   o que estagia é o delay (.152s por barra ≈ 7s ÷ 46). Anima background,
   que é paint e não compositor — em 46 barras minúsculas o repaint é
   barato, medido sem queda. A cauda de 12% dá o rastro de decaimento. */
@keyframes onda-toca {
  0%   { background: var(--accent); }
  4%   { background: var(--accent); }
  16%  { background: #2f2942; }
  100% { background: #2f2942; }
}

/* hover: a imagem respira dentro da moldura, que continua parada */
.peca .sheet a, .peca-modulos .mod { overflow: hidden; }
.peca .sheet a img, .peca-modulos .mod > img {
  transition: transform .5s var(--ease), box-shadow .35s var(--ease), filter .35s var(--ease);
}
.peca .sheet a:hover img, .peca .sheet a:focus-visible img { transform: scale(1.035); }
.peca-modulos li:hover .mod > img { transform: scale(1.04); }
/* a seta do botão sai na direção que promete */
.peca-cta span[aria-hidden] { transition: transform .2s var(--ease); }
.peca-cta:hover span[aria-hidden], .peca-cta:focus-visible span[aria-hidden] { transform: translate(3px, -3px); }

@media (prefers-reduced-motion: reduce) {
  .peca-cta { transition: none; }
  .peca-cta:hover, .peca-cta:focus-visible { transform: none; }
  /* quem pediu menos movimento recebe a peça inteira parada, já montada */
  .row.open .peca-topo, .row.open .peca h3, .row.open .peca-indice,
  .row.open .peca-texto > *, .row.open .peca-rodape,
  .row.open .peca :is(.sheet figure, .peca-modulos li, .peca-marcas li, .peca-frentes li, .peca-fluxo li, .entrega),
  .row.open .peca .sheet a img, .row.open .peca-modulos .mod > img,
  .row.open .peca-crono i, .row.open .peca-crono li span,
  .row.open .peca-fluxo .no, .row.open .peca-fluxo li:not(:last-child)::after,
  .row.open .peca-fluxo span:not(.no),
  .row.open .entrega-fila b, .row.open .entrega-logo::after, .row.open .entrega::after,
  .row.open .peca-onda i { animation: none; }
  /* o fio do topo não anima, mas continua desenhado: sem isto ele fica em
     scaleX(0) e a peça perde a moldura de acento */
  .row.open .peca::after { animation: none; transform: none; opacity: .5; }
  .peca .sheet a img, .peca-modulos .mod > img,
  .peca-cta span[aria-hidden] { transition: none; }
  .peca .sheet a:hover img, .peca .sheet a:focus-visible img,
  .peca-modulos li:hover .mod > img { transform: none; }
}

/* material: hyperlinks do trabalho, inline no corpo do texto (estilo do
   CV em PDF). Azul deliberado: não é cor de trilha, é convenção de link. */
.panel-inner .plink {
  color: #4d9fff; font-style: italic; text-decoration: underline;
  text-underline-offset: 2px; transition: color .2s var(--ease);
}
.panel-inner .plink:hover, .panel-inner .plink:focus-visible { color: #8ec2ff; }

/* ---------- sala de projeção ----------
   O pôster não abre um card flutuando por cima da página: apaga a casa. Como
   o site já é preto absoluto, um ::backdrop também preto não lê como "camada
   cinza por cima", lê como "a luz baixou". Sobram o retângulo do vídeo e três
   linhas tipográficas alinhadas à mesma largura dele.
   <dialog> nativo de propósito: ele entrega de fábrica prisão de foco real,
   resto do documento inerte, Esc e top layer sem disputa de z-index com
   .rail (30), .top (30), .grain (40) e .skip (50). */
.proj {
  width: 100%; max-width: 100%; height: 100%; max-height: 100dvh;
  margin: 0; padding: 0; border: 0; overflow: hidden;
  /* clip por cima de hidden: o grain da película transborda -25% e hidden
     ainda deixaria a sala rolar por foco ou script; clip proíbe rolagem de
     vez. Navegador sem clip ignora a linha e fica no hidden de sempre. */
  overflow: clip;
  background: var(--bg); color: var(--ink);
}
/* #000 literal e não var(--bg): herança de custom property no ::backdrop
   chegou tarde em alguns motores, e um backdrop cinza padrão estragaria
   exatamente o efeito de apagar a luz. */
.proj::backdrop { background: #000; }

.proj-room {
  --proj-chrome: 11rem;                 /* cabeça + rodapé + respiros */
  --proj-w: min(100%, calc((100dvh - var(--proj-chrome)) * 16 / 9), 1240px);
  height: 100%; display: flex; flex-direction: column;
  padding: clamp(1rem, 3.5vw, 2.6rem) clamp(1rem, 4vw, 3rem) clamp(1rem, 3vw, 2.2rem);
}
.proj-room[data-shape="ig"] { --proj-w: min(100%, 400px); }
/* podcast: o embed do Spotify tem altura fixa e largura de card */
.proj-room[data-shape="som"] { --proj-w: min(100%, 720px); }

/* cabeça, palco e rodapé na MESMA largura do retângulo: sangria vertical
   única, do jeito que o resto do site alinha. Nada encosta na tela, porque em
   sala de projeção não há nada ao lado da tela. */
.proj-head, .proj-stage, .proj-foot { width: var(--proj-w); margin-inline: auto; }

.proj-head {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding-bottom: .6rem; border-bottom: 1px solid var(--line);
}
.proj-meta {
  font-family: var(--mono); font-size: .62rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--dim);
}
.proj-idx { color: var(--accent); }     /* número de seção: uso sancionado do acento */
.proj-bar { margin: 0 .55em; color: var(--line); }

.proj-x {
  appearance: none; background: none; border: 0; cursor: pointer;
  min-width: 44px; min-height: 44px; margin-right: -.7rem;
  display: grid; place-items: center;
  font-family: var(--mono); font-size: 1.45rem; line-height: 1; color: var(--dim);
  transition: color .25s var(--ease);
}
.proj-x:hover, .proj-x:focus-visible { color: var(--accent); }

.proj-stage { flex: 1 1 auto; min-height: 0; display: grid; place-items: center; }

/* ---- índice de episódios ----
   Quando o pôster é uma playlist, a sala ganha o rolo daquela produção ao
   lado da tela. Não é widget de player: é o MESMO índice tipográfico que o
   site inteiro usa na seção de Trabalho, número em mono e nome em caixa
   alta, só que em escala de sala. Por isso ele lê como parte da casa e não
   como carrossel de template.
   Os títulos vêm do bloco <script type="application/json" id="proj-listas">
   assado no HTML, então nenhuma chamada de API roda em tempo de execução. */
.proj-room[data-shape="list"] {
  --proj-w: min(100%, calc((100dvh - var(--proj-chrome)) * 16 / 9 + 17rem), 1400px);
}
/* justify-items e align-items separados de propósito: place-items depois de
   align-items reescreveria os dois e esticaria o quadro na vertical, o que
   quebra o aspect-ratio 16/9 do vídeo (medido: virava 1.74). */
.proj-room[data-shape="list"] .proj-stage {
  grid-template-columns: minmax(0, 1fr) 15rem;
  gap: 1.6rem; justify-items: stretch; align-items: start;
}
/* a lista acompanha a altura do vídeo e rola por dentro se passar disso */
.proj-list { min-width: 0; overflow-y: auto; max-height: 100%; }
.proj-list[hidden] { display: none; }
.proj-list-cab {
  font-family: var(--mono); font-size: .58rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--dim);
  padding-bottom: .6rem; border-bottom: 1px solid var(--line);
}
.proj-list ol { list-style: none; margin: 0; padding: 0; }
.proj-faixa {
  appearance: none; background: none; border: 0; width: 100%; cursor: pointer;
  display: grid; grid-template-columns: 2.1rem 1fr; gap: .7rem;
  align-items: baseline; text-align: left;
  padding: .7rem 0; border-bottom: 1px solid var(--line);
  color: var(--dim); transition: color .25s var(--ease);
}
.proj-faixa .n { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; }
.proj-faixa .t {
  font-family: var(--body); font-size: .78rem; line-height: 1.35;
  color: inherit; overflow-wrap: anywhere;
}
.proj-faixa:hover, .proj-faixa:focus-visible { color: var(--ink); }
/* a faixa em exibição é estado ativo, um dos usos sancionados do acento */
.proj-faixa[aria-current="true"] { color: var(--accent); }
.proj-faixa[aria-current="true"] .n::after {
  content: ""; display: block; height: 1px; margin-top: .35rem; background: var(--accent);
}
.proj-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.proj-room[data-shape="ig"] .proj-frame { aspect-ratio: auto; height: min(70dvh, 720px); }
.proj-room[data-shape="som"] .proj-frame { aspect-ratio: auto; height: 352px; }
/* fio interno via ::after e não box-shadow inset no próprio quadro: o iframe
   pinta por cima do inset e o fio sumiria */
.proj-frame::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);
}
.proj-slot, .proj-slot iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block;
}

.proj-foot {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: .6rem 1.5rem; flex-wrap: wrap; padding-top: .75rem; border-top: 1px solid var(--line);
}
.proj-title {
  font-family: var(--display); font-size: clamp(1.1rem, 3.2vw, 2.1rem);
  line-height: 1; text-transform: uppercase; color: var(--ink);
}
.proj-exit { display: flex; align-items: center; gap: 1.2rem; flex-wrap: wrap; }
.proj-out, .proj-esc, .proj-blind {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--dim); text-decoration: none;
}
.proj-out { transition: color .25s var(--ease); }
.proj-out:hover, .proj-out:focus-visible { color: var(--accent); }
.proj-esc { opacity: .55; }
.proj-blind[hidden], .proj-nav[hidden] { display: none; }

/* ROLO: avança entre as produções da MESMA folha de contato, pelo rodapé. */
.proj-nav { display: inline-flex; align-items: center; gap: .3rem; }
.proj-step {
  appearance: none; background: none; border: 0; cursor: pointer;
  min-width: 44px; min-height: 44px; display: grid; place-items: center;
  font-family: var(--mono); font-size: .95rem; line-height: 1; color: var(--dim);
  transition: color .25s var(--ease);
}
.proj-prev::before { content: "\2190"; }
.proj-next::before { content: "\2192"; }
.proj-step[disabled] { opacity: .22; cursor: default; }
.proj-step:not([disabled]):hover, .proj-step:focus-visible { color: var(--accent); }
/* botão de mesa: o press acende seco e apaga na transition que cada controle
   já tem. Cor apenas, nada se move sob o dedo — cobre o vazio enquanto o
   iframe novo nasce. O :not([aria-current]) é obrigatório: sem ele esta
   regra empataria em especificidade com a da faixa em exibição e venceria
   por vir depois — o press apagaria o violeta do episódio atual, que é
   estado, não feedback. */
.proj-x:active, .proj-out:active, .proj-step:not([disabled]):active { color: var(--accent); transition-duration: 0s; }
.proj-faixa:not([aria-current="true"]):active { color: var(--ink); transition-duration: 0s; }
.proj-count {
  font-family: var(--mono); font-size: .6rem; letter-spacing: .14em; color: var(--dim);
  font-variant-numeric: tabular-nums; min-width: 4.4em; text-align: center;
}

/* abertura em três tempos: a luz baixa, a tela abre de uma fresta, os rótulos
   entram. clip-path e não scale: escalar iframe borra o vídeo durante a
   animação, clip-path só recorta. */
@keyframes proj-luz   { from { opacity: 0; } to { opacity: 1; } }
@keyframes proj-abre  { from { clip-path: inset(50% 0 50% 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes proj-letra { from { opacity: 0; transform: translateY(.4rem); } to { opacity: 1; transform: none; } }

.proj[open]             { animation: proj-luz .2s linear both; }
.proj[open]::backdrop   { animation: proj-luz .2s linear both; }
.proj[open] .proj-frame { animation: proj-abre .42s var(--ease) .12s both; }
.proj[open] .proj-head,
.proj[open] .proj-foot  { animation: proj-letra .34s var(--ease) .3s both; }

/* película viva: com a sala aberta, o grain salta como emulsão sob luz de
   projetor — 5 posições por ciclo de .45s (um salto a cada 90ms, cadência de
   grão de filme; mais lento que isso lê como tique). Preso a [open]: nada
   anima em repouso, e o grain global da casa continua parado. O absolute
   resolve contra o dialog e o overflow:hidden dele clipa o transbordo de
   -25%; z-index 40 explícito mantém a película por cima do vídeo e o
   pointer-events:none herdado de .grain deixa os cliques passarem. steps(1)
   segura cada posição inteira e corta para a próxima: salto, não deslize. */
.proj .grain { position: absolute; inset: -25%; opacity: .04; z-index: 40; }
.proj[open] .grain { animation: pelicula .45s steps(1, end) infinite; }
@keyframes pelicula {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-2.1%, 1.4%); }
  40%  { transform: translate(1.6%, -2.4%); }
  60%  { transform: translate(-1.2%, -1.7%); }
  80%  { transform: translate(2.3%, .9%); }
}

/* troca de produção é CORTE SECO: o iframe some e nasce, sem transição. Só os
   rótulos re-entram, para a mudança registrar. Corte seco é a gramática da
   casa: o próprio CSS chama os blocos de "corte". */
.proj.is-cut .proj-head, .proj.is-cut .proj-foot { animation: proj-letra .2s var(--ease) both; }

/* FECHAMENTO. .proj.is-closing (0,2,0) empata em especificidade com
   .proj[open] (0,2,0) e só vence por vir depois no arquivo. Não reordenar
   estes dois blocos, senão a sala fecha sem animar e o animationend não
   dispara no elemento esperado. */
.proj.is-closing             { animation: proj-luz .16s linear .16s reverse both; }
.proj.is-closing::backdrop   { animation: proj-luz .16s linear .16s reverse both; }
.proj.is-closing .proj-frame { animation: proj-abre .22s var(--ease) reverse both; }
.proj.is-closing .proj-head,
.proj.is-closing .proj-foot  { animation: proj-letra .12s var(--ease) reverse both; }

@media (max-width: 640px) {
  .proj-room { --proj-chrome: 9.5rem; padding: .9rem .9rem 1rem; }
  /* no celular não há coluna ao lado: o índice desce para baixo da tela e
     ganha rolagem própria, com altura limitada para o vídeo continuar sendo
     o assunto principal */
  .proj-room[data-shape="list"] { --proj-w: min(100%, 100%); }
  .proj-room[data-shape="list"] .proj-stage {
    grid-template-columns: minmax(0, 1fr); gap: .9rem; align-content: start;
  }
  .proj-room[data-shape="list"] .proj-list { max-height: 32dvh; }
  .proj-esc { display: none; }          /* sem teclado, sem dica de teclado */
  .proj-title { font-size: clamp(1rem, 5.2vw, 1.5rem); }
  .proj-exit { gap: .8rem; width: 100%; justify-content: space-between; }
  .proj-room[data-shape="ig"] .proj-frame { height: min(64dvh, 640px); }
}
/* celular deitado: sobra pouca altura, o retângulo manda */
@media (orientation: landscape) and (max-height: 520px) {
  .proj-room { --proj-chrome: 6rem; padding: .5rem .9rem .6rem; }
  .proj-head { padding-bottom: .35rem; }
  .proj-foot { padding-top: .4rem; }
  .proj-title { font-size: 1rem; }
  .proj-step { min-height: 34px; }
}

/* ---------- competências ---------- */
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: clamp(1.5rem, 4vw, 3.5rem); }
.col h3 {
  font-family: var(--mono); font-size: .72rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); padding-bottom: 1rem; margin-bottom: 1rem; border-bottom: 1px solid var(--line);
}
.col li { color: var(--dim); font-size: .95rem; padding: .35rem 0; }

/* ---------- contato ---------- */
.end { min-height: 46svh; display: flex; flex-direction: column; justify-content: center; padding: clamp(3rem, 5.5vh, 4.2rem) var(--edge); }
/* o fecho estava competindo com o nome no topo: 13vw punha "CONVERSAR"
   em 166px num 1280 contra os 153px do próprio nome. Baixado para 9.5vw
   com teto de 8.5rem — continua sendo o maior tipo da página depois do
   hero, e volta a ser fecho em vez de segunda entrada. */
.end-big { font-family: var(--display); font-size: clamp(2.4rem, 9.5vw, 8.5rem); line-height: .88; text-transform: uppercase; letter-spacing: -.02em; }
.end a.mail { color: var(--accent); text-decoration: none; border-bottom: 2px solid transparent; transition: border-color .3s var(--ease); }
.end a.mail:hover { border-color: var(--accent); }
.end-links { display: flex; gap: 2rem; flex-wrap: wrap; margin-top: clamp(2rem, 5vh, 3.5rem); font-family: var(--mono); font-size: .78rem; letter-spacing: .06em; }
.end-links a { color: var(--dim); text-decoration: none; transition: color .3s var(--ease); }
.end-links a:hover { color: var(--ink); }

.foot {
  display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem;
  padding: 2rem var(--edge); border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; color: var(--dim);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .cut > * { opacity: 1; transform: none; transition: none; }
  /* .row-out .row-name precisa da mesma especificidade da regra que liga a
     transição lá em cima, senão ela vence aqui e o movimento sobrevive */
  .row-name, .row-out .row-name { transition: none; }
  .row-out:hover .row-ext, .row-out:focus-within .row-ext { transform: none; }
  /* o fio de acento continua acendendo no hover, mas sem a transição */
  .sheet img { transition: none; }
  .hero-name.split .ch { transform: none; transition: none; }
  .hero-name .ln::before, .hero-name .ln::after { animation: none; opacity: 0; }
  .marcas-trilho { animation: none; }
  .marcas-faixa { -webkit-mask-image: none; mask-image: none; }
  /* timecode congelado é peso morto: some. O JS nem arma o módulo, esta
     regra cobre o caso de a preferência mudar depois do boot (a media query
     do CSS é viva; o snapshot do JS, não). */
  .tc-vivo .rail-tc { display: none; }
  /* bloco de carreira parado é a composição final: barras inteiras */
  .gantt.cut.in .g-barra { animation: none; }
  .marcas-trilho ul:last-child { display: none; }
  .marcas-trilho ul { flex-wrap: wrap; gap: .8rem 1.4rem; }
  /* a varredura de entrada some inteira; o peso permanente do "+" (Camada 1,
     fora deste bloco) continua de pé como única pista, sem movimento */
  :is(#trabalho, #work) .work.cut.in .row-name,
  :is(#trabalho, #work) .work.cut.in .row-plus,
  :is(#trabalho, #work) .work.cut.in .row::after { animation: none; }
  /* a faixa que atravessa no hover também para; o fio de estado do ::after
     continua acendendo, então o hover segue sinalizado, só sem movimento */
  .row:not(.row-out):hover::before { animation: none; opacity: 0; }
  /* a sala aparece e some sem transição alguma. O efeito sobrevive porque
     nunca dependeu do movimento, dependeu do preto: a luz não baixa, ela já
     está apagada. Atenção: com animation:none o animationend NUNCA dispara,
     por isso o JS consulta o MediaQueryList vivo antes de esperar por ele. */
  .proj, .proj::backdrop,
  .proj[open], .proj[open]::backdrop,
  .proj[open] .proj-frame, .proj[open] .proj-head, .proj[open] .proj-foot,
  .proj.is-cut .proj-head, .proj.is-cut .proj-foot,
  .proj.is-closing, .proj.is-closing::backdrop,
  .proj.is-closing .proj-frame, .proj.is-closing .proj-head,
  .proj.is-closing .proj-foot { animation: none; }
  /* a película volta a ser grain estático: o efeito da sala depende do
     preto, não do movimento */
  .proj[open] .grain { animation: none; }
  .proj-x, .proj-out, .proj-step { transition: none; }
}

/* ---------- entrada das outras seções ----------
   Trabalho já tinha vocabulário próprio (varredura na lista, peça viva ao
   abrir). O resto do site entrava com um fade-up só, todos os filhos ao
   mesmo tempo: honesto, mas sem hierarquia — quatro colunas de competência
   chegando juntas não dizem por onde começar a ler.

   Aqui cada bloco ganha ordem, nada mais. Nenhuma animação nova em laço,
   nenhum movimento em repouso: tudo continua pendurado no .cut.in que o
   IntersectionObserver de js/site.js já aplica uma vez só. O que muda é o
   atraso de cada filho, guardado em --d, e a duração, que cai de 800ms
   para 500–600ms porque agora existe escalonamento em cima.

   Este bloco fica DEPOIS do @media de prefers-reduced-motion acima, então
   ele venceria o `transition: none` de lá por ordem. O bloco de repetição
   no fim do arquivo é o que reata o desligamento — não remover. */

/* cabeçalho de seção: o número chega, o título logo atrás */
.head.cut > * {
  transition: opacity .5s var(--ease) var(--d, 0s), transform .5s var(--ease) var(--d, 0s);
}
.head.cut > h2 { --d: .07s; }

/* sobre: o segundo parágrafo é aparte, chega depois do principal */
.about.cut > * {
  transition: opacity .6s var(--ease) var(--d, 0s), transform .6s var(--ease) var(--d, 0s);
}
.about.cut > *:nth-child(2) { --d: .1s; }
.about.cut > *:nth-child(3) { --d: .2s; }
.gantt.cut > * { transition: opacity .55s var(--ease) var(--d, 0s), transform .55s var(--ease) var(--d, 0s); }
.gantt.cut > .g-eixo { --d: .05s; }
.gantt.cut > .g-plot { --d: .1s; }
.gantt.cut > .g-formacao { --d: .18s; }

/* competências: as colunas assentam da esquerda para a direita. As listas
   dentro NÃO escalonam: item entrando dentro de coluna entrando multiplica
   opacidade por opacidade e o que era ordem vira borrão. */
.cols.cut > * {
  transition: opacity .55s var(--ease) var(--d, 0s), transform .55s var(--ease) var(--d, 0s);
}
.cols.cut > *:nth-child(2) { --d: .08s; }
.cols.cut > *:nth-child(3) { --d: .16s; }
.cols.cut > *:nth-child(4) { --d: .24s; }

/* o fio de acento acende por cima da régua que JÁ existe sob o rótulo da
   coluna: nenhum traço novo no desenho, só o que estava lá ganhando cor por
   um instante. Mesmo gesto do fio da peça e do fio de estado da linha. */
.col h3 { position: relative; }
.col h3::after {
  content: ""; position: absolute; left: 0; bottom: -1px; height: 1px; width: 100%;
  background: linear-gradient(90deg, var(--accent), transparent);
  transform: scaleX(0); transform-origin: left; opacity: 0; pointer-events: none;
}
.cols.cut.in .col h3::after {
  animation: sec-fio .8s var(--ease) forwards;
  animation-delay: calc(var(--d, 0s) + .18s);
}
@keyframes sec-fio {
  from { transform: scaleX(0); opacity: 1; }
  to { transform: scaleX(1); opacity: .55; }
}

/* escrita: as três linhas em fila. Continua sem a varredura de Trabalho de
   propósito — aqui sublinhado e seta já ficam visíveis o tempo todo, o
   reforço seria redundante. */
:is(#escrita, #writing) .work.cut > * {
  transition: opacity .55s var(--ease) var(--d, 0s), transform .55s var(--ease) var(--d, 0s);
}
:is(#escrita, #writing) .work.cut > *:nth-child(2) { --d: .09s; }
:is(#escrita, #writing) .work.cut > *:nth-child(3) { --d: .18s; }

/* contato: o único lugar do site com entrada por máscara. É o fecho e é o
   endereço de e-mail — se existe um gesto grande para gastar, é aqui.

   O padding com margem negativa alarga a caixa de recorte sem mexer no
   layout: com line-height .85 o topo do Anton passa da linha e seria
   decepado pelo overflow. O título não recebe o fade do .cut, senão a
   máscara e a opacidade se somariam e o gesto viraria névoa. */
.end.cut > .end-big { opacity: 1; transform: none; transition: none; }
.end-ln { display: block; overflow: hidden; padding: .12em 0; margin: -.12em 0; }
.js .end-ln > span { transform: translateY(105%); }
.end-ln > span {
  display: block; transition: transform .75s var(--ease) var(--d, 0s);
}
.end-ln:nth-child(2) > span { --d: .09s; }
.end.cut.in .end-ln > span { transform: none; }
.end.cut > .end-links {
  transition: opacity .6s var(--ease) .3s, transform .6s var(--ease) .3s;
}

@media (prefers-reduced-motion: reduce) {
  .head.cut > *, .about.cut > *, .cols.cut > *,
  :is(#escrita, #writing) .work.cut > *,
  .end.cut > .end-links { transition: none; }
  /* o fio da coluna não anima, mas continua desenhado: sem isto ele fica em
     scaleX(0) e a régua perde o acento */
  .cols.cut.in .col h3::after { animation: none; transform: none; opacity: .55; }
  .end-ln { overflow: visible; }
  /* .js na frente para bater a especificidade da regra que empurra a linha
     para fora da caixa; sem isso o fecho ficaria escondido para sempre */
  .js .end-ln > span { transform: none; transition: none; }
}
