/* ==============================================================
   PROJETOS — página de listagem (projetos.html) e o template usado
   por todas as subpáginas de case study (projetos/*.html). Qualquer
   estilo novo aqui vale automaticamente para os próximos projetos
   publicados, sem precisar tocar em home/conteúdo/contato.
   ============================================================== */

/* ===== Projects listing page ===== */
.projects-page { background: #f5f5f5; }

/* Título do hero nesta página sem o padding lateral de 80px que
   .page-hero-title carrega por padrão (ver modulos/componentes.css). */
.projects-page .page-hero-title {
  padding: 0;
}

/* TESTE: sequência de entrada orquestrada (ver initProjectsIntro em
   script.js) — tela em branco (só o header) até o título, que nasce lá
   embaixo (alinhado com .projects-filter, medido via JS) e SURGE ali
   mesmo — junto com o wipe-mask, sem fade de opacity no bloco todo, só
   o mask interno (.wipe-text/.wipe-inner) controla a visibilidade do
   texto. Só depois que o wipe termina o título escorrega (só posição,
   sem opacity de novo) pra posição atual, e só então libera o hero-info
   e o resto da página. Referência: brand.squarespace.com/typography.
   .page-hero-title/.page-hero-info são compartilhados com Serviços/
   Conteúdos/Quem somos, por isso tudo aqui vive escopado em
   body.projects-page. data-custom-reveal no HTML faz esse título/info
   pularem os 3 loops genéricos de revealEl (ver guarda em script.js). */
.projects-page .page-hero-title {
  transform: translateY(var(--intro-offset, 0px));
  transition: transform 1.2s cubic-bezier(0.65, 0, 0.35, 1);
}

.projects-page .page-hero-info {
  opacity: 0;
}

.projects-page .page-hero-info.is-intro-visible {
  opacity: 1;
  transition: opacity 0.4s ease;
}

.projects-page .projects-listing {
  opacity: 0;
  transform: translateY(100px);
}

.projects-page .projects-listing.is-intro-visible {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 1.6s cubic-bezier(0.16, 1, 0.3, 1), transform 1.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.projects-listing { padding: 48px 0; }

.projects-filter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 40px;
}

.filter-group {
  display: flex;
  flex-wrap: wrap;
  gap: 54px;
}

/* Dropdown customizado (Figma node 2303:665) sobre o <select> nativo, que
   fica hidden no DOM só pra manter o .value/'change' que o filtro em
   script.js já lê — o trigger e a lista de opções são construídos ali a
   partir dos <option> reais, então os rótulos nunca duplicam entre HTML/JS. */
.filter-select {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.filter-select-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  border: none;
  border-bottom: 1px solid var(--gray-07);
  background: none;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 20px;
  color: var(--gray-07);
  cursor: pointer;
}

.filter-select-trigger img {
  width: 10px;
  height: 6px;
  transition: transform 0.2s ease;
}

.filter-select.is-open .filter-select-trigger img {
  transform: rotate(180deg);
}

/* O espaçamento de 16px até o trigger vira padding-top aqui (não `top:
   calc(100% + 16px)`) de propósito: padding fica dentro da caixa do
   elemento, então esse respiro continua "dentro" da área de hover do
   mouseleave em script.js. Com o gap como margin/offset puro, o mouse
   passava por uma zona morta ao descer do trigger até o painel e
   disparava o fechamento antes de alcançá-lo. */
/* Mesmo tipo de animação do menu do header do clay.global: anima a altura
   de verdade (0 -> altura real do conteúdo, medida em JS), não
   opacity/transform — com o mesmo easing (cubic-bezier 0.19,1,0.22,1) e
   duração (~0.6s) que eles usam no dropdown do nav deles. */
.filter-select-panel {
  position: absolute;
  top: 100%;
  left: -24px;
  z-index: 20;
  padding-top: 16px;
  height: 0;
  overflow: hidden;
  pointer-events: none;
  transition: height 0.6s cubic-bezier(0.19, 1, 0.22, 1);
}

/* [hidden] tem que vencer o display do elemento acima — como author-CSS
   sempre sobrepõe a folha de estilo do user-agent, uma classe já bastaria
   pra anular o [hidden] nativo se não fosse isso. JS só aplica [hidden]
   depois que a transição de saída termina (ver closePanel em script.js),
   pra dar tempo da animação acontecer. */
.filter-select-panel[hidden] {
  display: none;
}

.filter-select.is-open .filter-select-panel {
  pointer-events: auto;
}

.filter-select-panel-inner {
  display: flex;
  flex-direction: column;
  width: 240px;
  gap: 16px;
  padding: 24px;
  background: var(--white);
  border-radius: 4px;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
}

.filter-select-option {
  display: block;
  width: 100%;
  border: none;
  background: none;
  text-align: left;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 20px;
  color: var(--gray-07);
  cursor: pointer;
  transition: color 0.2s ease;
}

.filter-select-option:hover {
  color: var(--gray-06);
}

/* O <span> interno (não o botão, que é width:100% pra manter a área de
   clique inteira) é o que recebe a linha — assim ela só vai até o final
   da palavra. JS move a classe .is-active pra opção que bate com o valor
   atual do <select>, então a linha sempre marca o filtro ativo, seja
   qual for a opção escolhida (não fica fixa na primeira). Mesma técnica
   de scaleX + flip do transform-origin usada em .project-tile-content::after
   e .news-card-content::after, só que ao contrário: em vez de visível-e-
   apaga-no-hover, começa apagada e entra no hover (ou fica sempre visível
   se a opção for a ativa). */
.filter-select-option-text {
  position: relative;
  display: inline-block;
}

.filter-select-option-text::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.35s ease;
}

