/* ==========================================================================
   LISTA COM IMAGEM FLUTUANTE — prefixo peek-
   Seis linhas grandes (número, título display, categoria e seta) e uma foto
   que acompanha o cursor com atraso, inclinando conforme a velocidade da mão.

   Tokens usados:
     --display --ease      tipografia do título e curva única do projeto
     --fg --fg-2           título aceso (linha ativa) e apagado (repouso)
     --fg-muted            número da linha
     --accent              número e seta da linha ativa
     --rule                filete entre as linhas e fundo da foto carregando
     --radius-media        canto da foto flutuante e da miniatura

   A SEÇÃO É SÓ .wrap (contrato §4). Nada aqui sangra: a lista vive na coluna
   e a foto é `position:fixed`, ou seja, mora na JANELA e não na coluna. Não
   há inset, não há margem negativa e não há 100vw.

   TRÊS CAMADAS NA FOTO, e a divisão é o componente inteiro:

     .peek-float      só POSIÇÃO. transform reescrito a cada quadro, sem
                      transition nenhuma.
     .peek-float-box  só ENTRADA. opacidade, blur e escala, com transição.
     .peek-float-img  só a TROCA. duas imagens em fusão cruzada.

   Por que separar. `transform` é UMA propriedade: uma transition nela
   alcançaria o translate escrito por quadro, e o navegador passaria a
   interpolar entre duas posições que já são interpolação — a foto ficaria
   atrás da mão em degraus de meio segundo, com a curva do --ease empilhada
   sobre o lerp do script. Também não dá para escapar disso com a propriedade
   `scale` individual: ela é aplicada ANTES do `transform` na conta da matriz
   (Transforms 2, §Individual Transform Properties), e o resultado é
   S·T — a translação inteira sai multiplicada por 0,9 enquanto a escala
   sobe, o que num deslocamento de 1200px desloca a foto 120px. Por isso a
   escala mora na caixa de dentro, onde não há o que escalar além dela mesma.
   ========================================================================== */

.peek{
  /* Largura da foto flutuante. A altura sai do aspect-ratio 3/4 — foto de
     retrato, que é o recorte dos placeholders e o que cabe ao lado de uma
     lista alta sem cobrir duas linhas de uma vez. */
  --peek-w:clamp(230px,19vw,310px);

  /* Deslocamento da foto EM RELAÇÃO AO CURSOR. O x é px; o y é porcentagem
     da própria foto, e -50% é o que a centra verticalmente na ponta do
     ponteiro. Trocar por 0 encosta o topo da foto no cursor. */
  --peek-dx:24px;
  --peek-dy:-50%;

  /* Margem mínima até a borda da janela, em px. Lida pelo script, que
     limita a posição antes de escrevê-la. Só px — o script não resolve
     porcentagem aqui. */
  --peek-edge:16px;

  /* Fração do vão que a foto vence por quadro a 60Hz. O script converte
     para a taxa real da tela, então 120Hz não anda o dobro. Perto de 1 a
     foto cola no cursor (e o efeito some); abaixo de .08 ela parece
     arrastada por um elástico. */
  --peek-lerp:.16;

  /* Inclinação máxima, em GRAUS. Lida pelo script. 0 desliga a inclinação
     sem tocar no resto. */
  --peek-tilt:8;

  --peek-row-pad:clamp(18px,2.6vw,30px);  /* respiro vertical da linha */
  --peek-dim:.35;                         /* opacidade das linhas não ativas */
  --peek-dur:.35s;                        /* troca de estado da linha */
}

/* ---------------------------------------------------------------- a lista */
/* O filete de cima é do item e o de baixo é da lista: assim a última linha
   fecha sem precisar de :last-child, e acrescentar um <li> não exige mexer
   em regra nenhuma. */
.peek-list{
  margin-top:clamp(34px,5vh,56px);
  border-bottom:1px solid var(--rule);
}
.peek-item{border-top:1px solid var(--rule)}

.peek-row{
  display:flex;align-items:center;
  gap:clamp(14px,2.2vw,30px);
  padding:var(--peek-row-pad) 0;
  transition:opacity var(--peek-dur) var(--ease);
}

