/* components.css — os seis blocos compartilhados do site (specs/DESIGN.md §3).
   Cada um isolado, prefixado com "c-", pronto para virar componente React
   sem reescrever CSS: a classe vira o nome do componente, os tokens não mudam.

   Índice:
   1. c-header      — site-header (+ 1b. c-floatbar, a nav global)
   2. c-label       — section-label
   3. c-eyebrow     — eyebrow
   4. c-divider     — divider (✦)
   5. c-footer      — contact-footer
   6. c-figure      — media-figure (+ c-figure-group)
   7. c-case-card   — case-card (Fase 2, Home)
   8. c-scroll-strip — scroll-strip (Fase 2, Home)
   9. c-story-row   — story-row (Fase 3, About + Fase 4, Quick stories)
  10. c-display / c-title — tipografia de título, compartilhada
  11. c-glyph-list  — lista com marcador ✦ (Fase 5, case)
  12. c-details     — acordeão nativo (Fase 5, case)
  13. c-hmw         — pergunta "How might we" (Fase 5, case)
  16. c-nav-arrow   — botão de seta, dividido pelas duas galerias
  17. c-media-carousel — galeria em faixa horizontal (vários itens)
  18. c-media-stepper  — galeria de um estado por vez (crossfade)
  19. c-gallery-marquee — galeria em faixa, loop infinito, reutilizável
  20. c-link-arrow  — link com seta que desliza no hover (Fase 8)
  33. c-project-showcase — faixa de projeto de /visual-craft (texto + galeria)
  34. c-divider--wide — o ✦ na largura da faixa de projeto
  35. c-project-gallery / c-project-stack — as pilhas de /visual-craft
  36. c-work-panel — os três cartões cinza que são a página /work */

/* ===================================================================
   10. c-display / c-title — os dois tamanhos de título serifado.
   Separados do bloco que os usa porque três componentes diferentes
   precisam do mesmo desenho: capa de case, título de break point e
   card da Home.
   =================================================================== */
.c-display,
.c-title {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 700;
  color: var(--color-text);
  margin: 0;
}

/* Mesmo tamanho de `.c-title` (tokens.css) desde 28/08: o título do case
   e o título de break point precisavam bater, pedido do Breno pra
   alinhar o título de `/case-prepaid` com o de `/work`. Só o
   `text-align: center` separa as duas classes agora. */
.c-display {
  font-size: var(--type-title-size);
  line-height: var(--type-title-lh);
  text-align: center;
}

.c-title {
  font-size: var(--type-title-size);
  line-height: var(--type-title-lh);
}

/* ===================================================================
   1. c-header — avatar + nome + linha de papel + a float bar

   Um só DOM, dois layouts, com a MESMA grade nos dois: duas colunas
   (avatar | resto) e duas linhas (identidade | nav — a segunda linha
   sempre com as duas colunas, "nav nav"). O que muda no breakpoint é
   só o comportamento do avatar, o gap e como a nav se posiciona nessa
   linha larga: no mobile ela preenche; no desktop ela centraliza
   (`width: fit-content; margin-inline: auto`), no mesmo referencial de
   largura que a versão flutuante usa — estática ou fixa, o pill mora
   sempre no centro do mesmo eixo.

   O header é o ÚNICO bloco do site sem `.reveal`, de propósito: um
   ancestral com `opacity` menor que 1 vira containing block do que é
   `position: fixed` dentro dele — a float bar ficaria presa ao header
   em vez de ir para o topo da viewport. Pior: numa página aberta já
   rolada (refresh, voltar, link com âncora) o observer do reveal nunca
   dispararia e a barra ficaria invisível. Como o header nasce sempre
   acima da dobra, o fade ali não estava dizendo nada mesmo.
   =================================================================== */
.c-header {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "avatar identity"
    "nav    nav";
  column-gap: var(--space-6);
  row-gap: var(--space-6);
  align-items: start;
}

.c-header__avatar-wrap { grid-area: avatar; }

.c-header__avatar {
  width: var(--avatar);
  height: var(--avatar);
  border-radius: var(--radius-full);
  object-fit: cover;
  flex-shrink: 0;
}

.c-header__identity {
  grid-area: identity;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding-inline-start: 5%;
}

/* `align-items: center`, não `baseline`: o filho da direita é um
   `<button>` com um ponto (sem texto pra dar baseline) e um `<span>` de
   texto que fica invisível por padrão — a baseline que sobra daí não
   bate com a do nome, então o botão ficava alto ou baixo demais.
   Centralizar pela caixa inteira é o que alinha os dois de verdade.

   `gap: --space-1` (4px), não `--space-2`: o botão já soma seu próprio
   `padding: --space-1` de cada lado, então o respiro nome→ponto vira
   4+4=8px de fato — o MESMO valor do `gap: --space-2` (8px) que separa
   ponto→texto DENTRO do botão. Com `gap: --space-2` aqui os dois
   respiros ficavam 12px vs 8px, desbalanceados. */
.c-header__name-row {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.c-header__name {
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--type-name-size);
  line-height: 26px;
  color: var(--color-text);
}

/* "Product Designer" em serifa itálica 600, " based in Brasil" em Inter 400.
   Duas famílias na mesma linha, colada pela mesma line-height de 180%. */
.c-header__tagline {
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  color: var(--color-text-body);
}

.c-header__tagline em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 600;
}

/* ===================================================================
   1c. c-header__status — o ponto de disponibilidade ao lado do nome.

   Substitui a lista `c-status` (§26) que morava na Home entre a
   headline e "See more about me": um único indicador, sempre verde
   ("Open to work"), que mora no header e por isso aparece em toda
   página, não só na Home.

   É um `<button>`, não decoração: hover/`:focus-visible` revela o texto
   com fade + slide da esquerda pra direita (CSS puro, translateX de -6px
   a 0). Um clique nesse estado rola até o rodapé de contato (JS, âncora
   `#contact`). Em touch não existe hover de verdade — o primeiro toque
   só revela (`.is-revealed`, JS liga por classe) e o segundo rola; ver
   main.js. `pointer-events` no ponto/texto internos ficam none-free
   porque quem recebe o clique é sempre o `<button>` inteiro.

   `border: 0; background: none` porque isto não deve LER como botão
   convencional — é um indicador que por acaso é clicável. Chegou a
   existir um segundo ponto decorativo no canto do avatar, ecoando este;
   removido a pedido do Breno depois de ver o primeiro render — um ponto
   só, aqui, é suficiente. */
.c-header__status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-1);
  border: 0;
  border-radius: var(--radius-full);
  background: none;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--type-eyebrow-size);
  line-height: 1;
  color: var(--color-accent);
}

.c-header__status:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.c-header__status-dot {
  flex: 0 0 auto;
  width: var(--status-dot);
  height: var(--status-dot);
  border-radius: var(--radius-full);
  background: var(--color-accent);
}

.c-header__status-text {
  white-space: nowrap;
  opacity: 0;
  transform: translateX(-6px);
  transition: opacity var(--duration-ui) var(--ease),
              transform var(--duration-ui) var(--ease);
}

.c-header__status:hover .c-header__status-text,
.c-header__status:focus-visible .c-header__status-text,
.c-header__status.is-revealed .c-header__status-text {
  opacity: 1;
  transform: translateX(0);
}

/* MOBILE: o texto do status deixa de RESERVAR largura enquanto está
   escondido. Ele é `opacity: 0`, mas ocupava ~70px de linha mesmo
   invisível, e com o `c-theme-toggle` na ponta direita (§1d) isso passou
   a empurrar "Breno Otávio" pra duas linhas numa tela de 375px. O nome
   tem prioridade sobre um texto que ninguém está vendo.

   Por que só abaixo de 700px: acima do breakpoint a largura reservada é
   DELIBERADA, e é a correção de layout shift no hover fechada em 26/08
   (DESIGN.md §14) — colapsar a largura lá faria a linha inteira andar
   quando o ponteiro passa por cima. Abaixo do breakpoint não existe
   hover: a revelação vem de um toque explícito, e crescer em resposta
   a um toque é retorno de ação, não solavanco acidental.

   `min-width: 0` + `overflow: hidden` no botão fecham o caso da
   revelação numa tela muito estreita: quem cede espaço é o texto do
   status, que é clipado, porque ele é o único dos três que pode ser
   lido pela metade sem perder a função (o ponto verde já diz o que
   importa). O nome não quebra e o disco não some. */
@media (max-width: 699px) {
  .c-header__name { white-space: nowrap; }

  .c-header__status {
    min-width: 0;
    overflow: hidden;
  }

  .c-header__status-text {
    max-width: 0;
    transition: opacity var(--duration-ui) var(--ease),
                transform var(--duration-ui) var(--ease),
                max-width var(--duration-ui) var(--ease);
  }

  .c-header__status:focus-visible .c-header__status-text,
  .c-header__status.is-revealed .c-header__status-text {
    max-width: 12ch;
  }
}

/* ===================================================================
   1d. c-theme-toggle — o disco que troca o tema, na ponta direita da
   linha do nome.

   ONDE MORA, e por quê. Ele fica dentro de `.c-header__name-row`, não
   na float bar. A float bar desgruda e vira `position: fixed`: um
   controle de tema ali ficaria pairando sobre o conteúdo a página
   inteira, competindo com a navegação, que é a função dela. Aqui ele
   rola junto com o header e sai de cena, que é o comportamento certo
   pra um ajuste que se faz uma vez.

   `margin-inline-start: auto` é o que separa ele do par nome/status:
   os dois ficam colados à esquerda pela ordem natural do flex, e o
   `auto` come toda a sobra até a borda direita da linha. É de propósito
   que ele NÃO fique adjacente ao status — são duas coisas diferentes (um
   indicador de disponibilidade e um controle de aparência) e encostar
   uma na outra leria como um par.

   `flex: 0 0 auto` fecha o requisito de prioridade no mobile: quando a
   linha aperta, quem cede é o nome, nunca o disco. O disco é o único
   elemento da linha que some por completo se encolher, porque um
   círculo de 32px com ícone de 24 não tem para onde encolher.

   TRÊS ÍCONES NO HTML, um visível. Os três SVGs do Lucide (sun-dim,
   sun, moon) nascem no markup e o CSS escolhe qual aparece pelo
   `data-theme` do `<html>`. Trocar por JS daria o mesmo resultado
   visual, mas só depois que o JS rodasse: com o CSS decidindo, o ícone
   já nasce certo na primeira pintura, junto com o tema que o bootstrap
   do `<head>` aplicou. Sem JS nenhum o botão não faz nada (é um
   controle, não conteúdo), mas ao menos não mostra o ícone errado.

   SEM FUNDO EM NENHUM ESTADO — pedido do Breno depois de ver o disco
   preenchido: só o ícone deve aparecer, o resto da caixa de 32px é
   pura área de toque. `--color-control-surface` ficou órfão no
   tokens.css por causa disso (ver comentário lá) — não apagado, porque
   um fundo pode voltar a ser pedido, e o token já está calibrado por
   tema.

   BORDA, NÃO SOMBRA. A sombra `--shadow-control-hover` saiu inteira; no
   lugar, hover/foco pedem a MESMA borda do pill ativo da float bar —
   `var(--color-border)`, sem token novo, para os dois lerem como a
   mesma linguagem de "isto é interativo" em vez de dois desenhos
   diferentes pro mesmo papel. A técnica é a de `.c-floatbar__link`
   (§1b): borda transparente do mesmo peso no estado normal, só a COR
   troca no hover — o layout nunca pula 1px quando a borda aparece. */
.c-theme-toggle {
  flex: 0 0 auto;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  background: none;
  color: var(--color-control-icon);
  cursor: pointer;
}

.c-theme-toggle:hover,
.c-theme-toggle:focus-visible {
  border-color: var(--color-border);
}

.c-theme-toggle:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.c-theme-toggle svg {
  display: none;
  width: 24px;
  height: 24px;
}

/* Sem atributo o tema é light (o default do :root), então o sun-dim
   responde pelos dois casos. */
:root:not([data-theme]) .c-theme-toggle__icon--light,
:root[data-theme="light"]  .c-theme-toggle__icon--light,
:root[data-theme="creamy"] .c-theme-toggle__icon--creamy,
:root[data-theme="dark"]   .c-theme-toggle__icon--dark {
  display: block;
}

/* Giro curto no clique. A classe entra no JS e sai sozinha no
   `animationend`, então dois cliques seguidos giram duas vezes em vez
   de a segunda animação morrer no berço. */
@keyframes c-theme-toggle-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

.c-theme-toggle.is-spinning svg {
  animation: c-theme-toggle-spin 350ms var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .c-theme-toggle.is-spinning svg { animation: none; }
}

/* ===================================================================
   1b. c-floatbar — a nav global, em pill.

   É UM elemento só em dois estados, não duas navs. Em fluxo ele vive
   dentro do header; com `.is-floating` vira `position: fixed` no topo.
   O slot em volta é quem segura a altura reservada, para a página não
   dar um salto de ~53px no instante em que a barra desgruda.
   =================================================================== */
.c-header__nav-slot {
  grid-area: nav;
  /* a altura vem do JS (assets/js/main.js), medida na barra estática.
     Sem JS a barra nunca flutua, então o slot só embrulha e o fallback
     `auto` está certo. */
}

.c-floatbar {
  display: flex;
  align-items: center;
  justify-content: space-between;   /* mobile: os três itens ocupam a largura */
  gap: var(--floatbar-gap);
  padding: var(--floatbar-pad);
  border: 1px solid var(--color-border-bar);
  border-radius: var(--radius-bar);
  background: transparent;
  transition:
    background var(--duration-ui) var(--ease),
    border-color var(--duration-ui) var(--ease);
}

.c-floatbar__link {
  display: inline-flex;
  align-items: center;
  padding: var(--floatbar-link-pad);
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  /* já era explícito antes do verde virar padrão de `a` (base.css §3),
     então o pill de nav nunca ficou verde. Fica registrado como
     exceção deliberada, não como coincidência. */
  color: var(--color-text);
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  white-space: nowrap;
  transition: background-color var(--duration-ui) var(--ease),
              border-color var(--duration-ui) var(--ease);
}

/* Item ativo: pill preenchida. O estilo sai do atributo, não de uma
   classe extra, então não dá para o visual e a semântica discordarem.

   DOIS VALORES, de propósito. `page` é a página exata (Home em `/`,
   Work em `/work`). `true` é "você está DENTRO desta seção sem estar
   nesta página": é o caso de `/quick-stories` e `/case-prepaid`, que
   vivem sob Work desde que Work virou índice próprio. Marcá-las com
   `page` seria mentira semântica para leitor de tela; deixá-las sem
   marca nenhuma apagaria a orientação na nav, que é o que acontecia
   antes com o case. Os dois acendem a mesma pill. */
.c-floatbar__link[aria-current="page"],
.c-floatbar__link[aria-current="true"] {
  background: var(--color-surface-subtle);
  border-color: var(--color-border);
}

/* Hover é só contorno, sem preenchimento — reusa --color-border, o mesmo
   traço do item ativo, então a diferença entre hover e ativo fica só no
   background. Padding e border-radius nunca mudam entre os três estados
   (default, hover, ativo): só cor de fundo e de borda, para não empurrar
   os itens vizinhos. */
.c-floatbar__link:hover {
  text-decoration: none;
  border-color: var(--color-border);
}

/* -------------------------------------------------------------------
   Estado flutuante. Centrada por `inset-inline: 0 + margin-inline: auto`
   e não por `left:50% + translateX(-50%)`: assim o `transform` fica
   livre para a animação de entrada.
   ------------------------------------------------------------------- */
.c-floatbar.is-floating {
  position: fixed;
  top: var(--floatbar-top);
  inset-inline: var(--gutter);
  margin-inline: auto;
  z-index: 50;
  background: var(--color-surface-blur);
  -webkit-backdrop-filter: blur(var(--blur-radius));
  backdrop-filter: blur(var(--blur-radius));
  animation: floatbar-in var(--duration-ui) var(--ease);
}

@keyframes floatbar-in {
  from { opacity: 0; transform: translateY(calc(var(--floatbar-top) * -1)); }
}

/* Onde não há blur, o vidro de 50% deixaria o texto sobre o conteúdo
   que passa por baixo. Sobe para 85%, que é opaco o bastante para o
   contraste do link continuar em pé. */
@supports not ((backdrop-filter: blur(10px)) or (-webkit-backdrop-filter: blur(10px))) {
  .c-floatbar.is-floating { background: var(--color-surface-blur-solid); }
}

@media (prefers-reduced-motion: reduce) {
  .c-floatbar,
  .c-floatbar.is-floating {
    transition: none;
    animation: none;
  }
}

/* Telas de 320 a 374px: com o espaçamento cheio os três rótulos somam
   ~298px e vazam a pill. Aperta o gap e o padding lateral em vez de
   quebrar em duas linhas — a pill precisa continuar sendo uma linha. */
@media (max-width: 374px) {
  .c-floatbar {
    --floatbar-gap: var(--space-1);
    --floatbar-link-pad: var(--space-1) var(--space-3);
  }
}

/* ===================================================================
   1c. Header desktop — acima do breakpoint o avatar volta a esticar
   pelas duas linhas (como no primeiro desenho), emoldurando nome +
   tagline + nav como um bloco só. A nav continua na área "nav", que já
   cobre as duas colunas por herança da base — ela só ganha, aqui, um
   `grid-row: 1 / 3` no avatar por CIMA dela: como a área nomeada "nav"
   (string, retangular) não deixa a área nomeada "avatar" invadir a
   linha 2, a extensão do avatar é feita por posição numérica, não por
   nome — as duas coexistem na mesma célula da coluna 1, linha 2, sem
   erro, porque `grid-row` explícito não respeita o retângulo da área.
   Não colidem: a pill centralizada nasce ~149px da borda esquerda da
   linha (600px de largura, ~301px de pill), o avatar só vai até ~132px
   (116px + 16px de gap) — sobra folga.

   A nav centraliza com `width: fit-content; margin-inline: auto` dentro
   dessa linha inteira (avatar + identidade) — mesmo referencial de
   centro que a flutuante usa na página toda. Os dois batem porque os
   dois eixos passam pelo mesmo centro (o do `.page`), não por
   coincidência de medida: um gap fixo aqui só acertaria por acaso, e
   ficaria torto se a fonte, o avatar ou os rótulos da nav mudassem de
   tamanho um dia. Centralizar pelo eixo é o que "considera onde fica o
   meio da página" de verdade — um número fixo não considera nada, só
   acerta hoje.
   =================================================================== */
@media (min-width: 700px) {
  .c-header {
    column-gap: var(--space-4);
    row-gap: var(--space-2);
    align-items: center;
  }

  .c-header__avatar-wrap {
    grid-row: 1 / 3;
    align-self: center;
  }

  .c-floatbar {
    width: max-content;
    margin-inline: auto;
  }

  .c-floatbar.is-floating {
    inset-inline: 0;
    max-width: calc(100% - var(--gutter) * 2);
  }
}

/* ===================================================================
   2. c-label — section-label (o rótulo que abre uma seção)

   QUARTA parada na mesma sessão de 28/08: serifa itálica 700/14px ->
   Plex Sans 600/12px -> Instrument Serif 400/14px -> IBM Plex Serif
   400/14px UPRIGHT (esta). Volta pra família editorial do resto do site
   (`--font-serif`, mesma dos títulos e da tagline do header), só que
   sem itálico e num peso mais leve — o rótulo se distingue de título/
   tagline por PESO e ESTILO, não por família nem tamanho.

   `font-style: normal` explícito por documentação: todo outro uso de
   `--font-serif` no site é itálico (tagline, `c-display`/`c-title`),
   então este é o único upright — vale deixar escrito. `c-lead` usava
   `--font-serif` itálico aqui também até 03/09; trocou pra
   `--font-sans` (§13) e saiu da lista.
   `text-transform: lowercase` é a fonte da caixa baixa, não o HTML: a
   copy de rótulo continua em Title Case no markup, então nada quebra se
   o transform for removido depois. */
.c-label {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--type-label-size);
  line-height: var(--type-label-lh);
  text-transform: lowercase;
  color: var(--color-text);
  margin: 0 0 var(--space-4);
}

/* EXCEÇÃO ao verde padrão de `a` (base.css §3). Na Home dois rótulos de
   seção são links ("About me" e "Quick stories", index.html). Continuam
   sendo rótulo antes de serem link: quem lê a página vê um cabeçalho de
   seção, não uma frase clicável no meio do texto. O verde aqui leria
   como link de prosa e quebraria a hierarquia. O sublinhado no hover
   (base.css §3) continua, e é ele que entrega que dá pra clicar. */
.c-label a { color: inherit; }

/* ===================================================================
   3. c-eyebrow — rótulo curto em caixa alta acima de um parágrafo
   =================================================================== */
.c-eyebrow {
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-lh);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}

/* variante usada só em "BREAK POINT 01" no case */
.c-eyebrow--sm {
  font-size: var(--type-eyebrow-sm-size);
  line-height: var(--type-eyebrow-sm-lh);
}

/* bloco "Break point 0X" + título + frase de apoio, centralizado como
   um grupo (specs/design.md — só este trio, não c-title/c-lead no geral,
   que também servem o card da Home e o "How might we" alinhados à
   esquerda). */
.c-bp-header {
  text-align: center;
}

/* ===================================================================
   4. c-divider — o ✦, sempre decorativo (aria-hidden no HTML).
   A estrutura já é seccionada por heading; o glifo não carrega
   informação para quem usa leitor de tela.
   =================================================================== */
.c-divider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

.c-divider__rule {
  flex: 1 1 auto;
  height: 1px;
  background: var(--color-rule);
}

.c-divider__glyph {
  flex: 0 0 auto;
  font-size: var(--type-glyph-size);
  line-height: var(--type-glyph-lh);
  color: var(--color-text-faint);
}

/* ===================================================================
   5. c-footer — contact-footer, igual em todas as páginas
   =================================================================== */
/* EXCEÇÃO ao tracking padrão de `.c-label` (§2, `--type-label-tracking`,
   0,06em): pedido do Breno pra "Contact" nascer sem letter-spacing. Só
   este rótulo — os outros (`work`, `Quick Stories` etc.) mantêm o
   tracking. Escopado por `.c-footer .c-label` em vez de mexer no token
   global, porque o footer é o único lugar do site onde essa mudança
   deveria valer. */
.c-footer .c-label { letter-spacing: normal; }

.c-footer__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin: 0;
  padding: 0;
}

