/* Tipografia base */
body {
  font-size: var(--text-body);
  line-height: 1.6;
  /* um degrau acima do normal (400) do peso variavel da SF Pro — pedido
     explicito para dar mais presenca ao texto de corpo */
  font-weight: 500;
  hyphens: none;
}

/* Menu no topo: so navegacao + switch de texto (perfil e contato sairam).
   "↩ Inicio" (quando existe) fica segregado a esquerda da coluna; o
   restante do menu agrupa a direita (ul em width:100% no layout.css). */
.aside-menu li:has(> a[href="index.html"]) {
  margin-right: auto;
}

/* Marca ao final do nome — mesmo simbolo do favicon, mas inline (nao <img
   src favicon.svg>): o favicon.svg tem uma regra dark-mode que o deixa
   branco quando o navegador esta no tema escuro, o que apagaria o icone
   aqui. fill: currentColor acompanha a cor do texto. Base 0.75em +15%. */
.aside-brand-mark {
  height: 0.8625em;
  width: auto;
  flex-shrink: 0;
}

/* Nome + cargo no topo do main (index e curriculo). Titulos em 14px;
   nome em negrito. Mesmo padrao tipografico do .article-header. */
.page-intro {
  margin-bottom: 32px;
}

.page-intro h1 {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-body);
  line-height: 1.6;
  font-weight: 600;
}

.page-intro .role {
  display: block;
  margin-top: 4px;
  font-size: var(--text-body);
  color: var(--color-ink-muted);
  font-weight: 400;
}

/* Links de navegacao do menu (Curriculo, Galeria, Inicio): azul + peso 500 */
.aside-menu .cv-link,
.aside-menu a[href="galeria.html"] {
  color: var(--color-link);
  font-size: var(--text-body);
  font-weight: 500;
  /* ajuste optico, ver comentario acima de a[href="index.html"] abaixo */
  margin-left: -1px;
}

.aside-menu .cv-link:hover,
.aside-menu a[href="galeria.html"]:hover {
  color: var(--color-link);
}

/* Ajuste optico: o traco curvo de "↩" e o "C" (Curriculo, acima) tem menos
   tinta colada na margem esquerda que as hastes retas de L/W/E, entao os
   dois pareciam levemente deslocados para a direita apesar de comecarem no
   mesmo x — mesmo efeito de por que um "O" parece desalinhado ao lado de
   um "H". Correcao manual, nao ha regra que resolva isso sozinha. */
.aside-menu a[href="index.html"] {
  margin-left: -2px;
  /* Inicio com o mesmo peso do link Curriculo (.cv-link): os dois sao a
     mesma chamada de navegacao entre paginas. */
  color: var(--color-link);
  font-weight: 500;
}

.aside-menu a[href="index.html"]:hover {
  color: var(--color-link);
}

/* Texto do link de e-mail (ver js/copiar-email.js): o span anima
   opacidade+blur no crossfade entre "E-mail" e "Copiado", igual ao gesto
   usado nos cards do acervo. is-copied fica depois de :hover para vencer o
   empate de especificidade e o azul nao ser ofuscado no hover. */
.aside-email__label {
  display: inline-block;
  transition: opacity 0.18s ease, filter 0.18s ease;
}

.aside-email__label--veiled {
  opacity: 0;
  filter: blur(4px);
}

.bio a.is-copied {
  color: var(--color-link);
}

/* Botao switch de texto grande (acessibilidade, tarefa 29) — ver
   js/text-scale.js. Dois "A" em texto (nao mais o icone de refs/switch.svg,
   que tinha os dois glifos em tamanhos diferentes) — mesmo tamanho e peso
   dos outros itens do menu (font: inherit), so a cor muda por estado. */
/* Switch A−/A+ oculto por enquanto — reativar removendo esta regra. */
.aside-menu li:has(.text-scale-toggle) {
  display: none;
}

.text-scale-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-weight: 400;
  cursor: pointer;
  transition: opacity 0.15s ease;
}

.text-scale-toggle:hover,
.text-scale-toggle:focus-visible {
  opacity: 0.75;
}

/* o "A" azul indica qual tamanho esta ativo agora — nao "clique aqui".
   Repouso (sem zoom): o A- (tamanho normal) fica azul, o A+ cinza.
   Ligado (zoom aplicado): inverte — o A+ fica azul, o A- cinza. */
.text-scale-toggle__a-small {
  color: var(--color-link);
}

.text-scale-toggle__a-large {
  color: var(--color-ink-muted);
}

.text-scale-toggle[aria-pressed="true"] .text-scale-toggle__a-small {
  color: var(--color-ink-muted);
}

.text-scale-toggle[aria-pressed="true"] .text-scale-toggle__a-large {
  color: var(--color-link);
}

/* fio entre os dois "A": so um separador, nunca reage ao estado */
.text-scale-toggle__divider {
  width: 1px;
  height: 1em;
  background: var(--color-border);
}

/* Bio / paragrafos corridos */
/* o espaco depois da bio agora vem da secao, nao dela */
.bio {
  display: flex;
  flex-direction: column;
  gap: 24px;
  margin-bottom: 0;
}

.bio p {
  color: var(--color-ink);
  text-wrap: pretty;
  hyphens: none;
}

.bio a {
  color: var(--color-link);
  text-decoration: none;
}

/* Link em destaque (home) — miniatura 4:3 a esquerda + texto */
.featured-link {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}

.featured-link + .featured-link {
  margin-top: 24px;
}

.featured-link__body {
  flex: 1;
  min-width: 0;
}

/* Miniatura de preview ao pairar o mouse num artigo (tarefa 30): mesmo
   tamanho/borda/sombra do card do Acervo, mostrando o topo real da pagina
   do artigo dentro de um iframe. A secao Artigos precisa de position:relative
   para a miniatura (position:absolute) se posicionar em relacao a ela, nao
   a janela inteira. */
.section-articles {
  position: relative;
}

