← Biblioteca componentes/midia/case-ficha-flutuante/ficha.md CódigoTela cheia
midia/case-ficha-flutuante

Case com ficha flutuante

🟢 estavel complexidade média prefixo cff- cases portfolio foto video loop ficha legenda-na-foto link prova-social institucional

Incluir · as linhas que se colam num site novo, depois do core (CLAUDE.md §10)

<link rel="stylesheet" href="componentes/midia/case-ficha-flutuante/estilo.css">
<script src="componentes/midia/case-ficha-flutuante/script.js"></script>

markup.html · 147 linhas · 7,9 KB

<!-- ==========================================================================
     CASE COM FICHA FLUTUANTE — um caso por linha, a ficha pousada na mídia
     Prefixo: cff-
     Requer: core (tokens, base, motion.css, motion.js) + este estilo.css.
             O script.js só é preciso na variação com vídeo em loop.

     A IDEIA. Cada caso ocupa a coluna inteira: uma mídia larga — foto ou um
     loop de vídeo mudo — e, pousada no canto de baixo dela, uma ficha clara
     com o tema, UMA frase sobre o trabalho, quem era o cliente e as frentes
     envolvidas. A frase é a peça: ela diz o que mudou, e o resto da ficha
     só situa. É a grade de cases (midia/grade-cases-foto) com um caso de
     cada vez e espaço para dizer alguma coisa sobre ele.

     A FICHA DECLARA O PRÓPRIO TOM no markup (.sec-light), em vez de o
     componente escrever cor: é um bloco claro dentro de uma dobra escura, a
     exceção nomeada do §3. Trocar por .sec-paper ou .sec-deep troca a ficha
     inteira, sem tocar no CSS.

     O CASO INTEIRO É CLICÁVEL, MAS O LINK É O TÍTULO. O <a> mora dentro do
     <h3> (o sumário de cabeçalhos do leitor de tela continua com os casos),
     e um ::after dele se estica sobre o <article>. É o padrão do
     prova/manchetes-imprensa, e é o que permite o botão de pausa da
     variação com vídeo existir: um <button> dentro de um <a> é HTML
     inválido, e aqui o botão é irmão do link, acima do ::after.

     O CONTEÚDO É DE ASSESSORIA, e por isso a frase fala do TRABALHO, nunca
     de rentabilidade, e o cliente não tem nome — tem um retrato em cinco
     palavras. A .nota no fim é parte da peça. Ver Compliance na ficha.
     ========================================================================== -->