/* .is-hovering diz "uma linha está em foco de atenção" — vem do ponteiro ou
   do teclado, e o script põe nos dois casos. O nome guarda a origem do
   padrão; o papel é mais largo que ele. */
.peek-list.is-hovering .peek-row{opacity:var(--peek-dim)}
.peek-list.is-hovering .peek-row.is-active{opacity:1}

.peek-num{
  flex:0 0 auto;
  font-size:11px;letter-spacing:.18em;
  color:var(--fg-muted);
  transition:color var(--peek-dur) var(--ease);
}
.peek-row.is-active .peek-num{color:var(--accent)}

/* O título nasce em --fg-2 e acende em --fg. Seis títulos em corpo grande
   e tinta cheia de uma vez fazem a dobra gritar; e sem o degrau não haveria
   de onde "acender". A opacidade das outras linhas é o segundo sinal, não o
   primeiro. */
.peek-title{
  flex:1 1 auto;min-width:0;
  font-family:var(--display);font-weight:700;
  font-size:clamp(26px,3.4vw,52px);
  line-height:1.08;letter-spacing:-.022em;
  color:var(--fg-2);
  transition:color var(--peek-dur) var(--ease);
}
.peek-row.is-active .peek-title{color:var(--fg)}

/* Reaproveita a .pill do core: forma, respiro e tracking vêm de lá e nada
   aqui os sobrescreve — só o comportamento de caixa dentro da linha. */
.peek-cat{flex:0 0 auto}

/* MINIATURA DO TOQUE. Ela é o desenho da versão sem ponteiro, não um
   fallback: onde há hover, quem mostra a foto é a camada flutuante, e as
   duas juntas seriam a mesma imagem duas vezes na mesma linha. */
.peek-thumb{
  flex:0 0 auto;
  width:64px;height:80px;
  object-fit:cover;
  border-radius:var(--radius-media);
  background:var(--rule);
}
@media(hover:hover){
  .peek-thumb{display:none}
}

.peek-arrow{
  flex:0 0 auto;
  color:var(--accent);
  opacity:0;transform:translateX(-10px);
  transition:opacity var(--peek-dur) var(--ease),transform var(--peek-dur) var(--ease);
}
.peek-row.is-active .peek-arrow{opacity:1;transform:none}
@media(prefers-reduced-motion:reduce){
  .peek-arrow{transform:none;transition:opacity var(--peek-dur) linear}
  .peek-row.is-active .peek-arrow{transform:none}
}

/* ================================================================ a foto */
/* CAMADA DE POSIÇÃO. Só transform, e nenhuma transition — ver o cabeçalho.

   position:fixed porque a referência é a JANELA: as coordenadas que chegam
   do ponteiro são clientX/clientY, que já são de janela. Com `absolute`
   seria preciso somar o scroll a cada quadro e a foto atrasaria durante a
   rolagem. O preço é conhecido: fixed vira absolute dentro de qualquer
   ancestral com transform, filter, perspective, contain:paint ou
   will-change:transform. Por isso o script pendura esta div como filha
   DIRETA da <section> — o core dá position:relative a toda seção e nada
   mais, então não há ancestral que a capture. Se alguém puser data-rv na
   seção, o filter:blur(14px) do reveal quebra o fixed até o .in chegar.

   will-change:transform mantém a foto numa camada própria do compositor: o
   transform muda a cada quadro, que é exatamente o caso para o qual a
   propriedade existe. O custo é uma textura de ~310×413 (≈510 KB de memória
   de GPU) enquanto a dobra existe. Foi medido como o preço mais barato dos
   dois: sem ela, cada quadro repinta a foto.

   pointer-events:none é obrigatório: uma foto de 310px seguindo o cursor
   ficaria SOB o ponteiro o tempo todo, roubaria o hover da linha e a lista
   piscaria entre ativa e inativa a cada quadro. */
.peek-float{
  position:fixed;left:0;top:0;z-index:5;
  pointer-events:none;
  width:var(--peek-w,300px);
  aspect-ratio:3/4;
  will-change:transform;
  transform:translate3d(var(--peek-x,0px),var(--peek-y,0px),0)
            translate(var(--peek-dx,24px),var(--peek-dy,-50%))
            rotate(var(--peek-rot,0deg));
}