.article-preview {
  position: absolute;
  left: 0;
  width: 280px;
  height: 280px;
  overflow: hidden;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius);
  box-shadow: var(--shadow-card);
  background: var(--color-bg);
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  filter: blur(12px);
  transform: translateY(8px);
  transition: opacity 0.35s ease, filter 0.35s ease, transform 0.35s ease;
}

.article-preview.is-visible {
  opacity: 1;
  filter: none;
  transform: none;
}

/* iframe com viewport fixo (independente da largura real da janela de quem
   visita) — ver js/article-preview.js para o motivo do tamanho fixo e do
   CSS injetado que remove o menu lateral e trava a largura do .main. Escala
   UNIFORME (0,4 = 280/700) nos dois eixos, para o conteudo nunca esticar —
   a largura do iframe (490px = 700 x 0,7, os 30% de contracao pedidos) e
   quem recorta o respiro, nao a escala: o iframe captura uma fatia mais
   estreita da pagina (menos largura de conteudo real), a mesma escala Y
   preenche a altura inteira (sem margem no topo/base) e sobra 42px de
   margem de cada lado horizontal (280 - 490*0,4 = 196; (280-196)/2 = 42) —
   a caixa do card continua 280x280. */
.article-preview__frame {
  position: absolute;
  top: 0;
  left: 42px;
  width: 490px;
  height: 700px;
  border: 0;
  transform: scale(0.4);
  transform-origin: top left;
}

@media (prefers-reduced-motion: reduce) {
  .article-preview {
    transition: none;
  }
}

.featured-link a {
  display: block;
  color: var(--color-link);
  font-size: var(--text-body);
  font-weight: 500;
  transition: color 0.2s ease;
}

.featured-link .subtitle {
  margin-top: 12px;
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  transition: color 0.2s ease;
}

/* No hover de um artigo, os outros titulos perdem destaque (cinza muted). */
@media (hover: hover) {
  .section-articles:hover .featured-link:not(:hover) a {
    color: var(--color-ink-muted);
  }
}

/* Cascata de entrada no index (fade + leve subida, delay escalonado). */
@keyframes cascade-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.cascade {
  opacity: 0;
  animation: cascade-in 0.4s ease forwards;
}

.cascade--1 { animation-delay: 0ms; }
.cascade--2 { animation-delay: 100ms; }
.cascade--3 { animation-delay: 200ms; }
.cascade--4 { animation-delay: 300ms; }
.cascade--5 { animation-delay: 400ms; }
.cascade--6 { animation-delay: 500ms; }

@media (prefers-reduced-motion: reduce) {
  .cascade {
    opacity: 1;
    animation: none;
  }
}

/* Rotulo de secao (Galeria, Formacao, Cursos, Experiencia Profissional) —
   mesmo estilo adotado para os titulos de projeto dos cards do Acervo. */
.section-label {
  display: block;
  color: var(--color-ink-muted);
  font-size: var(--text-body);
  font-weight: 600;
  margin-bottom: 24px;
}

.section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 24px;
}

.section-header .section-label {
  margin-bottom: 0;
}

.carousel-nav {
  display: flex;
  gap: 8px;
}

/* As setas seguem as da secao Artigos: mesmo peso 500 e mesmo azul de link.
   Aqui o azul carrega informacao — a seta so e azul quando ha rolagem
   disponivel naquele sentido (o `disabled` e ligado e desligado conforme a
   posicao do trilho, em acervo.js). Sem acao, ela volta ao cinza. */
.carousel-nav__btn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-link);
  font-size: var(--text-strong);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
}

.carousel-nav__btn:disabled {
  color: var(--color-ink-muted);
  opacity: 0.3;
  cursor: default;
}

/* Galeria (home) — carrossel: uma unica linha, sem quebra, com scroll
   horizontal. O proprio "trilho" (viewport visivel) nao fica mais preso aos
   584px de .main — a borda esquerda continua onde .main comeca, mas a
   direita estica até quase a borda da janela (mesma logica de breakout
   usada antes para o card expandido: metade de main-width + metade da
   janela, com uma folga de seguranca). */
:root {
  /* Folga interna do trilho: o overflow do carrossel recorta tudo que passa
     das bordas, e a sombra dos cards passa. O padding abre espaco para ela e
     a margem negativa de mesmo valor devolve o trilho a posicao original, de
     forma que o alinhamento do primeiro card com .main nao muda. Nao ha folga
     em cima porque --shadow-card nao sobe acima do card. */
  --carousel-bleed-x: 24px;
  --carousel-bleed-bottom: 32px;

  /* A vitrine visivel encolhe --carousel-bleed-x para a folga da direita caber
     dentro da janela: sem isso o trilho terminaria depois da borda e a pagina
     ganharia rolagem horizontal. */
  --carousel-track: min(
    1200px,
    calc((var(--main-width) / 2) + 50vw - 24px - var(--carousel-bleed-x))
  );
}

.gallery-grid {
  display: flex;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: var(--gallery-gutter);
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  width: calc(var(--carousel-track) + var(--carousel-bleed-x) * 2);
  padding: 0 var(--carousel-bleed-x) var(--carousel-bleed-bottom);
  margin: 0 calc(-1 * var(--carousel-bleed-x))
          calc(-1 * var(--carousel-bleed-bottom));
  /* sem isso o snap gruda o card na borda do trilho, que agora fica
     --carousel-bleed-x a esquerda de onde o card deve parar */
  scroll-padding-left: var(--carousel-bleed-x);
}

.gallery-grid::-webkit-scrollbar {
  display: none;
}

/* Com um card expandido o snap atrapalha: as larguras ficam desiguais e, a
   cada mudanca de layout, o navegador puxa a rolagem de volta para o card
   vizinho, desfazendo o alinhamento a esquerda. Religar depois e seguro: o
   card alinhado ao inicio do trilho ja e exatamente um ponto de snap. */
.gallery-grid--has-expanded {
  scroll-snap-type: none;
}