<section class="sec-dark sec-pad cff" data-cff>
  <div class="wrap">

    <div class="cff-cabeca">
      <span class="eyebrow" data-rv>Casos</span>
      <h2 class="sec-title ttl">O trabalho, <em>caso a caso</em></h2>
      <p class="sec-lead" data-rv data-d="1">Três situações que chegaram à primeira conversa e o que foi feito com cada uma. Os clientes autorizaram a descrição, sem identificação.</p>
    </div>

    <!-- <ol>: a ordem é escolha editorial (o caso mais forte primeiro), e
         role="list" devolve a semântica que o reset do core apaga no Safari
         (MANUAL §6, armadilha 4). O data-rv vai no <li>, e não no <article>:
         o reveal termina em filter:blur(0), que continua sendo filtro, e o
         <article> é o contexto do link esticado e do botão de pausa — fica
         limpo de qualquer coisa que o core escreva (MANUAL §5). -->
    <ol class="cff-lista" role="list">

      <li class="cff-item" data-rv>
        <article class="cff-caso">
          <div class="cff-midia">
            <img class="cff-img" src="../../../showroom/demo/ph-wide-2.webp"
                 alt="" width="1600" height="900" loading="lazy" decoding="async">
          </div>
          <div class="cff-ficha sec-light">
            <p class="cff-olho">Sucessão familiar</p>
            <h3 class="cff-titulo">
              <a class="cff-link" href="#caso-sucessao">Três empresas e duas gerações passaram a decidir <em>pelo mesmo protocolo</em></a>
            </h3>
            <p class="cff-quem">Família do agronegócio, terceira geração</p>
            <ul class="cff-tags" role="list">
              <li class="pill">Holding</li>
              <li class="pill">Acordo de sócios</li>
              <li class="pill">Política de investimento</li>
            </ul>
            <span class="cff-ver" aria-hidden="true">
              Ver o caso
              <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 7H12M8 3L12 7L8 11"/></svg>
            </span>
          </div>
        </article>
      </li>

      <li class="cff-item" data-rv>
        <article class="cff-caso">
          <div class="cff-midia">
            <img class="cff-img" src="../../../showroom/demo/ph-wide-5.webp"
                 alt="" width="1600" height="900" loading="lazy" decoding="async">
          </div>
          <div class="cff-ficha sec-light">
            <p class="cff-olho">Liquidez</p>
            <h3 class="cff-titulo">
              <a class="cff-link" href="#caso-liquidez">A venda de uma participação virou <em>renda para vinte anos</em>, com regra escrita</a>
            </h3>
            <p class="cff-quem">Empresária, saída de sociedade</p>
            <ul class="cff-tags" role="list">
              <li class="pill">Planejamento de liquidez</li>
              <li class="pill">Tributação</li>
            </ul>
            <span class="cff-ver" aria-hidden="true">
              Ver o caso
              <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 7H12M8 3L12 7L8 11"/></svg>
            </span>
          </div>
        </article>
      </li>

      <li class="cff-item" data-rv>
        <!-- VARIAÇÃO COM VÍDEO EM LOOP: troque o <img> por este bloco. O
             vídeo só toca quando está na tela, mudo e em loop, e o botão de
             pausa (WCAG 2.2.2) nasce [hidden] — quem o mostra é o script, e
             só se houver arquivo para tocar. Sem `src` nem <source>, fica o
             pôster, que é o estado deste preview.
               <div class="cff-midia">
                 <video class="cff-video" data-cff-video muted loop playsinline
                        preload="none" poster="/midia/caso-internacional.webp">
                   <source src="/midia/caso-internacional.mp4" type="video/mp4">
                 </video>
               </div>
               <button class="btn-disc btn-play cff-pausa" type="button" data-cff-pausa hidden
                       data-rot-pausar="Pausar o vídeo do caso"
                       data-rot-tocar="Tocar o vídeo do caso"> …os dois ícones do .btn-play… </button>
        -->
        <article class="cff-caso">
          <div class="cff-midia">
            <video class="cff-video" data-cff-video muted loop playsinline preload="none"
                   poster="../../../showroom/demo/ph-wide-1.webp"></video>
          </div>
          <button class="btn-disc btn-play cff-pausa" type="button" data-cff-pausa hidden
                  data-rot-pausar="Pausar o vídeo do caso" data-rot-tocar="Tocar o vídeo do caso"
                  aria-label="Pausar o vídeo do caso">
            <svg class="btn-play-pausa" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M8 5h3v14H8zM13 5h3v14h-3z" fill="currentColor"/></svg>
            <svg class="btn-play-tocar" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M8 5.5v13l10-6.5z" fill="currentColor"/></svg>
          </button>
          <div class="cff-ficha sec-light">
            <p class="cff-olho">Patrimônio em dois países</p>
            <h3 class="cff-titulo">
              <a class="cff-link" href="#caso-internacional">Contas, imóveis e herdeiros em dois países <em>num só relatório</em></a>
            </h3>
            <p class="cff-quem">Casal de médicos, Brasil e Portugal</p>
            <ul class="cff-tags" role="list">
              <li class="pill">Estrutura internacional</li>
              <li class="pill">Sucessão</li>
            </ul>
            <span class="cff-ver" aria-hidden="true">
              Ver o caso
              <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 7H12M8 3L12 7L8 11"/></svg>
            </span>
          </div>
        </article>
      </li>

    </ol>

    <p class="nota cff-nota" data-rv>Casos descritos com autorização dos clientes e sem identificá-los. Descrevem o trabalho feito, não resultado financeiro, e não constituem promessa de resultado.</p>

  </div>
</section>

estilo.css · 254 linhas · 9,3 KB