.c-footer__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  margin: 0;
}

.c-footer__row dt {
  color: var(--color-text-muted);
  min-width: 64px;
}

.c-footer__row dd {
  margin: 0;
  color: var(--color-text);
}

/* :not(.c-link-arrow) porque o E-mail e o Linkedin agora usam o
   componente c-link-arrow (§20), que troca o sublinhado pelo deslize
   do glifo — sem a exclusao esta regra especifica (0,2,2) venceria a
   remocao de sublinhado do link-arrow (0,2,0) por especificidade. */
.c-footer__row dd a:not(.c-link-arrow):hover { text-decoration: underline; }

/* ===================================================================
   6. c-figure — media-figure, três variantes de moldura
   =================================================================== */
.c-figure {
  margin: 0;
}

.c-figure img {
  width: 100%;
  height: auto;
}

/* dentro da coluna, sem moldura — screenshots de produto no case */
.c-figure--plain img { display: block; }

/* card com borda e raio pequeno — thumbnails da Home e Quick stories */
.c-figure--card img {
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border-card);
}

/* imagem de destaque, raio maior, sem borda — capa de case */
.c-figure--hero img {
  border-radius: var(--radius-md);
}

/* diagrama do ciclo de vida do pré-pago: maior que a coluna de texto,
   sangrando pros lados mas sempre centralizado. Centraliza em relação
   à própria coluna (que já é centralizada na viewport), e trava a
   largura em telas estreitas pra não estourar o scroll horizontal. */
.c-figure--lifecycle img {
  width: min(920px, calc(100vw - var(--gutter) * 2));
  max-width: none;
  margin-left: 50%;
  transform: translateX(-50%);
}

.c-figure__caption {
  margin-top: var(--figure-caption-gap);
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  letter-spacing: var(--tracking-caption);
  color: var(--color-text);
}

/* legenda antes da imagem — <figcaption> aceita ser primeiro ou último
   filho de <figure>, então a ordem visual é a mesma do DOM e o leitor
   de tela ouve o texto antes da imagem que ele descreve. */
.c-figure__caption--above {
  margin-top: 0;
  margin-bottom: var(--space-4);
}

/* Par de figuras que estoura a coluna (comparativo Before/After em
   Quick stories). Trava a largura pra não virar sangria total, e
   recolhe pra uma coluna abaixo do breakpoint.

   TRÊS FAIXAS, não duas:
   · < 700px  — o grupo nem existe. No lugar dele, a mesma marcação
     usa `c-media-stepper` (ver §18): um estado por vez em vez de dois
     lado a lado, porque numa coluna estreita comparar Before/After
     empilhado é pior do que ver um de cada vez.
   · 700–999px — lado a lado (a comparação já cabe), mas ainda preso à
     trilha `content`: sangrar até a borda numa tela desse tamanho
     deixava o par pequeno demais E fora do alinhamento do resto da
     página, que é a mesma regra que já vale pro mobile.
   · ≥ 1000px — sangria de verdade, capada em 1100px. Só aqui sobra
     largura o bastante pra a sangria significar alguma coisa.

   A sangria em si (largura 100vw + recentragem) NÃO mora aqui: este
   componente só existe hoje dentro do `case-toc-layout` de Quick
   stories (a única página que usa `c-figure-group`), então a regra que
   estoura a coluna e recentraliza o grupo capado vive junto das outras
   duas ("sangria sempre" e "sangria condicional") na seção
   case-toc-layout, mais abaixo neste arquivo — procure por
   `.c-figure-group.bleed` lá. Sem `justify-self:center` aqui: essa
   propriedade só tem efeito num item de GRID, e o pai de
   `c-figure-group` desde 28/08 é um `.stack` (flex), onde ela não faz
   nada — a centralização real acontece via `transform`, na regra
   citada acima. */
.c-figure-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  width: 100%;
  max-width: 1100px;
}

@media (min-width: 700px) {
  .c-figure-group {
    flex-direction: row;
    gap: var(--space-8);
  }

  .c-figure-group > * { flex: 1 1 0; }
}

/* Modificador — não do componente em si, mas de UM USO dele: o par
   Before/After (Debt Negotiation) some no mobile e dá lugar ao
   `c-media-stepper--figure-swap` (ver §18). Outros usos de
   `c-figure-group` (ex.: Smartphone Discount, imagem única) NÃO levam
   esta classe — continuam empilhados no mobile como sempre foram, só
   ganhando de graça a correção de margem no tablet acima. */
.c-figure-group--figure-swap { display: none; }

@media (min-width: 700px) {
  .c-figure-group--figure-swap { display: flex; }
}

/* ===================================================================
   7. c-case-card — título + imagem, bloco inteiro num único link.
   Padrão replicável: é o que a Home usa para o case do pré-pago, e o
   que um case novo reusa depois sem reescrever estrutura (backlog).
   =================================================================== */
/* EXCEÇÃO ao verde padrão de `a` (base.css §3): o card é um link que
   embrulha um BLOCO (título + imagem), não uma frase. O título já traz
   a própria cor de .c-title, então hoje nada verde aparece — o
   `inherit` é o que garante que continue assim se um dia entrar texto
   solto dentro do card. */
.c-case-card {
  display: block;
  color: inherit;
}

/* o desenho do título vem de .c-title; aqui só o respiro até a imagem */
.c-case-card__title { margin-bottom: var(--space-6); }

/* ===================================================================
   8. c-scroll-strip — faixa horizontal que sangra a coluna.
   overflow-x nativo, sem biblioteca de marquee: rolagem por teclado
   funciona de graça, porque focar um link dentro da faixa já traz o
   elemento para dentro da área visível.
   =================================================================== */
.c-scroll-strip {
  display: flex;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-1) var(--gutter);
  list-style: none;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: thin;
}

.c-scroll-strip__item {
  flex: 0 0 auto;
  width: min(72vw, var(--item-w, 460px));
  scroll-snap-align: start;
}

/* Mesma exceção do c-case-card: link que embrulha um bloco, não texto. */
.c-scroll-strip__item a {
  display: block;
  color: inherit;
}

/* galerias de tela do case — telas de app são mais estreitas que os
   cards da Home. As três galerias do case usam a mesma largura, ainda
   que os nativos variem entre 320 e 352, porque uniformidade dentro da
   mesma página vale mais que os 2% de densidade que se perde. */
.c-scroll-strip--screens { --item-w: 352px; }

/* Imagens dentro da scroll strip não devem esticar para preencher o
   item: exibem na largura intrínseca (= metade do @2x) e ficam
   nítidas em tela retina. max-width: 100% evita overflow no item. */
.c-scroll-strip img {
  width: auto;
  max-width: 100%;
}

/* ===================================================================
   11. c-glyph-list — lista onde o ✦ substitui o bullet.
   O glifo entra por ::before (não vai pro accessibility tree); a
   semântica de lista já vem do <ul>/<li>.
   =================================================================== */
.c-glyph-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.c-glyph-list li {
  display: flex;
  gap: var(--space-3);
  align-items: baseline;
}

.c-glyph-list li::before {
  content: "\2726";
  flex: 0 0 auto;
  font-size: var(--type-glyph-size);
  line-height: var(--type-body-lh);
  color: var(--color-text-faint);
}

/* ===================================================================
   12. c-details — acordeão em <details> nativo.
   Sem JS: abre e fecha sozinho, e o estado expandido já é anunciado
   pelo próprio elemento. O "+"/"−" vem do CSS, então não há texto
   duplicado pra leitor de tela.

   COR: `--c-green-700` (#006B4F) direto, não `--color-accent`/
   `--color-link` (#1B7A4A, o verde padrão do resto do site). Pedido
   explícito do Breno — o verde herdado da produção, que até 28/08
   ficava só registrado na paleta sem consumidor nenhum.

   HOVER: sem sublinhado, de propósito — igual ao `c-link-arrow` (§20),
   que também troca o sublinhado padrão de link (base.css §3) por um
   glifo que desliza. Aqui quem desliza é o próprio "+"/"−" do
   `::before`, mesma duração/easing do link-arrow, porque é o outro
   padrão "oficial" de hover verde que o site já tem — só sublinhado
   não seria consistente com nenhum dos dois. */
/* Caixa: borda cinza só em cima e embaixo, sem laterais — divide o
   acordeão do resto da coluna sem virar um card fechado. `display:flex`
   é o que permite reordenar visualmente o summary pro fim quando aberto
   (ver `[open]` abaixo), sem mexer na ordem do DOM: o `<summary>`
   continua sendo o primeiro filho, que é o que o HTML nativo exige pra
   funcionar como toggle. */
.c-details {
  display: flex;
  flex-direction: column;
  border-block: var(--border-hairline);
  padding-block: var(--space-6);
}

.c-details__summary {
  order: 1;
  cursor: pointer;
  color: var(--color-accent-strong);
  list-style: none;
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
}

.c-details__summary::-webkit-details-marker { display: none; }

.c-details__summary::before {
  content: "+";
  display: inline-block;
  width: 1ch;
  transition: transform var(--duration-link-arrow) var(--ease);
}

.c-details[open] .c-details__summary::before { content: "\2212"; }

.c-details__summary:hover::before,
.c-details__summary:focus-visible::before {
  transform: translateX(var(--link-arrow-shift));
}

/* Aberto: o conteúdo passa pra ordem 1 (visualmente antes do summary,
   que sobe pra ordem 2) — o gatilho "- details" some do topo e reaparece
   no fim, abaixo do conteúdo revelado, sem duplicar o toggle nem
   precisar de JS. `order` só reordena a PINTURA, não o DOM nem a ordem
   de foco/leitura, que continuam summary-primeiro — é o mesmo elemento,
   só muda onde ele aparece na tela. */
.c-details[open] .c-details__summary { order: 2; }

.c-details__content {
  order: 1;
  margin-top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

.c-details[open] .c-details__content { margin-top: 0; margin-bottom: var(--space-6); }

/* ===================================================================
   13. c-lead — a frase logo abaixo do título de cada break point
   ("The first interaction that didn't work" etc.). Nem rótulo nem
   corpo — é a frase que dá o tom do bloco.

   SANS, NÃO SERIFA — trocado em 03/09, pedido do Breno. Até aqui usava
   `--font-serif` itálico, no mesmo desenho do `c-title` acima dela;
   virou `--font-sans` reto (sem itálico) pra ler como a linha
   descritiva que ela é, não como uma segunda peça de título. O peso
   600 fica: mesmo em Inter, é o que ainda distingue esta frase de um
   parágrafo comum, sem precisar do itálico pra isso. O rótulo (`.c-eyebrow`,
   "BREAK POINT 0X") e o título serifado (`.c-title`) acima dela não
   mudam — só esta frase. */
.c-lead {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--type-caption-size);
  line-height: var(--type-title-lh);
  color: var(--color-text);
  margin: 0;
}

/* quando segue um título, cola nele em vez de flutuar */
.c-title + .c-lead { margin-top: var(--space-2); }

/* ===================================================================
   13b. c-hmw — a pergunta "How might we" dentro do acordeão de Details.
   Até 28/08 dividia classe com `.c-lead` (mesmo papel: "frase serifada
   de destaque"), mas o spec do Figma pede números próprios — 16px em
   vez de 12px, peso 500 em vez de 600, cor #555555 em vez do preto do
   título — então virou componente separado em vez de modificador. Em
   03/09 o `.c-lead` trocou pra `--font-sans` reto (ver §13); o `.c-hmw`
   não mudou, então os dois papéis (subtítulo de break point, pergunta
   HMW) hoje não compartilham valor nenhum, nem a família. */
.c-hmw {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 500;
  font-size: var(--type-hmw-size);
  line-height: var(--type-hmw-lh);
  color: var(--color-text-hmw);
  margin: 0;
}

/* ===================================================================
   14. c-case-meta — cliente e ano abaixo do título do case
   =================================================================== */
.c-case-meta {
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-lh);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: var(--space-4) 0 0;
  text-align: center;
}

/* Work page, item da capa em destaque (31/08): "Selected Work" precisa
   nascer exatamente assim, sem virar caixa alta — única instância de
   c-case-meta com este pedido. */
.c-case-meta--no-uppercase {
  text-transform: none;
}

/* ===================================================================
   15. c-lightbox — <dialog> nativo. Sem JS o atributo `open` nunca fica
   true, então isto nunca aparece: o navegador não sabe abrir um <dialog>
   sozinho. A imagem de origem continua clicável só visualmente depois
   que o JS liga .c-figure__img--zoomable (cursor de zoom).

   Três papéis dentro do mesmo <dialog>:
   · __stage  — caixa do tamanho da viewport, transparente, que rola
                quando a imagem passa a ser maior que ela (zoom).
   · __nav    — setas de assunto, só aparecem em grupo de 2+ imagens.
   · __close / __counter — cantos fixos.

   Os controles usam `position: fixed` e não `absolute`: dentro da top
   layer o referencial do fixed é a viewport, então eles ficam colados
   nas bordas da TELA em vez das bordas da imagem — que muda de tamanho
   a cada assunto e a cada nível de zoom.
   =================================================================== */
.c-figure__img--zoomable {
  cursor: zoom-in;
}

.c-lightbox {
  width: 100vw;
  max-width: 100vw;
  height: 100vh;
  height: 100dvh;
  max-height: 100dvh;
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  overflow: hidden;
}

.c-lightbox::backdrop {
  background: var(--color-scrim);
}

/* O `<dialog>` agora recebe foco de verdade ao abrir (main.js, `open()`
   — precisa de alvo confiável pras setas de teclado, não depende mais
   da escolha automática do navegador). Sem esta regra o anel genérico
   de `:focus-visible` (base.css §4) desenharia ao redor do viewport
   inteiro, já que o diálogo cobre a tela — nenhum controle interno
   perde o próprio anel, eles têm o dele (`.c-lightbox__close`,
   `.c-nav-arrow` e companhia continuam com o `:focus-visible` padrão). */
.c-lightbox:focus-visible { outline: none; }

/* O respiro lateral/vertical é o que impede a imagem de nascer por
   baixo do botão de fechar e das setas. */
.c-lightbox__stage {
  display: flex;
  width: 100%;
  height: 100%;
  /* no mobile o respiro maior é embaixo: é lá que ficam as setas e o
     contador, lado a lado. Nas laterais não cabe botão — 44px de cada
     lado comeriam quase um terço de uma tela de 375. */
  padding: var(--space-9) var(--space-5) var(--space-10);
  overflow: auto;
  overscroll-behavior: contain;
}

/* `margin: auto` e não `align-items: center`: num contêiner flex que
   rola, centralizar pelo alinhamento torna o transbordo de cima e da
   esquerda inalcançável (bug clássico do flexbox). Margem automática
   centraliza enquanto sobra espaço e colapsa pra zero quando não sobra,
   que é exatamente o que o zoom precisa. */
.c-lightbox__img {
  display: block;
  margin: auto;
  /* não encolher NUNCA. Item de flex encolhe por padrão, e o piso desse
     encolhimento (`min-width:auto`) para um elemento substituído é o
     tamanho intrínseco da imagem — bem menor que a largura que o zoom
     pede. Sem isto o zoom "funcionava" em tela estreita e era engolido
     em tela larga: a imagem era espremida de volta na largura do palco
     em vez de transbordar e virar área rolável. */
  flex: 0 0 auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  border-radius: var(--radius-sm);
  cursor: zoom-in;
}

/* No zoom o JS escreve width em px inline e solta os dois máximos; aqui
   fica só o que é aparência. */
.c-lightbox__img.is-zoomed {
  border-radius: 0;
  cursor: zoom-out;
}

/* Vídeo do lightbox — mesmo encaixe do `__img` (centraliza, nunca
   estoura o palco), sem cursor de zoom nem `is-zoomed`: vídeo não
   amplia por clique, o `controls` nativo já é a interface dele. Os
   dois dividem o mesmo palco e nunca aparecem juntos — main.js escreve
   `hidden` em quem não é a mídia da vez. */
.c-lightbox__video {
  display: block;
  margin: auto;
  flex: 0 0 auto;
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  border-radius: var(--radius-sm);
}

.c-lightbox__img[hidden],
.c-lightbox__video[hidden] {
  display: none;
}

.c-lightbox__close,
.c-lightbox__nav {
  position: fixed;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  color: var(--color-text);
  line-height: 1;
  cursor: pointer;
}

.c-lightbox__close {
  top: var(--space-5);
  right: var(--space-5);
  width: 40px;
  height: 40px;
  font-size: 20px;
}

/* Mobile: setas embaixo, uma de cada lado do contador — uma barrinha
   de controles só. Desktop: setas nas laterais, na altura dos olhos.
   O afastamento de 100px do centro deixa 26px entre a seta e o
   contador, que tem ~60px de largura. */
.c-lightbox__nav {
  bottom: var(--space-4);
  width: 44px;
  height: 44px;
  font-size: 24px;
  /* o glifo ‹ › tem a caixa alta desalinhada; este recuo ótico centra */
  padding-bottom: 3px;
}

.c-lightbox__nav--prev { left: calc(50% - 100px); }
.c-lightbox__nav--next { right: calc(50% - 100px); }

.c-lightbox__counter {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  margin: 0;
  padding: var(--space-1) var(--space-4);
  border-radius: var(--radius-full);
  background: var(--color-bg);
  color: var(--color-text);
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-lh);
}

@media (min-width: 700px) {
  .c-lightbox__stage { padding: var(--space-9) var(--space-11); }

  .c-lightbox__nav {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
  }

  .c-lightbox__nav--prev { left: var(--space-4); right: auto; }
  .c-lightbox__nav--next { right: var(--space-4); left: auto; }

  .c-lightbox__counter { bottom: var(--space-5); }
}

.c-lightbox__nav[hidden],
.c-lightbox__counter[hidden] { display: none; }

/* ===================================================================
   9. c-story-row — duas colunas: período à esquerda, conteúdo à
   direita. Usado no Work Experience e Education do About, nos três
   cases do Quick stories (Fase 4), onde a coluna esquerda leva
   título + cliente/ano em vez de só período, e na seção "now" da Home
   (28/08).

   NA SEÇÃO "now" quem ocupa a coluna esquerda é o PRÓPRIO `c-label` da
   seção (`<h2 class="c-label c-story-row__meta">`), não um `<p>` à
   parte: o pedido era alinhar o rótulo "now" com a primeira linha do
   título, igual a como "2023 — Now" alinha com "Senior Product
   Designer at Global Hitss" no About. Duas classes na mesma tag: a
   tipografia (família, 14px, minúsculo) vem de `.c-label`; a posição
   (coluna de 130px no desktop) e a cor (`--color-text-muted`) vêm de
   `.c-story-row__meta`, que GANHA nos dois — mesma especificidade, e
   `.c-story-row__meta` vem depois no arquivo. É a única seção do site
   sem um `.c-label` sozinho antes do conteúdo: aqui rótulo e "coluna do
   período" são o mesmo elemento, de propósito.
   Empilha abaixo de 700px — uma coluna, "now" em cima do título, igual
   ao resto do componente nesse breakpoint.
   =================================================================== */
.c-story-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.c-story-row__meta {
  margin: 0;
  color: var(--color-text-muted);
}

/* EXCEÇÃO ao verde padrão de `a` (base.css §3), gêmea da que `.c-label`
   já tem: em `/work` o rótulo "Visual Craft" é o link pra página do
   projeto. Continua sendo rótulo de seção antes de ser link, e o verde
   ali leria como link de prosa. O sublinhado no hover, herdado do
   global, é o que entrega que dá pra clicar. */
.c-story-row__meta a { color: inherit; }

/* "now" (Home, 31/08): este rótulo é h2.c-label + c-story-row__meta na
   mesma tag (§9 acima explica por quê) e herda --color-text-muted daqui.
   Pedido do Breno pra este rótulo específico: continuar --color-text,
   igual a qualquer outro .c-label sozinho. ID em vez de outra classe
   porque o elemento já carrega duas; mais uma ficaria confuso. */
#now-label {
  color: var(--color-text);
}

.c-story-row__title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: var(--type-body-size);
  line-height: var(--type-body-lh);
  color: var(--color-text);
  margin: 0;
}

.c-story-row__institution {
  margin: 0;
  color: var(--color-text-muted);
}

.c-story-row__body > p:not(:first-child) { margin-top: var(--space-2); }

/* variante compacta — Education, sem parágrafo de descrição */
.c-story-row--compact .c-story-row__body { display: flex; flex-direction: column; gap: var(--space-1); }

@media (min-width: 700px) {
  .c-story-row {
    flex-direction: row;
    gap: var(--space-8);
  }

  .c-story-row__meta {
    flex: 0 0 130px;
  }

  .c-story-row__body {
    flex: 1 1 410px;
    min-width: 0;
  }
}

/* Modificador "now" (Home, 31/08): ao contrário das outras instâncias
   de .c-story-row (About, Quick stories), esta nunca vira duas colunas
   — fica empilhada em qualquer largura. O link saiu de dentro do
   parágrafo (item próprio, ver index.html) e o ritmo rótulo -> corpo ->
   link segue --space-4, o mesmo respiro que .c-label usa pra separar
   rótulo de conteúdo em toda outra seção do site. */
.c-story-row--now {
  gap: var(--space-4);
}

.c-story-row--now .c-story-row__body > p.c-story-row__link {
  margin-top: var(--space-4);
}

@media (min-width: 700px) {
  .c-story-row--now {
    flex-direction: column;
    gap: var(--space-4);
  }

  /* flex-basis vira ALTURA em flex-direction:column — sem isto o
     rótulo (130px) e o corpo (410px) herdavam alturas fixas do layout
     de duas colunas que este modificador cancela. */
  .c-story-row--now .c-story-row__meta,
  .c-story-row--now .c-story-row__body {
    flex: none;
    min-width: 0;
  }
}

/* Modificador "quick-case" (Quick stories, 31/08): mesma lógica do
   "now" acima — nunca duas colunas, mesmo componente, página diferente.
   Meta centralizado e título em serifa itálica um degrau acima na
   escala (--type-body-size -> --type-name-size), só aqui. */
.c-story-row--quick-case .c-story-row__meta {
  text-align: center;
}