.card {
  position: relative;
  flex: 0 0 280px;
  scroll-snap-align: start;
  background: var(--color-surface);
  /* mesma borda das midias do artigo (.article-figure__frame, .analise__viewer) */
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius);
  /* branch teste-fundo-linha-sombra: sombra sutil da referencia. Fica fora do
     overflow: hidden (que so recorta o conteudo), entao a capa continua
     cortada no raio e o halo aparece por fora da borda. */
  box-shadow: var(--shadow-card);
  overflow: hidden;
  /* a largura/altura reais da transicao sao setadas via JS (px explicitos)
     durante a animacao; isso so precisa existir para o transition pegar.
     Duracao/curva espelhadas em TRANSITION_MS no acervo.js — mudar juntas. */
  transition: flex-basis 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              height 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.12s ease;
}

.card:not(.card--expanded) {
  aspect-ratio: 1 / 1;
}

.card--expanded {
  /* Com o texto embaixo do video, a largura do card e a largura do video.
     Ancorada em --main-width para o card alinhar com a coluna da pagina: os
     584px descontados do respiro deixam o video em 558px, praticamente o
     mesmo tamanho que ele tinha no layout lado a lado (562,9px).
     Limitado a --carousel-track (a "vitrine" visivel do carrossel) para o
     card nunca passar da tela em janelas estreitas. Nao usar 100%:
     porcentagem em flex-basis cai para o tamanho de conteudo (280px da
     capa) dentro deste container. */
  flex: 0 0 min(var(--main-width), var(--carousel-track));
}

/* Projetos 1 (LogIA) e 3 (Flor do Caribe): arte mais larga que alta
   (1280x720). Em vez de recortar as laterais (object-fit: cover), mostramos
   a largura inteira e preenchemos as sobras de cima/baixo com a mesma cor de
   fundo da arte. */
.card[data-project="1"] {
  background: #edebed;
}

.card[data-project="3"] {
  background: #d3d3d3;
}

.card[data-project="1"] .card-cover,
.card[data-project="3"] .card-cover {
  object-fit: contain;
}

/* ...mas expandidos (video + info) voltam a ser brancos, como os outros cards */
.card[data-project="1"].card--expanded,
.card[data-project="3"].card--expanded {
  background: var(--color-surface);
}

.card-trigger {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.card-trigger[hidden] {
  display: none;
}

/* Cross-fade entre as duas camadas do card (a capa e o conteudo expandido).
   --overlay tira do fluxo a camada que esta saindo, para ela nao entrar na
   medicao do tamanho final do card e poder sumir por cima da que entra.
   --veiled e o estado invisivel de onde (ou para onde) as duas transicionam. */
.card-trigger,
.card-content {
  transition: opacity 0.35s ease, filter 0.35s ease, transform 0.35s ease;
}

/* sem `inset: 0` de proposito: a camada que sai tem largura/altura congeladas
   em px pelo JS, senao ela seria redimensionada junto com o card a cada frame
   e redimensionar o video a cada frame faria o colapso engasgar */
.card-layer--overlay {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  pointer-events: none;
}

.card-layer--veiled {
  opacity: 0;
  filter: blur(12px);
  transform: translateY(8px);
}

/* Retorno ao clique: o elemento encolhe 3% enquanto pressionado.
   Nos links e botoes isso vem de :active — assim o feedback aparece sem
   interceptar a navegacao (ctrl+clique, botao do meio e "abrir em nova aba"
   continuam funcionando). No card, que abre por JS e nao navega, a classe
   .card--pressed garante que o encolhimento seja visto ANTES da expansao;
   ver PRESS_MS no acervo.js. */
.featured-link,
.aside-menu a,
.carousel-nav__btn {
  transition: transform 0.12s ease;
}

/* transform nao se aplica a caixa inline */
.aside-menu a {
  display: inline-block;
}

.card--pressed,
.aside-menu a:active,
.carousel-nav__btn:active {
  transform: scale(0.97);
}

/* O bloco de artigo ocupa a largura inteira da coluna (584px), quase o dobro
   do card. A 3% cada borda dele andaria 8,8px contra os 4,2px do card — mesmo
   tempo, muito mais caminho, e o movimento le como arrastado. 1,5% devolve
   um deslocamento de ~4,4px, parelho com o resto. */
.featured-link:active {
  transform: scale(0.985);
}

@media (prefers-reduced-motion: reduce) {
  .card,
  .card-trigger,
  .card-content,
  .gallery-grid,
  .featured-link,
  .aside-menu a,
  .carousel-nav__btn {
    transition: none;
    scroll-behavior: auto;
  }

  .card--pressed,
  .featured-link:active,
  .aside-menu a:active,
  .carousel-nav__btn:active {
    transform: none;
  }
}

.card-cover {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Conteudo expandido do card, no fluxo da pagina: video em cima, texto
   embaixo, em qualquer largura de tela.
   flex-start (e nao o stretch padrao) e obrigatorio na direcao cruzada; os
   dois filhos pedem width: 100% justamente por causa dele. */
.card-content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* o gap separa video e texto entre si; nao entra na conta do raio
     concentrico, por isso nao acompanha o --card-padding */
  gap: 16px;
  padding: var(--card-padding);
}

.card-content[hidden] {
  display: none;
}

/* O raio precisa estar aqui tambem, e nao so no player: esta caixa e que
   pinta o fundo atras do video, e sem o overflow ela apareceria por baixo
   dos cantos do video.
   O fundo acompanha o do card em vez de ser preto: nos cantos a suavizacao
   mistura o fundo com a borda do video, e um preto ali virava um fio escuro
   visivel. */
.card-content__media {
  flex: none;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: var(--color-surface);
  border-radius: var(--radius-inner);
  overflow: hidden;
}

.card-content__player {
  width: 100%;
  height: 100%;
  border: 0;
  border-radius: var(--radius-inner);
  display: block;
}

.card-content__info {
  flex: none;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* baseline (e nao center) para o X continuar alinhado a primeira linha do
   titulo quando ele quebra em duas linhas */
.card-content__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* Titulo + badge do ano agrupados, para o X permanecer sozinho no canto. */
.card-content__heading {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

/* mesmo corpo dos titulos da secao Artigos (--text-body): os dois sao o
   titulo de um item listado na home, e o card estava um degrau acima */
.card-content__title {
  font-size: var(--text-body);
  font-weight: 600;
}

/* Badge do ano, ao lado do titulo. O raio acompanha o do card. */
.card-content__year {
  padding: 4px 8px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
  line-height: 1;
}

.card-content__description {
  font-size: var(--text-body);
  color: var(--color-ink-muted);
}

/* Link externo do projeto, ao fim dos textos */
.card-content__link {
  align-self: flex-start;
  color: var(--color-link);
  font-size: var(--text-body);
  font-weight: 500;
}

.card-content__close {
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-ink-muted);
  font-size: var(--text-strong);
  line-height: 1;
  cursor: pointer;
}

/* =====================================================================
   Artigo
   O texto dos artigos e escrito em .md e transposto para ca, entao este
   conjunto cobre exatamente o que o Markdown produz: titulo, subtitulo,
   secao (h2), subsecao (h3), paragrafo, lista com e sem numero, imagem
   com legenda e citacao. O mapeamento md -> HTML esta em
   artigos/MODELO_ARTIGO.md; nada fora dessa lista deve aparecer nos
   artigos, para o texto novo nunca precisar de CSS novo.
   Nenhum tamanho novo foi criado: h2 usa --text-strong e h3 usa o corpo
   com peso 600 — os dois degraus que ja existiam para titulo de card e
   rotulo de secao.
   ===================================================================== */
.article-header {
  margin-bottom: 48px;
}

.article-header h1 {
  font-size: var(--text-body);
  line-height: 1.6;
  font-weight: 600;
}

.article-header .subtitle {
  display: block;
  margin-top: 6px;
  font-size: var(--text-body);
  color: var(--color-ink-muted);
}

/* Coluna flex: o espaco entre blocos vem do gap, nao de margens dos
   filhos — assim nenhum bloco precisa saber o que vem antes dele. Os
   titulos ganham margin-top (abre a secao) e margin-bottom (+50% no
   respiro ate o texto: gap 24 + 12 = 36px abaixo do h2). */
.article-body {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.article-body h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
  margin-bottom: 12px;
  font-size: var(--text-body);
  line-height: 1.4;
  font-weight: 600;
}

/* Trave cinza a direita do titulo ate a borda da coluna (nao abaixo). */
.article-body h2::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  min-width: 24px;
  background: var(--color-divider);
}

.article-body h3 {
  margin-top: 8px;
  margin-bottom: 12px;
  font-size: var(--text-body);
  line-height: 1.4;
  font-weight: 600;
}

/* um titulo abrindo o corpo nao tem o que empurrar */
.article-body > :first-child {
  margin-top: 0;
}

/* links dentro do texto corrido: sublinhados, como os do bloco de contato
   da home — no meio do paragrafo a cor sozinha nao basta para distinguir */
.article-body a {
  color: var(--color-link);
  text-decoration: underline;
}

.article-body p {
  text-wrap: pretty;
  hyphens: none;
}

/* Listas. O recuo de 24px alinha o texto do item, e nao o marcador, com a
   margem da coluna. `ol` precisa do reset aqui porque o reset.css so zera
   `ul`. */
.article-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.article-list li {
  position: relative;
  padding-left: 24px;
}

ul.article-list li::before {
  content: "";
  position: absolute;
  left: 8px;
  top: 8px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-ink-muted);
}