/* ==========================================================================
   CASE COM FICHA FLUTUANTE — prefixo cff-
   Um caso por linha: mídia larga e, pousada no canto de baixo dela, uma
   ficha clara com tema, frase, cliente e frentes de trabalho.

   Tokens usados:
     --display --sans        a frase e o resto da ficha
     --bg --fg --fg-2 --fg-muted --rule  da ficha (escopo .sec-light no
                             markup) e da dobra
     --accent-ink            o realce da frase e a seta, sobre o fundo da FICHA
     --radius-media          canto da mídia e da ficha
     --ease                  a curva das respostas ao ponteiro
     --dark-2                fundo da mídia enquanto a imagem não pinta

   A FICHA FICA DENTRO DA IMAGEM (contrato §6, texto sobre imagem), e não
   abaixo dela — em toda largura, inclusive no celular. Não é véu sobre a
   foto: é um bloco sólido, de fundo do escopo dela, pousado no canto. Por
   isso não tem backdrop-filter: a regra do véu (§6) existe para a caixa de
   vidro sobre imagem, e uma caixa de vidro reta aqui leria "chapado". Um
   bloco opaco de canto não tem borda a endurecer.

   PARAFUSOS (custom properties do próprio componente, §3):
     --cff-ratio     proporção da mídia no desktop (16/9)
     --cff-ficha-w   largura da ficha
     --cff-inset     distância da ficha às bordas da mídia
     --cff-gap       vão entre um caso e o seguinte
     --cff-tempo     duração das respostas ao ponteiro
   ========================================================================== */

.cff{
  --cff-ratio:16 / 9;
  --cff-ficha-w:min(46%,520px);
  --cff-inset:clamp(16px,2.4vw,32px);
  --cff-gap:clamp(28px,5vh,56px);
  --cff-tempo:.5s;
}

.cff-cabeca{margin-block-end:clamp(36px,6vh,64px)}

.cff-lista{
  display:grid;
  /* minmax(0,1fr), e não a coluna automática: o mínimo automático de um item
     de grade é o CONTEÚDO dele, e o conteúdo aqui é um <img> de 1600px de
     largura intrínseca. Medido sem isto: caso de 1307px numa coluna de
     1096, e a página estourando 32px para o lado. */
  grid-template-columns:minmax(0,1fr);
  gap:var(--cff-gap);
}

/* O <article> é o contexto do link esticado: o ::after do .cff-link se
   estende sobre ele inteiro. Mídia e ficha moram na MESMA célula de grade,
   uma sobre a outra — ver o comentário da ficha, logo abaixo, para o porquê
   de não ser position:absolute. */
.cff-caso{
  position:relative;
  display:grid;
  grid-template-columns:minmax(0,1fr);     /* mesma razão do .cff-lista */
  border-radius:var(--radius-media);
  overflow:hidden;
  background:var(--dark-2);
}

/* ------------------------------------------------------------- a mídia */
.cff-midia{
  grid-area:1 / 1;
  aspect-ratio:var(--cff-ratio);
}
/* `block-size` em 100% e não auto, e a diferença é a do MANUAL §6
   (armadilha 2): o <img> declara width e height para reservar a caixa, e o
   atributo height viraria altura definida de 900px. Aqui quem manda na
   altura é o .cff-midia, pelo aspect-ratio; a mídia só preenche. */
.cff-img,
.cff-video{
  display:block;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  /* Um quase nada abaixo do pleno no repouso, para o hover ter para onde
     ir sem mexer na geometria (§6: o retorno mora no brilho). */
  filter:brightness(.92);
  transition:filter var(--cff-tempo) var(--ease);
}
.cff-caso:hover .cff-img,
.cff-caso:hover .cff-video,
.cff-caso:focus-within .cff-img,
.cff-caso:focus-within .cff-video{filter:brightness(1.04)}

/* ------------------------------------------------------------- a ficha */
/* Bloco sólido no canto de baixo. O escopo de tom vem do markup
   (.sec-light), e o fundo dela é o --bg desse escopo — o componente não
   escreve cor.

   EMPILHADA POR GRADE, NÃO POR position:absolute, e é isso que faz o caso
   inteiro ser clicável. O link mora dentro da ficha e estica um ::after
   absoluto sobre o caso; um ::after absoluto se mede contra o ancestral
   POSICIONADO mais próximo. Com a ficha em position:absolute, esse
   ancestral seria ela, e o alvo do clique encolheria para o tamanho da
   ficha — a foto deixaria de ser link sem nada acusar. Na mesma célula de
   grade, a ficha não é posicionada e o ::after chega ao <article>.
   O z-index funciona sem position porque item de grade aceita z-index; e
   precisa dele, porque a mídia tem `filter`, e elemento com filtro é
   pintado na camada dos posicionados — por cima de um bloco comum. */
.cff-ficha{
  grid-area:1 / 1;
  align-self:end;
  justify-self:start;
  margin:var(--cff-inset);
  inline-size:var(--cff-ficha-w);
  z-index:2;
  display:grid;
  gap:14px;
  justify-items:start;
  padding:clamp(20px,2.2vw,30px);
  background:var(--bg);
  border-radius:var(--radius-media);
}