/* CABEÇALHO EM TRÊS PARTES desde 03/09, espelhando o `.c-bp-header` do
   case (rótulo → título → subtítulo, components.css §"c-bp-header" e
   §13): rótulo do projeto ACIMA do título (reaproveita o mesmo
   `.c-story-row__institution` que antes só existia embaixo, com o
   cliente/ano que a história já tinha), e um segundo
   `.c-story-row__institution` ABAIXO do título faz o papel de
   subtítulo, com o nome que antes era o título sozinho. Duas instâncias
   da MESMA classe, papéis diferentes por posição — não duas classes
   novas, porque a tipografia (sans, cinza) é idêntica nas duas.
   `> * + *` em vez de nomear as três tags: o meta tem rótulo, título e
   subtítulo empilhados, e o respiro entre eles é o mesmo --space-2 que
   separa eyebrow/título/lead no case. */
.c-story-row--quick-case .c-story-row__meta > * + * {
  margin-top: var(--space-2);
}

.c-story-row--quick-case .c-story-row__title {
  font-family: var(--font-serif);
  font-style: italic;
  font-size: var(--type-name-size);
  line-height: var(--type-name-lh);
}

@media (min-width: 700px) {
  .c-story-row--quick-case {
    flex-direction: column;
  }

  .c-story-row--quick-case .c-story-row__meta,
  .c-story-row--quick-case .c-story-row__body {
    flex: none;
    min-width: 0;
  }
}

/* ===================================================================
   17. c-media-carousel — galeria em faixa horizontal de figuras.
   Substitui a c-scroll-strip nas galerias de case, onde a legenda faz
   parte da figura e precisa viajar junto com ela.

   Três decisões desenham o componente:

   1. ALTURA fixa, largura livre. O contrário do que a faixa da Home faz.
      Imagens de proporções diferentes ficam alinhadas pela mesma linha
      de base sem serem cortadas nem esticadas.
   2. Começo alinhado ao texto, fim sangrando. A faixa ocupa a largura
      inteira (`full`), mas o primeiro item nasce onde a coluna de texto
      começa. A partir daí as imagens escapam da coluna até a borda.
   3. CSS primeiro. Sem JS a faixa é um scroller nativo com snap; o JS
      só acrescenta os dois botões, que ficam escondidos até `.js` existir.
   =================================================================== */
.c-media-carousel {
  --nav-arrow-size: 40px;

  /* Onde a coluna de texto começa. É a mesma conta da grade da página
     (base.css §6) resolvida numa linha só, porque aqui o elemento vive
     em `full` e precisa saber sozinho onde `content` estaria. */
  --carousel-inset: max(var(--gutter), (100% - var(--measure)) / 2);
  --gallery-height: var(--gallery-height-panel);
}

.c-media-carousel--screens { --gallery-height: var(--gallery-height-screen); }
.c-media-carousel--cards   { --gallery-height: var(--gallery-height-card); }

.c-media-carousel__track {
  display: flex;
  gap: var(--carousel-gap);
  margin: 0;
  padding: var(--space-1) var(--gutter) var(--space-1) var(--carousel-inset);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  /* o par do padding: é isto que faz o snap parar a próxima imagem no
     início da coluna de texto, e não colada na borda da tela. */
  scroll-padding-inline-start: var(--carousel-inset);
  /* barra escondida por estética; a rolagem continua inteira */
  scrollbar-width: none;
}

.c-media-carousel__track::-webkit-scrollbar { display: none; }

.c-media-carousel__item {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

/* A altura manda; a largura vem da proporção da imagem. `max-width:none`
   anula a regra global de base.css, que aqui espremeria a imagem. */
.c-media-carousel__item .c-figure img {
  height: calc(var(--gallery-height) * var(--gallery-scale));
  width: auto;
  max-width: none;
}

/* A legenda tem que preencher a largura da figura sem participar do
   cálculo dessa largura — senão uma frase longa esticaria o item e a
   imagem deixaria de mandar. `width:min-content` a tira da conta;
   `min-width:100%` a devolve ao tamanho da imagem depois que ela já
   foi decidida. */
.c-media-carousel__item .c-figure__caption {
  width: min-content;
  min-width: 100%;
}

/* ---- controles ---------------------------------------------------
   Só existem com JS: sem ele seriam dois botões mortos ao lado de um
   scroller que já funciona sozinho.
   ------------------------------------------------------------------ */
.c-media-carousel__controls {
  display: none;
  gap: var(--space-2);
  justify-content: flex-end;
  /* alinhados ao FIM da coluna de texto, não ao fim das imagens: numa
     tela larga a faixa vai até a borda e os botões ficariam longe demais
     de tudo que se lê. O mesmo recuo do início, espelhado. */
  padding-inline-end: var(--carousel-inset);
  margin-top: var(--space-6);
}

.js .c-media-carousel__controls { display: flex; }

/* nada para rolar (todas as imagens couberam): o JS marca hidden */
.c-media-carousel__controls[hidden] { display: none; }


/* ===================================================================
   16. c-nav-arrow — o botão redondo de seta.
   Componente próprio porque as duas galerias usam o mesmo desenho: a
   faixa horizontal o põe embaixo, à direita; o stepper o põe nas
   margens laterais, ladeando a imagem.
   =================================================================== */
.c-nav-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: var(--nav-arrow-size);
  height: var(--nav-arrow-size);
  border-radius: var(--radius-full);
  border: var(--border-hairline);
  background: var(--color-surface-subtle);
  color: var(--color-text);
  transition: opacity var(--duration-ui) var(--ease),
              background var(--duration-ui) var(--ease);
}

.c-nav-arrow:hover { background: var(--color-border); }

/* aria-disabled, e não o atributo disabled: o botão continua focável,
   então quem chega ao fim pelo teclado não perde o foco no meio da
   página. O clique é ignorado pelo JS. */
.c-nav-arrow[aria-disabled="true"] {
  opacity: 0.35;
  cursor: default;
  background: var(--color-surface-subtle);
}

.c-nav-arrow svg {
  width: 16px;
  height: 16px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ===================================================================
   18. c-media-stepper — a outra galeria: um estado por vez, no mesmo
   espaço, trocando por crossfade.

   Serve o caso em que as imagens são estados da MESMA tela. Lado a
   lado, o olho compara telas; empilhadas no mesmo lugar, o olho vê o
   que mudou. É por isso que existem duas galerias e não uma com
   modificador.

   Empilhamento por GRID, não por position:absolute. Todas as figuras
   ocupam a célula 1/1, então a altura da célula é a da figura mais
   alta e a legenda mais longa cabe sem precisar de altura fixa nem
   causar pulo de layout na troca.

   Setas e indicadores vivem na MESMA linha, abaixo do palco — não nas
   margens laterais da imagem. Ladeando a imagem elas ficavam distantes
   demais em telas largas e, no mobile, coladas na borda da tela; uma
   fileira só embaixo resolve as duas pontas do problema.
   =================================================================== */
.c-media-stepper {
  --nav-arrow-size: 40px;
}

/* Modificador — não do componente em si, mas de UM USO dele: quando o
   stepper existe só pra substituir o `c-figure-group` no mobile (ver
   §6, Before/After de Quick stories), ele precisa desaparecer assim que
   o par lado a lado assume, no mesmo breakpoint em que o grupo liga o
   próprio `display`. As outras instâncias do stepper (o case inteiro)
   não levam esta classe e continuam visíveis em qualquer largura. */
.c-media-stepper--figure-swap { display: block; }

@media (min-width: 700px) {
  .c-media-stepper--figure-swap { display: none; }
}

.c-media-stepper__stage {
  /* sem JS não há como saber qual estado está ativo, então as figuras
     ficam empilhadas na vertical e todas continuam legíveis. */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-8);
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* No grid o eixo se inverte: `justify-items` é que centra na horizontal,
   e `align-items:start` cola todas as figuras no topo da célula. Sem esse
   start a figura mais curta ficaria centrada na altura da mais alta e a
   imagem escorregaria alguns pixels para baixo. */
.js .c-media-stepper__stage {
  display: grid;
  align-items: start;
  justify-items: center;
}

/* A figura encolhe até a largura da imagem em vez de esticar no palco:
   é a imagem que define onde a legenda começa e termina. `max-width`
   segura o caso inverso, a coluna estreita do mobile. */
.c-media-stepper__slide {
  margin: 0;
  max-width: 100%;
}

.js .c-media-stepper__slide {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  /* a visibilidade só cai DEPOIS do fade — senão a figura que sai
     some de uma vez e não há crossfade. */
  transition: opacity var(--duration-fade) var(--ease),
              visibility 0s linear var(--duration-fade);
}

.js .c-media-stepper__slide[data-active] {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--duration-fade) var(--ease), visibility 0s;
}

/* a imagem manda na altura; object-fit segura a proporção quando a
   coluna é estreita demais para a largura natural (mobile). */
.c-media-stepper__slide img {
  height: calc(var(--stepper-height) * var(--gallery-scale));
  width: auto;
  max-width: 100%;
  object-fit: contain;
  margin-inline: auto;
}

/* --figure-swap (Debt Negotiation): a regra acima existe pras telas de
   celular RETRATO do stepper original, onde a largura raramente estoura
   o contêiner. Aqui as imagens são PAISAGEM (560×354) e no mobile
   estouram sempre — o object-fit:contain sobrava em letterbox vertical
   dentro da caixa de altura fixa, e esse vazio crescia conforme a tela
   apertava. A legenda nunca se moveu (a margem é `--figure-caption-gap`,
   constante); o que crescia era o espaço vazio ENTRE a imagem de
   verdade e a borda da caixa que a margem media a partir dela.

   Aqui a LARGURA manda: a imagem preenche 100% do palco e a altura sai
   da proporção dela, sem sobra nenhuma pro object-fit disfarçar. Before
   e After têm exatamente as mesmas dimensões — as duas sempre calculam
   a mesma altura pra qualquer largura de tela, então a célula do grid
   nunca varia entre os dois estados sem precisar de altura combinada. */
.c-media-stepper--figure-swap .c-media-stepper__slide img {
  height: auto;
  width: 100%;
  max-width: 100%;
  object-fit: fill;
}

/* mesmo truque do carrossel: a legenda preenche a largura da figura
   sem entrar no cálculo dela, senão seria o texto, e não a imagem,
   a decidir o tamanho. */
.c-media-stepper__slide .c-figure__caption {
  width: min-content;
  min-width: 100%;
}

/* ---- controles: seta, indicadores, seta ---------------------------
   Uma linha só, centralizada abaixo do palco. O estado ativo é o
   mesmo ✦ que abre as seções do site; os inativos são círculos. Os
   dois tons de cinza já existem: o do glifo é o de texto secundário,
   o do círculo é o mesmo da borda das setas.
   ------------------------------------------------------------------ */
.c-media-stepper__controls {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.js .c-media-stepper__controls { display: flex; }

.c-media-stepper__dots {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.c-media-stepper__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* alvo de toque de 24px em volta de um ícone de 16 */
  width: var(--space-6);
  height: var(--space-6);
  border-radius: var(--radius-full);
  color: var(--color-border);
  transition: color var(--duration-ui) var(--ease);
}

.c-media-stepper__dot:hover { color: var(--color-text-faint); }

.c-media-stepper__dot[aria-current="true"] { color: var(--color-text-muted); }

/* SVG, não glifo de texto: um `viewBox` só, com o círculo e a estrela
   desenhados em torno do MESMO centro geométrico (8,8). É esse
   compartilhamento de centro — não uma calibração medida a olho — que
   garante o alinhamento entre os dois estados. Glifo de texto (✦ por
   `content`) foi tentado antes e descartado: a métrica de fonte da
   estrela não é simétrica em volta da própria linha de base, então o
   olho pegava o salto ao trocar de estado. */
.c-media-stepper__dot-icon {
  width: var(--stepper-dot-active);
  height: var(--stepper-dot-active);
  fill: currentColor;
}

.c-media-stepper__dot-star { display: none; }

.c-media-stepper__dot[aria-current="true"] .c-media-stepper__dot-circle { display: none; }
.c-media-stepper__dot[aria-current="true"] .c-media-stepper__dot-star { display: block; }

/* Sem movimento: troca seca, sem fade e sem atraso na visibilidade.
   O bloco global de base.css já esmaga a duração, mas não o delay. */
@media (prefers-reduced-motion: reduce) {
  .js .c-media-stepper__slide,
  .js .c-media-stepper__slide[data-active] { transition: none; }
}

/* No mobile as setas encolhem junto com a fileira de controles, para
   não competir de tamanho com os indicadores entre elas. */
@media (max-width: 699px) {
  .c-media-stepper { --nav-arrow-size: 32px; }
}

/* ---------------------------------------------------------------
   case-toc-layout — shell de duas colunas em volta do corpo do case
   (título até o fechamento). Existe só para dar ao case-toc uma coluna
   própria na margem esquerda, alta o bastante pro position:sticky ter
   onde grudar durante toda a rolagem do case — sticky gruda dentro da
   altura da própria caixa em que vive, então essa caixa precisa ser tão
   alta quanto o conteúdo.

   Mesma conta de grade do .page (base.css §6), duplicada aqui porque
   este elemento vive em `full` e precisa saber sozinho onde `content`
   começa — a mesma técnica que --carousel-inset já usa por elemento.
   --------------------------------------------------------------- */
.case-toc-layout {
  /* float-bar-top (16px) + altura estimada da barra (~53px) + respiro =
     88px. Mesmo valor do scroll-margin-top das âncoras logo abaixo —
     uma variável só, pra nunca divergir. */
  --toc-top: 88px;

  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter), 1fr)
    [content-start] min(var(--measure), 100% - var(--gutter) * 2) [content-end]
    minmax(var(--gutter), 1fr) [full-end];
}

.case-toc-layout__content { grid-column: content; }

/* Hero -> título (case-prepaid, 31/08): irmãos direto de <main> ficam
   --rhythm (32/56px, base.css §6) afastados por padrão. Pedido do Breno
   era --space-6 (24px) aqui, igual ao respiro capa -> título do
   c-work-feature (§28, Work page) — margem negativa cancela a diferença
   entre os dois tokens, então o respiro final é sempre --space-6, nas
   duas larguras de tela. */
.c-figure--hero + .case-toc-layout {
  margin-top: calc(var(--space-6) - var(--rhythm));
}

/* Sangria dos .bleed que vivem dentro do case-toc-layout: nenhum é mais
   item direto do grid do `.page` — moram dentro do `.stack` flex acima,
   então `grid-column:full` não significa nada pra eles aqui. Mesma
   sangria, matemática de viewport em vez de trilha de grid: funciona
   porque `.case-toc-layout__content` já está centralizado na viewport,
   do mesmo jeito que a coluna `content` do `.page`.

   TRANSFORM, não a margem antiga. A primeira versão (28/08, sessão
   anterior) usava `width:100vw; margin-inline: calc(50% - 50vw)` — o
   mesmo truque de sangria de qualquer container centralizado. Funciona
   perfeito pros componentes SEM `max-width` próprio (carrossel, stepper:
   ocupam os 100vw inteiros, então "sangrar" e "centralizar" são a MESMA
   coisa). Quebra pros que TÊM `max-width` (`c-figure-group`, capado em
   1100px): a margem daquele truque é calculada pra empurrar a caixa até
   a borda ESQUERDA do viewport supondo que ela vai preencher os 100vw
   inteiros — quando o `max-width` corta a largura depois, só o lado
   direito encolhe, e a caixa fica colada na borda esquerda em vez de
   centralizada. Foi o bug reportado pelo Breno (offset visível nas
   galerias de Quick stories).

   `left:50%` (aqui, `margin-left:50%`, mesma coisa numa caixa em fluxo
   normal) + `transform: translateX(-50%)` não tem esse problema: o
   `margin-left:50%` ancora a caixa no CENTRO do container (que já é o
   centro do viewport), e o `translateX(-50%)` desloca pela METADE DA
   PRÓPRIA LARGURA JÁ RESOLVIDA da caixa — 720px se ela ocupa os 1440px
   inteiros, 550px se o `max-width` já cortou pra 1100px. Funciona igual
   nos dois casos, sem precisar saber se existe `max-width` ou não.

   `position:relative` + `z-index` (mais alto que o do `.case-toc` mais
   abaixo, ver ali) é o que garante que a galeria pinte POR CIMA do
   índice lateral quando os dois ocupam a mesma faixa vertical — antes
   disso era o inverso (índice sempre por cima, com o halo branco em
   `.case-toc::before`, removido nesta mesma rodada) e agora é a galeria
   que vence; o índice se apaga sozinho nesse instante (ver
   `.case-toc.is-covered` mais abaixo, controlado por main.js).

   O STEPPER (`c-media-stepper`) FICA DE FORA dessa troca de camada —
   pedido do Breno: o índice deve continuar visível por cima dele, sem
   escurecer. Por isso ele NÃO leva `position`/`z-index` aqui (só a
   sangria): sem `position`, o elemento continua "não posicionado", e um
   elemento POSICIONADO com z-index (o próprio `.case-toc`, sticky +
   `z-index:1`) sempre pinta por cima de conteúdo não posicionado,
   qualquer que seja a ordem no DOM — é o empilhamento padrão do CSS,
   não precisa de regra extra pra garantir isso. `main.js` espelha a
   mesma exceção do lado do JS: o stepper não entra na lista de galerias
   que disparam `.is-covered`. */

/* Sangria SEMPRE, sem `max-width` próprio: carrossel e stepper de
   /case-prepaid preenchem o `.case-toc-layout` inteiro, em qualquer
   largura em que apareçam. */
.case-toc-layout__content > .c-media-carousel.bleed {
  position: relative;
  z-index: 2;
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

.case-toc-layout__content > .c-media-stepper.bleed {
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

/* c-gallery-marquee sangra sempre, igual carrossel e stepper: pedido do
   Breno, a faixa é feita pra rolar edge-to-edge inclusive no mobile. */
.case-toc-layout__content > .c-gallery-marquee.bleed {
  position: relative;
  z-index: 2;
  width: 100vw;
  margin-left: 50%;
  transform: translateX(-50%);
}

/* Sangria CONDICIONAL: `c-figure-group` sangra só a partir de 1000px
   (onde ganha o `max-width:1100px`) — abaixo disso a largura padrão do
   componente (100% do `.case-toc-layout__content`, a própria coluna de
   leitura) já é o comportamento "sem sangria" que se quer. */
@media (min-width: 1000px) {
  .case-toc-layout__content > .c-figure-group.bleed {
    position: relative;
    z-index: 2;
    width: 100vw;
    margin-left: 50%;
    transform: translateX(-50%);
  }
}

/* Sentinela de 1px colada ao fim do bloco de título: dispara a
   visibilidade do case-toc (ver abaixo) quando sai por cima do viewport.
   position:relative no título é o que ancora o `bottom:0` do sentinela. */
.case-title { position: relative; }

.case-toc-sentinel {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 1px;
  height: 1px;
}

/* ---------------------------------------------------------------
   case-toc — índice lateral, desktop-only (≥ 1000px)
   Nasce em fluxo na altura do título (a caixa acima o coloca lá) e
   gruda ao rolar via position:sticky nativo — sem JS de posição.
   IntersectionObserver em main.js gerencia só .is-active e aria-current.
   display:none no mobile: invisível para leitores de tela em telas
   pequenas, onde o landmark seria ruído sem o visual correspondente.
   --------------------------------------------------------------- */
.case-toc { display: none; }

@media (min-width: 1000px) {
  .case-toc {
    display: block;
    grid-column: full-start / content-start;
    /* align-items:stretch é o padrão do grid: sem align-self:start a
       caixa do próprio nav esticaria até a altura da linha inteira
       (a altura de todo o corpo do case) e o sticky não teria como
       "flutuar" dentro dela — a caixa já seria do tamanho do contêiner
       em que deveria se mover. */
    align-self: start;
    justify-self: start;
    margin-inline-start: var(--gutter);
    position: sticky;
    top: var(--toc-top);
    width: 148px;
    text-align: left;
    /* Mais baixo que o `z-index:2` das galerias .bleed (ver seção
       case-toc-layout, mais acima neste arquivo): quando as duas caixas
       ocupam a mesma faixa vertical, é a galeria que pinta por cima —
       antes disso o índice sempre vencia, e o halo branco em
       `.case-toc::before` (removido em 28/08, pedido do Breno) existia
       só pra disfarçar essa sobreposição. Agora não tem o que disfarçar:
       o índice se apaga sozinho nesse instante (`.is-covered`, main.js
       controla via scroll, ver abaixo). */
    z-index: 1;

    /* some enquanto o título está à vista, aparece com fade assim que
       sai por cima (JS em main.js, via sentinela). visibility, não
       display: sticky exige que o item continue no fluxo do grid pra
       manter a conta de altura. O delay no estado oculto adia o
       visibility:hidden pro fim do fade-out, então o item não some de
       repente enquanto ainda estava opaco; no estado visível o
       visibility troca na hora, pra não atrasar o começo do fade-in. */
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--duration-fade) var(--ease),
                visibility 0s linear var(--duration-fade);
  }

  .case-toc.is-visible {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--duration-fade) var(--ease),
                visibility 0s linear;
  }

  /* Apaga o índice (sem tocar `visibility`, que continua "visible" o
     tempo todo aqui — só a opacidade anima) enquanto uma galeria .bleed
     ocupa a mesma faixa vertical na tela; main.js mede isso a cada
     scroll (`getBoundingClientRect` do `.case-toc` contra cada `.bleed`
     do case-toc-layout) e liga/desliga esta classe. Sobe a
     especificidade de propósito (0,3,0) pra vencer `.case-toc.is-visible`
     (0,2,0) sem precisar de `!important`. `pointer-events:none` evita
     que a faixa de 148px do índice, mesmo invisível, continue roubando
     clique da imagem por baixo — sem isso um clique bem na coluna
     esquerda de uma galeria acertaria um link do índice transparente em
     vez da imagem. */
  .case-toc.is-visible.is-covered {
    opacity: 0;
    pointer-events: none;
  }

  /* mesma família/peso do subtítulo do header ("Product Designer",
     Serif SemiBold Italic) — cor muted porque aqui é rótulo, não corpo. */
  .case-toc__label {
    font-family: var(--font-serif);
    font-style: italic;
    font-weight: 600;
    font-size: var(--type-body-size);
    line-height: var(--type-body-lh);
    color: var(--color-text-muted);
    margin-bottom: var(--space-3);
  }

  .case-toc__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
  }

  .case-toc__link {
    display: block;
    font-family: var(--font-sans);
    font-size: var(--type-eyebrow-size);
    line-height: 1.6;
    color: var(--color-text-muted);
    text-decoration: none;
    transition: color var(--duration-ui) var(--ease);
  }

  /* EXCEÇÃO ao verde padrão de `a` (base.css §3), documentada em
     specs/design.md §17: `case-toc` é nav, não link de prosa — fica
     escuro, cinza pro texto principal. `text-decoration:none` explícito
     aqui é o que faltava: sem ele, o sublinhado padrão de `a:hover`
     (base.css §3, especificidade 0,1,1) vencia o `text-decoration:none`
     da regra base do link (0,1,0, perde por especificidade mesmo vindo
     depois no arquivo) e sublinhava o item no hover — bug visível,
     corrigido em 28/08. `:focus-visible` entra junto: mesmo feedback pra
     quem navega por teclado, que antes não tinha nenhum. */
  .case-toc__link:hover,
  .case-toc__link:focus-visible {
    color: var(--color-text);
    text-decoration: none;
  }

  .case-toc__link.is-active {
    color: var(--color-text);
    font-weight: 700;
  }
}