.filter-select-option:hover .filter-select-option-text::after,
.filter-select-option.is-active .filter-select-option-text::after {
  transform: scaleX(1);
  transform-origin: left;
}

.view-toggle {
  display: flex;
  align-items: center;
  gap: 20px;
}

.view-toggle-btn {
  display: flex;
  color: var(--gray-05);
}

.view-toggle-btn svg { width: 18px; height: 18px; }
.view-toggle-btn[data-view="list"] svg { width: 20px; height: 14px; }

.view-toggle-btn.is-active { color: var(--orange); }

.view-toggle-btn:not(.is-active):hover { color: var(--gray-07); }

.listing-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--grid-gutter);
  margin-bottom: 40px;
  transition: opacity 0.2s ease;
}

.listing-grid .project-tile {
  grid-column: span 4;
}

/* Crossfade for the grid/list view toggle: JS adds this right before
   swapping the rendered markup, then removes it once the new layout is
   in the DOM, so the old shape fades out and the new one fades in
   instead of just popping to the new shape. */
#listing-grid.is-switching {
  opacity: 0;
}

/* Reserves roughly the space a loaded page of tiles/cards will take up
   before data/projects.json has actually been fetched — otherwise the
   grid is briefly empty, the page is shorter than the viewport, and the
   footer flashes into view at the bottom of the screen before the
   fetched content pushes it back below the fold. */
#listing-grid {
  min-height: 60vh;
}

.project-tile {
  display: block;
  position: relative;
}

/* Superfície branca + sombra atrás do tile no hover — mesma receita de
   .content-card (modulos/conteudo.css): ::before com halo (inset
   negativo) em vez de background direto, porque a imagem encosta nas
   bordas do tile sem padding lateral. */
.project-tile::before {
  content: "";
  position: absolute;
  inset: -24px;
  z-index: -1;
  background: transparent;
  border-radius: 8px;
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0);
  transition: background-color 0.4s cubic-bezier(0.16, 1, 0.3, 1),
    box-shadow 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.project-tile:hover::before,
.project-tile:focus-visible::before {
  background: var(--white);
  box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
}

@media (pointer: fine) {
  .project-tile { cursor: none; }
}

/* Grid view: tiles cascade in one after another, individually (JS sets
   each tile's animation-delay based on its index). A keyframe animation
   rather than a transition, since the class and the render can land in
   the same tick. */
@keyframes tile-cascade-in {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(0); }
}

.project-tile.tile-cascade {
  opacity: 0;
  animation: tile-cascade-in 2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

.project-tile-image {
  aspect-ratio: 421 / 329;
  overflow: hidden;
  margin-bottom: 24px;
}

/* Projeto ainda sem capa real (ver data/projects.json): bloco cinza no
   lugar do <img>, fácil de trocar depois quando a foto chegar. */
.project-tile-image.is-placeholder {
  background: var(--gray-04);
}

.project-tile-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s cubic-bezier(0.16, 1, 0.3, 1), filter 1.2s cubic-bezier(0.16, 1, 0.3, 1);
}