.cff-olho{
  font-size:11px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--fg-muted);
}

.cff-titulo{
  font-family:var(--display);
  font-weight:600;
  font-size:clamp(19px,1.9vw,27px);
  line-height:1.22;
  letter-spacing:-.018em;
  color:var(--fg);
}
/* O realce lê a tinta de realce DO ESCOPO DA FICHA: o fundo atrás do
   glifo é o da ficha, claro, e não a foto. É a regra do MANUAL §3 — fundo
   do escopo, --accent-ink; foto com véu, --accent. */
.cff-titulo em{
  font-style:italic;
  color:var(--accent-ink,var(--accent));
}

.cff-link{
  color:inherit;
  /* O filete do link, como no .ltr-cta do hero/letreiro-marca: decoração
     que cresce sob o texto, sem mexer em caixa nenhuma. Aqui é sublinhado
     de verdade, porque o link tem várias linhas. */
  text-decoration-line:underline;
  text-decoration-color:transparent;
  text-decoration-thickness:1px;
  text-underline-offset:.18em;
  transition:text-decoration-color var(--cff-tempo) var(--ease);
}
/* O CASO INTEIRO É ALVO: o ::after estica o link sobre o <article>. É ele
   que recebe o clique na foto. O anel de foco do core desenha em volta do
   <a> (o texto), e o :focus-within do caso acende a foto junto — quem chega
   de teclado vê o mesmo retorno de quem chega de mouse. */
.cff-link::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:1;
}
.cff-caso:hover .cff-link,
.cff-link:focus-visible{text-decoration-color:currentColor}

.cff-quem{
  font-size:14px;
  line-height:1.5;
  color:var(--fg-2);
}

.cff-tags{
  display:flex;
  flex-wrap:wrap;
  gap:6px;
}
/* A .pill do core tem tracking largo e caixa alta; três delas numa ficha de
   460px quebravam em duas linhas cada. Corpo e tracking menores aqui, sem
   tocar na primitiva. */
.cff-tags .pill{font-size:10px;letter-spacing:.12em;padding:4px 10px}

.cff-ver{
  display:inline-flex;
  align-items:center;
  gap:9px;
  font-family:var(--display);
  font-weight:700;
  font-size:11px;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--fg);
}
.cff-ver svg{color:var(--accent-ink,var(--accent))}

/* ------------------------------------------------- o botão de pausa */
/* Só na variação com vídeo. Fica ACIMA da ficha e do link esticado dela
   (z-index 3 contra 2), no canto de cima à direita, longe da ficha. A cor
   é de ilha escura: está sobre a mídia, não sobre o fundo da dobra.
   Duas classes no seletor de propósito: .btn-disc, do core, também pinta
   cor e borda com uma classe só, e empatar especificidade deixaria a cor
   na mão da ordem de carregamento (§6.4). */
.cff-caso .cff-pausa{
  position:absolute;
  inset-block-start:var(--cff-inset);
  inset-inline-end:var(--cff-inset);
  z-index:3;
  color:var(--light);
  border-color:color-mix(in srgb,var(--light) 50%,transparent);
  background:color-mix(in srgb,var(--dark) 40%,transparent);
}
.cff-caso .cff-pausa:hover{color:var(--accent);border-color:var(--accent)}
/* O .btn-play do core já desliga o [hidden] dele; esta linha é a do §6.5
   para o display que a .btn-disc dá — redundante hoje, e aqui porque a
   ordem de carregamento não é garantida (§6.4). */
.cff-pausa[hidden]{display:none}

.cff-nota{margin-block-start:clamp(24px,4vh,36px)}

@media (prefers-reduced-motion:reduce){
  .cff-img,
  .cff-video,
  .cff-link{transition:none}
}

/* ---------------------------------------------------------------- mobile */
/* A FICHA CONTINUA DENTRO DA MÍDIA em toda largura, e é a MÍDIA que muda de
   proporção para caber nela. Pôr a ficha embaixo da foto seria a saída
   fácil e é o que o §6 proíbe: uma peça gráfica viraria duas. O
   enquadramento da mídia tem de prever os recortes — ver a especificação
   dos assets na ficha.

   Os degraus saíram de medição, com a ficha mais alta do preview:
     760px em 16:9    mídia 376, ficha 301 — a ficha cobria 80% da foto
     760px em 4:3     mídia 513, ficha ~250
     320px em 4:5     mídia 331, ficha 323 — a ficha passava da mídia e a
                      célula crescia
     320px em 3:4     sem as pílulas, a ficha cabe com a foto à vista */