/* CAMADA DE ENTRADA. Nada entra seco (§5): opacidade, blur e escala juntos.
   O blur só corre nos 0,5s de entrada e de saída — em regime, a foto está
   em blur(0) e o custo por quadro é o de um transform puro.
   visibility tira a caixa da pintura em repouso e volta sem atraso na
   entrada; é o que garante que uma foto invisível não custe nada enquanto
   ninguém passa o mouse na dobra. */
.peek-float-box{
  position:absolute;inset:0;
  border-radius:var(--radius-media);
  overflow:hidden;
  background:var(--rule);
  opacity:0;visibility:hidden;
  filter:blur(14px);
  transform:scale(.9);
  transition:opacity .5s var(--ease),filter .5s var(--ease),
             transform .5s var(--ease),visibility 0s linear .5s;
}
.peek-float.is-on .peek-float-box{
  opacity:1;visibility:visible;
  filter:blur(0);
  transform:none;
  transition:opacity .5s var(--ease),filter .5s var(--ease),
             transform .5s var(--ease),visibility 0s linear 0s;
}
/* Com movimento reduzido a foto não escala nem desfoca: sobra a opacidade,
   que não é deslocamento. O script já a ancora na linha em vez de fazê-la
   perseguir a mão — ver o gotcha da ficha. */
@media(prefers-reduced-motion:reduce){
  .peek-float-box{
    filter:none;transform:none;
    transition:opacity .2s linear,visibility 0s linear .2s;
  }
  .peek-float.is-on .peek-float-box{
    filter:none;transform:none;
    transition:opacity .2s linear,visibility 0s linear 0s;
  }
}

/* DUAS IMAGENS, e não uma com o src trocado. Reescrever o src de um <img>
   já pintado apaga o quadro atual antes de o novo decodificar: a troca de
   linha pisca em branco, e piscada é o oposto do que a fusão quer dizer.
   Com duas, o script escreve o src na que está apagada, espera o load e só
   então troca qual das duas está acesa. */
.peek-float-img{
  position:absolute;inset:0;
  width:100%;height:100%;
  object-fit:cover;
  opacity:0;
  transition:opacity .28s var(--ease);
}
.peek-float-img.is-shown{opacity:1}
@media(prefers-reduced-motion:reduce){
  .peek-float-img{transition:none}
}

/* ---------------------------------------------------------------- toque */
/* Sem ponteiro não existe seguidor: o script sai no guard e a foto passa a
   ser a miniatura da linha, que aqui nasce visível. A seta some porque ela
   é sinal de hover, e sem hover ficaria acesa em nada. */
@media(hover:none){
  .peek-arrow{display:none}
  .peek-float{display:none}
  .peek-title{font-size:clamp(19px,4.4vw,26px)}
}

/* ---------------------------------------------------------------- mobile */
/* A linha vira grade de duas faixas: o número ocupa as duas, o título fica
   em cima e a categoria embaixo dele. Em flex, empurrar a categoria para a
   linha de baixo exigiria flex-basis:100% no título, o que também jogaria a
   miniatura para uma terceira linha. A grade resolve os três de uma vez. */
@media(max-width:760px){
  .peek-row{
    display:grid;
    grid-template-columns:auto minmax(0,1fr) auto;
    grid-template-areas:"num title media"
                        "num cat   media";
    align-items:center;
    column-gap:14px;row-gap:9px;
  }
  .peek-num{grid-area:num}
  .peek-title{grid-area:title;font-size:clamp(22px,6vw,30px)}
  .peek-cat{grid-area:cat;justify-self:start}
  /* MINIATURA E SETA DIVIDEM A MESMA CÉLULA. Uma coluna para cada parecia
     mais arrumado e cobrava caro: a que estivesse escondida deixava o
     column-gap dela para trás — 14px de vão vazio colado na borda direita,
     medido em 390px. Dividir a célula é seguro porque o feature `hover` vale
     sempre `hover` ou `none`, nunca os dois: onde a seta aparece, a
     miniatura está em display:none, e vice-versa. */
  .peek-thumb{grid-area:media}
  .peek-arrow{grid-area:media;justify-self:end}
}

@media(max-width:420px){
  .peek{--peek-row-pad:16px}
}