/* âncoras do case: compensam a float bar fixa ao rolar via link ou
   teclado. Herdam --toc-top de .case-toc-layout, já que são descendentes
   dele agora — mesmo valor que o sticky usa, os dois sempre alinhados. */
#toc-context, #toc-role, #toc-bp1, #toc-bp2, #toc-bp3, #toc-closing {
  scroll-margin-top: var(--toc-top);
}


/* ===================================================================
   19. c-gallery-marquee — galeria em faixa, rolando sozinha, em loop
   infinito, e também rolável na mão (trackpad, touch). Componente
   reutilizável: nada aqui conhece Digital Subscriptions ou qualquer
   outro conteúdo específico — a altura vem de `--gallery-height`,
   redeclarável por instância/modificador exatamente como
   `c-media-carousel` faz com `--screens`/`--cards` (§17). O uso atual
   (`--gallery-height: var(--gallery-height-marquee)`) é só o valor
   default.

   A ROLAGEM É NATIVA, não `@keyframes`. `overflow-x: auto` fica ligado
   o tempo todo — nunca vira `hidden` — e é o próprio `scrollLeft` do
   elemento que se move, tanto no auto-scroll (main.js empurra ele a
   cada quadro) quanto no gesto do visitante (o browser já faz isso de
   graça). Os dois escrevem na MESMA propriedade, então nunca competem
   entre si; ver o comentário grande em main.js pra como o loop sem
   emenda e a pausa na interação funcionam em cima disso.

   `scroll-behavior: auto` é necessário: sem ele a faixa herdaria o
   `scroll-behavior: smooth` global do `<html>` (base.css §1), e cada
   micro-incremento do auto-scroll tentaria suavizar por conta própria
   em vez de deslizar direto.

   O JS entra em duas coisas que CSS não sabe fazer:

   1. CLONAR. Pra emenda não abrir buraco, a faixa precisa ser larga o
      bastante pra cobrir a viewport MAIS um grupo inteiro — nem mais
      que isso: o JS conta o mínimo de cópias que fecha essa conta pra
      cada largura de tela, não um número fixo. Os clones ganham
      `data-gallery-marquee-clone` + `aria-hidden`: continuam clicáveis
      (abrem o lightbox na imagem original), mas não contam duas vezes
      no índice do grupo nem no leitor de tela.

   2. AVANÇAR `scrollLeft` NO RITMO CERTO. `--marquee-speed` é px/s, o
      mesmo parâmetro das libs de marquee. Calibrar é mexer só no 45 de
      tokens.css.

   Sem JS (ou com reduced motion) nada disso acontece: não há clone, não
   há avanço automático, e a faixa fica um scroller horizontal nativo
   com as imagens no tamanho normal — já rolável na mão, porque
   `overflow-x: auto` nunca muda. É o mesmo conteúdo, sem movimento.
   =================================================================== */
.c-gallery-marquee {
  --gallery-height: var(--gallery-height-marquee);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-behavior: auto;

  /* BARRA DE ROLAGEM ESCONDIDA, ROLAGEM INTACTA. A faixa é um scroller
     de verdade (é `scrollLeft` que se move, ver main.js), então o
     browser desenha a barra nativa por baixo dela — visível o tempo
     todo em sistemas com barra clássica, e um risco cinza atravessando
     o desenho. Esconder é só a barra: arrastar com dedo ou trackpad
     continua funcionando igual, e o auto-scroll também, porque nada
     disto mexe em `overflow`.

     Precisa dos três: `scrollbar-width` é o padrão (Firefox e agora os
     baseados em Chromium recentes), `-ms-overflow-style` cobre o Edge
     legado, e o pseudo-elemento cobre WebKit/Chromium mais antigo. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.c-gallery-marquee::-webkit-scrollbar { display: none; }

/* A faixa sangra em QUALQUER largura, inclusive mobile — pedido do
   Breno. A regra que liga essa sangria (`.c-gallery-marquee.bleed`, sem
   `--frames`) vive na seção case-toc-layout, junto de carrossel e
   stepper, que sangram sempre pela mesma razão. */

/* "my photographies" (--frames) sangra em QUALQUER largura, ao
   contrario do marquee de Digital Subscriptions acima: sao fotos
   soltas, nao telas de app, e nao esticam a pagina no mobile.
   Especificidade igual a regra de mobile logo acima (0,3,0): vence por
   vir depois no arquivo, sem precisar de !important. */
.page > .c-gallery-marquee--frames.bleed,
.page > main > .c-gallery-marquee--frames.bleed { grid-column: full; }

.c-gallery-marquee__track {
  display: flex;
  width: max-content;
}

/* O respiro entre telas é margem, não `gap`. Com margem cada item já
   embute o próprio respiro, então somar as larguras reais (é isso que
   o JS faz — ver `groupWidth()` em main.js) dá o comprimento exato de
   um grupo, emenda incluída, mesmo que as imagens não tenham larguras
   idênticas. Com `gap` o último vão ficaria de fora dessa soma e a
   faixa saltaria a cada volta. */
.c-gallery-marquee__item {
  flex: 0 0 auto;
  margin-inline-end: var(--marquee-gap);
}

/* A ALTURA manda — mesma convenção de `c-media-carousel` (§17): a
   largura de cada tela sai da proporção da própria imagem, então
   telas de proporções diferentes ficam alinhadas pela mesma linha de
   base sem corte nem distorção.

   `.c-gallery-marquee__item img`, não `.c-gallery-marquee__item
   .c-figure img`: aqui o item É a figura (mesma tag, duas classes),
   não um wrapper em volta dela como no carrossel — não existe
   descendência entre as duas classes pra um combinador exigir. */
.c-gallery-marquee__item img {
  height: calc(var(--gallery-height) * var(--gallery-scale));
  width: auto;
  max-width: none;
}


/* ===================================================================
   20. c-link-arrow — link verde + seta, com a seta deslizando no hover
   no lugar do sublinhado padrão. Usos: "See more details" na Home,
   "Full cases available on request" em Quick stories, e os dois links
   de contato do footer (E-mail, Linkedin) nas quatro páginas.

   O glifo mora num <span> à parte no HTML, não dentro do texto corrido:
   é o que permite animar só ele. `transform` não participa do fluxo,
   então o texto nunca se desloca — a seta desliza por cima do espaço
   em branco que já existia depois dela. O glifo pode ser um caractere
   (→, ↓, ↗) ou um `<svg>` pequeno (ver `.c-icon-envelope` abaixo,
   E-mail no footer) — a regra só anima o `<span>`, não importa o que
   está dentro dele.

   A DIREÇÃO é o modificador (`--right` / `--down` / `--up-right`),
   escrito como uma variável local de eixo em vez de regras de
   `transform` inteiras repetidas — a distância (`--link-arrow-shift`,
   tokens.css) e a duração (`--duration-link-arrow`) são as mesmas nos
   três sentidos, só o eixo muda. `--up-right` seta os dois eixos ao
   mesmo tempo (X positivo, Y negativo): é o mesmo `translate()` de
   sempre, só que na diagonal — nenhuma regra nova precisa saber disso,
   o `translate(var(--tx), var(--ty))` já aceita os dois eixos juntos.
   Reduced motion não precisa de regra própria: a crushagem global de
   `transition-duration` (base.css §8) já cobre esta transição, como
   cobre todas as outras do site.
   =================================================================== */
.c-link-arrow {
  text-decoration: none;
}

/* especificidade (0,2,0). Precisa vencer tanto a regra genérica
   `a:hover` (0,1,1) de base.css §3 quanto a regra específica do
   footer (0,2,2) — por isso o hover do footer foi movido pra excluir
   `.c-link-arrow` (ver `.c-footer__row dd a:hover` acima) em vez de
   depender só de especificidade pra ganhar dela. */
.c-link-arrow:hover {
  text-decoration: none;
}

.c-link-arrow__glyph {
  display: inline-block;
  transition: transform var(--duration-link-arrow) var(--ease);
}

/* :focus-visible também dispara o deslize — quem navega por teclado
   merece o mesmo sinal de "isto é clicável" que o mouse recebe no
   hover; o contorno de foco (base.css §4) continua por cima, isto é
   só o extra. */
.c-link-arrow:hover .c-link-arrow__glyph,
.c-link-arrow:focus-visible .c-link-arrow__glyph {
  transform: translate(var(--link-arrow-tx, 0), var(--link-arrow-ty, 0));
}

.c-link-arrow--right    { --link-arrow-tx: var(--link-arrow-shift); }
.c-link-arrow--down     { --link-arrow-ty: var(--link-arrow-shift); }
.c-link-arrow--up-right {
  --link-arrow-tx: var(--link-arrow-shift);
  --link-arrow-ty: calc(var(--link-arrow-shift) * -1);
}

/* ícone de envelope do link de E-mail no footer: SVG monocromático,
   `currentColor` pra herdar o verde do link sem token de cor próprio.
   O reset global (base.css §1) já força `svg { display: block }`; o
   `<span class="c-link-arrow__glyph">` em volta é quem dá o contexto
   inline, então isto não quebra a linha de texto. */
.c-icon-envelope {
  width: 14px;
  height: 11px;
}

/* ===================================================================
   21. c-photo-frame — o passe-partout branco das fotos pessoais do
   About. Componente de UMA responsabilidade: emoldurar. Não sabe se
   está numa faixa em loop, numa pilha em leque ou solto no meio de um
   parágrafo — as duas seções de foto o reusam sem modificador nenhum.

   Três decisões que fazem a moldura ler como papel e não como card:

   1. O RADIUS É SÓ DA MOLDURA. A imagem dentro fica retangular, canto
      vivo. Arredondar as duas junto daria "card com imagem"; arredondar
      só a de fora dá "foto dentro de uma borda", que é o que o Figma
      pede.

   2. A ALTURA MANDA, a largura é livre — mesma convenção das três
      galerias (§17, §18, §19). Cada instância redeclara
      `--photo-frame-h` e a largura de cada foto sai da proporção dela,
      então retrato e paisagem convivem alinhados pela mesma linha de
      base, sem corte e sem distorção.

   3. `inline-flex` e não `block`. A moldura precisa encolher até a
      largura real da imagem: como bloco ela esticaria até o contêiner e
      sobraria branco de um lado da foto.

   A SOMBRA é a única do site, e é deliberada — ver a nota completa em
   tokens.css (--shadow-photo-frame). Moldura branca sobre página branca
   sem sombra simplesmente não é uma moldura.
   =================================================================== */
.c-photo-frame {
  display: inline-flex;
  background: var(--color-bg);
  padding: var(--photo-frame-pad);
  border-radius: var(--photo-frame-radius);
  box-shadow: var(--shadow-photo-frame);
}

/* `max-width: none` cancela o reset global de base.css §1 (`img { max-width:
   100% }`): aqui a largura vem da proporção e não pode ser capada pela
   caixa, senão a foto encolhe e a moldura fica com sobra branca. */
.c-photo-frame img {
  display: block;
  height: var(--photo-frame-h);
  width: auto;
  max-width: none;
  border-radius: 0;
}

/* -------------------------------------------------------------------
   Ponte com o c-gallery-marquee (§19): a faixa em loop passa a carregar
   molduras em vez de telas cruas.

   `.c-gallery-marquee__item img` (§19) e `.c-photo-frame img` têm a
   MESMA especificidade (0,1,1) e definem a mesma propriedade, então
   quem vence é a ordem no arquivo — frágil demais para deixar
   implícito. O modificador resolve por altura: alimenta
   `--photo-frame-h` a partir da escala de galeria, e as duas regras
   passam a calcular o mesmo valor, ganhe quem ganhar.

   O `padding-block` existe por causa da sombra. `overflow-x: auto`
   (§19) recorta o eixo perpendicular na borda da caixa de padding
   mesmo sem rolagem nenhuma: sem esse respiro a sombra das molduras
   seria decepada em cima e embaixo.
   ------------------------------------------------------------------- */
.c-gallery-marquee--frames {
  --gallery-height: var(--gallery-height-photo);
  --photo-frame-h: calc(var(--gallery-height) * var(--gallery-scale));
  padding-block: var(--space-4);

  /* O titulo "My photographies" mora numa <section> IRMA desta faixa
     (nao dentro dela, ver about.html), entao o respiro entre os dois
     vem do row-gap do grid da pagina (--rhythm: 32/56px) em vez do
     margin-bottom que .c-label usa em toda outra secao. Sem isto o
     titulo ficaria com um respiro maior que o de "About me" ou "My
     life" ali do lado.

     A conta parte de --photo-section-gap, nao do margin-bottom padrao
     de .c-label (--space-4, 16px, desde 28/08): foto emoldurada precisa
     de mais ar (ver o token em tokens.css). Como o h2 ja contribui os
     proprios 16px de margin-bottom, o que falta somar e a DIFERENCA
     entre os dois — por isso esta conta ainda subtrai --space-4 mesmo
     depois do rotulo padrao ter mudado de 8 para 16px.

     Um item de grid estabelece contexto de formatacao proprio pros
     filhos, entao a margem do h2 nao "vaza" pra fora da <section> (nao
     colapsa com a borda dela, diferente do que aconteceria em fluxo
     normal); ela fica contida dentro da altura da propria secao. O
     row-gap do grid conta a partir DALI, ja com os 16px do h2 inclusos
     — por isso a conta subtrai --space-4 uma vez. */
  margin-top: calc(
    var(--photo-section-gap) - var(--space-4)
    - var(--space-4) - var(--rhythm)
  );
}

.c-gallery-marquee--frames .c-gallery-marquee__item img {
  height: var(--photo-frame-h);
}


/* ===================================================================
   22. c-photo-stack — as 5 fotos de "my life" espalhadas como um leque
   de cartas na mesa. O oposto do marquee ao lado: ali o conjunto passa,
   aqui o conjunto fica parado e você escolhe uma.

   A SOBREPOSIÇÃO É MARGEM NEGATIVA, não `transform` nem `position:
   absolute`. As fotos ocupam de verdade menos espaço horizontal, então
   a pilha inteira continua sendo um item de fluxo normal: encolhe com a
   coluna, centraliza sozinha e não precisa de altura fixa no pai.

   O ÂNGULO vem por posição (`:nth-child`), não por atributo no HTML:
   é desenho do componente, não dado do conteúdo. Cinco ângulos pequenos
   e alternados (-6, 4, -3, 5, 2) — pequenos porque leque de foto de
   família é bagunça controlada, não pilha derrubada.

   NO HOVER a foto sob o cursor faz três coisas ao mesmo tempo: sobe,
   endireita PARCIALMENTE (fica com 35% do ângulo original, não com
   zero — endireitar de todo mata o leque) e vem para a frente. O
   z-index é o que faz ela aparecer inteira, por cima das vizinhas que
   estavam cobrindo a borda dela.

   `:has(:focus-visible)`, não `:focus-within`, acompanha o hover: o
   lightbox devolve o foco pra imagem clicada ao fechar
   (`lastTrigger.focus()` em main.js), sempre, clique de mouse ou
   teclado. Com `:focus-within` esse `focus()` bastava pra deixar a foto
   "presa" levantada depois de fechar o lightbox — o cursor já tinha
   saído dela havia muito. `:focus-visible` (na imagem, verificado via
   `:has()` no `<li>` porque é ela, não o item, que recebe o foco) só
   acende quando o navegador decide que o foco deveria ser visível
   (heurística de teclado): abrir via clique de mouse e fechar não deixa
   rastro, abrir via teclado (Enter/Espaço) continua mostrando o
   destaque ao voltar, que é o comportamento esperado pra quem navega
   assim.
   =================================================================== */
.c-photo-stack {
  display: flex;
  justify-content: center;
  align-items: center;
  /* Mesmo respiro de titulo da faixa em loop, mas a conta e OUTRA, e a
     diferenca e real: aqui o h2 e a pilha sao irmaos em FLUXO NORMAL
     dentro da mesma <section>, entao as margens adjacentes COLAPSAM
     (fica a maior das duas, nao a soma). Basta pedir o valor cheio: os
     8px do h2 somem dentro dos 40px daqui.

     Na faixa em loop o h2 esta numa <section> irma e a faixa e um item
     de GRID da pagina — item de grid nao colapsa margem com ninguem,
     entao la a conta e aditiva e precisa descontar os 8px do h2. Duas
     formulas diferentes para o mesmo respiro visual, por causa do
     contexto de formatacao de cada uma. */
  margin: var(--photo-section-gap) 0 0;
  padding: 0;
  list-style: none;
}

.c-photo-stack__item {
  --rot: 0deg;
  position: relative;   /* sem isto o z-index do hover não tem efeito */
  flex: 0 0 auto;
  --photo-frame-h: var(--photo-stack-h);
  transform: rotate(var(--rot));
  transition: transform var(--duration-reveal) var(--ease);
}

/* FRAME FIXO: ao contrário do `.c-photo-frame img` genérico (§21, largura
   livre pela razão de aspecto de cada foto, pensado pro marquee ao lado),
   aqui as 5 fotos precisam ser cartões IGUAIS — retrato uniforme, não
   passe-partout de tamanho variável. Seletor mais específico que o de §21
   (0,2,1 contra 0,1,1) então ganha sem precisar de `!important`; o
   marquee continua largura-livre, intocado.

   `object-fit: cover` preenche o cartão cortando o excesso. A ORIENTAÇÃO
   de cada fonte decide sozinha qual eixo é cortado: fonte mais larga que
   o cartão (razão > 0.7) perde as laterais, mais alta perde topo/base —
   não precisa de regra por imagem. Conferido nas 5 fotos atuais que o
   corte central mantém rosto/sujeito em quadro nas duas fontes que
   chegam mais largas (my-cats, me-and-my-bf); se uma foto nova cortar
   errado, ajuste só o `object-position` dela via seletor extra aqui,
   não mexa na regra base. */
.c-photo-stack__item .c-photo-frame img {
  width: var(--photo-stack-w);
  object-fit: cover;
  object-position: center;
}

/* cada foto entra um pedaço na anterior. O seletor irmão evita margem
   negativa na primeira, que puxaria a pilha inteira para a esquerda e
   desalinharia a centralização. */
.c-photo-stack__item + .c-photo-stack__item {
  margin-inline-start: var(--photo-stack-overlap);
}

/* ABAIXO DO DESKTOP (<1000px, mesmo limiar que `c-figure-group` usa pra
   sangria de verdade): o leque troca tamanho fixo em px por PORCENTAGEM
   do próprio contêiner — pedido do Breno depois de ver folga ou estouro
   nos dois degraus fixos antigos (120px/210px) em larguras
   intermediárias como 606px, que não caíam exatamente num dos dois
   breakpoints.

   A CONTA: 5 cartões, cada um entrando 35% no anterior
   (`--photo-stack-overlap` de sempre = -0.35 do cartão). Espaço total
   ocupado = 5 cartões − 4 sobreposições de 35% = 5 − 1,4 = 3,6 larguras
   de cartão. Pra esse total bater exatamente 100% do contêiner
   (`.c-photo-stack`, que não sangra — é a própria coluna de leitura),
   cada cartão vale 100% / 3,6. `flex-basis` e `margin-inline-start` em
   % resolvem os dois contra a MESMA base (a largura do `.c-photo-stack`,
   que é o container flex dos itens), então a soma bate sem sobrar nem
   faltar pixel — fluido em QUALQUER largura abaixo do desktop, não só
   nos degraus que existiam antes. `width:100%` na imagem (não mais
   `var(--photo-stack-w)`) é 100% do PRÓPRIO item agora que ele tem
   largura definida pelo `flex-basis`, não mais do contêiner inteiro.

   ALTURA por `aspect-ratio`, não mais `--photo-frame-h` em px: com a
   largura fluida, uma altura fixa por breakpoint não bateria mais a
   proporção de retrato do cartão (0,7) em larguras intermediárias.
   `aspect-ratio` mantém a MESMA proporção em qualquer tamanho, sem
   precisar de mais um salto de breakpoint. Especificidade igual à regra
   base (0,2,1) mas depois dela no arquivo, então vence sem precisar de
   `!important` — `object-fit`/`object-position` continuam vindo da
   regra base, só largura/altura mudam aqui. */
@media (max-width: 999px) {
  .c-photo-stack__item {
    flex-basis: calc(100% / 3.6);
  }

  /* `.c-photo-frame` (§21) é `inline-flex`, do tamanho da própria
     imagem (hug-content) — de propósito lá, onde a largura vem da
     proporção de cada foto solta no marquee. Aqui precisa do oposto:
     a moldura vira BLOCO cheio (100% do item, que já tem largura
     definida pelo `flex-basis` acima), senão a imagem em `width:100%`
     resolveria contra um contêiner sem largura própria (circular:
     moldura hug-content dependendo do tamanho da imagem, imagem em %
     dependendo do tamanho da moldura) e colapsava pra 0. `display:
     block`, não `flex`: dentro de flex a imagem vira item de flexbox e
     o `width:100%` some por causa do algoritmo de flex-basis — como
     bloco normal, `width:100%` da imagem resolve direto contra o
     content-box da moldura, sem essa camada extra. */
  .c-photo-stack__item .c-photo-frame {
    display: block;
    width: 100%;
  }

  .c-photo-stack__item .c-photo-frame img {
    width: 100%;
    height: auto;
    aspect-ratio: 0.7;
  }

  .c-photo-stack__item + .c-photo-stack__item {
    margin-inline-start: calc((100% / 3.6) * -0.35);
  }
}

.c-photo-stack__item:nth-child(1) { --rot: -6deg; }
.c-photo-stack__item:nth-child(2) { --rot:  4deg; }
.c-photo-stack__item:nth-child(3) { --rot: -3deg; }
.c-photo-stack__item:nth-child(4) { --rot:  5deg; }
.c-photo-stack__item:nth-child(5) { --rot:  2deg; }

.c-photo-stack__item:hover,
.c-photo-stack__item:has(:focus-visible) {
  z-index: 2;
  transform:
    translateY(var(--photo-stack-lift))
    rotate(calc(var(--rot) * var(--photo-stack-straighten)));
}

/* Reduced motion tira o MOVIMENTO, não o desenho. O leque estático fica
   (rotação é composição, não animação); o que sai é o deslocamento do
   hover. O z-index continua subindo: trazer a foto inteira para a
   frente é informação, e não custa movimento nenhum. */
@media (prefers-reduced-motion: reduce) {
  .c-photo-stack__item:hover,
  .c-photo-stack__item:has(:focus-visible) {
    transform: rotate(var(--rot));
  }
}


/* ===================================================================
   23. c-headline — a frase de abertura da Home. Inter 32 medium, e a
   ÚNICA quebra deliberada da regra "praticamente tudo é 14px" do
   DESIGN.md §1: a Home precisa de uma entrada, e a hierarquia dela vem
   de tamanho porque a serifa itálica já está ocupada com o papel de
   rótulo de seção em todo o resto do site.

   Sans e não serifa de propósito: `c-display`/`c-title` (§10) são
   serifa itálica e falam "título de case". Esta frase é a voz direta,
   não um título de peça.
   =================================================================== */
/* -------------------------------------------------------------------
   c-intro — o bloco de abertura da Home (headline + status + CTA).

   Houve aqui, por um dia, uma regra que estourava este bloco para fora
   da coluna de leitura (`content-start / full-end` mais um
   `--measure-hero` de 860px), para caber a headline em três linhas. Foi
   REMOVIDA a pedido: o transbordo não era desejado. A headline voltou
   para a medida de leitura e passou a quebrar em quatro linhas, que é o
   que cabe em 600px sem esticar nada.
   ------------------------------------------------------------------- */

.c-headline {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--type-headline-size);
  line-height: var(--type-headline-lh);
  letter-spacing: -0.01em;
  color: var(--color-text);
  margin: 0;
  text-wrap: pretty;
}