@media (max-width:980px){
  .cff{--cff-ratio:4 / 3;--cff-ficha-w:min(62%,460px)}
}
@media (max-width:560px){
  .cff{--cff-ratio:3 / 4;--cff-inset:12px}
  .cff-ficha{
    justify-self:stretch;
    inline-size:auto;
    gap:10px;
    padding:18px;
  }
  .cff-titulo{font-size:clamp(17px,4.8vw,20px)}
  .cff-quem{font-size:13px}
  /* As frentes saem no celular: são o slot mais secundário da ficha, e
     três pílulas em duas linhas eram a diferença entre a ficha caber ou
     não na foto a 320px. O caso aberto continua dizendo quais foram. */
  .cff-tags{display:none}
}

script.js · 91 linhas · 4,0 KB

/* ==========================================================================
   CASE COM FICHA FLUTUANTE — cff-
   Só serve à variação com vídeo em loop. Sem nenhum [data-cff-video], ou
   com vídeos sem arquivo, este script não faz nada — o componente com foto
   é CSS puro.

   O QUE ELE FAZ, POR VÍDEO:
     1. confere que há o que tocar (src ou <source>). Sem arquivo, fica o
        pôster e o botão de pausa segue [hidden], como nasceu;
     2. entrega o botão ao LIB.pausavel do core (WCAG 2.2.2: loop que passa
        de cinco segundos pede um jeito de parar);
     3. toca SÓ quando o caso está na tela e a aba está à vista — três
        vídeos tocando fora da tela são três downloads e três decodificações
        que ninguém vê;
     4. com movimento reduzido ou economia de dados, NÃO toca sozinho: o
        botão aparece no estado "tocar", e quem apertar pediu. Movimento
        reduzido desliga o que anda sozinho, nunca o que a pessoa pede
        (MANUAL §5).

   A FONTE DA VERDADE É O PEDIDO DA PESSOA, E NÃO O <video>. Diferente do
   hero/video-fundo (que acompanha os eventos play/pause do elemento), aqui
   o vídeo é pausado e retomado pelo script o tempo todo — ao sair da tela,
   ao trocar de aba —, e esses pauses não são a pessoa pedindo nada. O
   botão só reflete o clique dela.

   ESTA É A SEGUNDA PEÇA DA CASA COM LOOP DE VÍDEO E PAUSA (a primeira é o
   hero/video-fundo). O gatilho de promoção do §6 é três; a contagem está na
   fila do MANUAL.
   ========================================================================== */
(function () {
  var raizes = document.querySelectorAll('[data-cff]');
  if (!raizes.length) return;                    // guard: saída silenciosa
  if (!window.LIB) return;                       // sem o core, fica o pôster

  var economia = !!(navigator.connection && navigator.connection.saveData);
  var podeSozinho = !LIB.reduce && !economia;

  [].forEach.call(raizes, function (raiz) {
    [].forEach.call(raiz.querySelectorAll('[data-cff-video]'), function (video) {
      var caso = video.closest('.cff-caso');
      var botao = caso && caso.querySelector('[data-cff-pausa]');
      if (!video.getAttribute('src') && !video.querySelector('source')) return;

      var pausadoPelaPessoa = !podeSozinho;      // reduce: nasce parado
      var naTela = false;
      var quebrou = false;

      function aplicar() {
        if (quebrou) return;
        if (naTela && !pausadoPelaPessoa && !document.hidden) {
          if (video.preload !== 'auto') video.preload = 'auto';
          var p = video.play();
          /* Play recusado (política de autoplay, aba sem foco) devolve
             promessa rejeitada; sem o catch vira erro vermelho no console. */
          if (p && p.catch) p.catch(function () {});
        } else if (!video.paused) {
          video.pause();
        }
      }

      var ctl = LIB.pausavel(botao, function (pausado) {
        pausadoPelaPessoa = pausado;
        aplicar();
      });
      if (ctl && !podeSozinho) ctl.trocar(true);  // ícone e rótulo de "tocar"

      /* Arquivo que não carrega: o <source> dispara `error` nele mesmo, e
         não no <video> — daí a escuta em captura. O botão sai de cena,
         porque comanda algo que não existe, e o pôster fica. */
      video.addEventListener('error', function () {
        quebrou = true;
        if (botao) botao.hidden = true;
      }, true);

      /* Observer de VISIBILIDADE, não de entrada: a entrada do caso é do
         data-rv do core. Este só liga e desliga o vídeo. 35% porque abaixo
         disso o caso está saindo da tela e tocar é desperdício. */
      if ('IntersectionObserver' in window) {
        new IntersectionObserver(function (es) {
          naTela = es[0].isIntersecting;
          aplicar();
        }, { threshold: 0.35 }).observe(video);
      } else {
        naTela = true;
        aplicar();
      }

      document.addEventListener('visibilitychange', aplicar);
    });
  });
})();