ol.article-list {
  counter-reset: article-item;
}

ol.article-list li::before {
  counter-increment: article-item;
  content: counter(article-item) ".";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--color-ink-muted);
  font-variant-numeric: tabular-nums;
}

/* Imagem com legenda. O frame existe mesmo sem imagem: enquanto a arte nao
   chega, ele reserva o espaco no fluxo (e a diagramacao ja pode ser
   avaliada). Quando a imagem chegar, basta coloca-la dentro — o
   .article-figure__placeholder sai e nada mais muda. */
.article-figure {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
}

.article-figure__frame {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-bg);
}

.article-figure__frame--wide {
  aspect-ratio: 16 / 9;
}

.article-figure__frame--square {
  aspect-ratio: 1 / 1;
}

/* mesma sombra dos cards do index (--shadow-card): usada so nas midias em
   destaque do artigo (video do prototipo), nao nos placeholders comuns. */
.article-figure__frame--shadow {
  box-shadow: var(--shadow-card);
}

.article-figure__frame img,
.article-figure__frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.article-figure__placeholder {
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
}

.article-caption {
  text-align: center;
  font-size: var(--text-meta);
  color: var(--color-ink-muted);
  /* equilibra o comprimento das linhas quando a legenda quebra (bloco de
     video+diagrama, ~280px): sem isso a quebra deixa uma linha longa e uma
     curta, em vez de um bloco proporcional. */
  text-wrap: balance;
}

/* Frase de teste de rodape (tarefa 9): antes do "Leia mais:", mesma cor e
   tamanho da legenda sob imagens, peso do corpo, alinhada a esquerda. */
.article-footer-note {
  font-size: var(--text-meta);
  font-weight: 500;
  color: var(--color-ink-muted);
}

/* Citacao / destaque. O fio de 1px e o mesmo dos outros contornos da
   pagina; o recuo de 16px afasta o texto sem o deslocar da leitura. */
.article-quote {
  margin: 0;
  padding-left: 16px;
  border-left: 1px solid var(--color-border);
  color: var(--color-ink-muted);
}

/* Rodape de artigos relacionados (fim de toda pagina de artigo): 2 colunas,
   mesma aparencia dos links da secao Artigos do index (.featured-link a +
   .subtitle) — seta + titulo + descricao, sem miniatura. Antecedido por um
   ".section-label" ("Leia mais:") — a divisoria fica acima do rotulo (nao
   do grid), ver regra abaixo. */