/* -------------------------------------------------------------------
   c-role-swap — o trecho da headline que alterna entre "product
   designer", "graphic designer", "motion designer" e "design engineer".

   A cor é o vermelhão (`--color-accent-emphasis`), acento categórico raro:
   exatamente o papel que a hierarquia de acento (tokens.css §2b)
   reservava para ele, e nunca cor de link.

   LARGURA RESERVADA É O PONTO INTEIRO DO COMPONENTE. A versão anterior
   deixava a linha refluir e o texto depois do trecho ("turning messy
   and...") andava a cada troca de palavra — era o defeito principal.
   Aqui o host vira `inline-block` com `min-width` da frase MAIS LARGA
   do conjunto, então a caixa nunca muda de tamanho e o que vem depois
   fica imóvel.

   `text-align: center`: as quatro frases variam 15px entre si (0,47em),
   e a folga tem que morar em algum lado da caixa. Já passou pelas duas
   pontas — ESQUERDA deixava a folga inteira DEPOIS da frase, colada em
   "turning" (lia como espaço duplo); DIREITA jogava a folga inteira
   ANTES, longe de "A". As duas erravam pelo mesmo motivo: concentravam
   os 15px inteiros de um lado só. Centralizado, cada lado recebe só a
   METADE da folga (~7px), então nem o respiro antes de "A" nem o de
   antes de "turning" chega a se destacar como espaço extra — o efeito
   é o mesmo motivo, aplicado nos dois lados em vez de um.

   `min-width` e não `width`: se uma frase nova e mais longa entrar no
   conjunto amanhã, a caixa cresce em vez de estourar o texto para fora.

   `white-space: nowrap` para a frase não quebrar no meio. Se a linha
   estourar, o bloco inteiro desce junto e a frase continua inteira —
   quebrar "product" / "designer" em duas linhas destruiria a reserva
   de largura, que é medida numa linha só.

   A TROCA é blur + fade, sem deslocamento nenhum. Nada de `transform`
   de propósito: qualquer deriva vertical leria como o movimento que
   este componente existe para eliminar.

   Sob reduced motion o JS não inicia o ciclo: fica a primeira frase,
   estática, na largura reservada — o estado parado já é o estado
   bonito. A largura continua sendo travada mesmo aí, para o texto
   nascer no mesmo lugar dos dois jeitos.
   ------------------------------------------------------------------- */
/* `.c-role` é o papel ESTÁTICO, o que a Home usa hoje: só a cor e o
   peso, sem reserva de largura nenhuma (não troca de palavra, então não
   há o que reservar). `.c-role-swap` continua existindo para a versão
   que anima, e `.scramble-role` para o `index-alt.html`; as três
   dividem a mesma cor para o vermelhão viver num lugar só.

   `nowrap` porque o trecho cai no meio da linha 2 da headline ("A
   product designer turning messy"): sem ele, "product" e "designer"
   podem se separar numa quebra e desmontar o desenho de três linhas. */
.c-role,
.c-role-swap,
.scramble-role {
  color: var(--color-accent-emphasis);
  font-weight: 500;
}

.c-role { white-space: nowrap; }

.c-role-swap {
  display: inline-block;
  min-width: var(--role-swap-w);   /* piso; o JS remede e reescreve em px */
  white-space: nowrap;
  text-align: center;
}

.c-role-swap__text {
  display: inline-block;
  transition:
    opacity var(--duration-role-swap) var(--ease),
    filter  var(--duration-role-swap) var(--ease);
}

.c-role-swap.is-swapping .c-role-swap__text {
  opacity: 0;
  filter: blur(var(--role-swap-blur));
}

/* `.scramble-role .dud` fica para o `index-alt.html`, o backup do Breno,
   que ainda roda a versão de embaralhar caractere. Nenhuma página
   publicada usa. */
.scramble-role .dud { opacity: 0.7; }


/* ===================================================================
   24. c-work-row / c-work-list — as três linhas da seção "work" da
   Home. Cada linha é um bloco inteiro dentro de um `<a>`, mesmo padrão
   do `c-case-card` (§7): o alvo de clique é a linha toda, não só o
   texto.

   Herda a exceção de cor do §7 — `color: inherit` cancela o verde
   padrão de `a` (base.css §3), porque isto é card, não link de texto.
   =================================================================== */
.c-work-list {
  position: relative;   /* referencial do balão de preview */
}

.c-work-list__rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.c-work-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--work-row-pad);
  background: var(--color-surface-subtle);
  border-radius: var(--radius-md);
  color: inherit;
  opacity: 1;
  transition:
    background-color var(--duration-ui) var(--ease),
    opacity var(--duration-ui) var(--ease);
}

/* Dark: Chromium achata o cinza da linha contra o fundo; um degrau
   acima devolve a separação que o Safari já mostrava. */
:root[data-theme="dark"] .c-work-row {
  background: #1b1b1f;   /* ajuste fino: 1 ou 2 pontos acima do --color-bg escuro */
}


/* Passar o mouse ou o foco por UMA linha apaga as outras duas — o
   contrário do que .c-work-preview (§25) faz sozinho não deixaria claro
   qual linha está "escolhida" quando o balão de capa também aparece.
   `:hover`/`:focus-within` no CONTÊINER (dispara com qualquer filho
   ativo) mais `:not(:hover)`/`:not(:focus-within)` em cada linha: só a
   que está de fato ativa escapa do dimming. Funciona sem :has() e sem
   JS — puro CSS, então cobre teclado de graça (o `:focus-within` do
   contêiner liga junto com o da própria linha focada). A crushagem
   global de `transition-duration` (base.css §8) já cobre reduced
   motion: a opacidade ainda muda, só que instantânea. */
.c-work-list__rows:hover .c-work-row:not(:hover),
.c-work-list__rows:focus-within .c-work-row:not(:focus-within) {
  opacity: var(--work-row-dim);
}

/* SÓ tira o sublinhado que `a:hover` (base.css §3) aplicaria por
   padrão: a linha hoveada NÃO muda de fundo nem de nenhuma outra
   forma. A diferenciação é inteira das OUTRAS linhas apagando (regra
   acima); mudar a hoveada também competiria com esse sinal em vez de
   reforçá-lo. `--color-surface-raised` continua declarado em
   tokens.css como registro, mas perdeu este consumidor. */
.c-work-row:hover {
  text-decoration: none;
}

/* `--color-text-muted-tint` (#5A5A62, tokens.css §2) e NAO
   `--color-text-muted` (#71717A): o muted padrao do site so passa AA em
   fundo BRANCO (4,83:1) — sobre o fundo tingido desta linha
   (--color-surface-subtle, #F0F0F2) ele cai pra 4,25:1, abaixo do
   minimo. O par calibrado da 6,00:1 aqui, com folga real. Rotulo
   claramente mais claro que a descricao e a hierarquia que o desenho
   pede: "Selected Case" precisa ler como categoria, nao como o mesmo
   peso do texto ao lado. */
.c-work-row__label {
  flex: 0 0 auto;
  color: var(--color-text-muted-tint);
}

/* --color-text-body (#3F3F46, 9,18:1 no mesmo fundo), nao --color-text
   (#111113): a descricao e corpo de texto, nao titulo — preto quase
   puro competiria com o proprio h1 do header em peso visual. */
.c-work-row__desc {
  color: var(--color-text-body);
  min-width: 0;
}

/* Acima do breakpoint as duas partes ficam lado a lado, alinhadas pela
   linha de base — o mesmo desenho de duas colunas do `c-story-row`
   (§9). Abaixo dele empilham, porque 124px de rótulo mais uma frase
   inteira não cabem lado a lado numa tela de 375px sem picotar a
   descrição em quatro linhas. */
@media (min-width: 700px) {
  .c-work-row {
    flex-direction: row;
    align-items: baseline;
    /* --space-3 (10px), não --space-6: o título mais comprido ("How we
       redesigned the three break points in the prepaid journey", 429px
       medido) só cabe numa linha se rótulo + gap não passar de ~120px
       dentro dos 552px de conteúdo da linha (600px de coluna menos o
       padding). */
    gap: var(--space-3);
  }

  .c-work-row__label { width: var(--work-row-label-w); }
}


/* ===================================================================
   25. c-work-preview — o balão que aparece à direita da linha de work
   no hover, mostrando a capa daquele item.

   ENRIQUECIMENTO, NÃO CONTEÚDO. `pointer-events: none` e `aria-hidden`:
   o balão nunca intercepta o clique que ia para a linha, e nunca é
   anunciado (a informação já está no texto da linha e no `alt` da capa
   na página de destino). Quem chega por teclado recebe o mesmo balão
   no `:focus-visible` da linha, mesma paridade que `c-link-arrow` (§20)
   e `c-photo-stack` (§22) já seguem.

   SÓ EM PONTEIRO FINO E TELA LARGA. A media query junta as três
   condições: largura (>=1024px, onde sobra gutter para o balão caber
   fora da coluna de 600px), `hover: hover` e `pointer: fine`. Num
   tablet ou celular o `:hover` de toque dispara no primeiro tap e
   ficaria grudado até o tap seguinte — por isso o componente inteiro
   só existe dentro da media query, em vez de ser escondido por cima.

   POSIÇÃO. `position: absolute` dentro de `.c-work-list`, não `fixed`:
   um ancestral com `opacity` < 1 (o `.reveal` durante o fade) viraria
   containing block de `fixed` e deslocaria o balão — a mesma armadilha
   que prende a float bar. `absolute` não se importa com opacidade.
   O `top` de cada linha é escrito pelo JS; o `translateY(-50%)` centra
   o balão na linha em hover.
   =================================================================== */
.c-work-preview { display: none; }

@media (min-width: 1024px) and (hover: hover) and (pointer: fine) {
  .c-work-preview {
    display: block;
    position: absolute;
    left: 100%;
    top: 0;
    margin-inline-start: var(--work-preview-gap);
    width: var(--work-preview-w);
    padding: var(--work-preview-pad);
    /* --color-surface-subtle, não --color-bg: o balão passou a casar
       com o fundo cinza-claro que a PRÓPRIA linha de work usa
       (.c-work-row), em vez de branco puro contra ele. O bico (abaixo,
       nas duas orientações) repete o mesmo valor de propósito — bico
       de cor diferente do corpo do balão pareceria um erro de registro,
       não uma seta. */
    background: var(--color-surface-subtle);
    border-radius: var(--work-preview-radius);
    box-shadow: var(--shadow-work-preview);
    pointer-events: none;
    opacity: 0;
    transform: translateY(-50%) scale(0.96);
    transform-origin: left center;
    transition:
      opacity var(--duration-ui) var(--ease),
      transform var(--duration-ui) var(--ease);
  }

  .c-work-preview.is-visible {
    opacity: 1;
    transform: translateY(-50%) scale(1);
  }

  .c-work-preview img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--work-preview-inner-radius);
  }

  /* o bico. Triângulo de borda, não quadrado rotacionado: rotacionar um
     quadrado exigiria repintar a sombra por baixo dele para o encaixe
     não aparecer. */
  .c-work-preview::before {
    content: "";
    position: absolute;
    top: 50%;
    inset-inline-start: calc(var(--work-preview-tip) * -1);
    transform: translateY(-50%);
    border-block: var(--work-preview-tip) solid transparent;
    border-inline-end: var(--work-preview-tip) solid var(--color-surface-subtle);
  }

  /* Quando não cabe à direita (janela estreita, zoom alto), o balão vira
     para o outro lado em vez de sangrar para fora da viewport. O JS
     mede e liga a classe; aqui só o espelho geométrico, com o bico
     trocando de borda junto. */
  .c-work-preview.is-flipped {
    left: auto;
    right: 100%;
    margin-inline-start: 0;
    margin-inline-end: var(--work-preview-gap);
    transform-origin: right center;
  }

  .c-work-preview.is-flipped::before {
    inset-inline-start: auto;
    inset-inline-end: calc(var(--work-preview-tip) * -1);
    border-inline-end: none;
    border-inline-start: var(--work-preview-tip) solid var(--color-surface-subtle);
  }
}


/* ===================================================================
   26. c-status — os dois indicadores de disponibilidade no header.
   Ponto colorido + texto curto. O verde é `--color-accent`, que a
   hierarquia de acento já reservava exatamente para "status dot"; o
   âmbar é `--color-accent-detail`, no papel de "detalhe raro" previsto para
   ele. Nenhum dos dois tinha consumidor até aqui.

   O texto carrega o significado sozinho ("Open to new roles",
   "Freelance, booked for now"), então o ponto é decorativo e leva
   `aria-hidden` no HTML — cor nunca é o único portador de informação.
   =================================================================== */
.c-status {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}

.c-status__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-lh);
  color: var(--color-text-muted);
}

.c-status__dot {
  flex: 0 0 auto;
  width: var(--status-dot);
  height: var(--status-dot);
  border-radius: var(--radius-full);
  background: var(--status-dot-color, var(--color-text-faint));
}

.c-status__dot--open   { --status-dot-color: var(--color-accent); }
.c-status__dot--booked { --status-dot-color: var(--color-accent-detail); }


/* ===================================================================
   27. c-work-grid / c-work-card — o índice de trabalhos da página /work.

   Relação com a Home: lá o `c-work-row` (§24) é uma LISTA de linhas
   compacta, teaser; aqui o card é a versão com capa, para a página que
   existe só para indexar o trabalho. Os dois apontam para os mesmos
   destinos de propósito, mas nenhum é reuso do outro — a Home precisa
   caber num bloco curto, esta página precisa mostrar imagem.

   O CARD INTEIRO É O LINK, mesmo padrão do `c-case-card` (§7), e herda
   a mesma exceção de cor: `color: inherit` cancela o verde padrão de
   `a` (base.css §3), porque isto é card e não link de texto.

   AS CAPAS TÊM PROPORÇÕES DIFERENTES (600×338 e 460×290), então a caixa
   fixa a proporção e o `object-fit: cover` recorta. Sem isso os dois
   cards teriam alturas diferentes e a grade ficaria torta.
   =================================================================== */
.c-work-grid {
  display: grid;
  gap: var(--space-8);
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 700px) {
  .c-work-grid { grid-template-columns: 1fr 1fr; gap: var(--space-7); }
}

.c-work-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  color: inherit;
}

.c-work-card:hover { text-decoration: none; }

.c-work-card__cover {
  margin: 0;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border-card);
  background: var(--color-surface-subtle);
}

/* O <picture> precisa esticar junto. Ele é só um wrapper, mas `height:
   100%` no <img> resolve contra o PAI, e um <picture> de altura
   automática devolve "auto" — a imagem parava na altura natural e
   sobrava uma faixa do fundo da moldura embaixo. Apareceu só na capa de
   proporção mais larga (600x338 numa caixa 16/10), a outra por acaso
   já enchia. */
.c-work-card__cover picture {
  display: block;
  width: 100%;
  height: 100%;
}

.c-work-card__cover img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-reveal) var(--ease);
}

/* A capa aproxima de leve no hover. `overflow: hidden` na moldura é o
   que segura o excedente. Reduced motion não precisa de regra própria:
   a crushagem global de `transition-duration` (base.css §8) já cobre. */
.c-work-card:hover .c-work-card__cover img,
.c-work-card:focus-visible .c-work-card__cover img {
  transform: scale(1.03);
}

/* Rótulo CENTRADO, que é o que distingue este formato do `c-work-row`
   da Home: ali rótulo e descrição ficam lado a lado, alinhados à
   esquerda; aqui os dois centralizam sob a capa. */
.c-work-card__label {
  margin: 0;
  text-align: center;
  font-family: var(--font-serif);
  font-style: normal;
  font-weight: 400;
  font-size: var(--type-label-size);
  line-height: var(--type-label-lh);
  letter-spacing: var(--type-label-tracking);
  color: var(--color-text);
}

.c-work-card__caption {
  margin: 0;
  text-align: center;
  color: var(--color-text-body);
  text-wrap: pretty;
}

/* Link "See all work" abaixo das linhas da Home. Margem própria em vez
   de entrar no `gap` da lista: ele não é um item de work, e herdar o
   respiro da lista o faria parecer uma terceira linha. */
.c-work-list__more { margin: var(--space-5) 0 0; }


/* ===================================================================
   28. c-work-feature — o bloco de Selected Case da página /work.

   Estrutura, reescrita duas vezes em 28/08 a pedido do Breno. Primeira
   versão pareava rótulo e título numa linha, no estilo `c-story-row`.
   Segunda: coluna só, de cima para baixo, rótulo "Selected Case"
   sozinho, capa, título. ESTA versão tirou o rótulo de vez: a capa
   agora é o PRIMEIRO ELEMENTO dentro de `<main>`, sem nada acima —
   pedido explícito de alinhar a posição vertical da capa com a de
   `/case-prepaid` (que também é o primeiro elemento de `<main>` ali,
   sem rótulo antes dela). As duas páginas medem igual agora: mesmo
   `<main>`, mesma primeira linha de grade, mesmo topo de imagem, pixel
   a pixel. O título continua abaixo da capa, no mesmo desenho
   tipográfico do título de `/case-prepaid` (`.c-display`, §10: serifa
   itálica, 32px, centralizado) — é a mesma leitura que a capa do case
   tem na própria página do case: primeiro a imagem, depois o nome do
   trabalho. O nome acessível da seção vem do título via
   `aria-labelledby` no HTML, não precisa de rótulo visível pra isso.

   O BLOCO INTEIRO É O LINK, mesmo padrão do `c-case-card` (§7) e do
   `c-work-card` (§27), e herda a mesma exceção de cor: `color: inherit`
   cancela o verde padrão de `a` (base.css §3), porque isto é bloco e
   não link de prosa. O título nasce preto porque herda de `.c-display`
   (§10), que já fixa a própria cor.

   A CAPA É A MESMA MOLDURA do `c-work-card__cover` (§27) — borda,
   raio, recorte e o zoom de 1,03 no hover. Reusar por composição em vez
   de reescrever: o `<figure>` leva as duas classes.
   =================================================================== */
.c-work-feature {
  display: block;
  color: inherit;
}

.c-work-feature:hover { text-decoration: none; }

/* Único acréscimo à moldura herdada de §27 (a borda, o raio e o recorte
   já vêm de lá): a PROPORÇÃO, 16/9 em vez de 16/10, porque esta capa
   aparece sozinha, sem uma segunda ao lado para alinhar, então não há
   motivo para recortar os 600×338 nativos. Na faixa de Quick Stories
   (§29) o 16/10 continua valendo, que lá a linha de base entre três
   capas é que manda. SEM `margin-top`: a caixa é o primeiro elemento
   do bloco agora (o rótulo que pedia esse respiro saiu), e qualquer
   margem aqui desalinharia o topo da capa com o topo do hero de
   `/case-prepaid` — ver o comentário do componente, acima. */
.c-work-feature__cover {
  aspect-ratio: 16 / 9;
}

/* O zoom no hover é o mesmo de §27, mas a regra de lá casa por
   `.c-work-card:hover` e aqui o pai é outro elemento. Mesma transição,
   mesmo valor, mesmo motivo: o `overflow: hidden` da moldura segura o
   excedente, e reduced motion já é coberto pela crushagem global de
   `transition-duration` (base.css §8). */
.c-work-feature:hover .c-work-card__cover img,
.c-work-feature:focus-visible .c-work-card__cover img {
  transform: scale(1.03);
}

/* Único acréscimo à tipografia herdada de `.c-display` (§10): o respiro
   até a capa acima. Tamanho, itálico, peso e centralização vêm todos de
   lá — é a mesma classe que `/case-prepaid` usa no próprio h1, de
   propósito, para os dois títulos lerem como o mesmo componente em
   páginas diferentes. */
.c-work-feature__title { margin-top: var(--space-6); }