Um caso por linha, na largura da coluna. Cada um é uma mídia larga — foto, ou um loop de vídeo mudo — e, pousada no canto de baixo dela, uma ficha clara: o tema em caixa alta, uma frase sobre o trabalho com um trecho em realce, quem era o cliente em cinco palavras e as frentes envolvidas em pílulas. O olho bate na imagem, desce para a frase e decide se abre o caso.

É a midia/grade-cases-foto com espaço para dizer alguma coisa. A grade mostra quatro situações de uma vez e não tem onde pôr uma frase; aqui cada caso ganha uma dobra quase inteira e a frase é a peça.


Quando usar

Quando não usar


Padrão editorial

Slot Regra
Tema 1 a 4 palavras: a situação, não o produto ("Sucessão familiar", não "Holding")
Frase 8 a 16 palavras sobre o que mudou no trabalho, com um trecho em <em> que é o ponto dela
Cliente 3 a 7 palavras, retrato anônimo: perfil e situação. Paralelo entre os casos
Frentes 1 a 3 pílulas, opcionais. Somem abaixo de 560px
Ressalva .nota obrigatória em site de assessoria

O que mantém a peça: as frases têm o mesmo fôlego. Uma frase de 8 palavras ao lado de uma de 16 faz as fichas terem alturas diferentes, e a fila de casos perde o ritmo.

Especificação dos assets


Como usar

<link rel="stylesheet" href="componentes/midia/case-ficha-flutuante/estilo.css">
<!-- só na variação com vídeo: -->
<script src="componentes/midia/case-ficha-flutuante/script.js"></script>

Ganchos

Gancho Papel
[data-cff] a seção
.cff-ficha.sec-light a ficha declara o próprio escopo de tom no markup
.cff-link o <a> dentro do <h3>; o ::after dele cobre o caso inteiro
[data-cff-video] o <video> da variação em loop
[data-cff-pausa] o .btn-disc.btn-play do caso com vídeo; nasce [hidden]
--cff-ratio proporção da mídia: 16/9, 4/3 abaixo de 980px, 3/4 abaixo de 560px
--cff-ficha-w / --cff-inset / --cff-gap / --cff-tempo largura da ficha, recuo dela, vão entre casos, tempo de resposta ao ponteiro

Variações


Compliance


Gotchas


Movimento reduzido / toque

Com foto, nada anda: o retorno de hover é brilho na mídia e sublinhado na frase (§6), e no toque a ficha já mostra o "Ver o caso" sem depender de hover.

Com vídeo, nada toca sozinho sob reduce nem com economia de dados ligada: o pôster fica, e o botão aparece no estado "Tocar". Quem apertar pediu, e o vídeo toca. Medido com um webm gerado na hora (não há vídeo de produção no repositório): sem reduce, o vídeo fica parado fora da tela, toca ao entrar, para no clique com o rótulo virando "Tocar o vídeo do caso", volta no segundo clique e para de novo ao sair da tela com o rótulo intacto; com reduce, nasce parado com "Tocar", toca no clique. Console limpo nos dois.


Histórico

Origem Quando O quê
referência: Neto Scariot 2026-10-01 Clone de referência, reescrito do zero

Referência visual: os cases da home do portfólio — vídeo 16:9 de canto arredondado e, sobre ele, o cartão claro com categoria ("REAL TIME TRADING"), a frase de resultado com um trecho em destaque, o cliente e as tags.

Normalizações aplicadas na tradução: o cursor customizado "CLICK TO VIEW PROJECT" (com cursor:none no caso inteiro) virou o "Ver o caso" visível e o anel de foco do core — esconder o ponteiro tira de quem usa mouse a única pista de onde ele está; o loop ganhou pausa (WCAG 2.2.2), toca só na tela e não toca sozinho sob reduce; a frase de resultado de negócio virou o trabalho feito, sem cliente identificado e com a .nota; no celular o cartão continua dentro da mídia, que muda de proporção. Framer não atravessa. Registro consolidado em projetos/referencias-externas.md.