.article-footer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 24px;
}

/* :has() para nao afetar os outros usos de .section-label (index, curriculo) —
   so o que antecede o rodape de artigos ganha o respiro/divisoria que
   separava o corpo do texto do bloco relacionado. */
.article-body .section-label:has(+ .article-footer) {
  margin-top: 16px;
  margin-bottom: 16px;
  padding-top: 32px;
  border-top: 1px solid var(--color-divider);
}

.article-footer__item a {
  display: block;
  color: var(--color-link);
  font-size: var(--text-body);
  font-weight: 500;
  /* mesmo titulo de artigo do index (.featured-link a): sem sublinhado —
     o sublinhado de .article-body a e so para links no meio do texto corrido */
  text-decoration: none;
}

.article-footer__item .subtitle {
  margin-top: 8px;
  color: var(--color-ink-muted);
  font-size: var(--text-body);
}

@media (max-width: 640px) {
  .article-footer {
    grid-template-columns: 1fr;
  }
}

/* Bloco estatico com duas midias lado a lado, cada uma com sua legenda
   (video do prototipo + diagrama da metodologia, artigo "Evoluir e errar").
   Recorte proprio (aspect-ratio 635/720, object-fit cover — cada midia
   corta o conteudo centralizado em vez de distorcer), dividindo os 584px
   da coluna em vez do tamanho fixo de 306px: as duas juntas nao podem
   passar da largura do corpo, entao cada .article-figure fica com flex:1
   (280px cada, descontado o gap de 24px). */
.article-split {
  display: flex;
  gap: 24px;
  width: 100%;
}

.article-split > .article-figure {
  flex: 1 1 0;
  min-width: 0;
}

.article-split__media {
  aspect-ratio: 635 / 720;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-bg);
  box-shadow: var(--shadow-card);
}

.article-split__media img,
.article-split__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* video ampliado 20% dentro da propria caixa — o overflow:hidden do
   .article-split__media corta o excesso, centralizado (transform-origin
   padrao). Só o video, a imagem do diagrama fica no recorte normal. */
.article-split__media video {
  transform: scale(1.2);
}

@media (max-width: 640px) {
  .article-split {
    flex-direction: column;
  }

  .article-split > .article-figure {
    flex-basis: auto;
  }

  /* no desktop os 306px casam com .analise__viewer; no mobile empilhado
     nao ha motivo pra manter esse teto — a caixa ocupa toda a largura da
     coluna (o aspect-ratio 635/720 cuida da altura proporcional). */
  .article-split__media {
    max-width: 100%;
  }
}

/* =====================================================================
   Componente da secao Analise (artigo "Evoluir e errar")
   Unico elemento fora do vocabulario do MODELO_ARTIGO.md: uma imagem que
   troca conforme o topico lido ao lado. O destaque sobre a regiao do print
   que o paragrafo critica ja vem embutido na propria imagem (nao e mais
   um retangulo sobreposto via CSS). O corpo vaza a coluna do artigo (584px)
   desde o primeiro paint — foto em 306px de largura e coluna de texto em
   406px (508px menos 20%). width:max-content escapa do stretch de largura
   fixa herdado do .article-body (flex column), que e o que faz o corpo
   vazar para a direita.
   Todas as transicoes reusam o par blur+opacidade+translateY ja usado no
   crossfade dos cards (.card-layer--veiled) e no reveal (.reveal__inner);
   o clique usa a mesma reducao de 3% do .card--pressed (js/acervo.js).

   Duas linhas de grid: a dica ("Clique sobre o topico...") fica na
   primeira, fora do recorte de altura da foto, mas alinhada horizontalmente
   a coluna de texto (mesma coluna 2). Viewer e leitura ocupam a segunda
   linha, lado a lado — e e so essa linha que precisa caber na altura da
   foto (ver .analise__leitura). */
.analise {
  display: grid;
  width: max-content;
  grid-template-columns: 306px 406px;
  grid-template-rows: auto auto;
  column-gap: 24px;
  row-gap: 16px;
}

.analise__dica {
  grid-column: 2;
  grid-row: 1;
  margin: 0;
  font-size: var(--text-meta);
  color: var(--color-ink-muted);
}

.analise__viewer {
  grid-column: 1;
  grid-row: 2;
  position: relative;
  aspect-ratio: 1230 / 2400;
  border: 1px solid var(--color-border-soft);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--color-bg);
  box-shadow: var(--shadow-card);
}

.analise__shot {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  filter: blur(12px);
  transition: opacity 0.35s ease, filter 0.35s ease;
}

.analise__shot.is-active {
  opacity: 1;
  filter: none;
  z-index: 1;
}

.analise__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* altura travada na altura da foto (306px de largura, aspect-ratio
   1230/2400 => ~597px) — o "elemento de paragrafos" (historico + ativo +
   fila) tem que caber exatamente ate o fim da imagem, nao mais que isso.
   historico e o paragrafo ativo pegam a altura que precisam; a fila
   (flex:1) absorve o que sobra e e ela quem teve o overflow+degrade — ver
   .analise__fila. */