/* ===================================================================
   29. c-cover-strip — faixa horizontal de capas clicáveis, ESTÁTICA.

   Irmã do `c-gallery-marquee` (§19) pelo formato e oposta dele pelo
   comportamento: aqui cada capa é um LINK para outra página, e faixa
   que anda sozinha transforma clicar num alvo em movimento. Nada se
   move sem gesto do visitante.

   Sem JS, e sem precisar de JS: `overflow-x: auto` com snap nativo já
   entrega rolagem por trackpad, dedo e teclado — focar uma capa fora
   da área visível já a traz para dentro, de graça.

   COMEÇO ALINHADO AO TEXTO, FIM SANGRANDO — a mesma decisão (e a mesma
   conta) do `c-media-carousel` (§17): a faixa vive em `full`, mas a
   primeira capa nasce onde a coluna de texto começa, e daí em diante as
   capas escapam até a borda da tela. É o que diz, sem legenda nenhuma,
   que há mais coisa à direita.

   As capas TÊM PROPORÇÕES DIFERENTES (460×290, 460×355, 460×353), e
   quem normaliza é a moldura do `c-work-card__cover` (§27), com
   `aspect-ratio` fixo e `object-fit: cover`. Sem isso as legendas
   ficariam em alturas diferentes e a faixa perderia a linha de base.
   =================================================================== */
.c-cover-strip {
  /* Onde a coluna de texto começa. Mesma conta da grade da página
     (base.css §6) resolvida numa linha só, porque o elemento vive em
     `full` e precisa saber sozinho onde `content` estaria. */
  --strip-inset: max(var(--gutter), (100% - var(--measure)) / 2);
  --strip-item-w: 460px;

  display: flex;
  gap: var(--space-6);
  margin: 0;
  padding: var(--space-1) var(--gutter) var(--space-1) var(--strip-inset);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* o par do padding: é isto que faz o snap parar a próxima capa no
     início da coluna de texto, e não colada na borda da tela. */
  scroll-padding-inline-start: var(--strip-inset);
  /* barra escondida por estética; a rolagem continua inteira, os três
     seletores pelo mesmo motivo do §19 */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.c-cover-strip::-webkit-scrollbar { display: none; }

/* `min(84vw, …)` no lugar de uma largura seca: no mobile a capa precisa
   deixar uma nesga da próxima aparecendo, que é o único aviso de que a
   faixa rola. Com largura fixa a segunda capa ficaria exatamente fora
   da tela em alguns aparelhos e a faixa pareceria uma imagem só. */
.c-cover-strip__item {
  flex: 0 0 auto;
  width: min(84vw, var(--strip-item-w));
  scroll-snap-align: start;
}

/* Variante do card de §27 para dentro da faixa: sem rótulo próprio (o
   rótulo da seção já disse "Quick Stories", repetir em cada capa seria
   ruído) e com a legenda À ESQUERDA. Centralizar só funciona na grade,
   onde o card é largo e isolado; em fila, texto centralizado quebra a
   coluna de leitura que as capas alinhadas acabaram de criar. */
.c-work-card--strip { gap: var(--space-3); }
.c-work-card--strip .c-work-card__caption { text-align: left; }


/* ===================================================================
   30. c-story-anchor — respiro de chegada nas âncoras de Quick stories.

   As três histórias viraram destino de link a partir da faixa de /work,
   e a float bar fica fixa no topo depois da rolagem: sem margem de
   chegada o navegador para com o título exatamente debaixo dela.

   88px é o mesmo `--toc-top` do índice do case (§18) — um valor só de
   "altura de barra mais respiro" no site inteiro, em vez de dois que
   podem divergir.
   =================================================================== */
.c-story-anchor { scroll-margin-top: 88px; }


/* ===================================================================
   31. c-intro-enter — entrada do texto de Context & Problem em
   /case-prepaid, pedido do Breno.

   Só este bloco (os dois parágrafos "At Claro, every time..." e "The
   through-line..."), não a seção inteira: o rótulo "Context & Problem"
   acima continua no fade plano de `.reveal` (base.css §8), que é o
   comportamento padrão do resto do site. Criar um componente novo em
   vez de mexer em `.reveal` global era a única opção que não mudava a
   entrada de TODA seção revelada em toda página.

   SEM JS NOVO: o gatilho é o MESMO que a seção `#toc-context` já tinha
   — quando o `IntersectionObserver` genérico de main.js marca a seção
   pai como `.reveal.is-visible`, este bloco anima junto, só que com um
   desenho próprio (fade + subida) em vez do fade plano herdado.

   `transition-delay: var(--duration-ui)` é o atraso leve pedido: o
   rótulo (sem delay) aparece primeiro, o texto entra um instante
   depois — mesma direção de leitura, um passo atrás do outro.

   Reduced motion: a seção ainda ganha `.is-visible` na hora (main.js
   já pula a espera do scroll quando a preferência está ligada), então
   o texto nasce na posição final sem nunca precisar animar; a
   crushagem global de `transition-duration` (base.css §8) já zera a
   duração, e `transition-delay: 0s !important` aqui, no mesmo padrão
   que `.highlight` já segue logo acima, garante que nem os 150ms de
   atraso sobrevivam à preferência. */
.js .c-intro-enter {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--duration-reveal) var(--ease) var(--duration-ui),
              transform var(--duration-reveal) var(--ease) var(--duration-ui);
}

.js .reveal.is-visible .c-intro-enter {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .c-intro-enter { transition-delay: 0s !important; }
}


/* ===================================================================
   32. Raio nas imagens de Before & After e Claro Clube em
   /quick-stories, pedido do Breno.

   Escopo por COMPONENTE, não por página: `.c-figure-group` (sem
   `--frames`) só existe em `/quick-stories` hoje — as duas galerias
   pedidas (o par Before/After da negociação de dívidas e a imagem
   única do Claro Clube) vivem dentro dele, então uma regra só pega as
   duas. `.c-media-stepper--figure-swap` é o equivalente mobile do par
   Before/After (troca pra ele abaixo de 700px, ver §18) — mesmas
   imagens, mesmo raio. O modificador `--figure-swap` é o que isola
   esta regra do stepper "Emergency Balance" de `/case-prepaid`, que
   usa `c-media-stepper` sem o modificador e não foi pedido pro raio.

   `--radius-sm` (8px), o mesmo valor de `.c-figure--card` — raio
   pequeno o bastante pra não competir com o `--radius-md` das capas
   maiores (hero do case, `c-work-card`). Só o raio, sem borda: estas
   imagens continuam "plain" (`.c-figure--plain`), a moldura com borda
   é outro componente (`.c-figure--card`) que ninguém pediu aqui. */
.c-figure-group img,
.c-media-stepper--figure-swap img {
  border-radius: var(--radius-sm);
}


/* ===================================================================
   33. c-project-showcase — a faixa de projeto de `/visual-craft`.

   Cada projeto é UMA seção com dois blocos: a coluna de texto (título,
   cliente, ficha técnica, descrição) e a galeria de mídia paginada.
   Medidas tiradas a pixel do Figma (`referencias/figma/visual-craft`,
   frame de 1491px): coluna de texto 232px, respiro texto→mídia 32px,
   recuo lateral da faixa 44px, mídia com 530px de altura fixa e
   larguras variadas, 32px entre um item e o próximo.

   TRÊS DECISÕES DESENHAM O COMPONENTE.

   1. ALTURA FIXA, LARGURA LIVRE, como nas outras três galerias do site
      (DESIGN.md §2.5). Uma peça em paisagem e uma em retrato se alinham
      pela mesma linha de base sem corte nem distorção, e a mídia real
      pode entrar depois com qualquer proporção sem tocar no CSS.

   2. TEXTO E GALERIA DIVIDEM A MESMA CÉLULA no desktop, não duas
      colunas de grade. A galeria é uma faixa de largura inteira que
      NASCE recuada (`padding-inline-start` = recuo + coluna de texto +
      respiro), então em repouso ela parece uma segunda coluna, mas ao
      paginar as peças escorregam POR CIMA do texto. Se fossem duas
      colunas de verdade a mídia estaria presa na coluna da direita e a
      sobreposição não teria como acontecer. É a mesma conta do
      `--carousel-inset` do §17, com a coluna de texto no lugar da
      coluna de leitura.

   3. SEM `scroll-snap`. As outras faixas do site usam snap obrigatório;
      aqui ele atrapalharia por dois motivos concretos: a primeira
      página descansa em `scrollLeft: 0` com o item recuado (fora de
      qualquer ponto de snap), e a última página é o fim da rolagem
      (clampada), que também não cai num ponto de snap. Com snap
      obrigatório o navegador desfaria as duas. Sem JS sobra um scroller
      nativo, arrastável com o dedo e com o trackpad, que é o que o snap
      dava de melhor mesmo.
   =================================================================== */
.c-project-showcase {
  --nav-arrow-size: 40px;

  /* onde a mídia começa. No mobile é só o recuo da faixa; no desktop
     abre espaço pra coluna de texto inteira (ver o breakpoint abaixo). */
  --showcase-media-inset: var(--showcase-inset);

  display: grid;
  gap: var(--space-6);
  align-items: start;
}

/* ---- coluna de texto --------------------------------------------- */
.c-project-showcase__text {
  padding-inline: var(--showcase-inset);
  max-width: calc(var(--measure) + var(--showcase-inset) * 2);
  margin-inline: auto;
}

.c-project-showcase__title {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 700;
  font-size: var(--type-showcase-title-size);
  line-height: var(--type-showcase-title-lh);
  color: var(--color-text);
  margin: 0;
}

/* cliente (CYRELA, INTELBRAS, MULTIPLUS). Mesmo desenho do
   `c-eyebrow--sm` do case, escrito aqui porque é dado do projeto e não
   rótulo de seção: quem lê vê o nome do cliente sob o título, não uma
   sobrancelha anunciando o que vem. */
.c-project-showcase__client {
  font-size: var(--type-eyebrow-sm-size);
  line-height: var(--type-eyebrow-sm-lh);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: var(--space-2) 0 0;
}

/* ---- ficha técnica ------------------------------------------------
   Grade de duas colunas dentro de um <dl>: rótulo cinza em caixa alta,
   valor preto. A coluna de rótulo é fixa (60px + 8px de respiro = os
   68px medidos no Figma) pra os cinco valores nascerem alinhados entre
   si; o valor ocupa o resto e quebra em duas linhas quando precisa
   (o Team de "Where to find?" quebra).
   ------------------------------------------------------------------ */
.c-project-showcase__meta {
  display: grid;
  grid-template-columns: 60px 1fr;
  column-gap: var(--space-2);
  margin: var(--space-6) 0 0;
  font-size: var(--type-eyebrow-size);
  line-height: var(--type-eyebrow-lh);
}

.c-project-showcase__meta dt {
  grid-column: 1;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.c-project-showcase__meta dd {
  grid-column: 2;
  margin: 0;
  color: var(--color-text);
}

.c-project-showcase__desc {
  margin: var(--space-6) 0 0;
  font-size: var(--type-showcase-body-size);
  line-height: var(--type-showcase-body-lh);
}

/* ---- galeria ------------------------------------------------------ */
/* `min-width: 0` porque a galeria é item de grade e a faixa dentro dela
   é mais larga que a tela por definição. Item de grade nasce com
   `min-width: auto`, que é "não encolha abaixo do conteúdo": sem esta
   linha a caixa da galeria cresce até a soma das peças (2392px numa
   tela de 1491), o scroller nunca tem o que rolar, e os controles se
   dão por inúteis e somem. Achado ao vivo, não na teoria. */
.c-project-showcase__gallery { min-width: 0; }

.c-project-showcase__track {
  display: flex;
  align-items: flex-start;
  gap: var(--space-7);
  margin: 0;
  padding-inline: var(--showcase-media-inset) var(--showcase-inset);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;

  /* A faixa cobre a coluna de texto no desktop (decisão 2 lá em cima),
     e o recuo dela é área vazia por cima do que se lê. Sem esta linha
     essa área engoliria o clique e o arrasto de seleção do texto que
     está VISÍVEL embaixo dela. Os itens devolvem o ponteiro pra si
     logo abaixo, então arrastar a faixa pelo dedo continua igual. */
  pointer-events: none;
}

.c-project-showcase__track::-webkit-scrollbar { display: none; }

.c-project-showcase__item {
  position: relative;
  flex: 0 0 auto;
  pointer-events: auto;
}

/* PLACEHOLDER — bloco temporário no lugar da imagem ou do vídeo que
   ainda não existe. A altura manda e a proporção vem do modificador.
   `.c-project-showcase__media` sozinha (sem tag) é a base que os três
   tipos de mídia dividem: altura fixa, largura livre. O que muda entre
   eles é só o seletor com tag logo abaixo — `div` fica com a proporção
   fabricada do placeholder (`aspect-ratio`, fundo rosa); `img`/`video`,
   com a proporção de verdade do arquivo, não tem por que herdar nada
   disso. `aria-hidden` no placeholder: caixa vazia não tem o que
   anunciar, e o nome da galeria já vem do `aria-label` da faixa. */
.c-project-showcase__media {
  display: block;
  height: calc(var(--gallery-height-showcase) * var(--gallery-scale));
  width: auto;
  max-width: none;
}

div.c-project-showcase__media {
  aspect-ratio: var(--showcase-ratio, 246 / 530);
  background: var(--color-surface-placeholder);
  display: flex;
  align-items: center;
  justify-content: center;
}

.c-project-showcase__item--wide { --showcase-ratio: 928 / 530; }

/* mídia de verdade É clicável — abre no lightbox. Imagem NÃO leva
   `cursor:pointer` aqui: o JS marca toda imagem de lightbox com
   `.c-figure__img--zoomable` (components.css §15), que já escreve
   `zoom-in` — o mesmo cursor que o carrossel do case usa pro mesmo
   gesto, então as duas galerias combinam. Vídeo não tem essa classe (não
   amplia, só toca), por isso o `pointer` mora aqui, escopado só a ele. A
   marca de acessibilidade real (tabindex, role, aria-label) é escrita
   pelo JS junto com o resto da ligação ao lightbox (main.js). */
video.c-project-showcase__media {
  cursor: pointer;
}

/* o ícone de play SÓ existe em item de vídeo (real ou placeholder).
   Absoluto contra o `<li>` — não contra a mídia — porque `<video>` não
   aceita filho renderizado por cima de si (o conteúdo dentro de
   `<video>` é só o alternativo pra quem não suporta o elemento, nunca
   uma sobreposição visual). `pointer-events:none` devolve o clique pra
   mídia debaixo, então não precisa de handler próprio no ícone. O disco
   translúcido por trás é o que dá contraste em qualquer still, claro
   ou escuro: sem ele o triângulo branco sumia num frame já claro. As
   duas cores vêm de `--color-media-scrim`/`--color-media-on-scrim`, o
   par que de propósito NÃO muda por tema (ver tokens.css §2). */
.c-project-showcase__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
  padding: 14px;
  box-sizing: border-box;
  border-radius: var(--radius-full);
  background: var(--color-media-scrim);
  fill: var(--color-media-on-scrim);
  pointer-events: none;
}

/* ---- mobile: altura fixa e compacta, mídia inteira sem corte -------
   Abaixo de 700px a mídia de verdade (`<img>`/`<video>`, nunca o
   placeholder `<div>`) troca de regra: a altura volta a mandar, como
   no desktop, só que num valor mais baixo (a mesma escala que o
   placeholder já usava, `--gallery-scale`) — "fixo e compacto" pra
   faixa nunca encolher nem saltar de tamanho entre um item retrato e
   um paisagem, o pedido do Breno depois de ver o item "respirando" a
   cada página. `object-fit: contain` centraliza e faz o letterbox: a
   imagem/vídeo inteiro sempre visível, sobra moldura em vez de corte
   nos dois lados do eixo que estiver de sobra. Isto substitui o
   desenho anterior (largura manda, altura acompanha, altura da faixa
   escrita por JS a cada página) — a faixa agora tem uma altura só,
   sempre a mesma, e não precisa mais de `overflow-y` nem de JS pra
   sincronizar nada. */
@media (max-width: 699px) {
  .c-project-showcase__item:has(> img.c-project-showcase__media),
  .c-project-showcase__item:has(> video.c-project-showcase__media) {
    width: min(84vw, 420px);
  }

  img.c-project-showcase__media,
  video.c-project-showcase__media {
    height: calc(var(--gallery-height-showcase) * var(--gallery-scale));
    width: 100%;
    max-width: none;
    object-fit: contain;
    object-position: center;
  }
}

/* ---- capa dupla: desktop primeiro, mobile primeiro embaixo do
   breakpoint ----------------------------------------------------
   Os dois arquivos de capa (`--cover-desktop`/`--cover-mobile`) ficam
   os dois no DOM, sempre nessa ordem-fonte — o que muda por viewport é
   só o `order` do flex item, nunca a marcação. Quando o projeto tem só
   uma capa, nenhuma das duas classes é usada e este bloco não se
   aplica: a ordem-fonte já é a única ordem.

   Os valores são NEGATIVOS, não 1/2: todo item comum da faixa fica sem
   `order` (inicial 0), e um flex ordena por esse número antes de olhar
   pra posição no DOM. Com 1/2 as duas capas iam parar DEPOIS do resto
   da galeria, que ficava em 0 — bug visto ao vivo no desktop, onde o
   segundo item da faixa (não uma capa) aparecia primeiro. */
.c-project-showcase__item--cover-desktop { order: -2; }
.c-project-showcase__item--cover-mobile { order: -1; }

@media (max-width: 699px) {
  .c-project-showcase__item--cover-desktop { order: -1; }
  .c-project-showcase__item--cover-mobile { order: -2; }
}

/* ---- controles: seta, indicadores, seta ---------------------------
   Mesma fileira do `c-media-stepper` (§18) e o mesmo `c-nav-arrow`
   (§16), com uma diferença: aqui os pontos são PÁGINAS de rolagem, não
   estados, e quantas páginas existem depende da largura da tela. Por
   isso a lista nasce vazia no HTML e o JS a preenche (e a refaz no
   resize). Sem JS não há ponto nenhum, e a fileira inteira fica
   escondida: a faixa já rola na mão.
   ------------------------------------------------------------------ */
.c-project-showcase__controls {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-7);
}

.js .c-project-showcase__controls { display: flex; }
.c-project-showcase__controls[hidden] { display: none; }

.c-project-showcase__dots {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.c-project-showcase__dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--space-6);
  height: var(--space-6);
  border-radius: var(--radius-full);
  color: var(--color-border);
  transition: color var(--duration-ui) var(--ease);
}

.c-project-showcase__dot:hover { color: var(--color-text-faint); }
.c-project-showcase__dot[aria-current="true"] { color: var(--color-text-muted); }

/* mesmo par círculo/estrela do stepper, desenhado em volta do mesmo
   centro geométrico (8,8) pra não haver salto na troca de estado. */
.c-project-showcase__dot-icon {
  width: var(--stepper-dot-active);
  height: var(--stepper-dot-active);
  fill: currentColor;
}

.c-project-showcase__dot-star { display: none; }
.c-project-showcase__dot[aria-current="true"] .c-project-showcase__dot-circle { display: none; }
.c-project-showcase__dot[aria-current="true"] .c-project-showcase__dot-star { display: block; }

/* ---- desktop: texto e galeria na mesma célula --------------------- */
@media (min-width: 700px) {
  .c-project-showcase {
    --showcase-media-inset: calc(
      var(--showcase-inset) + var(--showcase-text) + var(--showcase-gap)
    );
    grid-template-columns: 1fr;
  }

  /* as duas caixas ocupam a MESMA célula. A galeria vem depois no DOM,
     então pinta por cima sem precisar de z-index; o `z-index` explícito
     fica só como documentação de quem cobre quem. */
  .c-project-showcase__text,
  .c-project-showcase__gallery { grid-area: 1 / 1; }

  .c-project-showcase__text {
    position: relative;
    z-index: 0;
    /* o recuo some do lado direito: a largura declarada é recuo +
       coluna, e um padding dos dois lados comeria 44px da coluna de
       texto (medido ao vivo: 188px de texto em vez de 232, com a
       descrição quebrando duas linhas antes da hora). */
    padding-inline: var(--showcase-inset) 0;
    width: calc(var(--showcase-text) + var(--showcase-inset));
    max-width: none;
    margin-inline: 0;
    transition: opacity var(--duration-fade) var(--ease);
  }

  .c-project-showcase__gallery {
    position: relative;
    z-index: 1;
  }

  /* PAGINOU, O TEXTO SAI. Estado escrito pelo JS assim que a faixa sai
     do repouso (`scrollLeft > 0`), que é exatamente o instante em que a
     primeira peça começa a invadir a coluna de texto. Não é enfeite: o
     texto desapareceria de qualquer jeito debaixo da mídia opaca, e o
     fade é o que evita a leitura de "imagem cortando frase no meio".
     Sem JS o estado nunca entra, a faixa continua rolável e as peças
     passam por cima do texto sem o fade — nenhum conteúdo se perde,
     nenhuma UI morta fica na tela.

     Sem `visibility` nem `aria-hidden`: o texto continua no fluxo e no
     leitor de tela, porque ele é o conteúdo do projeto, não decoração
     da galeria. */
  .js .c-project-showcase.is-paged .c-project-showcase__text { opacity: 0; }

  /* Controles alinhados ao mesmo recuo do divisor largo (§34) e da
     mídia (`--showcase-inset`, o padding-inline-end de
     `.c-project-showcase__track`) — sem o padding aqui, `flex-end`
     empurraria os controles até a borda viva do bleed, um recuo maior
     que o resto da faixa e fora de eixo com o filete do divisor logo
     abaixo. */
  .c-project-showcase__controls {
    justify-content: flex-end;
    padding-inline-end: var(--showcase-inset);
  }
}

/* A entrada da seção reusa o `c-intro-enter` (§31) sem JS novo: o
   mesmo IntersectionObserver que marca `.reveal.is-visible` dispara a
   subida dos dois blocos. A galeria entra um passo atrás do texto,
   pra faixa parecer que CHEGA na página em vez de já estar lá. Só o
   atraso muda; a duração e a curva continuam as do componente.

   Isto ficou no lugar do empilhamento com sobreposição entre seções
   (`position: sticky`): ver MEMORY.md de 31/08 pro porquê. */
.js .c-project-showcase__gallery.c-intro-enter {
  transition-delay: calc(var(--duration-ui) * 2);
}

/* ===================================================================
   34. c-divider--wide — o mesmo ✦ do §4, na largura da FAIXA de
   projeto em vez da coluna de leitura.

   `/visual-craft` usa os dois: o divisor da intro fecha a coluna de
   600px, como em toda outra página; os divisores entre projetos
   acompanham a faixa, começando e terminando exatamente onde o texto e
   os controles do projeto começam e terminam. Sem isso o filete
   pararia no meio da faixa e a página perderia o alinhamento vertical
   que segura as cinco seções juntas.
   =================================================================== */
.c-divider--wide { padding-inline: var(--showcase-inset); }