.project-tile:hover .project-tile-image img,
.project-tile:focus-visible .project-tile-image img {
  transform: scale(1.04);
  filter: blur(5px);
}

.project-tile-content {
  padding-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Espaçamento entre título, descrição e categorias igual ao card de
   projetos da home (.home-project-card-body, home.css): gap:8px no
   flex-column do container, não margin por elemento — por isso o
   margin-bottom próprio saiu daqui. */
.project-tile-heading {
  display: flex;
  align-items: flex-end;
  gap: 16px;
}

.project-tile-title {
  font-family: var(--font-heading);
  font-weight: 600;
  font-variation-settings: "wdth" 100;
  font-size: 32px;
  color: var(--gray-07);
}

.project-tile-summary {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 18px;
  line-height: 1.4;
  color: var(--gray-07);
}

/* Linha de categorias (Tipo | Setor): mesmo tratamento hover-reveal do
   card de projetos da home (.home-project-card-tags em home.css) —
   texto simples, invisível em repouso, aparece no hover/focus do tile. */
.project-tile-tags {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 16px;
  color: var(--gray-06);
  opacity: 0;
  transition: opacity 0.2s ease;
}

.project-tile:hover .project-tile-tags,
.project-tile:focus-visible .project-tile-tags {
  opacity: 1;
}

.listing-empty {
  font-size: 16px;
  color: var(--gray-06);
  margin-bottom: 40px;
}

/* List view (table) */
.listing-grid.is-list-view {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* padding-top:16px é o espaço até o texto; o espaço depois do texto até a
   próxima linha soma padding-bottom (8px) + gap do container (8px) = 16px,
   mesma medida do topo. */
.projects-table-row {
  display: flex;
  align-items: center;
  gap: 20px;
  padding-top: 16px;
  padding-bottom: 8px;
  border-top: 1px solid var(--gray-03);
}

/* List view: rows cascade in one after another (JS sets each row's
   animation-delay based on its index). A keyframe animation rather than a
   transition, since the class and the render can land in the same tick. */
@keyframes row-cascade-in {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

.projects-table-row.row-cascade {
  opacity: 0;
  animation: row-cascade-in 1.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}

/* .projects-table-cell é um <p>, e o reset global (base.css) dá a todo
   <p> font-family/weight próprios (Exo 2) — isso vence a herança vinda de
   .projects-table-header porque mira o elemento em si, não um ancestral.
   Por isso a regra precisa repetir aqui nos .projects-table-cell de
   dentro, não só na div da linha. */
.projects-table-header,
.projects-table-header .projects-table-cell {
  font-family: 'Anybody';
  font-weight: 600;
  font-variation-settings: "wdth" 100;
  font-size: 20px;
  color: var(--gray-07);
}

.projects-table-row:not(.projects-table-header) {
  font-size: 20px;
  color: var(--gray-07);
  transition: opacity 0.2s ease;
}

a.projects-table-row:hover,
a.projects-table-row:focus-visible {
  opacity: 0.6;
}

.projects-table-cell {
  flex: 1 0 0;
  min-width: 0;
}

.projects-table-cell--year {
  flex: none;
  width: 160px;
}

.projects-table-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* ===== Vamos conversar (igual à seção da home, ver .home-talk em
   home.css — classes próprias porque projetos.css não deve depender de
   home.css, mas o resultado visual é o mesmo) ===== */
.projects-talk {
  background: var(--gray-07);
}

.projects-talk-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--grid-gutter);
  align-items: flex-start;
  padding: 120px 0;
  border-bottom: 2px solid var(--gray-06);
}

.projects-talk-content {
  grid-column: 1 / span 4;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 40px;
  justify-content: center;
}

.projects-talk-content h2 {
  font-size: 40px;
  line-height: 1.1;
  color: var(--gray-01);
}

.projects-talk-copy {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 32px;
}

.projects-talk-lead {
  font-size: 20px;
  line-height: 1.5;
  color: var(--gray-01);
}

.projects-talk-sub {
  font-size: 20px;
  line-height: 1.5;
  color: var(--gray-05);
  max-width: 578px;
}

.projects-talk-whatsapp {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 16px;
  padding: 4px 0;
  border-bottom: 2px solid var(--gray-06);
  font-size: 20px;
  color: var(--gray-01);
}

.projects-talk-whatsapp:hover {
  border-bottom: 2px solid var(--gray-01);
}

.projects-talk-whatsapp:hover .seta-svg-link {
  color: var(--gray-01);
  transform: translateX(4px);
}

.projects-talk-form {
  grid-column: 7 / span 5;
  min-width: 0;
}

.projects-talk-form form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.projects-talk .form-field { margin-bottom: 0; max-width: 570px; }
.projects-talk .form-field label { color: var(--gray-01); }
.projects-talk .form-submit { max-width: 570px; }
.projects-talk .form-feedback { text-align: left; color: var(--gray-04); }
.projects-talk .form-feedback.is-success { color: var(--orange); }

/* ===== Project case study ===== */
.project-page { background: #f5f5f5; }

.project-hero {
  position: relative;
  margin: 0 40px;
  aspect-ratio: 1840 / 500;
}

/* Recorte da foto isolado num wrapper próprio (em vez de overflow:hidden
   direto no .project-hero) pra seta (.project-hero-arrow, mais abaixo)
   poder passar um pouco da borda quando o cursor chega perto, sem ficar
   cortada — ela é irmã desse wrapper, não fica presa no clip. */
.project-hero-photo {
  position: absolute;
  inset: 0;
  overflow: hidden;
}

.project-hero-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-hero--bottom img {
  object-position: bottom;
}

/* Projeto ainda sem hero real (ver data/projects.json): bloco cinza no
   lugar do <img>, fácil de trocar depois quando a foto chegar. */
.project-hero.is-placeholder {
  background: var(--gray-04);
}

/* Seta da marca em cima do hero do projeto, sempre apontando pra
   direita — mesmo mecanismo da foto de "Quem lidera" (.about-lead-arrow
   em quem-somos.css), via JS (script.js: initLocalArrow). z-index
   simples (sem stacking context): nada mais na frente aqui disputa
   camada. */
.project-hero-arrow {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  pointer-events: none;
  transition: transform .3s ease-out;
  will-change: transform;
}

.project-hero-arrow-inner {
  transform: translate(-50%, -50%) rotate(-45deg) scale(0);
  color: var(--orange-accent);
  opacity: 0;
  will-change: transform, opacity;
}

.project-hero-arrow-icon {
  display: block;
  width: clamp(60px, 10vw, 190px);
  height: auto;
}

.project-hero-arrow.is-active .project-hero-arrow-inner {
  animation: local-arrow-appear .6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.project-hero-arrow.is-leaving .project-hero-arrow-inner {
  animation: local-arrow-disappear .5s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

@media (pointer: fine) {
  .project-hero { cursor: none; }
}

.project-title-block {
  flex: 1 0 0;
  min-width: 0;
  border-top: 3px solid var(--gray-07);
  padding-top: 8px;
}

.project-title-block h1 {
  font-size: 48px;
  font-weight: 700;
  line-height: 1.1;
}

.project-meta-col-label {
  font-family: var(--font-heading);
  font-weight: 600;
  font-variation-settings: "wdth" 100;
  text-transform: uppercase;
  margin-bottom: 8px;
}

/* Título + subtítulo acima do hero (Costero Residence only, per Figma
   "Página - Projeto Remodelada", node 2297:123): substitui o antigo rail
   editorial (título+meta-cols ao lado do hero). Reaproveita
   .project-title-block (mesmo h1 com wipe-reveal já ligado em script.js).
   padding-top:300px iguala a altura em que o título wipe começa na tela
   à de .page-hero (conteudos.html/projetos.html/servicos.html — medido
   via CDP: h1 a 380px do topo da viewport em todas, header de 80px +
   300px de respiro); ver breakpoint de 900px pra a versão 56px. */
.project-lede {
  position: relative;
  padding: 300px 0 80px;
}

/* Seta de "role pra baixo" encostada na última coluna (borda direita do
   .container, já que .project-lede ocupa as 12 colunas inteiras),
   alinhada com o topo do título — top precisa repetir o padding-top de
   .project-lede (300px aqui, 56px no breakpoint de 900px, ver mais
   abaixo), já que top:0 sozinho bateria na borda do padding-box (que
   inclui esse respiro), não no início do h1. */
.project-lede-arrow {
  position: absolute;
  top: 300px;
  right: 0;
  width: 24px;
  height: 24px;
  color: var(--gray-04);
}


/* Título e subtítulo do case nunca passam de 6 das 12 colunas do grid —
   .project-lede não é grid (só empilha os filhos em bloco), então o
   limite é replicado via calc: largura de 6 colunas + 5 gutters entre
   elas, na mesma proporção que um grid-column:span 6 real teria. */
.project-lede .project-title-block,
.project-lede-subtitle {
  max-width: calc((100% - 11 * var(--grid-gutter)) * 6 / 12 + 5 * var(--grid-gutter));
}

.project-lede .project-title-block {
  border-top: none;
  padding-top: 0;
}

.project-lede .project-title-block h1 {
  font-size: 60px;
  line-height: 1.03;
}

.project-lede-subtitle {
  margin-top: 8px;
  font-size: 24px;
  color: var(--gray-06);
}

/* Mesma estrutura/estilo de .article-byline (página de conteúdo): linha
   flex com separadores "|", só que com Cliente/Ano em vez de autor/data.
   margin-top 24px (não 8px) porque lá esse espaçamento é row-gap:16px do
   grid de .article-intro-block SOMADO ao margin-top:8px do próprio
   .article-byline — aqui, sem grid, replicamos o total (16+8) direto. */
.project-lede-byline {
  margin-top: 24px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-family: var(--font-body);
  font-weight: 500;
  font-size: 18px;
  color: var(--gray-06);
}

.project-lede-byline-value {
  color: var(--gray-07);
}

/* Hero do Costero: ocupa as 12 colunas do .container (mesmo tratamento do
   hero da página de conteúdo), por isso margin:0 aqui (a margem já vem do
   .container pai, não dos 40px de .project-hero padrão). Altura fixa de
   700px em vez de aspect-ratio — valor literal do frame do Figma. Continua
   sem paralaxe (rejeitada antes, ver memória) e usa o mesmo recorte
   "ancorado embaixo" de .project-hero--bottom. */
.project-hero--edge {
  margin: 0;
  height: 700px;
  aspect-ratio: unset;
}

.project-side-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-size: 20px;
  color: var(--gray-07);
}

/* Sticky chapter navigation + flowing chapter body (Costero Residence only,
   per Figma spec + redondo.design/case-studies/infinia reference): mesmo
   grid de 12 colunas/48px do site. Índice (2 colunas) / respiro (1) /
   corpo (6, igual à largura de .article-body na página de conteúdo — era
   8 antes) / respiro (1) / .project-side (2, Serviços/Setores empilhados,
   antes ficavam em .project-lead). 2+1+6+1+2 = 12, sem sobra.
   padding-top:80px substitui o respiro que existia entre o hero e o antigo
   .project-lead (removido — o parágrafo de abertura agora é o primeiro
   texto do chapter-01, dentro de .project-main). */
.project-body-layout {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--grid-gutter);
  padding-top: 80px;
  align-items: start;
}

.project-toc {
  grid-column: span 2;
  position: sticky;
  top: calc(var(--header-height) + 40px);
  display: flex;
  flex-direction: column;
}

/* Item do índice: mesma receita de .article-toc-item (conteudo.css) — o
   ícone (seta de .summary-arrow.svg, mesma usada em .nav-cta/.article-share)
   só aparece no item ativo (em negrito); nos demais o espaço reservado
   pra ele vira a indentação (pl-24) do texto. */
.project-toc-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 0 0 16px 24px;
  text-decoration: none;
}

/* height:20.8px = font-size:16px * line-height:1.3 de .project-toc-item-title
   (mesma linha de texto) + object-fit:contain: centraliza a seta dentro
   dessa altura sem esticar o SVG, então ela alinha com o centro da
   primeira linha do título mesmo quando ele quebra em 2+ linhas. */
.project-toc-item-icon {
  display: none;
  flex: none;
  width: 14px;
  height: 20.8px;
  object-fit: contain;
}

.project-toc-item.is-active {
  padding-left: 0;
}

.project-toc-item.is-active .project-toc-item-icon {
  display: block;
}

.project-toc-item-title {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.3;
  color: var(--gray-06);
  transition: color 0.2s ease, font-weight 0.2s ease;
}

.project-toc-item:hover .project-toc-item-title {
  color: var(--orange);
}

.project-toc-item.is-active .project-toc-item-title {
  font-weight: 600;
  color: var(--gray-07);
}

.project-main {
  grid-column: 4 / span 6;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Case study ainda sem capítulos reais (ver data/projects.json) — aviso
   honesto no lugar de .project-chapter, some sozinho quando o conteúdo
   real for escrito. */
.project-main-placeholder {
  font-family: var(--font-body);
  font-size: 20px;
  color: var(--gray-06);
}

.project-side {
  grid-column: 11 / span 2;
  position: sticky;
  top: calc(var(--header-height) + 40px);
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.project-chapter {
  padding: 0 0 80px;
  scroll-margin-top: calc(var(--header-height) + 24px);
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.project-chapter h2 {
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 40px;
  font-variation-settings: "wdth" 100;
  color: var(--gray-07);
  line-height: 1.15;
}

.project-chapter > p {
  font-family: var(--font-body);
  font-weight: 300;
  font-size: 20px;
  line-height: 1.6;
  color: #000;
}

.project-section-images {
  padding-top: 40px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Justified row, Behance-style: every image keeps its own natural aspect
   ratio and the row settles on a shared height, with a fixed 16px gap.
   Each <figure> carries an inline `flex-grow` equal to its image's aspect
   ratio (width/height) with flex-basis: 0 — the browser then distributes
   width proportionally to that ratio. Because each <img> is width: 100%
   with height: auto, its rendered height naturally works out to the same
   value for every image in the row (see the math in the HTML comment
   where these ratios are computed) — no cropping, no explicit row height. */
.project-gallery-row {
  display: flex;
  gap: 16px;
}

.project-gallery-row figure {
  flex-basis: 0;
  min-width: 0;
}

/* Chromium mis-resolves flex-grow for a lone portrait figure in a row
   (it settles on flex-grow-value × row-width instead of the full row
   width). Forcing grow to 1 for solo figures sidesteps it; the exact
   ratio only matters when a row has to divide space between siblings. */
.project-gallery-row figure:only-child {
  flex-grow: 1 !important;
}

.project-gallery-row img {
  display: block;
  width: 100%;
  height: auto;
  cursor: zoom-in;
}

/* Gallery lightbox: clicking any image in .project-section-images opens it
   full-size in an on-page overlay instead of navigating away. The overlay
   markup is built once in JS (see script.js) and toggled with .is-open. */
.project-lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  align-items: center;
  justify-content: center;
  background: rgba(15, 15, 16, 0.94);
  opacity: 0;
  transition: opacity 0.25s ease;
}

/* `display` only turns on once the `hidden` attribute is removed — setting
   it unconditionally would override `[hidden]` (author CSS beats the
   browser's own `[hidden] { display: none }` rule), leaving this full-
   viewport overlay sitting on top of the page — invisible via opacity, but
   still swallowing every click — even while "closed". */
.project-lightbox:not([hidden]) {
  display: flex;
}

.project-lightbox.is-open { opacity: 1; }

.project-lightbox-figure {
  width: 88vw;
  height: 82vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  transform: scale(0.97);
  transition: transform 0.25s ease;
}

.project-lightbox.is-open .project-lightbox-figure { transform: scale(1); }

.project-lightbox-image {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  flex: 1 1 auto;
  object-fit: contain;
  cursor: default;
}

.project-lightbox-caption {
  color: var(--gray-03);
  font-size: 14px;
  text-align: center;
  max-width: 60ch;
}

.project-lightbox-caption:empty { display: none; }

.project-lightbox-counter {
  position: absolute;
  top: 28px;
  left: 28px;
  color: var(--gray-04);
  font-size: 13px;
  letter-spacing: 0.02em;
}

.project-lightbox-close,
.project-lightbox-nav {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 0;
  background: rgba(255, 255, 255, 0.1);
  color: var(--white);
  cursor: pointer;
  transition: background 0.2s ease;
}

.project-lightbox-close:hover,
.project-lightbox-nav:hover,
.project-lightbox-close:focus-visible,
.project-lightbox-nav:focus-visible {
  background: rgba(255, 255, 255, 0.2);
}

.project-lightbox-close svg {
  width: 13px;
  height: 13px;
}

.project-lightbox-nav svg {
  width: 22px;
  height: 22px;
}

.project-lightbox-prev svg { transform: rotate(135deg); }
.project-lightbox-next svg { transform: rotate(-45deg); }

.project-lightbox-close {
  top: 28px;
  right: 28px;
}

.project-lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
}

.project-lightbox-prev { left: 28px; }
.project-lightbox-next { right: 28px; }

body.lightbox-open { overflow: hidden; }

.project-related { padding: 56px 0; }

/* Mesmo estilo de .article-related-heading (página de conteúdo), inclusive
   o espaçamento até a grid: lá é gap:80px no flex-column de
   .article-related-inner (conteudo.css) — aqui, sem esse wrapper,
   replicamos com margin-bottom direto no título. */
.project-related-heading {
  font-family: var(--font-heading);
  font-weight: 700;
  font-variation-settings: "wdth" 100;
  font-size: 40px;
  color: var(--gray-07);
  margin-bottom: 80px;
}

.project-related-grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-columns), 1fr);
  gap: var(--grid-gutter);
}