.analise__leitura {
  grid-column: 2;
  grid-row: 2;
  height: 597px;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.analise__historico,
.analise__topicos,
.analise__fila {
  margin: 0;
  padding: 0;
  list-style: none;
}

.analise__historico:empty {
  display: none;
}

/* traco do historico: um topico colapsado. Mesma largura do divisor de
   Ferramentas (.skill-list__divider) — curto, nao uma regua que ocupa a
   coluna inteira — mas com a mesma grossura (2px) do fio que destaca o
   paragrafo ativo, para os dois lerem como a mesma linguagem de "marcador
   de paragrafo". Pontas arredondadas (border-radius) nos dois.
   O espacamento entre tracos vira `gap` (nao margin colapsavel): um unico
   numero para ajustar, sem depender de como margins vizinhas se combinam. */
.analise__historico {
  display: flex;
  flex-direction: column;
  /* reduzido a 1/3 do espacamento anterior entre tracos (16px -> 5,33px) */
  gap: calc(16px / 3);
  /* mantem o respiro entre o ultimo traco colapsado e o paragrafo em
     destaque logo abaixo — esse valor nao mudou. */
  margin-bottom: 16px;
}

/* Magnificacao por proximidade do mouse (JS seta --proximity de 0 a 1 por
   traco, ver js/analise.js) — o traco mais perto do cursor alarga e escurece,
   os vizinhos seguem o mesmo movimento em degrade, como o dock do macOS.
   Repouso em 12px (metade do tamanho antigo); no pico da magnificacao
   (proximity 1) o traco chega aos 24px que era o comprimento de repouso
   anterior. Sem JS (ou fora do :hover), --proximity fica em 0 e o traco
   volta ao tamanho/cor padrao. */
.analise__historico button {
  appearance: none;
  position: relative;
  display: block;
  width: calc(12px + var(--proximity, 0) * 12px);
  height: 2px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--color-link) calc(var(--proximity, 0) * 100%), var(--color-border));
  cursor: pointer;
  transition: width 0.1s ease, background 0.1s ease, transform 0.12s ease;
}

/* area de toque 3x maior que a linha visivel (2px -> 6px), centralizada no
   mesmo eixo vertical do traco — o traco em si nao cresce, so a regiao que
   aceita o clique/toque ao redor dele. */
.analise__historico button::before {
  content: "";
  position: absolute;
  inset: -2px 0;
}

.analise__historico button:hover,
.analise__historico button:focus-visible {
  background: var(--color-link);
}

/* Fila: todos os topicos seguintes, nao so o proximo. flex:1 faz a fila
   herdar o que sobrar de altura depois do historico e do paragrafo ativo
   dentro dos 347px fixos de .analise__leitura — a base do calculo e o fim
   da imagem, nao um numero fixo aqui. min-height:0 e o que permite um item
   flex encolher abaixo do tamanho do conteudo (senao overflow:hidden nao
   segura nada). O mask-image apaga em degrade o que nao coube, terminando
   em opacidade zero exatamente na borda de baixo (nao um corte seco). */
.analise__fila {
  flex: 1 1 auto;
  min-height: 0;
  margin-top: 16px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 100%);
  mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 100%);
}

.analise__fila li + li {
  margin-top: 16px;
}

.analise__item--fila {
  color: var(--color-ink-muted);
}

.analise__item--fila button {
  appearance: none;
  display: block;
  width: 100%;
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: left;
  color: inherit;
  cursor: pointer;
  transition: color 0.15s ease, transform 0.12s ease;
}

.analise__item--fila button:hover,
.analise__item--fila button:focus-visible {
  color: var(--color-ink);
}

/* o fio azul vira um elemento proprio (pseudo) em vez de border-left: um
   border nao aceita border-radius so numa lateral sem arredondar a caixa
   inteira, e a linha precisa das pontas arredondadas, igual ao traco do
   historico.
   O fio mora no <li>, nao no <p>: quem recebe o veu de blur+opacidade na
   troca e so o paragrafo que sai (ver .analise.is-veiled abaixo), e
   opacity num pai apaga os filhos junto — com o fio dentro do <p> ele
   sumiria em vez de descer ate o proximo topico. Separando os dois, o
   texto apaga e o fio continua visivel viajando. */
.analise__item--ativo {
  position: relative;
  padding-left: 16px;
}

/* O fio se comporta como uma mola sendo puxada: as duas pontas andam, mas
   em ritmos diferentes — a da frente avanca rumo ao topico de destino, a de
   tras acompanha so uma fracao do caminho. Dai sai ao mesmo tempo o
   DESLOCAMENTO (o fio inteiro caminha) e o ESTICAMENTO (a distancia entre
   as pontas cresce). Duas variaveis, escritas por js/analise.js a partir da
   geometria real dos dois <li>:
     --magnet-slide   quanto a ponta de tras andou (px, sempre positivo; o
                      sentido quem da e o CSS, ver is-magnet-up);
     --magnet-stretch razao entre o comprimento esticado e o natural.
   O JS limita a ponta da frente a parar pouco antes da extremidade do
   paragrafo que entra (STRETCH_REACH) e nunca deixa o deslize comer o
   esticamento a ponto de encurtar o fio — por construcao a mola nem passa
   do alvo nem fica menor que o natural. Na troca o <li> e recriado e o
   pseudo novo nasce em repouso, ou seja, "volta ao tamanho do novo".
   A ordem translate -> scale importa: com transform-origin na ponta de
   tras, o translate leva essa ponta ate --magnet-slide e o scale estica o
   restante a partir dali. transform-origin troca de ponta conforme o
   sentido: descendo a mola cresce pra baixo (ancora no topo), subindo
   cresce pra cima (ancora no pe). Tres regimes de transicao:
     is-dragging  nenhuma — o ease-out em JS ja da a resistencia do gesto,
                  uma transicao CSS por cima so atrasaria a resposta;
     is-traveling ease padrao em 0.18s (= TRANSITION_MS), o estiramento
                  final enquanto o paragrafo antigo se apaga;
     repouso      bezier com bounce, a mola recolhendo sozinha quando o
                  gesto e abandonado no meio. */
.analise__item--ativo::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  border-radius: 9999px;
  background: var(--color-link);
  transform-origin: top center;
  transform: translateY(var(--magnet-slide, 0px)) scaleY(var(--magnet-stretch, 1));
  transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.analise.is-magnet-up .analise__item--ativo::before {
  transform-origin: bottom center;
  transform: translateY(calc(-1 * var(--magnet-slide, 0px))) scaleY(var(--magnet-stretch, 1));
}

.analise__item--ativo p {
  margin: 0;
  color: var(--color-ink);
}