/* ===================================================================
   35. c-project-gallery / c-project-stack — a página `/visual-craft`
   inteira depois de 01/09, quando ela deixou de ser cinco vitrines com
   ficha técnica e virou galeria pura: uma pilha de imagens por projeto,
   legenda embaixo, nada mais.

   SÃO DOIS COMPONENTES, não um. `c-project-gallery` é a CORRIDA (como
   as pilhas se distribuem na página) e `c-project-stack` é a PEÇA (como
   as imagens se empilham dentro de uma). Separados porque só o primeiro
   muda de comportamento por viewport; o segundo é o mesmo desenho em
   qualquer largura, e é ele que um dia vira componente React sozinho.

   DOIS COMPORTAMENTOS, UM BREAKPOINT, na corrida:

   · >=700px — grade de duas colunas, contida, sem rolagem lateral e
     sem transbordo nenhum: os cinco projetos fluem 2+2+1, a última
     pilha sozinha na própria linha. Era o desenho do tablet (frame do
     Figma, `referencias/figma/visual-craft/`) e passou a valer também
     no desktop em 02/09, a pedido do Breno — a versão anterior fazia a
     corrida escapar pela direita em rolagem horizontal; decisão
     revertida porque a página funciona melhor contida.
   · <700px — uma pilha por linha.

   O ALINHAMENTO É O QUE SEGURA A GRADE. Todo cartão da página tem a
   mesma largura e a mesma proporção, então todas as pilhas fecham na
   mesma altura e topo e legenda de todas caem na mesma linha — por mais
   que os cartões subam, desçam e girem lá dentro. A bagunça é
   deliberada DENTRO da peça e inexistente ENTRE as peças. O `--dy` de
   cada cartão é `transform`, que não ocupa espaço, e por isso pode
   variar à vontade sem empurrar legenda nenhuma.

   ONDE MORA A CONTA. `--stack-count`, `--stack-span-max` e
   `--stack-card-basis` ficam no ITEM da grade, não na pilha: propriedade
   customizada só desce na árvore, nunca sobe. Declarando no item, a
   pilha herda os três de graça. A base usada em QUALQUER viewport é
   sempre a de cinco cartões (`--stack-span-max`), não a da própria
   pilha: as células da grade são todas do mesmo tamanho, então dividir
   cada uma pela contagem da própria pilha daria cartão grande na pilha
   de quatro e cartão pequeno na de cinco, com as legendas caindo em
   alturas diferentes. Com a base fixa, o cartão é o mesmo em todas e a
   pilha de quatro só ocupa menos largura, centrada na célula.
   =================================================================== */
.c-project-gallery {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--stack-gap) var(--stack-gallery-gap-col);
  margin: 0;
  /* A galeria vive em `full` (a `.bleed` no HTML), mais larga que a
     coluna de leitura de 600px para caber duas pilhas por linha sem
     apertar — mesmo desenho do frame do Figma. `--showcase-inset` é o
     token que já cuidava exatamente disto na página (44px a partir de
     700px, a gutter abaixo), e agora vale em toda largura acima do
     breakpoint, não só no tablet. */
  padding-inline: var(--showcase-inset);
  /* TETO + CENTRALIZAÇÃO, mesmo par que `.c-project-showcase__text`
     (§33) já usa: `max-width` trava a caixa (padding incluso, o reset
     global é `border-box`) e `margin-inline: auto` centra o que sobra
     dentro da célula `full`, que continua ocupando o viewport inteiro
     por trás. Inerte por construção abaixo de `--gallery-freeze-w`
     (1024px): a grade de 2 colunas já nasce mais estreita que isso em
     qualquer tela onde ela existe, então não precisa de media query
     pra isolar o efeito — o teto só passa a valer quando o viewport
     de fato ultrapassa 1024px. Sem ele, o vão entre as duas pilhas
     crescia com a tela (a coluna alarga, a pilha centralizada nela
     não, o vazio nas bordas da coluna vira distância a mais entre uma
     pilha e a outra); com ele, o excesso de tela vira margem FORA do
     bloco de 2 colunas, e o espaçamento interno fica preso no valor
     que tinha a 1024px. */
  max-width: var(--gallery-freeze-w);
  margin-inline: auto;
  list-style: none;
}

.c-project-gallery__item {
  display: flex;
  justify-content: center;
  min-width: 0;
  /* `position: relative` só existe pra dar ao `z-index` abaixo algo pra
     comparar: sem posicionamento, z-index não faz nada. Não muda o
     lugar do item na grade, ele continua um item de grid normal. */
  position: relative;
  z-index: 0;
  /* A opacidade é do DIM DE IRMÃOS (regra logo abaixo). Fica declarada
     aqui, e não só no estado apagado, pra transição existir nos dois
     sentidos. */
  opacity: 1;
  transition: opacity var(--duration-ui) var(--ease);
}

/* SOBREPOR, NÃO EMPURRAR. O leque de §37 abre por `transform`, que não
   ocupa espaço de layout — a célula da grade nunca muda de largura, e
   por isso as folhas abertas de um projeto largo (6 itens) podem
   avançar visualmente sobre a célula vizinha em vez de arredar ela.
   Pra essa sobreposição pintar por CIMA da pilha ao lado (não atrás
   dela), o item ativo precisa subir na pilha de empilhamento — é isto
   que o z-index faz aqui, no mesmo trio de gatilhos do dim de irmãos
   logo acima (hover, foco por teclado, `.is-active` do scroll no
   mobile). Nenhum ancestral entre a folha e aqui corta esse transbordo
   — nem `.c-project-gallery`, nem este item, nem `.c-project-sheets`/
   `.c-project-sheets__row` (§37) declaram `overflow` nenhum, então o
   default `visible` do navegador fica de pé; se algum dia um desses
   ganhar `overflow: hidden` por outro motivo, o leque volta a cortar
   na borda da célula sem aviso nenhum aqui. */
@media (hover: hover) {
  .c-project-gallery__item:hover { z-index: 2; }
}

.c-project-gallery__item:has(:focus-visible),
.c-project-gallery__item.is-active {
  z-index: 2;
}

/* DIM DE IRMÃOS — mesma ideia do `c-work-row` (§24): passar o mouse (ou
   o foco) por UMA pilha apaga as outras, então o projeto em questão
   fica sozinho em cena enquanto as folhas dele se abrem.

   A DIFERENÇA PRO §24 É O GATILHO, e ela não é gosto. Lá o contêiner
   abraça as linhas de perto, então `:hover` NELE já significa "o mouse
   está numa linha". Aqui não: a galeria tem 44px de recuo lateral, uma
   goteira entre as colunas, e cada célula é bem mais larga que a
   pilha que mora nela. Com `:hover` no contêiner, passar o mouse por
   qualquer um desses vazios apagaria as CINCO pilhas ao mesmo tempo, o
   que lê como bug, não como foco. `:has()` amarra o gatilho no item de
   verdade: só apaga quando existe um item hoverado ou focado.

   `@media (hover: hover)` no ramo de ponteiro porque em tela de toque o
   `:hover` gruda depois do toque e deixaria as irmãs apagadas até o
   toque seguinte. Lá quem manda é o foco por rolagem (`.is-active`).

   O ramo de FOCO e o de ROLAGEM ficam fora da media query: teclado
   existe em tela de toque também, e o `.is-active` é justamente o
   caminho do mobile. */
@media (hover: hover) {
  .c-project-gallery:has(.c-project-gallery__item:hover)
    .c-project-gallery__item:not(:hover) {
    opacity: var(--sheet-dim);
  }
}

/* `:has(:focus-visible)` e não `:focus-within`: casa exatamente com o
   gatilho da abertura das folhas (§37), que também é `:focus-visible`.
   Com `:focus-within` um clique de mouse apagaria as irmãs sem abrir a
   pilha clicada — dois sinais discordando sobre quem está ativo. Os
   únicos focáveis dentro da galeria são os cinco botões, então não
   precisa nomear o botão no seletor. */
.c-project-gallery:has(:focus-visible)
  .c-project-gallery__item:not(:has(:focus-visible)) {
  opacity: var(--sheet-dim);
}

/* O MESMO APAGAR, dirigido por rolagem em vez de ponteiro. `.is-active`
   é escrito por um IntersectionObserver (main.js) no projeto mais
   visível da tela, só abaixo de 700px, onde não existe hover e a
   galeria é de uma coluna. */
.c-project-gallery:has(.c-project-gallery__item.is-active)
  .c-project-gallery__item:not(.is-active) {
  opacity: var(--sheet-dim);
}

@media (min-width: 700px) {
  .c-project-gallery { grid-template-columns: 1fr 1fr; }
}

/* ===================================================================
   c-project-stack — ARQUIVADA em 02/09. A peça: N cartões sobrepostos
   e girados, um alvo de clique.

   NÃO TEM MAIS CONSUMIDOR EM PÁGINA NENHUMA. `/visual-craft` passou a
   usar o `c-project-sheets` (§37), que troca o leque de polaroides
   giradas por folhas retas alinhadas pela base. Esta fica de pé, por
   inteiro e testável, em `specs/_design-system.html` (categoria Mídia,
   "c-project-stack, arquivado"): é a alternativa registrada, não código
   morto por esquecimento. Se voltar a ser usada um dia, basta remontar
   o HTML da demo — a peça é autossuficiente, ver a nota dos três
   custom properties logo abaixo.

   UM ALVO SÓ, e é o que separa esta pilha do leque do About (§22), onde
   cada foto é clicável por si. Aqui o `<button>` é a peça inteira: uma
   parada de foco, um clique, e o lightbox abre no primeiro cartão com
   as setas percorrendo o resto (`data-lightbox-single-target`, ver
   main.js). Cinco alvos por projeto seriam cinco paradas de tabulação
   para o mesmo assunto.

   A SOBREPOSIÇÃO É MARGEM NEGATIVA EM PORCENTAGEM, herdada do §22 e
   pelo mesmo motivo: os cartões ocupam de verdade menos espaço, então a
   pilha continua sendo um item de fluxo normal — encolhe com a coluna
   no mobile e no tablet, e no desktop recebe a largura calculada pelo
   item. Porcentagem resolve contra a MESMA base (a largura da pilha)
   nos dois lados da conta, então a soma fecha exata em qualquer
   viewport, sem um valor em px por breakpoint.

   O ÂNGULO VEM DO HTML, não do `:nth-child`. É a diferença deliberada
   para o §22, onde o ângulo é desenho do componente: lá é uma pilha só
   na página inteira, aqui são cinco lado a lado, e ângulo por posição
   faria as cinco caírem exatamente iguais. `--rot`, `--dy` e `--z` são
   escritos por cartão, e é isso que dá a cada projeto um desenho
   próprio.

   O LEQUE DO HOVER, ao contrário, é geometria e fica aqui: `--fan` abre
   os cartões a partir do centro por POSIÇÃO, que é a única coisa que a
   abertura precisa saber.
   =================================================================== */
.c-project-stack {
  /* ESTES TRÊS moravam em `.c-project-gallery__item` enquanto esta era a
     única peça da galeria. Vieram pra cá em 02/09, quando a página
     passou pro `c-project-sheets` (§37) e esta virou peça ARQUIVADA:
     ninguém mais consome eles no item da grade, e aqui dentro a peça
     fica inteira, montável sozinha em qualquer contêiner — é o que
     mantém a demo do design system viva sem depender do layout que a
     página usa hoje. `--stack-count` continua sendo o único que muda
     por instância, escrito no HTML da própria pilha. */
  --stack-count: 5;
  --stack-span-max: calc(1 + 4 * (1 - var(--stack-overlap)));
  --stack-card-basis: calc(100% / var(--stack-span-max));

  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--stack-caption-gap);
  width: 100%;
  /* `button` já vem zerado em base.css §1; só o cursor é próprio daqui.
     `zoom-in` e não `pointer` porque o clique amplia, não navega — a
     mesma leitura que `.c-figure__img--zoomable` dá no resto do site. */
  cursor: zoom-in;
  text-align: center;
}

/* O anel de foco do `:focus-visible` global (base.css §4) desenharia em
   volta do botão inteiro, legenda e folga da sombra incluídas: caixa
   larga e frouxa, que não mostra o que está sendo focado. Aqui ele volta
   a abraçar a pilha, com o mesmo traço e o mesmo respiro. */
.c-project-stack:focus-visible { outline: none; }

/* A ALTURA VEM DOS CARTÕES, não de um valor escrito. Como todo cartão da
   página tem a mesma largura e a mesma proporção, todos têm a mesma
   altura — e a pilha, que é só eles mais o respiro fixo, fecha na mesma
   altura em todas as cinco. É daí que sai o alinhamento das legendas,
   sem nenhuma medida à mão. O respiro é o que reserva espaço para o
   `--dy` de cada cartão e para a sombra da moldura, que `transform` e
   `box-shadow` não ocupam sozinhos. */
.c-project-stack__pile {
  display: flex;
  justify-content: center;
  align-items: center;
  /* `--stack-scale` (tokens.css) é 1 em toda largura e vira 0.9 a
     partir de 1000px: a pilha ocupa 90% da própria coluna no desktop
     em vez de 100%, e `align-items: center` no `.c-project-stack` pai
     recentraliza sozinho a sobra. Cartão e pilha encolhem JUNTOS
     porque a base do cartão (`--stack-card-basis`) é uma fração da
     largura DESTA pilha, não da coluna — não tem token duplicado. */
  width: calc(100% * var(--stack-scale));
  padding-block: var(--stack-pile-pad);
  border-radius: var(--radius-sm);
}

.c-project-stack:focus-visible .c-project-stack__pile {
  outline: 2px solid var(--color-focus);
  outline-offset: 3px;
}

.c-project-stack__card {
  --rot: 0deg;
  --dy: 0px;
  --fan: 0px;

  position: relative;          /* sem isto o --z não tem efeito */
  z-index: var(--z, 1);
  flex: 0 0 var(--stack-card-basis);
  min-width: 0;
  /* `translateZ(0)` no fim da cadeia não move nada (é zero), mas promove
     o cartão pra própria camada de composição da GPU. Sem isto, um
     retrato girado (`rotate`) e depois encolhido pela grade (a pilha
     no tablet renderiza uns 124px de largura, bem menor que a fonte)
     rasteriza a borda inclinada em software, sem suavização — dá
     serrilhado visível bem na moldura, que é onde o olho primeiro
     nota. `backface-visibility: hidden` reforça a mesma promoção de
     camada nos motores que não bastam com `translateZ` sozinho. As
     TRÊS declarações de `transform` do componente (esta, o leque do
     hover e o corte de reduced-motion) repetem o mesmo `translateZ(0)`
     de propósito — se só uma tivesse, o cartão entraria e saída da
     camada de GPU a cada hover, e a troca de camada nesse instante é
     ela mesma uma fonte de recomposição visível. */
  transform: translate(0, var(--dy)) rotate(var(--rot)) translateZ(0);
  backface-visibility: hidden;
  transition: transform var(--duration-reveal) var(--ease);
}

.c-project-stack__card + .c-project-stack__card {
  margin-inline-start: calc(var(--stack-card-basis) * -1 * var(--stack-overlap));
}

/* `.c-photo-frame` (§21) é `inline-flex`, do tamanho da própria imagem —
   certo lá, onde a largura vem da proporção de cada foto. Aqui precisa
   do oposto, pelo mesmo motivo já anotado em §22: a moldura vira bloco
   cheio do cartão, senão a imagem em `width:100%` resolveria contra um
   contêiner sem largura própria e colapsaria. */
.c-project-stack__card .c-photo-frame {
  display: block;
  width: 100%;
}

/* CARTÃO IGUAL PARA TODA FONTE (ver a nota do token em tokens.css).
   `object-position: center top` acerta as duas orientações com uma regra
   só: fonte mais larga que o cartão perde as laterais em partes iguais,
   fonte mais alta perde o pé e conserva o topo, que num print de tela é
   sempre onde está o assunto. */
.c-project-stack__card .c-photo-frame img {
  display: block;
  width: 100%;
  height: auto;
  max-width: none;
  aspect-ratio: var(--stack-card-ratio);
  object-fit: cover;
  object-position: center top;
  border-radius: 0;
  /* Explícito de propósito, não porque o padrão fosse outra coisa: a
     suavização em si vem do `srcset`/`sizes` no HTML (a fonte já chega
     perto do tamanho exibido, não precisa de reamostragem agressiva) e
     da promoção de camada no cartão (acima). `auto` documenta que
     ninguém deve trocar isto por `pixelated`/`crisp-edges` depois — um
     print de tela precisa continuar liso ao encolher. */
  image-rendering: auto;
}

/* O LEQUE. Hover e foco abrem os cartões a partir do centro e sobem a
   pilha inteira um fio. A rotação NÃO muda: endireitar mataria o
   desenho, e abrir já basta pra dizer que dá pra clicar. */
.c-project-stack:hover .c-project-stack__card,
.c-project-stack:focus-visible .c-project-stack__card {
  transform:
    translate(var(--fan), calc(var(--dy) + var(--stack-lift)))
    rotate(var(--rot))
    translateZ(0);
}

/* abertura por POSIÇÃO: os de fora andam o dobro dos de dentro, o do
   meio fica parado. Escrito até 5 porque 5 é o teto da pilha. */
.c-project-stack__card:nth-child(1) { --fan: calc(var(--stack-fan) * -2); }
.c-project-stack__card:nth-child(2) { --fan: calc(var(--stack-fan) * -1); }
.c-project-stack__card:nth-child(3) { --fan: 0px; }
.c-project-stack__card:nth-child(4) { --fan: var(--stack-fan); }
.c-project-stack__card:nth-child(5) { --fan: calc(var(--stack-fan) * 2); }

/* Reduced motion tira o MOVIMENTO, não o desenho — mesma regra do §22.
   A pilha girada continua girada (rotação é composição); o que sai é o
   leque e a subida. */
@media (prefers-reduced-motion: reduce) {
  .c-project-stack:hover .c-project-stack__card,
  .c-project-stack:focus-visible .c-project-stack__card {
    transform: translate(0, var(--dy)) rotate(var(--rot)) translateZ(0);
  }
}

/* A legenda é o único texto que sobrou da página. Serifa itálica, como
   todo título do site, num corpo entre o rótulo de seção (14px) e o
   nome do header (20px): ela nomeia a peça sem competir com o rótulo
   "visual craft" que abre a página. */
.c-project-stack__caption {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--type-stack-caption-size);
  line-height: var(--type-stack-caption-lh);
  color: var(--color-text);
}

/* A LISTA COMPLETA DE MÍDIA DO PROJETO, nunca desenhada. O atributo
   `hidden` já bastaria sozinho (nenhuma regra deste arquivo compete com
   ele pra esta classe nova), mas fica explícito aqui pelo mesmo motivo
   do `.c-lightbox__img[hidden]` em §15: documentar a intenção sem
   depender do leitor saber de cor o comportamento padrão do atributo.
   `subjectImages()` (main.js) lê o conteúdo dela quando o lightbox
   abre; o preview visível (`.c-project-stack__pile`, com no máximo 5
   cartões) é uma peça totalmente separada, e as duas nunca precisam
   bater no número de itens. */
.c-project-stack__media { display: none; }

/* -------------------------------------------------------------------
   Chegada de âncora. A faixa de capas de /work aponta pros `id` destes
   itens (`#whats-next` etc.). `scroll-margin-top` já vem de
   `c-story-anchor` (§30, aplicado no HTML) e resolve a chegada vertical
   — sem rolagem lateral nenhuma na grade, não precisa de mais nada
   aqui: o navegador rola a PÁGINA até o item, que já está contido na
   coluna, não escondido fora da viewport horizontal.
   ------------------------------------------------------------------- */


/* ===================================================================
   36. c-work-panel: os três cartões cinza que são a página `/work`
   inteira desde 02/09. Substituem o par `c-work-feature` (§28) +
   `c-cover-strip` (§29) + `c-story-row` (§9) que a página usava, e os
   três divisores ✦ que separavam as seções: o cartão é que separa
   agora, o ✦ sobrou só uma vez, antes do rodapé.

   UM COMPONENTE, TRÊS CONTEÚDOS. O casco é idêntico nos três (fundo,
   raio, padding, cabeçalho com título à direita), e o que muda é só o
   que entra embaixo do cabeçalho: uma capa (Selected work), uma fileira
   de três thumbnails (Quick stories), um leque de polaroides (Visual
   craft). É o que faz disto um componente e não três: em React viraria
   `<WorkPanel title eyebrow description>{children}</WorkPanel>`, com os
   três blocos de mídia como filhos.

   DOIS DELES SÃO UM LINK SÓ, o de Selected work e o de Visual craft:
   `<a class="c-work-panel">`, mesmo padrão do `c-case-card` (§7) e do
   `c-work-feature` (§28), com a mesma exceção de cor (`color: inherit`
   cancela o verde de `a` de base.css §3, porque isto é cartão e não
   link de prosa). O de Quick stories NÃO pode ser: os três thumbnails
   apontam para âncoras diferentes, então ele é `<section>` e cada tile
   é que é o link. Link dentro de link não existe em HTML, e mesmo que
   existisse seria um alvo ambíguo.

   NENHUMA LARGURA DO FIGMA ENTRA AQUI. O frame do Figma tem coluna de
   uns 446px e a do site tem 600; o que foi copiado do spec é o que não
   depende da largura (raio, padding, tipo, proporção) e todo o resto é
   fluido. Ver a nota dos tokens em tokens.css.
   =================================================================== */
.c-work-panels {
  display: flex;
  flex-direction: column;
  gap: var(--panel-gap);
}

.c-work-panel {
  display: block;
  /* explícito, e não só herdado do reset de base.css §1: o padding de
     8px precisa entrar na largura, senão o painel estoura a coluna. */
  box-sizing: border-box;
  padding: var(--panel-pad);
  border-radius: var(--radius-lg);
  background: var(--color-surface-panel);
  color: inherit;
}

/* o seletor de elemento só existe porque um dos três painéis não é
   link. Sem ele a regra casaria com a `<section>` também, onde
   `text-decoration` não significa nada. */
a.c-work-panel:hover { text-decoration: none; }