.project-related-grid .project-tile {
  grid-column: span 3;
}

@media (max-width: 1440px) {
  .project-body-layout { grid-template-columns: repeat(6, 1fr); }
  .project-toc { grid-column: span 1; }
  .project-main { grid-column: 3 / span 3; }
  .project-side { grid-column: 6 / span 1; }

  .project-related-grid { grid-template-columns: repeat(6, 1fr); }
  .project-related-grid .project-tile { grid-column: span 2; }
  .project-related-grid .project-tile:nth-child(4) { display: none; }

  .listing-grid { grid-template-columns: repeat(6, 1fr); }
  .listing-grid .project-tile { grid-column: span 2; }
}

@media (max-width: 900px) {
  .listing-grid { grid-template-columns: repeat(2, 1fr); }
  .listing-grid .project-tile { grid-column: span 1; }

  .project-lede .project-title-block h1 { font-size: 40px; }
  .project-lede { padding-top: 56px; padding-bottom: 40px; }
  .project-lede-arrow { top: 56px; }
  .project-hero-section { padding-top: 40px; }
  .project-hero--edge { height: 380px; }
  .project-body-layout { grid-template-columns: 1fr; gap: 8px; padding-top: 40px; }
  .project-toc {
    grid-column: span 1;
    position: static;
    width: 100%;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0 24px;
  }
  .project-toc-item { padding: 8px 0; }
  .project-toc-item-icon { display: none !important; }
  .project-main { grid-column: span 1; max-width: 100%; }
  .project-side { grid-column: span 1; position: static; }

  .project-related-heading { font-size: 30px; }
  .project-related-grid { grid-template-columns: repeat(2, 1fr); }
  .project-related-grid .project-tile { grid-column: span 1; }

  /* .projects-talk-grid nunca teve uma versão mobile (mesma lacuna
     pré-existente de .home-talk-grid/.content-newsletter-grid) — sem isso
     .projects-talk-form (grid-column 7/span5 numa grade de 12) estoura a
     largura da viewport. Vira 1 coluna, formulário abaixo do texto. */
  .projects-talk-grid { grid-template-columns: 1fr; padding: 56px 0; }
  .projects-talk-content,
  .projects-talk-form { grid-column: 1 / -1; }
  .projects-talk .form-field,
  .projects-talk .form-submit { max-width: 100%; }
}

@media (max-width: 720px) {
  .project-lightbox-close { top: 16px; right: 16px; }
  .project-lightbox-prev { left: 10px; }
  .project-lightbox-next { right: 10px; }
  .project-lightbox-counter { top: 16px; left: 16px; }

  .project-lightbox-close,
  .project-lightbox-nav {
    width: 40px;
    height: 40px;
  }

  .project-lightbox-nav svg {
    width: 18px;
    height: 18px;
  }

  .project-lightbox-figure {
    width: 94vw;
    height: 76vh;
  }
}

@media (max-width: 640px) {
  .listing-grid { grid-template-columns: 1fr; }
  .filter-group { gap: 24px; }
  .filter-select-trigger { font-size: 20px; }

  .project-hero { margin: 0 20px; aspect-ratio: 4 / 3; }
  .project-hero--edge { margin: 0; aspect-ratio: unset; height: 260px; }
  .project-title-block h1 { font-size: 34px; }
  .project-related-grid { grid-template-columns: 1fr; }
  .project-gallery-row { flex-direction: column; }
  .project-gallery-row figure { flex-basis: auto; }
}