/* Abertura da calha do fio: o paragrafo ativo tem padding-left:16px, que e
   exatamente o vao onde o fio azul mora; os itens da fila e os tracos do
   historico comecam colados no zero. Sem abrir esse vao antes da troca, o
   fio desce e esbarra no proximo paragrafo — nao ha para onde ele ir.
   Quem recebe o deslocamento e so o item marcado com .is-magnet-target por
   js/analise.js (o proximo da fila, ou o ultimo traco do historico se o
   gesto for pra tras) — os demais ficam parados, e o proximo paragrafo que
   sai da frente, nao a lista inteira. --magnet-open e sempre positivo (o
   vao abre pra direita nos dois sentidos) e vai ate 16px, encaixando com o
   padding que o item vai herdar ao virar ativo: no fim do gesto o texto ja
   esta na posicao final e so o fio precisa chegar. */
.analise .is-magnet-target {
  transform: translateX(var(--magnet-open, 0px));
  transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.analise.is-dragging .analise__item--ativo::before,
.analise.is-dragging .is-magnet-target {
  transition: none;
}

.analise.is-traveling .analise__item--ativo::before,
.analise.is-traveling .is-magnet-target {
  transition: transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}

/* redução de 3% no clique, espelhando .card--pressed (js/acervo.js) */
.analise__historico button.is-pressed,
.analise__item--fila button.is-pressed,
.analise__restart.is-pressed {
  transform: scale(0.97);
}

/* slot do botao de reiniciar: elemento proprio entre o paragrafo ativo e a
   fila (nao dentro do <li> ativo — nao e conteudo do paragrafo). So existe
   no ultimo topico (ver js/analise.js); :empty:display:none nos outros
   topicos evita reservar o respiro sem ter o que mostrar. O margin-top de
   16px repete o mesmo vao que .analise__historico abre embaixo de si,
   entre o historico e os paragrafos — aqui e o espelho, entre o paragrafo
   e o botao. */
.analise__restart-slot {
  margin-top: 16px;
}

.analise__restart-slot:empty {
  display: none;
  margin-top: 0;
}

/* botao de reiniciar, no mesmo estilo dos links do menu lateral
   (.aside-menu a[href="index.html"], .cv-link) — aqui como <button> porque
   nao navega, reinicia o estado do componente. */
.analise__restart {
  appearance: none;
  display: inline-block;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--text-body);
  font-weight: 1000;
  color: var(--color-link);
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease;
}

/* troca de estado: o veu de blur+opacidade+deslocamento (0.18s, mais
   rapido que o crossfade padrao do site de 0.35s) fica so no paragrafo que
   esta saindo — nao no bloco inteiro. O que se apaga e o texto do topico
   que deixa de ser ativo; o fio azul, que vive no <li> e nao no <p>, segue
   visivel descendo ate o proximo topico durante esse mesmo intervalo. */
.analise__item--ativo p {
  opacity: 1;
  filter: none;
  transform: none;
  transition: opacity 0.18s ease, filter 0.18s ease, transform 0.18s ease;
}

.analise.is-veiled .analise__item--ativo p {
  opacity: 0;
  filter: blur(12px);
  transform: translateY(8px);
}

@media (max-width: 640px) {
  .analise {
    width: 100%;
    grid-template-columns: 1fr;
  }

  /* uma coluna so: cai fora das linhas/colunas explicitas do desktop. A
     ordem visual (imagem, dica, leitura) e dada por "order", nao pela
     ordem do HTML — a dica precisa vir depois da foto no mobile. */
  .analise__dica,
  .analise__viewer,
  .analise__leitura {
    grid-column: 1;
    grid-row: auto;
  }

  .analise__viewer {
    order: 1;
  }

  .analise__dica {
    order: 2;
  }

  .analise__leitura {
    order: 3;
  }

  /* diferente do .article-split__media, a foto do .analise agora e bem
     mais alta que larga (aspect-ratio 1230/2400) — deixa-la ocupar a
     largura inteira da coluna faria a caixa passar de 600px de altura no
     mobile. Trava em 306px, centralizada, e a leitura volta a uma altura
     fixa menor (nao precisa mais casar com o fim da foto).
     width (nao max-width): dentro de uma linha de grid "auto" (coluna
     1fr), max-width so limita um stretch que nunca chega a se resolver —
     sem largura definida o navegador nao consegue calcular a altura via
     aspect-ratio antes de dimensionar a linha, e a caixa colapsa perto de
     0px. width:min(...) da um valor definido de largura, que resolve o
     aspect-ratio normalmente. */
  .analise__viewer {
    width: min(100%, 306px);
    margin: 0 auto;
  }

  .analise__leitura {
    height: 347px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .analise__shot,
  .analise__historico button,
  .analise__item--fila button,
  .analise__item--ativo p,
  .analise__item--ativo::before,
  .analise .is-magnet-target,
  .analise__restart {
    transition: none;
  }
}

/* Curriculo */
.cv-section {
  margin-bottom: 40px;
}

.cv-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
}

.cv-row + .cv-row {
  margin-top: 4px;
}

.cv-row .label {
  color: var(--color-ink);
}

.cv-row .date {
  color: var(--color-ink-muted);
  white-space: nowrap;
  font-size: var(--text-meta);
}

.courses-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 24px;
}

.course-item .meta {
  display: block;
  color: var(--color-ink-muted);
  font-size: var(--text-meta);
}

.course-item .name {
  display: block;
  margin-top: 4px;
  color: var(--color-ink);
}

.job-entry + .job-entry {
  margin-top: 24px;
}

/* titulo do cargo com o mesmo peso dos titulos de artigo do index
   (.featured-link a) — so a primeira linha (cargo + data), nao a da empresa */
.job-entry .cv-row:first-child .label {
  font-weight: 500;
}

.job-entry .bullets {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.job-entry .bullets li {
  position: relative;
  padding-left: 16px;
  font-size: var(--text-meta);
  color: var(--color-ink-muted);
}

.job-entry .bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-ink-muted);
}

/* Colunas de competencias (Areas de Interesse, Ferramentas, Afinidade com
   Processos) — mesma logica de grade dos Cursos, mas em 3 colunas alinhadas
   pelo topo, cada uma com seu rotulo e uma lista vertical propria. */