/* -------------------------------------------------------------------
   QUICK STORIES é o painel que NÃO é `<a>`: os três thumbnails vão
   pra âncoras diferentes, e link dentro de link não existe em HTML.
   O painel inteiro ainda precisa levar pra `/quick-stories`, então o
   alvo é um stretched link, padrão Bootstrap: um `<a>` real no título
   (`.c-work-panel__link`), com um `::after` absoluto cobrindo a
   `<section>` toda (que por isso ganha `position: relative`). Os três
   thumbnails sobem pra cima desse `::after` com `position: relative`
   e `z-index`, e é isso que deixa o clique deles chegar no link
   próprio em vez de cair no overlay do painel. */
.c-work-panel--stories {
  position: relative;
}

.c-work-panel__link {
  color: inherit;
  text-decoration: none;
}

.c-work-panel__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.c-work-panel--stories .c-work-thumb {
  position: relative;
  z-index: 1;
}

/* -------------------------------------------------------------------
   O CABEÇALHO. Mobile é coluna centrada com o título EM CIMA; a partir
   de 700px vira linha, texto à esquerda e título à direita.

   A inversão de ordem no mobile é `order`, não uma segunda cópia do
   markup: no DOM o bloco de texto vem primeiro (é ele que descreve o
   cartão, e é a ordem que faz sentido para leitor de tela e para o
   nome acessível do link), e só a pintura troca.
   ------------------------------------------------------------------- */
.c-work-panel__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  text-align: center;
}

.c-work-panel__title {
  order: -1;   /* só no mobile; a partir de 700px a linha reordena sozinha */
  margin: 0;
  font-family: var(--font-serif);
  font-style: italic;
  /* 450 não tem arquivo próprio: a família só traz um itálico display
     (fonts.css), e o casamento de peso do navegador resolve 450 no 500,
     que é esse arquivo. Fica 450 porque é o valor do spec e porque no
     dia em que a família ganhar um itálico mais leve a escolha muda
     sozinha, sem tocar aqui. */
  font-weight: 450;
  font-size: var(--type-title-size);
  line-height: var(--type-title-lh);
  letter-spacing: var(--panel-title-tracking);
  text-transform: lowercase;
  color: var(--color-text-body);
}

.c-work-panel__title a,
.c-work-panel__title a:hover {
  text-decoration: none;
}


.c-work-panel__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.c-work-panel__eyebrow {
  margin: 0;
  font-size: var(--type-eyebrow-sm-size);
  line-height: var(--type-eyebrow-sm-lh);
  color: var(--color-text-muted-panel);
}

.c-work-panel__desc {
  margin: 0;
  font-size: var(--type-caption-size);
  line-height: var(--type-caption-lh);
  color: var(--color-text);
  text-wrap: pretty;
}

@media (min-width: 700px) {
  .c-work-panel__head {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-6);
    text-align: left;
  }

  /* devolve o título à ordem do DOM, que na linha é a da direita. */
  .c-work-panel__title { order: 0; }

  /* QUICK STORIES foge do `space-between` genérico: o Figma mostra
     título e descrição colados na mesma linha, um espaço fixo entre
     os dois, não espalhados nas duas pontas do painel. */
  .c-work-panel--stories .c-work-panel__head {
    justify-content: flex-start;
    gap: var(--space-8);
  }
}

/* -------------------------------------------------------------------
   A MÍDIA de Selected work: a capa do case, largura interna cheia.
   `overflow: hidden` no `<figure>` (e não o raio na imagem) é o que
   segura o zoom de 1,03 do hover dentro do canto arredondado. Mesma
   mecânica do `c-work-card__cover` (§27), sem a borda de 1px que aquele
   tem: aqui a moldura é o painel.
   ------------------------------------------------------------------- */
.c-work-panel__media {
  margin: var(--panel-head-gap) 0 0;
  aspect-ratio: var(--panel-media-ratio);
  overflow: hidden;
  border-radius: var(--panel-media-radius);
}

/* A PROPORÇÃO MORA NA CAIXA, não na imagem, e isso não é gosto: os
   atributos `width`/`height` do `<img>` viram dica de apresentação de
   altura no UA stylesheet, e dica de apresentação vence `aspect-ratio`
   (que só age quando uma das duas dimensões é `auto`). Uma imagem com
   `width: 100%` e `aspect-ratio` no CSS continuava saindo com a altura
   NATIVA do arquivo. Com a proporção na caixa e `height: 100%` na
   imagem, os atributos continuam onde devem estar (reservando espaço
   contra layout shift) e a caixa é que manda. Mesma mecânica do
   `c-work-card__cover` (§27).

   O `<picture>` precisa esticar junto: ele é só um wrapper, e
   `height: 100%` na imagem resolve contra o PAI, e um `<picture>` de
   altura automática devolveria "auto" e sobraria faixa vazia embaixo. */
.c-work-panel__media picture {
  display: block;
  width: 100%;
  height: 100%;
}

.c-work-panel__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-reveal) var(--ease);
}

.c-work-panel:hover .c-work-panel__media img,
.c-work-panel:focus-visible .c-work-panel__media img {
  transform: scale(1.03);
}

/* -------------------------------------------------------------------
   OS THUMBNAILS de Quick stories. Três colunas iguais em qualquer
   largura. No mobile elas continuam três, encolhidas, com 4px de gap
   (`--panel-thumb-gap`), porque a fileira é a informação: são TRÊS
   histórias, e empilhar ou rolar esconderia isso.
   ------------------------------------------------------------------- */
.c-work-panel__thumbs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--panel-thumb-gap);
  margin: var(--panel-head-gap) 0 0;
}

.c-work-thumb {
  display: block;
  box-sizing: border-box;
  padding: var(--panel-thumb-pad);
  border-radius: var(--panel-thumb-radius);
  background: var(--color-bg);
  color: inherit;
}

.c-work-thumb:hover { text-decoration: none; }

/* mesma divisão de trabalho da capa acima: proporção na caixa, imagem
   esticando dentro dela. */
.c-work-thumb__frame {
  display: block;
  aspect-ratio: var(--panel-thumb-ratio);
  overflow: hidden;
  border-radius: var(--panel-media-radius);
}

.c-work-thumb__frame picture {
  display: block;
  width: 100%;
  height: 100%;
}

.c-work-thumb__frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--duration-reveal) var(--ease);
}

.c-work-thumb:hover .c-work-thumb__frame img,
.c-work-thumb:focus-visible .c-work-thumb__frame img {
  transform: scale(1.03);
}

/* -------------------------------------------------------------------
   O LEQUE de Visual craft: 5 polaroides, uma por projeto, na ordem em
   que os projetos aparecem em `/visual-craft`.

   REUSO REAL, não cópia: a moldura de cada foto é o `c-photo-frame`
   (§21) inteiro, o mesmo componente do leque do About (§22). Fundo,
   4px de passe-partout, raio de 4px e a sombra de `--shadow-photo-frame`
   vêm todos de lá, já calibrados por tema. O que este bloco escreve é
   só a geometria da fileira.

   TRÊS DIFERENÇAS deliberadas em relação ao §22:

   1. OS ÂNGULOS são os do spec (6,28 / 2,6 / 1,05 / −5,76 / −6,66) e
      não os do About (−6 / 4 / −3 / 5 / 2). Continuam vindo por
      `:nth-child` e não por atributo no HTML, pela mesma razão de lá:
      é desenho do componente, não dado do conteúdo.

   2. SEM LEVANTAR NO HOVER. Lá cada foto é um gatilho de lightbox e
      levantar é resposta ao alvo sob o cursor; aqui o painel inteiro é
      UM link para `/visual-craft`, e destacar uma foto sugeriria que
      ela leva a um lugar diferente das outras. As fotos não são alvo,
      são o teaser.

   3. LARGURA POR `min()` EM VEZ DE BREAKPOINT. A conta está explicada
      nos tokens; o efeito é que o leque encolhe continuamente e cabe
      dentro do padding do painel em qualquer largura, sem um degrau em
      700px que deixaria as larguras intermediárias erradas, o mesmo
      problema que o leque do About teve em 606px e que resolveu do
      mesmo jeito.
   ------------------------------------------------------------------- */
.c-work-fan {
  --fan-card-w: min(var(--fan-card-max), (100% + var(--fan-bulge-room)) / 5);

  display: flex;
  justify-content: center;
  align-items: center;
  /* `rotate()` não muda a caixa de layout, só a pintura: o canto de um
     cartão girado 6,66° desce uns 7px abaixo da linha onde o layout
     acha que ele termina. Medido nos dois extremos (6,9px na coluna de
     600px, 5,7px numa de 500px), então 8px de respiro vertical bastam
     pra fileira inteira caber dentro da PRÓPRIA caixa, e aí os 8px de
     padding do painel ficam livres, em vez de serem consumidos pelo
     que transborda daqui. Verificado em 320, 500, 700 e 1440px. */
  margin: var(--panel-head-gap) 0 0;
  padding-block: var(--space-2);
}

.c-work-fan__item {
  --rot: 0deg;
  flex: 0 0 var(--fan-card-w);
  transform: rotate(var(--rot));
}

/* mesma mecânica do §22: a sobreposição é margem negativa de verdade,
   então a fileira continua sendo um item de fluxo normal e centraliza
   sozinha. O seletor irmão evita margem negativa na primeira, que
   puxaria o leque inteiro para a esquerda. */
.c-work-fan__item + .c-work-fan__item {
  margin-inline-start: var(--fan-overlap);
}

.c-work-fan__item:nth-child(1) { --rot:  6.28deg; }
.c-work-fan__item:nth-child(2) { --rot:  2.6deg; }
.c-work-fan__item:nth-child(3) { --rot:  1.05deg; }
.c-work-fan__item:nth-child(4) { --rot: -5.76deg; }
.c-work-fan__item:nth-child(5) { --rot: -6.66deg; }

/* `.c-photo-frame` é `inline-flex` e hug-content por padrão (§21). Aqui
   precisa do oposto: bloco cheio, 100% do item, que já tem largura pelo
   `flex-basis`. Senão a imagem em `width: 100%` resolveria contra um
   contêiner sem largura própria e colapsava. Mesma correção (e mesma
   nota) que o §22 faz abaixo de 1000px. */
.c-work-fan__item .c-photo-frame {
  display: block;
  width: 100%;
}

.c-work-fan__item .c-photo-frame img {
  width: 100%;
  height: auto;
  max-width: none;
  aspect-ratio: var(--fan-card-ratio);
  object-fit: cover;
}

/* CORTE POR IMAGEM, a exceção que o próprio `c-photo-frame` prevê
   (ver a nota de §22): as cinco capas de projeto nascem em paisagem
   (1738x1348) e entram num cartão em retrato, então `cover` corta as
   laterais. Em três delas o centro é o assunto e o default resolve; em
   duas não, e aí o ajuste é `object-position` numa regra extra, nunca
   mexendo na regra base.

   4ª (Where to find?): o centro cai na sobreposição escura entre as
   duas telas e o cartão virava um retângulo preto. À esquerda está a
   lista de revendas, que é o assunto.
   5ª (Solar Energy): o centro corta a primeira tela ao meio. À esquerda
   ela aparece inteira, com o título legível. */
.c-work-fan__item:nth-child(4) .c-photo-frame img,
.c-work-fan__item:nth-child(5) .c-photo-frame img {
  object-position: left center;
}


/* ===================================================================
   37. c-project-sheets — a peça de `/visual-craft` desde 02/09, no
   lugar da pilha girada (`c-project-stack`, §35, arquivada).

   A METÁFORA MUDOU. Lá era um maço de polaroides jogado na mesa:
   cartões IGUAIS, girados, recortados por `object-fit` numa proporção
   única. Aqui são FOLHAS DE PAPEL empilhadas na borda de uma mesa:
   sem rotação nenhuma, cada folha com a orientação e a proporção que
   ela tem de verdade, todas encostadas na MESMA BASE. É a base comum
   que faz o conjunto ler como pilha; é a altura diferente de cada uma
   que dá o topo irregular.

   NADA DE CORTE. `--sheet-max-w` e `--sheet-max-h` formam uma CAIXA e a
   imagem se encaixa dentro dela sozinha, preservando a razão nativa
   (largura e altura em `auto`, os dois máximos fazendo o resto). Print
   de desktop bate na largura e sobra altura; tela de celular bate na
   altura e sobra largura. Nenhuma fonte é reenquadrada, que é
   exatamente o que a peça anterior fazia e esta não faz.

   A ABERTURA É `transform`, NÃO MARGEM — trocado em 03/09. A primeira
   versão abria por margem (devolver `--sheet-spread` do overlap em
   cada emenda), e isso ocupava layout de verdade: a fila engordava de
   fato, a célula da grade crescia, e num projeto de 6 itens as folhas
   abertas EMPURRAVAM a pilha vizinha pro lado. Pedido do Breno pra
   corrigir: a posição de cada projeto na grade não pode mudar nunca,
   hover ou não.

   Com `transform`, a pegada de layout da fila fica CONGELADA no valor
   de repouso (`margin-inline-start: -var(--sheet-overlap)`, sempre, em
   todo estado) — é só isso que decide a largura que a fila ocupa pra
   grade, e nunca muda. O leque abre por CIMA dessa pegada congelada,
   avançando pra fora dela; se avançar sobre a pilha vizinha, ele
   SOBREPÕE (a folha pinta por cima, z-index de `.c-project-gallery__item`
   sobe no §35), não desloca.

   A CONTA DO LEQUE virou explícita por precisar ser simétrica SEM
   depender de reflow: `--sheet-index` (0 a 5, via `:nth-child`) é a
   posição da folha na fila, `--sheet-count` é o total de itens do
   projeto (5 ou 6, escrito no HTML por instância — 4 folhas + pasta ou
   5 folhas + pasta). `--fan-x` centra os índices em zero —
   `índice − (contagem − 1) / 2` — e multiplica por `--sheet-spread`:
   o item do meio (ou os dois do meio, numa contagem par) fica perto de
   zero, as pontas ficam a `±(contagem−1)/2 × spread` cada lado. É a
   MESMA aritmética que a versão por margem produzia de graça via
   `justify-content: center` recentralizando a fila mais larga — só que
   agora calculada direto, porque `transform` não participa de layout e
   não tem mais recentralização nenhuma pra explorar.

   O DIM DOS IRMÃOS mora no §35, com a galeria: é comportamento da
   grade (quem apaga são os OUTROS itens), não da peça.
   =================================================================== */
.c-project-sheets {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sheet-caption-gap);
  width: 100%;
  /* `button` já vem zerado em base.css §1. `zoom-in` e não `pointer`
     pelo mesmo motivo da peça anterior: o clique amplia, não navega. */
  cursor: zoom-in;
  text-align: center;
}

/* O anel padrão de `:focus-visible` (base.css §4) abraçaria o botão
   inteiro, legenda incluída. Aqui ele volta a abraçar só a fila. */
.c-project-sheets:focus-visible { outline: none; }

/* `align-items: flex-end` é a regra que faz a pilha existir: é ela que
   encosta folhas de alturas diferentes na mesma base. `min-height`
   reserva a altura da folha mais alta possível, pra fila não mudar de
   altura conforme o projeto — sem isso as legendas de dois projetos
   lado a lado cairiam em linhas diferentes. */
.c-project-sheets__row {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  min-height: var(--sheet-max-h);
  border-radius: var(--radius-sm);
}

.c-project-sheets:focus-visible .c-project-sheets__row {
  outline: 2px solid var(--color-focus);
  outline-offset: 6px;
}

.c-project-sheets__item {
  flex: 0 0 auto;
  /* z-index crescente: cada folha pinta por cima da anterior, que é o
     que faz a emenda ler como papel sobreposto e não como recorte. A
     ordem do DOM já daria isso pra elementos posicionados; o
     `position: relative` é o que os torna posicionados. `transform`
     também criaria stacking context próprio sozinho, mas o
     `position: relative` já fazia esse trabalho antes do leque virar
     transform, e continua sendo o motivo mais simples de explicar. */
  position: relative;
  /* ÍNDICE DA FOLHA na fila, 0 a 5 — teto de 6 porque 6 é o máximo
     (5 folhas + pasta). Escrito por posição, não por conteúdo: a pasta
     é só mais um `:nth-child`, sem regra própria. */
  --sheet-index: 0;
  /* AVANÇO DO LEQUE pra este item: centra os índices em zero
     (`índice − (contagem−1)/2`) e escala por `--sheet-spread`. Ver a
     nota completa no cabeçalho da seção. `--sheet-count` tem fallback
     6 pra nunca ficar `NaN` se algum HTML esquecer de declarar. */
  --fan-x: calc(
    var(--sheet-spread) *
    (var(--sheet-index) - (var(--sheet-count, 6) - 1) / 2)
  );
  transform: translateX(0);
  transition: transform var(--duration-reveal) var(--ease);
}

.c-project-sheets__item:nth-child(1) { --sheet-index: 0; }
.c-project-sheets__item:nth-child(2) { --sheet-index: 1; }
.c-project-sheets__item:nth-child(3) { --sheet-index: 2; }
.c-project-sheets__item:nth-child(4) { --sheet-index: 3; }
.c-project-sheets__item:nth-child(5) { --sheet-index: 4; }
.c-project-sheets__item:nth-child(6) { --sheet-index: 5; }

/* A PEGADA DE LAYOUT — CONGELADA. Este é o único lugar que define
   quanto espaço a fila ocupa na grade, e ele NUNCA muda de estado
   nenhum: hover, foco ou `.is-active` só tocam `transform` (abaixo),
   que não é lido pelo layout. É isto que impede o projeto de empurrar
   o vizinho quando abre. */
.c-project-sheets__item + .c-project-sheets__item {
  margin-inline-start: calc(var(--sheet-overlap) * -1);
}

/* A ABERTURA. Hover e foco por teclado no desktop, `.is-active` (escrito
   pelo IntersectionObserver do main.js) no mobile, onde hover não
   existe. Os três estados aplicam o MESMO `--fan-x`, então a pilha abre
   igual venha de onde vier — folhas E pasta, que é só mais um item da
   mesma fila (ver a nota de largura em `__folder svg`). */
@media (hover: hover) {
  .c-project-sheets:hover .c-project-sheets__item {
    transform: translateX(var(--fan-x));
  }
}

.c-project-sheets:focus-visible .c-project-sheets__item,
.c-project-gallery__item.is-active .c-project-sheets__item {
  transform: translateX(var(--fan-x));
}

/* A FOLHA. Sem moldura: são peças de trabalho, não fotografia, e o
   passe-partout branco do `.c-photo-frame` (§21) lia como retrato numa
   parede, não como material impresso. A folha é só a imagem, de canto
   vivo, com a sombra abaixo fazendo o trabalho inteiro de separar uma
   da outra — sem ela, duas capturas de tela claras encostadas ficam
   impossíveis de distinguir. `--shadow-photo-frame` de novo aqui, não
   um token próprio: mesma sombra do §21, mesma calibragem por tema
   (mais fechada no dark, marrom no creamy), agora com um terceiro
   consumidor. Os DOIS máximos (largura e altura) continuam sendo o que
   preserva a orientação nativa de cada fonte — ver a nota do bloco
   acima. */
.c-project-sheets__item img {
  display: block;
  width: auto;
  height: auto;
  max-width: var(--sheet-max-w);
  max-height: var(--sheet-max-h);
  box-shadow: var(--shadow-photo-frame);
  /* Mesma nota do §35: a suavização ao encolher depende do `srcset` do
     HTML servir uma fonte perto do tamanho exibido. Explícito pra
     ninguém trocar por `pixelated` depois. */
  image-rendering: auto;
}

/* A PASTA — o único elemento da fila que não é mídia, e o que diz que a
   pilha abre. Fecha a fila (último filho) e entra na emenda como
   qualquer folha, então abre junto no leque.

   SVG fornecido pronto, com a cor de preenchimento já no próprio
   `path` (não vem de token nem muda por tema — pedido explícito, a
   pasta é sempre este amarelo). Por isso não tem `fill`/`stroke` aqui:
   qualquer coisa em CSS que tocasse `stroke` desenharia uma linha de
   contorno em cima do preenchimento, e o pedido foi claro, sem borda
   nenhuma.

   Dimensionada pela LARGURA (`width: var(--sheet-max-w)`), a mesma
   régua das folhas, e a altura segue sozinha pela proporção intrínseca
   do SVG (129 × 93 ≈ 1,4:1). Foi pela ALTURA numa primeira tentativa
   (bater com `--sheet-max-h`, a fileira inteira), mas a pasta é bem
   mais larga que alta — casada com a altura de uma folha em pé, ela
   engordava sozinha até um pouco menos da metade da fila inteira, o
   maior item de longe e o primeiro que o olho pega, não a mídia atrás
   dela. Pela largura ela vira só mais um item na fila, do mesmo porte
   de uma folha, e sobra baixa o bastante pra não competir com as
   folhas portrait ao lado — sentada na base, como qualquer uma delas
   mais curta que a mais alta da pilha. O desenho já toca a base do
   próprio viewBox (as três formas continuam até y≈93), então nenhuma
   compensação de baseline é necessária, diferente do ícone de traço do
   Lucide que este substituiu.

   `aria-hidden` no SVG: quem nomeia a ação é o `aria-label` do botão
   inteiro, e um segundo nome aqui só repetiria o mesmo assunto. */
.c-project-sheets__folder {
  display: flex;
  align-items: flex-end;
}

.c-project-sheets__folder svg {
  display: block;
  width: var(--sheet-max-w);
  height: auto;
}

/* Reduced motion. O corte global de `transition-duration` (base.css §8)
   já deixaria a troca instantânea; esta regra existe pra dizer a
   intenção por escrito, e porque ela vale pro estado por rolagem
   também — que, esse sim, é desligado inteiro no main.js: abrir e
   fechar pilha sozinho a cada rolagem é exatamente o tipo de movimento
   de que a preferência está fugindo. O DESENHO fica: a pilha continua
   empilhada, o dim continua apagando, só sem percurso entre os
   estados. */
@media (prefers-reduced-motion: reduce) {
  .c-project-sheets__item,
  .c-project-gallery__item {
    transition: none;
  }
}

/* Mesma legenda da peça anterior, escrita de novo em vez de dividir o
   seletor: as duas são independentes de propósito, e uma arquivada não
   deve poder quebrar a que está no ar. */
.c-project-sheets__caption {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  font-size: var(--type-stack-caption-size);
  line-height: var(--type-stack-caption-lh);
  color: var(--color-text);
}

/* A lista completa da pasta do projeto, nunca desenhada — mesma peça e
   mesmo motivo do §35, agora pendurada nesta. Ver a nota de lá e o
   `subjectImages()` do main.js. */
.c-project-sheets__media { display: none; }