.skills-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  align-items: start;
}

.skill-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* as caixas de texto (titulo e itens) ficam limitadas a 70% da largura da
   coluna — nomes mais longos quebram em vez de esticar ate a borda */
.skill-col .section-label,
.skill-list li {
  max-width: 70%;
}

.skill-list li {
  color: var(--color-ink);
  font-size: var(--text-body);
}

/* o "-" do documento vira um respiro entre o grupo principal e o resto.
   A regua acompanha a largura do titulo da coluna (nao a coluna inteira):
   6.1em medidos no texto "Ferramentas", ancorados ao mesmo token de fonte do
   titulo (--text-body) para escalar junto. */
.skill-list__divider {
  width: calc(var(--text-body) * 6.1);
  max-width: 100%;
  height: 1px;
  margin: 8px 0;
  background: var(--color-border);
}

/* =====================================================================
   Revelar-no-hover
   Conteudo extra recolhido que so aparece ao passar o mouse (ou focar via
   teclado) sobre o grupo. A transicao de estado espelha a dos cards do index:
   a ALTURA anima em cubic-bezier(0.22, 1, 0.36, 1) (via grid-template-rows
   0fr->1fr, que dispensa saber a altura final) e o conteudo faz o mesmo
   cross-fade das camadas do card — opacity + blur(12px) + translateY(8px).
   O indicativo (".reveal__toggle") colapsa em sincronia com a abertura, entao
   ".reveal" nunca encolhe abaixo do cursor e o hover nao pisca.
   ===================================================================== */
.reveal__toggle {
  appearance: none;
  min-height: 0;
  overflow: hidden;
  border: 0;
  background: none;
  padding: 4px 0 0;
  font: inherit;
  line-height: 1;
  text-align: left;
  /* mesma cor e peso dos titulos de artigo do index (.featured-link a) */
  color: var(--color-link);
  font-weight: 500;
  cursor: pointer;
  transition: color 0.15s ease, opacity 0.35s ease;
}

.reveal__toggle:hover,
.reveal__toggle:focus-visible {
  color: var(--color-link);
}

/* o indicativo vive numa faixa que colapsa (1fr -> 0fr) ao revelar */
.reveal__toggle-wrap {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* O estado aberto e uma classe (.is-open) posta por js/reveal.js, nao :hover:
   o mouse abre e o item TRAVA aberto; so um clique sobre a regiao expandida
   devolve ao estado contraido. */
.reveal.is-open > .reveal__toggle-wrap {
  grid-template-rows: 0fr;
}

.reveal.is-open .reveal__toggle {
  opacity: 0;
}

/* faixa do conteudo: cresce de 0fr a 1fr com o mesmo tempo/curva do card */
.reveal__more {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.reveal.is-open > .reveal__more {
  grid-template-rows: 1fr;
}

/* o conteudo em si entra com o cross-fade das camadas do card */
.reveal__inner {
  min-height: 0;
  overflow: hidden;
  opacity: 0;
  filter: blur(12px);
  transform: translateY(8px);
  transition: opacity 0.35s ease, filter 0.35s ease, transform 0.35s ease;
}

.reveal.is-open .reveal__inner {
  opacity: 1;
  filter: none;
  transform: none;
}

/* aberto, a regiao expandida convida ao clique que a fecha */
.reveal.is-open > .reveal__more {
  cursor: pointer;
}

/* variante em pilha (coluna Ferramentas): itens revelados seguem o mesmo
   empilhamento e respiro da lista */
.reveal--stack .reveal__inner {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* variante texto (bio): mesmo respiro entre paragrafos que .bio usa entre
   seus filhos diretos (gap: 24px) — aqui os paragrafos colapsados ficam
   dentro do reveal__inner, entao o gap tem que ser repetido aqui. */
.reveal--text .reveal__inner {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* link do artigo citado no 3o paragrafo da bio: peso maior que o corpo do
   texto (mesmo padrao de seletor por atributo de .aside-menu a[href=...]) */
.reveal--text a[href="artigo-casos-estudo-ia.html"] {
  font-weight: 500;
}

/* variante blocos (Experiencia profissional): o toggle fica na posicao do 2o
   cargo, com o mesmo respiro que separa os job-entry */
.job-entry + .reveal--jobs {
  margin-top: 24px;
}

@media (prefers-reduced-motion: reduce) {
  .reveal__toggle-wrap,
  .reveal__more,
  .reveal__inner,
  .reveal__toggle {
    transition: none;
  }
}

/* =====================================================================
   Mobile (<=640px)
   Abaixo de 640px nem a coluna cabe fixa: ela vira fluida e ocupa a largura
   util da tela. O menu (ja no topo em todo desktop) apenas reempilha e
   realinha ao padding da coluna.
   ===================================================================== */
@media (max-width: 640px) {
  :root {
    /* o breakout do carrossel nao faz sentido sem margem lateral sobrando —
       ele so empurraria o trilho para fora da tela. Aqui a vitrine e a
       propria largura util da coluna: a janela menos o padding de .main.
       Isso tambem redimensiona o card expandido, que ja deriva daqui. */
    --carousel-track: calc(100vw - 48px);
  }

  .main {
    width: 100%;
    padding-inline: 24px;
    padding-bottom: 64px;
  }

  /* coluna fluida: o menu realinha aos 24px de padding do .main e encosta
     um pouco mais no topo. */
  .aside-menu {
    flex-direction: column;
    align-items: normal;
    width: auto;
    margin-inline: 24px;
    padding-top: 32px;
  }

  .aside-menu ul {
    justify-content: flex-start;
    margin-top: 0;
  }

  /* o empilhamento do card nao esta aqui: video em cima e texto embaixo
     agora vale em qualquer largura */

  .courses-grid {
    grid-template-columns: 1fr;
  }

  .skills-grid {
    grid-template-columns: 1fr;
  }
}
