/* ==========================================================================
   TV LITORAL SUL PAULISTA — Sistema de Design Editorial
   Autor: reconstrução completa (protótipo v1)
   Arquitetura CSS: tokens -> reset -> primitivos -> layout -> componentes
   Sem framework. Sem dependência externa além das webfonts.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   Toda cor, espaço e tamanho do site sai daqui. Trocar a marca = trocar 3 linhas.
   -------------------------------------------------------------------------- */
:root {
  /* -- Cor: tinta (texto/estrutura) -- */
  /* Escala azul-acinzentada: serve de texto sobre claro E de fundo das faixas
     escuras. Medida: branco sobre --ink-900 = 14,4:1; --ink-300 = 7,8:1;
     --ink-400 = 5,0:1 (antes 3,9:1 num cinza-azulado mais claro). */
  --ink-900: #1c2b3a;
  /* Ciano escurecido e ouro velho — os dois botões da lateral do Ao vivo.
     Ficam aqui, e não soltos na regra do botão, porque cor de marca usada em
     um lugar hoje é cor usada em quatro daqui a um mês. */
  --cyan-escuro: #14707f;
  --cyan-escuro-forte: #0e5b67;
  --ouro-velho: #c9a227;
  /* O hover do ouro CLAREIA, não escurece — é o contrário do ciano, e de
     propósito: o texto dele é escuro, então escurecer o fundo aproximava os
     dois e derrubava o contraste para 4,4:1, abaixo do mínimo. Clareando, vai
     a 6,7:1. */
  --ouro-velho-forte: #d4ac2e;
  --ink-800: #2b3d4e;
  --ink-700: #3d5063;
  --ink-500: #66788a;
  --ink-400: #8b9aa8;
  --ink-300: #b3c0cb;
  --ink-200: #d9e0e6;
  --ink-100: #eaeef1;

  /* -- Cor: papel (fundos) -- */
  --paper: #ffffff;
  --paper-warm: #f7f5f1;
  --paper-cool: #f2f4f6;

  /* -- Cor: marca -- */
  --brand: #0b3d68;          /* azul marítimo institucional */
  --brand-dark: #072a49;
  --brand-soft: #e8f0f7;
  /* Destaque editorial: azul. O vermelho ficou reservado para o botão AO VIVO
     (--live), e para mais nada. */
  --accent: #15619b;         /* azul de destaque: kicker, botão, "no ar" */
  --accent-dark: #0f4a78;
  --accent-light: #7fbce9;   /* mesma família, para uso SOBRE fundo escuro */
  --live: #d21e2b;           /* vermelho: só o botão/selo AO VIVO */
  --live-dark: #a5161f;
  --gold: #b6842b;           /* selo, destaque de patrocínio */

  /* -- Tipografia -- */
  --font-display: "Playfair Display", "Times New Roman", Georgia, serif;
  --font-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;

  --fs-kicker: 0.6875rem;    /* 11px */
  --fs-meta: 0.75rem;        /* 12px */
  --fs-small: 0.875rem;      /* 14px */
  --fs-body: 1.0625rem;      /* 17px */
  --fs-lead: 1.1875rem;      /* 19px */
  --fs-h4: 1.25rem;
  --fs-h3: 1.5rem;
  --fs-h2: 2rem;
  --fs-h1: 2.75rem;
  --fs-hero: clamp(2rem, 1.2rem + 3.4vw, 3.75rem);
  --fs-masthead: clamp(1.75rem, 0.9rem + 3.6vw, 3.5rem);

  /* -- Espaço: escala 4px -- */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4rem;
  --s-9: 6rem;

  /* -- Layout -- */
  --wrap: 1280px;
  --wrap-narrow: 780px;
  --gutter: 1.25rem;
  --radius: 2px;             /* revista: canto reto, não arredondado */
  --radius-lg: 4px;

  /* -- Elevação -- */
  --shadow-1: 0 1px 2px rgba(28, 43, 58, 0.07), 0 2px 8px rgba(28, 43, 58, 0.05);
  --shadow-2: 0 4px 16px rgba(28, 43, 58, 0.12);
  --shadow-3: 0 12px 40px rgba(28, 43, 58, 0.18);

  /* -- Movimento -- */
  --t-fast: 140ms cubic-bezier(0.2, 0, 0.2, 1);
  --t-base: 240ms cubic-bezier(0.2, 0, 0.2, 1);

  --header-h: 56px;
}

/* --------------------------------------------------------------------------
   2. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 16px);
}

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: 1.6;
  color: var(--ink-800);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, picture, video, iframe { max-width: 100%; display: block; }
img { height: auto; }

h1, h2, h3, h4, h5, h6 { margin: 0; font-weight: 700; line-height: 1.15; color: var(--ink-900); }
p { margin: 0 0 var(--s-4); }
ul, ol { margin: 0; padding: 0; list-style: none; }
figure, blockquote { margin: 0; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }

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

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
/* EXCEÇÃO, por decisão do cliente em 05/09/2026: a esteira de apoiadores gira
   sempre, inclusive em máquina com efeitos de animação desligados no sistema.
   O corte global acima congelaria a esteira com `animation-duration: 0.01ms`;
   estas duas linhas devolvem o movimento. Escrito como regra separada (e não
   com `:not()` no seletor global) porque `:not()` com seletor complexo
   invalidaria a regra inteira em navegador que não o entende. */
@media (prefers-reduced-motion: reduce) {
  .carousel--marquee .carousel__track {
    animation-duration: var(--marquee-duration, 40s) !important;
    animation-iteration-count: infinite !important;
  }
}


/* --------------------------------------------------------------------------
   3. PRIMITIVOS
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap--narrow { max-width: var(--wrap-narrow); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: 0; top: -100px; z-index: 999;
  background: var(--ink-900); color: #fff;
  padding: var(--s-3) var(--s-5); font-weight: 600;
}
.skip-link:focus { top: 0; }

/* Kicker: a etiqueta de categoria acima do título. Assinatura do estilo revista. */
.kicker {
  display: inline-block;
  font-family: var(--font-ui);
  font-size: var(--fs-kicker);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: var(--s-2);
}
.kicker--brand { color: var(--brand); }
.kicker--muted { color: var(--ink-400); }
/* Sobre fundo escuro o azul de destaque cai para ~2:1. Nas faixas escuras o
   kicker (e o azul institucional) usam a variante clara. Medido: 7,1:1. */
.section--ink .kicker,
.live .kicker,
.page-hero .kicker,
.footer .kicker,
.mobile-menu .kicker { color: var(--accent-light); }
.section--ink .kicker--brand,
.live .kicker--brand,
.page-hero .kicker--brand,
.footer .kicker--brand { color: var(--accent-light); }

.meta {
  font-size: var(--fs-meta);
  /* ink-400 (#8b9aa8) sobre --paper (#fff) mede 2,9:1 — falha o mínimo de
     4,5:1 até para texto grande. ink-400 foi calibrado para texto CLARO
     sobre fundo ESCURO (ver o comentário dos tokens, linha ~15); usado aqui,
     em fundo claro, é o token errado. Medido e corrigido em 05/09/2026,
     junto com .quote footer e .field::placeholder — o mesmo engano nos três
     lugares. ink-500 sobre --paper mede 4,55:1. */
  color: var(--ink-500);
  letter-spacing: 0.02em;
}
.meta__dot { margin-inline: var(--s-2); color: var(--ink-200); }
/* .meta dentro de faixa escura continua precisando do tom claro — mesmo
   padrão já usado em .kicker--brand (acima) e .program p (abaixo). */
.section--ink .meta,
.live .meta,
.footer .meta,
.page-hero .meta,
.mobile-menu .meta { color: var(--ink-400); }

/* Cabeçalho de seção com filete — o "rule" das revistas */
.section {
  padding-block: var(--s-8);
  border-top: 1px solid var(--ink-100);
}
.section--flush { border-top: 0; }
.section--warm { background: var(--paper-warm); }
.section--cool { background: var(--paper-cool); }
.section--ink { background: var(--ink-900); color: var(--ink-200); border-top: 0; }
.section--ink h1, .section--ink h2, .section--ink h3, .section--ink h4 { color: #fff; }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  margin-bottom: var(--s-6);
  padding-bottom: var(--s-3);
  border-bottom: 3px solid var(--ink-900);
}
.section--ink .section-head { border-bottom-color: rgba(255, 255, 255, 0.25); }
/* h1 entra aqui junto do h2: cada página de listagem precisa de UM h1, e o
   título da seção é ele. Sem isso as páginas ficavam sem h1 nenhum — medido. */
.section-head h1,
.section-head h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 900;
  letter-spacing: -0.01em;
}
.section-head__link {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brand);
  white-space: nowrap;
  transition: color var(--t-fast);
}
.section-head__link:hover { color: var(--accent); }
.section--ink .section-head__link { color: #fff; }

/* Em telas estreitas o par título+link não cabe na mesma linha: empilha.
   Sem isso o link com white-space:nowrap empurra o documento e cria
   scroll horizontal — o defeito mais comum e mais visível no celular. */
@media (max-width: 599px) {
  .section-head { flex-direction: column; align-items: flex-start; gap: var(--s-2); }
  .section-head__link { white-space: normal; }
}

/* Botões */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border-radius: var(--radius);
  transition: background var(--t-fast), color var(--t-fast), transform var(--t-fast);
}
.btn:active { transform: translateY(1px); }
.btn--accent { background: var(--accent); color: #fff; }
.btn--accent:hover { background: var(--accent-dark); }
/* Única variante vermelha: os botões que dizem "ao vivo". Ver --live. */
.btn--live { background: var(--live); color: #fff; }
.btn--live:hover { background: var(--live-dark); }
.btn--brand { background: var(--brand); color: #fff; }
.btn--brand:hover { background: var(--brand-dark); }
.btn--ghost { border: 1.5px solid currentColor; color: var(--ink-800); }
.btn--ghost:hover { background: var(--ink-900); color: #fff; border-color: var(--ink-900); }
.btn--light { background: #fff; color: var(--ink-900); }
.btn--light:hover { background: var(--ink-100); }

/* Os dois botões da lateral do Ao vivo (06/09/2026, cores pedidas pelo
   cliente). Eram os dois brancos, e ficavam indistinguíveis um do outro
   embaixo do "Peça sua música" vermelho.

   O texto de cada um foi escolhido pelo contraste, não pelo gosto: branco
   sobre o ciano dá 5,7:1 e o ink-900 sobre o ouro dá 6,0:1 — os dois passam
   folgado do mínimo de 4,5:1 da WCAG AA. Texto branco sobre o ouro daria
   2,4:1 e seria ilegível ao sol, que é onde metade deste público abre o
   site. */
.btn--cyan { background: var(--cyan-escuro); color: #fff; }
.btn--cyan:hover { background: var(--cyan-escuro-forte); }
.btn--ouro { background: var(--ouro-velho); color: var(--ink-900); }
.btn--ouro:hover { background: var(--ouro-velho-forte); }

.btn--block { width: 100%; }

/* Selo AO VIVO */
.live-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 4px 10px;
  background: var(--live);
  color: #fff;
  font-size: var(--fs-kicker);
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: var(--radius);
}
.live-badge::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #fff;
  animation: pulse 1.8s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.35; transform: scale(0.75); }
}

/* Imagem: proporção + fallback quando o arquivo não existe */
.ph {
  position: relative;
  display: block;
  overflow: hidden;
  background: linear-gradient(135deg, var(--ink-100) 0%, var(--paper-cool) 100%);
}
.ph::after {
  content: attr(data-label);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: var(--fs-kicker);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-300);
  z-index: 0;
}
.ph > img {
  position: relative;
  z-index: 1;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 600ms cubic-bezier(0.2, 0, 0.2, 1), opacity var(--t-base);
}
.ph > img.is-broken { opacity: 0; }
.ratio-16x9 { aspect-ratio: 16 / 9; }
.ratio-4x3  { aspect-ratio: 4 / 3; }
.ratio-3x4  { aspect-ratio: 3 / 4; }
.ratio-1x1  { aspect-ratio: 1 / 1; }

/* --------------------------------------------------------------------------
   4. BARRA SUPERIOR
   -------------------------------------------------------------------------- */
.topbar {
  background: var(--ink-900);
  color: var(--ink-300);
  font-size: var(--fs-meta);
}
.topbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* `wrap` entrou com a assinatura da Queen's Heart (06/09/2026): é ele que
     permite à assinatura cair numa linha própria no celular. Sem isto o
     `flex-basis: 100%` dela espremeria a data e as redes até virarem nada. */
  flex-wrap: wrap;
  gap: var(--s-1) var(--s-4);
  min-height: 38px;
}
/* CORPO ÚNICO DA BARRA (06/09/2026). Data, redes e assinatura tinham três
   tamanhos e duas cores: a data em --fs-meta cinza, os ícones em 15px cinza e
   a assinatura em 17px branco. Lado a lado, a barra parecia desalinhada e a
   assinatura ficava com cara de aviso solto. Agora os três compartilham a
   MESMA medida (--topbar-fs) e a mesma cor — mudar o corpo da barra é mudar
   uma variável, não caçar três regras. */
.topbar {
  --topbar-fs: clamp(.875rem, .72rem + .42vw, 1.0625rem);
}
.topbar__left { order: 1; display: flex; align-items: center; gap: var(--s-4); }
.topbar__date {
  letter-spacing: 0.02em;
  font-size: var(--topbar-fs);
  font-weight: 700;
  color: #fff;
}
.topbar__date::first-letter { text-transform: uppercase; }
.topbar__weather {
  display: none; align-items: center; gap: var(--s-2);
  font-size: var(--topbar-fs);
  font-weight: 700;
  color: #fff;
}
/* `order: 2` explícito: sem ele o padrão (0) jogaria as redes na frente da
   data, porque os outros dois itens da barra agora são ordenados. */
.topbar__social { order: 2; display: flex; align-items: center; gap: var(--s-1); }
.topbar__social a {
  display: grid; place-items: center;
  /* A caixa acompanha o corpo do texto, para o ícone ter a mesma altura
     visual da data e da assinatura ao lado. */
  width: calc(var(--topbar-fs) + 1.15rem);
  height: calc(var(--topbar-fs) + 1.15rem);
  color: #fff;
  transition: color var(--t-fast), background var(--t-fast);
  border-radius: var(--radius);
}
.topbar__social a:hover { color: #fff; background: rgba(255, 255, 255, 0.18); }
.topbar__social svg { width: var(--topbar-fs); height: var(--topbar-fs); fill: currentColor; }

/* Assinatura da Queen's Heart na barra superior (06/09/2026). Branco e em
   negrito, como pedido, e em corpo maior que o resto da barra — é a única
   coisa aqui que precisa ser lida por quem NÃO veio pela emissora.

   No celular ela ocupa a linha inteira (`flex-basis: 100%` + `order: 3`),
   centralizada e abaixo da data e das redes: forçá-la a caber na mesma linha
   de 38px espremeria a data até o "…" e ainda deixaria a assinatura ilegível,
   que é o oposto do pedido. A barra cresce uns 24px no celular, e só ali. */
.topbar__credit {
  order: 3;
  flex: 1 1 100%;
  margin: 0;
  padding-bottom: var(--s-2);
  text-align: center;
  color: #fff;
  font-weight: 700;
  font-size: var(--topbar-fs);
  letter-spacing: .01em;
  line-height: 1.35;
}
.topbar__credit a {
  color: #fff;
  font-weight: 800;
  /* Sublinhado deslocado: o link tem que se ler como link mesmo num fundo já
     todo branco e negrito, e o contorno vermelho é o mesmo do AO VIVO. */
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.topbar__credit a:hover,
.topbar__credit a:focus-visible { text-decoration-color: #fff; }

@media (min-width: 720px) {
  .topbar__weather { display: flex; }
}
/* A partir daqui a barra volta a ser uma linha só: a assinatura fica no meio,
   entre a data e as redes, e o `flex: 1` centraliza o texto no espaço que
   sobra sem depender da largura dos dois vizinhos. */
@media (min-width: 900px) {
  .topbar__credit {
    order: 2;
    flex: 1 1 auto;
    padding-bottom: 0;
    padding-inline: var(--s-4);
  }
  .topbar__social { order: 3; }
}

/* --------------------------------------------------------------------------
   5. MASTHEAD (o "cabeçalho de revista")
   -------------------------------------------------------------------------- */
.masthead {
  background: var(--paper);
  padding-block: var(--s-6) var(--s-5);
  text-align: center;
  border-bottom: 1px solid var(--ink-100);
}
.masthead__logo {
  display: inline-block;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-masthead);
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--ink-900);
}
.masthead__logo span { color: var(--brand); }
.masthead__tagline {
  margin-top: var(--s-3);
  font-size: var(--fs-kicker);
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  /* ink-400 sobre papel branco mede 2,9:1 — abaixo do mínimo até para texto
     grande, e isto aqui tem 11px. É o MESMO engano já corrigido no .meta e no
     .field::placeholder (05/09/2026): ink-400 é para texto claro sobre fundo
     escuro. ink-500 mede 4,55:1. Corrigido em 06/09/2026, ao acrescentar o
     CNPJ nesta linha. */
  color: var(--ink-500);
}

/* CNPJ na assinatura do masthead (06/09/2026). No desktop segue a linha, com
   o mesmo ponto médio das outras partes; abaixo de 640px a linha inteira não
   cabe em uma só, e o número quebraria no meio — então ele vira uma segunda
   linha inteira, que é como documento de pessoa jurídica se lê. */
.masthead__cnpj { white-space: nowrap; }
.masthead__cnpj::before { content: " \00b7 "; }
@media (max-width: 639px) {
  .masthead__cnpj { display: block; margin-top: var(--s-1); }
  .masthead__cnpj::before { content: none; }
}
.masthead__rule {
  width: 56px; height: 3px;
  margin: var(--s-4) auto 0;
  background: var(--accent);
}

/* Marca com emblema: emblema + palavra, alinhados pela linha de base óptica. */
.masthead__brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-4);
}
.masthead__emblem {
  width: 62px; height: 62px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: 0 0 0 1px var(--ink-100), var(--shadow-1);
  flex: 0 0 auto;
}
.nav__compact-logo { display: none; align-items: center; gap: var(--s-2); }
.nav.is-stuck .nav__compact-logo { display: flex; }
.nav__compact-emblem { width: 26px; height: 26px; border-radius: 50%; object-fit: cover; }
@media (max-width: 480px) {
  .masthead__emblem { width: 46px; height: 46px; }
  .masthead__brand { gap: var(--s-3); }
}

/* --------------------------------------------------------------------------
   6. NAVEGAÇÃO
   -------------------------------------------------------------------------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--paper);
  border-bottom: 1px solid var(--ink-100);
  transition: box-shadow var(--t-base);
}
.nav.is-stuck { box-shadow: var(--shadow-2); }

.nav__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  min-height: var(--header-h);
}

.nav__compact-logo {
  display: none;
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.nav__compact-logo span { color: var(--brand); }
.nav.is-stuck .nav__compact-logo { display: block; }

.nav__list {
  display: none;
  align-items: center;
  gap: var(--s-1);
  flex: 1;
}
.nav__list a {
  display: block;
  padding: var(--s-3) var(--s-3);
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-700);
  position: relative;
  transition: color var(--t-fast);
}
.nav__list a::after {
  content: "";
  position: absolute;
  left: var(--s-3); right: var(--s-3); bottom: 8px;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-base);
}
.nav__list a:hover { color: var(--ink-900); }
.nav__list a:hover::after,
.nav__list a[aria-current="page"]::after { transform: scaleX(1); }
.nav__list a[aria-current="page"] { color: var(--ink-900); }

.nav__actions { display: flex; align-items: center; gap: var(--s-2); }

.nav__live {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  background: var(--live);
  color: #fff;
  font-size: var(--fs-meta);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: var(--radius);
  transition: background var(--t-fast);
}
.nav__live:hover { background: var(--live-dark); }
.nav__live::before {
  content: "";
  width: 7px; height: 7px; border-radius: 50%;
  background: #fff;
  animation: pulse 1.8s ease-in-out infinite;
}

.nav__toggle {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  color: var(--ink-900);
}
.nav__toggle svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 2; }
.nav__toggle .icon-close { display: none; }
.nav__toggle[aria-expanded="true"] .icon-open { display: none; }
.nav__toggle[aria-expanded="true"] .icon-close { display: block; }

@media (min-width: 1024px) {
  .nav__list { display: flex; }
  .nav__toggle { display: none; }
}

/* Menu móvel */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--ink-900);
  color: #fff;
  padding: var(--s-6) var(--gutter);
  overflow-y: auto;
  transform: translateY(-100%);
  visibility: hidden;
  transition: transform var(--t-base), visibility var(--t-base);
}
.mobile-menu.is-open { transform: translateY(0); visibility: visible; }
.mobile-menu__head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: var(--s-6);
}
.mobile-menu__logo { font-family: var(--font-display); font-weight: 900; font-size: 1.25rem; }
.mobile-menu__logo span { color: var(--brand); }
.mobile-menu__close { width: 40px; height: 40px; display: grid; place-items: center; color: #fff; }
.mobile-menu__close svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 2; }
.mobile-menu__list li { border-bottom: 1px solid rgba(255, 255, 255, 0.1); }
.mobile-menu__list a {
  display: block;
  padding: var(--s-4) 0;
  font-family: var(--font-display);
  font-size: 1.5rem;
  font-weight: 700;
  color: #fff;
}
.mobile-menu__list a:hover { color: var(--accent-light); }
.mobile-menu__foot { margin-top: var(--s-6); display: flex; gap: var(--s-3); flex-wrap: wrap; }

body.is-locked { overflow: hidden; }

/* --------------------------------------------------------------------------
   7. HERO EDITORIAL
   -------------------------------------------------------------------------- */
.hero { padding-block: var(--s-6) var(--s-7); }
.hero__grid {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
}

.hero__lead { display: block; }
.hero__lead .ph { margin-bottom: var(--s-4); }
.hero__lead:hover .ph > img { transform: scale(1.03); }
.hero__title {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin-bottom: var(--s-3);
}
.hero__lead:hover .hero__title { color: var(--brand); }
.hero__standfirst {
  font-size: var(--fs-lead);
  color: var(--ink-500);
  line-height: 1.5;
  margin-bottom: var(--s-3);
}

.hero__side { display: grid; gap: var(--s-5); align-content: start; }
.hero__side-item {
  display: grid;
  grid-template-columns: 104px 1fr;
  gap: var(--s-4);
  padding-bottom: var(--s-5);
  border-bottom: 1px solid var(--ink-100);
}
.hero__side-item:last-child { border-bottom: 0; padding-bottom: 0; }
.hero__side-item h3 {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.25;
}
.hero__side-item:hover h3 { color: var(--brand); }
.hero__side-item:hover .ph > img { transform: scale(1.05); }

@media (min-width: 900px) {
  .hero__grid { grid-template-columns: minmax(0, 1.85fr) minmax(0, 1fr); gap: var(--s-7); }
  .hero__side { border-left: 1px solid var(--ink-100); padding-left: var(--s-6); }
  .hero__side-item { grid-template-columns: 1fr; }
  .hero__side-item .ph { order: -1; }
  .hero__side-item h3 { font-size: 1.125rem; }
}

/* --------------------------------------------------------------------------
   8. FAIXA AO VIVO
   -------------------------------------------------------------------------- */
.live { background: var(--ink-900); color: var(--ink-200); }
.live__grid {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
  padding-block: var(--s-7);
}
.live__player {
  position: relative;
  background: #000;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-3);
}
.live__player .ph { background: linear-gradient(135deg, #2b3d4e, #1c2b3a); }
.live__player .ph::after { color: var(--ink-500); }

/* O player 24h (videocast/sync-player.js). A API do YouTube substitui a
   div #webtv-player por um <iframe> e não leva classe nenhuma junto — por
   isso a proporção mora no pai e o filho é posicionado por seletor
   universal, que vale para a div de antes e para o iframe de depois. */
.live__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}
/* SÓ o player. Um `> *` aqui esticava para a tela inteira TODO filho da
   moldura — inclusive o aviso e o botão de sair, que viravam um bloco de
   100% com o texto no meio da tela em vez de ficarem no canto. */
.live__frame > #webtv-player,
.live__frame > iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
/* Aviso, dica de som e botão de sair são desenhados pelo próprio script,
   que já traz o posicionamento inline; aqui só entram a fonte e o peso. */
.webtv-aviso, .webtv-som, .webtv-sair {
  font-family: var(--font-body, inherit);
  font-weight: 600;
}

/* Tela cheia pela API do navegador. A proporção 16:9 sai de cena: quem
   deixa o vídeo com a forma certa dentro da tela é o próprio player do
   YouTube, que já põe tarja onde sobra. */
.live__frame:fullscreen,
.live__frame:-webkit-full-screen {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
}

/* Plano B, para navegador sem a API em elemento que não seja <video>
   (iOS Safari): moldura fixa cobrindo a tela. */
.live__frame--cheia {
  position: fixed;
  inset: 0;
  z-index: 9999;
  aspect-ratio: auto;
  width: 100vw;
  height: 100vh;
  height: 100dvh; /* barra do navegador do celular entra e sai */
}
body.webtv-sem-rolagem { overflow: hidden; }

/* --------------------------------------------------------------------------
   8b. PEDIDOS DE MÚSICA (migration-34)
   A lista vive na lateral do Ao vivo, sobre o fundo escuro da faixa, e
   também na página /pedidos/, sobre fundo claro. Por isso a cor do texto é
   herdada em vez de fixada: `currentColor` resolve os dois casos com um
   bloco só, sem uma variante `--escura` que alguém esqueceria de atualizar.
   -------------------------------------------------------------------------- */
/* A lista de pedidas mora DENTRO DO MODAL (includes/lista-modal.php), atrás
   do botão "Lista de músicas". Ela já foi uma coluna na faixa Ao vivo e saiu
   em 06/09/2026, no mesmo dia: lista que cresce empurra o player e desalinha
   a faixa inteira.

   Rola sozinha: com trinta pedidos, a caixa do modal viraria uma página de
   rolagem e o botão de fechar sairia da tela. O teto em `vh` é o que mantém
   topo e rodapé do modal sempre visíveis. */
.live__pedidos { max-height: 55vh; overflow-y: auto; padding-right: var(--s-2); }
.live__pedidos .aside-block__title { margin-bottom: var(--s-3); }

.pedidos {
  list-style: none;
  margin: 0 0 var(--s-3);
  padding: 0;
  display: grid;
  gap: var(--s-2);
}
.pedido {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--radius-md, 8px);
  background: rgba(255, 255, 255, 0.06);
}
/* Fundo claro — na página /pedidos/ e dentro do modal, onde o cinza de 6%
   sobre branco desapareceria. */
.section .pedido,
.modal .pedido { background: var(--ink-100); }

.pedido__texto { min-width: 0; flex: 1; }
.pedido__texto strong {
  display: block;
  font-size: 0.95rem;
  line-height: 1.3;
  /* Título de vídeo do YouTube é longo e não tem espaço garantido — sem isto
     um único pedido empurra a lateral inteira. */
  overflow-wrap: anywhere;
}
.pedido__quem {
  display: block;
  font-size: var(--fs-meta);
  opacity: 0.7;
}

.pedido__voto { margin: 0; }
.pedido__botao {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid currentColor;
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fs-meta);
  line-height: 1;
  cursor: pointer;
  transition: background var(--t-base), opacity var(--t-base);
}
.pedido__botao:hover { background: rgba(255, 255, 255, 0.12); }
.section .pedido__botao:hover,
.modal .pedido__botao:hover { background: rgba(0, 0, 0, 0.06); }
.pedido__botao[disabled] { cursor: default; }
.pedido__botao.is-votado { background: var(--accent); border-color: var(--accent); color: #fff; }
.pedido__botao.is-repetido { opacity: 0.5; }
.pedido__votos { font-weight: 700; font-variant-numeric: tabular-nums; }

/* Já bateu a votação e está na programação da madrugada. */
.pedido--no-ar { border-left: 3px solid var(--accent); }

.pedidos__regra { opacity: 0.75; }
.pedidos__titulo { margin-top: var(--s-6); }

/* Texto de ajuda embaixo do campo do formulário de pedido. */
.campo__ajuda {
  display: block;
  margin-top: 4px;
  font-size: var(--fs-meta);
  color: var(--ink-500);
}
.live__play {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: grid;
  place-items: center;
  background: rgba(28, 43, 58, 0.35);
  transition: background var(--t-base);
}
.live__play:hover { background: rgba(28, 43, 58, 0.2); }
.live__play span {
  display: grid; place-items: center;
  width: 74px; height: 74px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 0 rgba(21, 97, 155, 0.55);
  animation: ring 2.4s ease-out infinite;
}
.live__play svg { width: 26px; height: 26px; fill: #fff; margin-left: 4px; }
@keyframes ring {
  0%   { box-shadow: 0 0 0 0 rgba(21, 97, 155, 0.5); }
  70%  { box-shadow: 0 0 0 26px rgba(21, 97, 155, 0); }
  100% { box-shadow: 0 0 0 0 rgba(21, 97, 155, 0); }
}
.live__overlay {
  position: absolute;
  left: var(--s-4); top: var(--s-4);
  z-index: 3;
  display: flex; gap: var(--s-2); align-items: center;
}
.live__viewers {
  padding: 4px 10px;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: var(--fs-kicker);
  font-weight: 600;
  letter-spacing: 0.08em;
  border-radius: var(--radius);
}

.live__aside { display: grid; gap: var(--s-5); align-content: start; }
.live__now {
  padding: var(--s-5);
  background: rgba(255, 255, 255, 0.05);
  border-left: 3px solid var(--accent-light);
  border-radius: var(--radius);
}
.live__now h3 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 900;
  color: #fff;
  margin-block: var(--s-2);
}
.live__now p { color: var(--ink-300); font-size: var(--fs-small); margin: 0; }
.live__next {
  font-size: var(--fs-small);
  /* Painel translúcido: o fundo efetivo é mais claro que a faixa. Com
     --ink-400 media 4,3:1; com --ink-300, 6,5:1. */
  color: var(--ink-300);
  padding-top: var(--s-4);
  margin-top: var(--s-4);
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.live__next strong { color: #fff; display: block; font-size: var(--fs-body); }
.live__cta { display: grid; gap: var(--s-3); }

@media (min-width: 900px) {
  .live__grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--s-7); }
}

/* --------------------------------------------------------------------------
   9. CARDS DE NOTÍCIA
   -------------------------------------------------------------------------- */
/* ATENÇÃO, comentário corrigido em 10/09/2026: esta barra NÃO é mais gerada
   por JS. O `initFiltroCidade` que a montava a partir do `data-city` dos
   cards não existe mais no `main.js` — a filtragem virou server-side quando o
   backend entrou. Hoje `.city-filter` é marcação impressa pelo PHP e serve a
   três coisas: os filtros de cidade e editoria da /noticias/, a paginação da
   /noticias/ e a da /eventos/, e o filtro de cidade da /eventos/.

   O `data-city` continua saindo em `includes/card-noticia.php` e hoje ninguém
   o consome — nem JS, nem CSS. É marcação inerte; se for removida um dia,
   remova o atributo e este parágrafo juntos. */
.city-filter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-bottom: var(--s-5);
}
.city-filter__btn {
  padding: .4rem .9rem;
  font-size: .8125rem;
  font-weight: 600;
  border: 1px solid var(--ink-200);
  border-radius: 999px;
  background: var(--paper);
  color: var(--ink-700, var(--ink-900));
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast);
}
.city-filter__btn:hover { border-color: var(--ink-300); }
.city-filter__btn[aria-pressed="true"] {
  background: var(--ink-900);
  color: #fff;
  border-color: var(--ink-900);
}
/* Contagem dentro do botão de cidade. `inherit` na cor de propósito: no botão
   ativo o fundo é escuro, e um cinza fixo aqui sumiria. */
.city-filter__n {
  font-weight: 400;
  opacity: .65;
  color: inherit;
}

/* Filtro de intervalo de datas da /eventos/ (10/09/2026).

   `<input type="date">` nativo — o calendário é o do sistema. Duas
   consequências de estilo que valem o comentário:

   1. `min-width: 0` nos campos. O input de data tem largura intrínseca
      generosa (reserva espaço para o widget do navegador) e num flex ele NÃO
      encolhe abaixo dela: a 390px o formulário empurrava o documento e
      devolvia o scroll horizontal que a invariante nº 2 proíbe.
   2. `font: inherit` e `-webkit-appearance` mantido. Zerar a aparência tira o
      ícone de calendário no WebKit — que é justamente o botão que o cliente
      pediu. Estiliza-se a moldura, não o controle. */
.date-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.date-filter__campo {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.date-filter__campo label {
  font-size: var(--fs-meta);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-500, var(--ink-900));
}
.date-filter input[type="date"] {
  font: inherit;
  font-size: .8125rem;
  min-width: 0;
  padding: .35rem .6rem;
  border: 1px solid var(--ink-200);
  border-radius: 2px;
  background: var(--paper);
  color: var(--ink-900);
}
.date-filter input[type="date"]:focus-visible { border-color: var(--brand); }
/* O botão do formulário é um <button>, não um <a>: precisa herdar a tipografia
   e o cursor que o reset dá aos links. */
.date-filter button.city-filter__btn { font: inherit; font-size: .8125rem; font-weight: 600; cursor: pointer; }
@media (max-width: 480px) {
  /* Abaixo de 480px os dois campos dividem a linha e os botões descem — em
     uma linha só, o "Filtrar" saía da tela. */
  .date-filter__campo { flex: 1 1 40%; }
  .date-filter input[type="date"] { width: 100%; }
}

.grid-news {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
}
@media (min-width: 640px) { .grid-news { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .grid-news { grid-template-columns: repeat(3, 1fr); } }

.card { display: block; }
.card .ph { margin-bottom: var(--s-3); }
.card:hover .ph > img { transform: scale(1.04); }
.card h3 {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 700;
  line-height: 1.25;
  margin-bottom: var(--s-2);
  transition: color var(--t-fast);
}
.card:hover h3 { color: var(--brand); }
.card p {
  font-size: var(--fs-small);
  color: var(--ink-500);
  margin-bottom: var(--s-2);
}

/* Lista lateral numerada (Top 10 / mais lidas) */
.rank { display: grid; gap: var(--s-4); }
.rank__item {
  display: grid;
  grid-template-columns: 34px 1fr;
  gap: var(--s-4);
  align-items: start;
  padding-bottom: var(--s-4);
  border-bottom: 1px solid var(--ink-100);
}
.rank__item:last-child { border-bottom: 0; padding-bottom: 0; }
.rank__n {
  font-family: var(--font-display);
  font-size: 1.75rem;
  font-weight: 900;
  line-height: 1;
  color: var(--ink-200);
}
.rank__item:hover .rank__n { color: var(--accent); }
.rank__item h4 { font-size: var(--fs-small); font-weight: 700; line-height: 1.35; }
.rank__item .meta { font-size: 0.6875rem; }

/* Rank SEM o número do dia — a barra "Já aconteceu" da /eventos/ e o estado
   vazio da agenda.

   DEFEITO QUE ISTO CORRIGE (10/09/2026): `.rank__item` é uma grade de
   `34px 1fr`, e a primeira coluna é do número do dia. Item sem o
   `<span class="rank__n">` põe o texto na PRIMEIRA coluna — 34px de largura.
   O resultado era o título e o endereço quebrando uma palavra por linha,
   escorrendo verticalmente pela barra lateral inteira. Não era falta de
   espaço: a coluna de 320px estava lá, vazia, ao lado.

   `minmax(0, 1fr)` e não `1fr`: endereço longo sem espaço em branco (URL,
   "Av.Pres.Costa...") estoura a faixa mínima de conteúdo de `1fr` e devolve o
   scroll horizontal que a invariante nº 2 do projeto proíbe. */
.rank--plain .rank__item { grid-template-columns: minmax(0, 1fr); }

/* Rede de segurança para o item que ninguém marcou com `.rank--plain`: se o
   `<div>` é o ÚNICO filho, não existe número do dia e ele ocupa as duas
   colunas. Sem isto, o mesmo defeito volta na próxima lista que reaproveitar
   `.rank__item` sem o `<span>` — e volta silencioso, porque não é erro de
   PHP nem de CSS, só texto estreito. */
.rank__item > div:only-child { grid-column: 1 / -1; }

/* Palavra sem espaço quebra DENTRO da caixa. Medido em 10/09/2026: um endereço
   ou uma URL colada no título de um evento empurrava o documento 78px no
   desktop e 95px no celular — scroll horizontal, a invariante nº 2 do projeto.
   O `minmax(0, 1fr)` acima segura a GRADE; não segura a palavra. Isto segura.
   `anywhere` e não `break-word`: só `anywhere` também reduz o tamanho mínimo
   de conteúdo, que é o que a grade consulta ao decidir a largura da coluna. */
.rank__item h4,
.rank__item .meta { overflow-wrap: anywhere; }

/* Layout de duas colunas: conteúdo + barra lateral */
.split {
  display: grid;
  gap: var(--s-7);
  grid-template-columns: 1fr;
}
@media (min-width: 1000px) {
  .split { grid-template-columns: minmax(0, 1fr) 320px; }
  .split__aside { border-left: 1px solid var(--ink-100); padding-left: var(--s-6); }
}
.aside-block { margin-bottom: var(--s-7); }
.aside-block__title {
  font-size: var(--fs-meta);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-900);
  padding-bottom: var(--s-2);
  margin-bottom: var(--s-4);
  border-bottom: 2px solid var(--ink-900);
}

/* --------------------------------------------------------------------------
   10. PROGRAMAÇÃO
   -------------------------------------------------------------------------- */
.days {
  display: flex;
  gap: var(--s-1);
  overflow-x: auto;
  margin-bottom: var(--s-5);
  padding-bottom: var(--s-2);
  scrollbar-width: thin;
}
.days button {
  flex: 0 0 auto;
  padding: var(--s-2) var(--s-4);
  font-size: var(--fs-small);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-500);
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  background: var(--paper);
  transition: all var(--t-fast);
  white-space: nowrap;
}
.days button:hover { border-color: var(--ink-900); color: var(--ink-900); }
.days button[aria-selected="true"] {
  background: var(--ink-900);
  border-color: var(--ink-900);
  color: #fff;
}

.schedule { display: grid; gap: 0; }
.slot {
  display: grid;
  grid-template-columns: 78px 1fr;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-4) var(--s-3);
  border-bottom: 1px solid var(--ink-100);
  transition: background var(--t-fast);
}
.slot:hover { background: var(--paper-warm); }
.slot__time {
  font-family: var(--font-ui);
  font-size: var(--fs-small);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--ink-400);
}
.slot__body h4 { font-size: var(--fs-body); font-weight: 700; line-height: 1.3; }
.slot__body p { margin: 2px 0 0; font-size: var(--fs-small); color: var(--ink-500); }
.slot.is-now {
  background: var(--brand-soft);
  border-left: 3px solid var(--accent);
  padding-left: calc(var(--s-3) - 3px);
}
.slot.is-now .slot__time { color: var(--accent); }
.slot__tag {
  margin-left: var(--s-3);
  font-size: var(--fs-kicker);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}

/* --------------------------------------------------------------------------
   11. APRESENTADORES (a vitrine de "celebridade")
   -------------------------------------------------------------------------- */
/* Linha de apoio da seção de apresentadores: diz que, fora o Jorge, quem
   aparece ali é parceiro e não contratado da emissora (05/09/2026). Herda a
   cor de .section--ink (--ink-200 sobre --ink-900), então não define cor
   própria — se esta seção um dia sair do fundo escuro, confira o contraste. */
.talent__lead {
  max-width: 68ch;
  margin-block: calc(var(--s-4) * -1) var(--s-5);
}

.talent {
  display: grid;
  gap: var(--s-5);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 700px) { .talent { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1000px) { .talent { grid-template-columns: repeat(5, 1fr); } }

.talent__card { display: block; text-align: center; }
.talent__photo {
  position: relative;
  margin-bottom: var(--s-3);
}
.talent__card:hover .ph > img { transform: scale(1.05); }
.talent__card h3 {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.2;
}
.talent__role {
  margin-top: 4px;
  font-size: var(--fs-kicker);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-400);
}
.section--ink .talent__role { color: var(--ink-400); }

/* --------------------------------------------------------------------------
   11-B. CARTAZES DE PROGRAMA
   Jorge Morais apresenta em outras emissoras. Isso não é a grade da web TV —
   é vitrine de carreira e argumento comercial. Por isso vive em bloco próprio,
   com cartaz vertical, que é como emissora expõe programa.
   -------------------------------------------------------------------------- */
.programs {
  display: grid;
  gap: var(--s-5);
  grid-template-columns: 1fr;
}
@media (min-width: 600px) { .programs { grid-template-columns: repeat(3, 1fr); } }

.program { display: block; }
.program__poster {
  position: relative;
  margin-bottom: var(--s-4);
  overflow: hidden;
  border-radius: var(--radius);
  box-shadow: var(--shadow-1);
}
.program:hover .ph > img { transform: scale(1.04); }
.program__net {
  position: absolute;
  left: var(--s-3); top: var(--s-3);
  z-index: 3;
  padding: 4px 9px;
  background: rgba(11, 22, 32, 0.82);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: var(--fs-kicker);
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border-radius: var(--radius);
}
/* --- Card que abre o canal do apresentador (06/09/2026) -------------------
   O link mora no <h2> e se estica sobre o card inteiro por um `::after`. O
   containing block dele é o `.program--link`, e por isso o pseudo NÃO pode
   nascer dentro do `.program__poster`: aquele `<figure>` é `position:relative`
   com `overflow:hidden`, então passaria a ser o containing block e o clique
   valeria só sobre o cartaz. */
.program--link { position: relative; }
.program__link { color: inherit; }
.program--link:hover .program__nome { color: var(--brand); }
.program__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
/* O selo do canal é enfeite: `pointer-events: none` deixa o clique atravessar
   até o link do título, senão haveria um retângulo morto sobre o cartaz. */
.program__net--yt {
  left: auto; right: var(--s-3);
  background: var(--live);
  pointer-events: none;
}
/* Acima da camada esticada — é o que mantém este link clicável. O negrito foi
   pedido pelo cliente: no meio de um `.meta` cinza, sem cor e sem sublinhado
   (o reset do projeto zera os dois), ele não se distinguia do texto. */
.program__conheca {
  position: relative;
  z-index: 2;
  font-weight: 700;
  color: var(--accent);
}
.program__conheca:hover { text-decoration: underline; }

.program h2,
.program h3 {
  font-family: var(--font-display);
  font-size: var(--fs-h4);
  font-weight: 900;
  line-height: 1.2;
  margin-bottom: var(--s-2);
}
.program:hover h2,
.program:hover h3 { color: var(--brand); }
.program p { font-size: var(--fs-small); color: var(--ink-500); margin-bottom: var(--s-2); }
.section--ink .program p { color: var(--ink-400); }

/* --------------------------------------------------------------------------
   11-C. BLOCO DO LIVRO
   -------------------------------------------------------------------------- */
.book {
  display: grid;
  gap: var(--s-5);
  align-items: center;
  grid-template-columns: 1fr;
  padding: var(--s-6);
  background: var(--paper);
  border: 1px solid var(--ink-100);
  border-left: 4px solid var(--gold);
  border-radius: var(--radius);
}
@media (min-width: 760px) { .book { grid-template-columns: 1fr auto; gap: var(--s-7); } }
.book h3 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 900;
  font-style: italic;
  line-height: 1.2;
  margin-bottom: var(--s-3);
}
.book p { font-size: var(--fs-small); color: var(--ink-500); margin-bottom: 0; max-width: 62ch; }

/* --------------------------------------------------------------------------
   12. GALERIA (mosaico)
   -------------------------------------------------------------------------- */
.mosaic {
  display: grid;
  gap: var(--s-2);
  grid-template-columns: repeat(2, 1fr);
}
/* GRADE UNIFORME, NÃO MOSAICO DE JORNAL — 07/09/2026.

   Havia aqui um mosaico de três alturas com posicionamento explícito por
   `nth-child(1..6)`: item 1 grande, 2 em pé, 3 e 4 pequenos, 5 e 6 em faixa
   larga. Bonito, e errado por duas razões medidas.

   A primeira é aritmética: as doze células do grid declarado eram ocupadas
   exatamente pelos seis primeiros itens. A sétima foto caía FORA do grid, numa
   linha implícita sem altura — motivo pelo qual `fotos()` limitava a seis e o
   álbum, que pede sessenta, nunca pôde crescer. Layout que proíbe a segunda
   página de conteúdo não é layout, é gargalo.

   A segunda é o material: o acervo é arte gráfica VERTICAL (cartaz, logotipo,
   chamada de programa), não fotografia de evento. Com `cover` numa faixa 3:1,
   o recorte come justamente o texto — que é o conteúdo da peça.

   A troca: quatro colunas de células quadradas, auto-placement (nunca deixa
   buraco, com qualquer quantidade de fotos) e `contain` sobre fundo neutro, que
   mostra a peça inteira em vez de recortá-la. O quadrado é o mesmo formato que
   o celular já usava — agora a seção tem UMA forma, não duas.

   Se um dia o acervo virar fotografia de evento de verdade, `cover` volta a
   ser a escolha certa: troque `contain` por `cover` nesta regra e nada mais. */
@media (min-width: 800px) {
  .mosaic { grid-template-columns: repeat(4, 1fr); }
}
.mosaic__item {
  position: relative;
  display: block;
  overflow: hidden;
  background: var(--ink-100);
  /* Com `contain`, a peça deixa faixas do fundo à mostra — e boa parte do
     acervo tem fundo claro. Sem a linha, arte branca sobre cinza claro some
     na página e o visitante não sabe onde uma peça termina e a outra começa. */
  border: 1px solid var(--ink-200);
}
.mosaic__item .ph { aspect-ratio: 1 / 1; height: auto; }
.mosaic__item img { width: 100%; height: 100%; object-fit: contain; }
.mosaic__item:hover img { transform: scale(1.06); }
.mosaic__item::after {
  content: attr(data-caption);
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: var(--s-5) var(--s-3) var(--s-3);
  background: linear-gradient(to top, rgba(11, 22, 32, 0.85), transparent);
  color: #fff;
  font-size: var(--fs-meta);
  font-weight: 600;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity var(--t-base), transform var(--t-base);
}
.mosaic__item:hover::after { opacity: 1; transform: translateY(0); }
@media (max-width: 799px) {
  /* No celular não há hover: a legenda fica sempre visível. */
  .mosaic__item::after { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   13. PODCASTS
   -------------------------------------------------------------------------- */
.pod { display: grid; gap: var(--s-4); }
.pod__item {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  gap: var(--s-4);
  align-items: center;
  padding: var(--s-3);
  background: var(--paper);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.pod__item:hover { border-color: var(--ink-300); box-shadow: var(--shadow-1); }
.pod__item h4 { font-size: var(--fs-body); font-weight: 700; line-height: 1.3; }
.pod__play {
  display: grid; place-items: center;
  width: 42px; height: 42px;
  border-radius: 50%;
  background: var(--brand);
  color: #fff;
  transition: background var(--t-fast);
}
.pod__play:hover { background: var(--accent); }
.pod__play svg { width: 15px; height: 15px; fill: currentColor; margin-left: 2px; }

/* --------------------------------------------------------------------------
   14. CARROSSEL DE APOIADORES
   Scroll-snap nativo em vez de transform animado: o dedo arrasta de graça no
   celular, a roda do mouse funciona, o teclado navega, e o navegador cuida da
   inércia. O JS só liga as setas — sem ele o carrossel continua rolando.
   -------------------------------------------------------------------------- */
.carousel { position: relative; }

.carousel__track {
  display: flex;
  gap: var(--s-4);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-block: var(--s-2);
  /* Esconde a barra sem matar a rolagem. */
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
}
.carousel__track::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) {
  .carousel__track { scroll-behavior: auto; }
}

.carousel__item {
  flex: 0 0 auto;
  scroll-snap-align: start;
  width: 240px;
  height: 160px;
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--paper);
  border: 1px solid var(--ink-100);
  border-radius: var(--radius);
  transition: border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast);
}
.carousel__item:hover,
.carousel__item:focus-visible {
  border-color: var(--ink-300);
  box-shadow: var(--shadow-1);
  transform: translateY(-2px);
}
/* A arte chega como cartaz, foto e cartão de visita, em oito proporções.
   Cada peça já é gerada numa moldura 600x400 uniforme (fundo neutro tirado
   da própria imagem), então aqui o card só precisa cobrir a moldura.
   Sem grayscale: cartaz de evento e foto perdem a leitura em cinza — o
   tratamento monocromático só serve para logo vetorial. */
.carousel__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--t-base);
}
.carousel__item:hover img,
.carousel__item:focus-visible img { transform: scale(1.04); }

@media (min-width: 700px) {
  .carousel__item { width: 270px; height: 180px; }
}

/* Setas: acima da faixa, alinhadas à direita. Seta flutuando sobre o slide
   cobre justamente o logo que o apoiador pagou para aparecer. */
.carousel__nav {
  display: none;
  justify-content: flex-end;
  gap: var(--s-2);
  margin-bottom: var(--s-3);
}
/* Só aparecem quando há mais slides do que cabem — e só com JS ativo. */
.js .carousel.has-overflow .carousel__nav { display: flex; }

.carousel__btn {
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  background: var(--paper);
  color: var(--ink-900);
  border: 1px solid var(--ink-200);
  border-radius: 50%;
  transition: background var(--t-fast), color var(--t-fast), opacity var(--t-fast), border-color var(--t-fast);
}
.carousel__btn svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 2.2; }
.carousel__btn:hover { background: var(--ink-900); color: #fff; border-color: var(--ink-900); }
.carousel__btn[disabled] { opacity: 0.28; pointer-events: none; }

/* Variante contínua (marquee): usada no carrossel de apoiadores. Em vez de
   paginar e parar nas pontas, os cards deslizam em esteira e nunca chegam ao
   fim — o JS duplica o conjunto de itens uma vez (cópia com aria-hidden) e a
   animação anda exatamente a largura de um conjunto, então o loop não tem
   costura visível. Cards menores que o carrossel padrão: aqui é vitrine de
   logo, não card de destaque. Pausa no hover/foco; para de vez com
   prefers-reduced-motion, caindo de volta no scroll manual. */
.carousel--marquee .carousel__nav { display: none !important; }
/* O recorte tem que ficar no contêiner (largura fixa, a do .wrap), nunca na
   esteira: a esteira agora mede exatamente a largura do próprio conteúdo
   (width: max-content), então "overflow: hidden" nela não recorta nada — o
   que precisa ficar invisível é o trecho da esteira que passa dos limites
   do carrossel. */
.carousel--marquee { overflow: hidden; }
.carousel--marquee .carousel__track {
  scroll-snap-type: none;
  width: max-content;
  animation: marquee-scroll var(--marquee-duration, 40s) linear infinite;
}
/* TAMANHO DO CARD — 3x o anterior (172x115 -> 516x345), pedido do cliente em
   06/09/2026. Uma única medida governa a esteira inteira: `--apoiador-w`. A
   altura sai dela por cálculo, na proporção 3:2, que é exatamente a da moldura
   600x400 em que a arte é gerada — assim o `object-fit: cover` não corta nada e
   a imagem, que tem 600px de origem, ainda não é ampliada além do natural.
   Para mudar o tamanho de novo, mexa só nestas duas declarações.

   O teto em `vw` não é enfeite: 450px não cabe num telefone de 390px, e o
   contêiner tem `overflow: hidden`. Sem o teto o card seria recortado e o
   visitante veria uma fatia do logo que o apoiador pagou para aparecer — o
   contrário de aumentar. As duas fórmulas são monotônicas na fronteira dos
   700px (476px de um lado, 450px do outro), então o card nunca encolhe quando
   a tela cresce. */
.carousel--marquee { --apoiador-w: min(450px, 80vw); }
.carousel--marquee .carousel__item {
  width: var(--apoiador-w);
  height: calc(var(--apoiador-w) / 1.5);
}
/* Nesta escala o card manda na calha: 1rem entre duas peças desse tamanho cola
   uma na outra e a esteira vira uma parede. */
.carousel--marquee .carousel__track { gap: var(--s-5); }
@media (min-width: 700px) {
  .carousel--marquee { --apoiador-w: min(516px, 68vw); }
}
/* Legenda com o nome do apoiador: fica sempre visível (não só no hover, como
   no mosaico da galeria) porque o card está sempre em movimento — ninguém
   pararia o suficiente pra passar o mouse e ler. É o que dá valor visível
   ao patrocínio, além do alt-text que só o leitor de tela anuncia. */
.carousel--marquee .carousel__item::after {
  content: attr(data-caption);
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: var(--s-6) var(--s-4) var(--s-3);
  background: linear-gradient(to top, rgba(11, 22, 32, 0.82), transparent);
  color: #fff;
  /* A legenda cresce junto com o card. Em .6875rem sobre um card de 516px ela
     virava rodapé de contrato — e é ela que dá nome ao patrocínio para quem
     enxerga. Sobe até 1rem e para: a peça é o logo, não o texto. */
  font-size: clamp(.8125rem, .62rem + .35vw, 1rem);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
}
.carousel--marquee .carousel__item { position: relative; }
/* Os cards da esteira deixaram de ser <a> em 05/09/2026 (pedido do cliente:
   link só quando o apoiador tiver site de verdade; antes o card sem site
   caía num link para o #contato da home). Como não há mais destino, o hover
   não pode continuar prometendo clique: o levantar de 2px e a sombra vão
   embora AQUI, só na esteira. O zoom da imagem fica — é o "expandir a
   imagem" que o cliente pediu, e não sugere navegação.
   As outras esteiras (galeria, eventos) seguem com links e com o efeito
   completo, por isso a regra é escopada em .carousel--marquee. */
.carousel--marquee .carousel__item:hover,
.carousel--marquee .carousel__item:focus-visible {
  transform: none;
  box-shadow: none;
  border-color: var(--ink-100);
}
/* ...MAS O CARD COM SITE VOLTOU A SER <a> (13/09/2026), e aí o hover PODE
   prometer clique, porque agora existe destino. A regra acima continua valendo
   para o card sem site — o seletor com `a` na frente é mais específico e vem
   depois, então ganha só onde deve ganhar. Se um dia todo apoiador tiver site,
   estas duas regras convivem sem conflito. */
.carousel--marquee a.carousel__item { cursor: pointer; }
.carousel--marquee a.carousel__item:hover,
.carousel--marquee a.carousel__item:focus-visible {
  transform: translateY(-2px);
  box-shadow: var(--shadow-1);
  border-color: var(--brand);
}
/* O selo ↗ — é ele que o aviso acima da esteira cita, e é o que distingue o
   card clicável do card que só mostra. Canto SUPERIOR direito de propósito: a
   legenda ocupa a faixa de baixo, e o logo do apoiador mora no meio. */
.carousel__ir {
  position: absolute;
  right: var(--s-3); top: var(--s-3);
  z-index: 3;
  display: grid;
  place-items: center;
  width: 1.75rem; height: 1.75rem;
  background: rgba(11, 22, 32, 0.82);
  backdrop-filter: blur(6px);
  color: #fff;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1;
  border-radius: 999px;
  /* O selo não recebe clique: quem recebe é o <a> inteiro, por baixo dele.
     Mesmo motivo do `.program__net--yt`. */
  pointer-events: none;
  transition: background var(--t-fast);
}
.carousel--marquee a.carousel__item:hover .carousel__ir,
.carousel--marquee a.carousel__item:focus-visible .carousel__ir { background: var(--brand); }

/* O aviso, acima da esteira. O ↗ dele é o MESMO glifo do selo — é essa
   igualdade que faz o aviso ser entendido sem legenda explicativa. */
.apoiadores-aviso { margin: calc(var(--s-3) * -1) 0 var(--s-4); }
.apoiadores-aviso__ir {
  display: inline-grid;
  place-items: center;
  width: 1.35em; height: 1.35em;
  vertical-align: -0.28em;
  background: var(--ink-900);
  color: #fff;
  font-size: 0.9em;
  font-weight: 700;
  line-height: 1;
  border-radius: 999px;
}

.carousel--marquee:hover .carousel__track,
.carousel--marquee:focus-within .carousel__track { animation-play-state: paused; }

@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-1 * var(--marquee-width, 50%))); }
}
/* A esteira gira sempre, inclusive para quem tem efeitos de animação
   desligados no sistema — decisão do cliente em 05/09/2026, depois de ver a
   faixa parada na própria máquina. Por isso NÃO há aqui um bloco
   prefers-reduced-motion para .carousel--marquee: o corte global de animação
   também abre exceção para ela (topo do arquivo). O par disso está em
   main.js: o módulo do marquee não consulta mais a preferência. */

/* --------------------------------------------------------------------------
   15. RECADOS + NEWSLETTER
   -------------------------------------------------------------------------- */
.quotes { display: grid; gap: var(--s-5); grid-template-columns: 1fr; }
@media (min-width: 760px) { .quotes { grid-template-columns: repeat(3, 1fr); } }
.quote {
  padding: var(--s-5);
  background: var(--paper);
  border: 1px solid var(--ink-100);
  border-top: 3px solid var(--brand);
  border-radius: var(--radius);
}
.quote p {
  font-family: var(--font-display);
  font-size: var(--fs-lead);
  font-style: italic;
  line-height: 1.45;
  color: var(--ink-800);
}
/* .quote só existe em fundo --paper (ver a regra `background` acima) — mesmo
   engano de contraste do .meta (05/09/2026): ink-400 aqui media 2,9:1. */
.quote footer { font-size: var(--fs-meta); color: var(--ink-500); }
.quote footer strong { display: block; color: var(--ink-900); font-size: var(--fs-small); }

.cta-band {
  display: grid;
  gap: var(--s-5);
  align-items: center;
  grid-template-columns: 1fr;
  padding-block: var(--s-7);
}
@media (min-width: 860px) { .cta-band { grid-template-columns: 1fr auto; gap: var(--s-7); } }
.cta-band h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  font-weight: 900;
  color: #fff;
  margin-bottom: var(--s-2);
}
.cta-band p { color: var(--ink-300); margin: 0; max-width: 56ch; }
.form-inline { display: flex; gap: var(--s-2); flex-wrap: wrap; }
.form-inline input {
  flex: 1 1 240px;
  min-width: 0;
  padding: var(--s-3) var(--s-4);
  font: inherit;
  font-size: var(--fs-small);
  color: var(--ink-900);
  background: #fff;
  border: 1px solid transparent;
  border-radius: var(--radius);
}
.form-inline input::placeholder { color: var(--ink-400); }
.form-inline input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* Campo de formulário em fundo claro (mural de recados etc.) — o
   .form-inline acima é para a faixa escura do rodapé, onde a borda
   transparente basta; aqui, sem essa classe, input e textarea caem no
   estilo padrão do navegador (sem largura, sem borda combinando com o
   .btn logo abaixo) — por isso width:100% explícito nos três campos. */
.field {
  display: block;
  width: 100%;
  padding: var(--s-3) var(--s-4);
  font: inherit;
  font-size: var(--fs-small);
  color: var(--ink-900);
  background: #fff;
  border: 1px solid var(--ink-200);
  border-radius: var(--radius);
  transition: border-color var(--t-fast);
}
/* Mesmo engano do .meta (05/09/2026): ink-400 é para texto claro sobre fundo
   ESCURO; `.field` só existe em fundo claro (comentário acima), e media
   2,9:1 — abaixo até do mínimo de texto grande. ink-500 mede 4,55:1. */
.field::placeholder { color: var(--ink-500); }
.field:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
textarea.field { resize: vertical; min-height: 6.5rem; }

/* --------------------------------------------------------------------------
   16. RODAPÉ
   -------------------------------------------------------------------------- */
.footer { background: var(--ink-900); color: var(--ink-400); padding-block: var(--s-8) var(--s-5); }
.footer__grid {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: 1fr;
  padding-bottom: var(--s-6);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
@media (min-width: 700px) { .footer__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1.2fr; gap: var(--s-7); } }

.footer__logo {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: 1.5rem;
  color: #fff;
  line-height: 1.1;
  display: inline-block;
  margin-bottom: var(--s-3);
}
/* Era var(--brand): 1,3:1 sobre o fundo escuro, ilegível. Agora 7,1:1. */
.footer__logo span { color: var(--accent-light); }
.footer p { font-size: var(--fs-small); line-height: 1.6; }
.footer h4 {
  font-size: var(--fs-meta);
  font-weight: 800;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #fff;
  margin-bottom: var(--s-4);
}
.footer__links li { margin-bottom: var(--s-2); }
.footer__links a { font-size: var(--fs-small); transition: color var(--t-fast); }
.footer__links a:hover { color: #fff; }
.footer__social { display: flex; gap: var(--s-2); margin-top: var(--s-4); }
.footer__social a {
  display: grid; place-items: center;
  width: 36px; height: 36px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: var(--radius);
  transition: all var(--t-fast);
}
.footer__social a:hover { background: #fff; color: var(--ink-900); border-color: #fff; }
.footer__social svg { width: 16px; height: 16px; fill: currentColor; }

.pix {
  padding: var(--s-4);
  background: rgba(255, 255, 255, 0.05);
  border-radius: var(--radius);
  margin-top: var(--s-4);
}
.pix code {
  display: block;
  margin-top: var(--s-2);
  font-family: ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: var(--fs-meta);
  color: #fff;
  word-break: break-all;
}

/* A LINHA DE BAIXO DO RODAPÉ (06/09/2026). Era --fs-meta cinza, o menor corpo
   do site — e é a linha que vende serviço de desenvolvimento. Passou ao mesmo
   corpo, à mesma cor e ao mesmo peso da assinatura do topo: quem chega ao fim
   da página lê a mesma coisa que leu no começo, e não uma versão apagada. */
.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  padding-top: var(--s-5);
  font-size: clamp(.875rem, .72rem + .42vw, 1.0625rem);
  font-weight: 700;
  color: #fff;
  line-height: 1.35;
}
.footer__credito a {
  color: #fff;
  font-weight: 800;
  /* Mesmo sublinhado deslocado da barra do topo: o link tem de se ler como
     link mesmo num fundo já todo branco e negrito, e o contorno vermelho é o
     mesmo do AO VIVO. */
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 3px;
  white-space: nowrap;
}
.footer__credito a:hover,
.footer__credito a:focus-visible { text-decoration-color: #fff; }

/* --------------------------------------------------------------------------
   16b. CLIMA (clima.php)
   Nove cidades, cada uma com o agora e cinco dias. O cartão é auto-suficiente:
   nada aqui depende de JavaScript, porque a página é servida pronta.
   -------------------------------------------------------------------------- */
.clima {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: 1fr;
}
@media (min-width: 640px)  { .clima { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .clima { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.clima__cidade {
  padding: var(--s-5);
  background: var(--paper-cool, #f5f7f9);
  border-radius: var(--radius-lg, 12px);
  border: 1px solid var(--ink-100);
}
/* As quatro de cobertura editorial ganham a marca do accent na lateral — o
   mesmo recurso do "No ar agora". Sem isso a página sugere que a emissora
   cobre as nove, o que não é verdade. */
.clima__cidade--cobertura { border-left: 3px solid var(--accent); }

.clima__topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-3);
}
.clima__nome {
  font-family: var(--font-display);
  font-size: var(--fs-h4, 1.25rem);
  font-weight: 900;
  margin: 0;
  line-height: 1.15;
}
.clima__estado { margin: 2px 0 0; font-size: var(--fs-meta); color: var(--ink-500); }
.clima__agora { display: flex; align-items: center; gap: var(--s-2); }
.clima__icone { font-size: 1.75rem; line-height: 1; }
.clima__temp {
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 900;
  line-height: 1;
  /* Tabular para as nove colunas não dançarem quando a dezena muda. */
  font-variant-numeric: tabular-nums;
}
.clima__detalhe {
  margin: var(--s-3) 0 0;
  font-size: var(--fs-meta);
  color: var(--ink-500);
}

.clima__dias {
  list-style: none;
  margin: var(--s-4) 0 0;
  padding: var(--s-3) 0 0;
  border-top: 1px solid var(--ink-100);
  display: grid;
  gap: var(--s-2);
}
.clima__dia {
  display: grid;
  /* Quatro colunas fixas: dia, ícone, máx/mín e chuva. Em `auto` a coluna do
     meio mudaria de largura conforme o emoji, e as cinco linhas do cartão
     ficariam desalinhadas entre si. */
  grid-template-columns: 4.5rem 1.75rem 1fr auto;
  align-items: center;
  gap: var(--s-2);
  font-size: var(--fs-meta);
}
.clima__data { color: var(--ink-500); }
.clima__dia-icone { font-size: 1.1rem; line-height: 1; text-align: center; }
.clima__minmax { font-variant-numeric: tabular-nums; }
.clima__minmax strong { font-weight: 800; }
.clima__minmax span { color: var(--ink-500); margin-left: 4px; }
.clima__chuva { color: var(--ink-500); font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   17. UTILIDADES
   -------------------------------------------------------------------------- */
.stack > * + * { margin-top: var(--s-4); }
.text-center { text-align: center; }
.mb-0 { margin-bottom: 0; }
.mt-5 { margin-top: var(--s-5); }
.hide-sm { display: none; }
@media (min-width: 640px) { .hide-sm { display: block; } }

/* Revelação ao rolar.
   Regra de ouro: o conteúdo NUNCA depende do JS para ficar visível.
   Só escondemos quando a classe .js confirma que o script está ativo. */
.reveal { opacity: 1; transform: none; }
.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 520ms ease, transform 520ms ease;
}
.js .reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .js .reveal { opacity: 1; transform: none; }
}

/* Botão de topo */
.to-top {
  position: fixed;
  right: var(--s-4); bottom: var(--s-4);
  z-index: 90;
  display: grid; place-items: center;
  width: 44px; height: 44px;
  background: var(--ink-900);
  color: #fff;
  border-radius: 50%;
  box-shadow: var(--shadow-2);
  opacity: 0;
  pointer-events: none;
  transform: translateY(10px);
  transition: opacity var(--t-base), transform var(--t-base);
}
.to-top.is-visible { opacity: 1; pointer-events: auto; transform: none; }
.to-top svg { width: 20px; height: 20px; stroke: currentColor; fill: none; stroke-width: 2; }

/* --------------------------------------------------------------------------
   18. PÁGINA DE MÍDIA KIT
   Documento comercial. Precisa ser legível na tela e imprimir bem em A4 —
   um anunciante ainda imprime proposta.
   -------------------------------------------------------------------------- */
.page-hero {
  position: relative;
  background: var(--ink-900);
  color: var(--ink-300);
  padding-block: var(--s-8);
  overflow: hidden;
}
.page-hero__bg {
  position: absolute; inset: 0; z-index: 0;
  object-fit: cover; width: 100%; height: 100%;
  opacity: 0.28;
}
.page-hero .wrap { position: relative; z-index: 1; }
.page-hero h1 {
  font-family: var(--font-display);
  font-size: var(--fs-hero);
  font-weight: 900;
  color: #fff;
  letter-spacing: -0.02em;
  margin-bottom: var(--s-3);
}
.page-hero p { font-size: var(--fs-lead); color: var(--ink-300); max-width: 60ch; margin-bottom: 0; }

/* Números de alcance */
.facts {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(2, 1fr);
  margin-top: var(--s-6);
}
@media (min-width: 760px) { .facts { grid-template-columns: repeat(4, 1fr); } }
.fact {
  padding: var(--s-4);
  background: rgba(255, 255, 255, 0.06);
  border-left: 3px solid var(--accent-light);
  border-radius: var(--radius);
}
.fact strong {
  display: block;
  font-family: var(--font-display);
  font-size: 2rem;
  font-weight: 900;
  color: #fff;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.fact span {
  display: block;
  margin-top: var(--s-2);
  font-size: var(--fs-kicker);
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Mesmo motivo do .live__next: painel translúcido sobre a faixa escura. */
  color: var(--ink-300);
}

/* Blocos do programa */
.spec { border-top: 1px solid var(--ink-100); }
.spec__row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-1);
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--ink-100);
}
@media (min-width: 700px) {
  .spec__row { grid-template-columns: 220px 1fr; gap: var(--s-5); align-items: baseline; }
}
.spec__row dt { font-weight: 800; color: var(--ink-900); }
.spec__row dd { margin: 0; font-size: var(--fs-small); color: var(--ink-500); }

/* Lista de cidades */
.cities {
  columns: 2;
  column-gap: var(--s-6);
  font-size: var(--fs-small);
}
@media (min-width: 700px) { .cities { columns: 3; } }
@media (min-width: 1000px) { .cities { columns: 4; } }
.cities li {
  break-inside: avoid;
  padding: 6px 0 6px var(--s-4);
  position: relative;
  border-bottom: 1px solid var(--ink-100);
}
.cities li::before {
  content: "";
  position: absolute; left: 0; top: 15px;
  width: 6px; height: 6px;
  background: var(--accent);
  border-radius: 50%;
}

/* Impressão: o mídia kit sai limpo em A4. */
@media print {
  /* `#apoiadores` entrou na lista em 06/09/2026, junto com o card 3x. A regra
     antiga escondia `.marquee`, que é classe de outra coisa — a esteira tem
     `.carousel--marquee` e nunca casou com ela. Passava despercebido enquanto o
     card media 172px; com 516px a esteira come uma folha A4 inteira do mídia
     kit, impressa em fatias, para mostrar dois logos e meio. Papel de proposta
     comercial é caro; a esteira está no site, ao vivo e girando. */
  .nav, .topbar, .live, .to-top, .marquee, #apoiadores, .mobile-menu,
  .footer__social, .modal, .cookies { display: none !important; }
  body { color: #000; background: #fff; }
  .page-hero { background: #fff; color: #000; padding-block: var(--s-5); }
  .page-hero__bg { display: none; }
  .page-hero h1, .fact strong { color: #000; }
  .fact { background: none; border-left: 2px solid #000; }
  .fact span, .page-hero p { color: #333; }
  .section { break-inside: avoid; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 10px; color: #555; }
}

/* --------------------------------------------------------------------------
   19. FORMULÁRIO DE CONTATO, MODAL, AVISO DE COOKIES E PÁGINA LEGAL
   Entraram em 06/09/2026. Nenhuma cor literal: tudo sai dos tokens do topo.
   -------------------------------------------------------------------------- */

/* Armadilha de robô. Fora da tela, mas NÃO com display:none nem
   visibility:hidden — parte dos robôs ignora campo assim, e é justamente o
   preenchimento dele que denuncia o robô. `aria-hidden` no HTML mantém o
   campo fora da leitura de tela. */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

.form-contato { margin-top: var(--s-5); }

.form-grid {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: 1fr;
}
@media (min-width: 600px) {
  .form-grid { grid-template-columns: 1fr 1fr; }
  .campo--largo { grid-column: 1 / -1; }
}

.campo { margin: 0; }
.campo label {
  display: block;
  margin-bottom: var(--s-2);
  font-size: var(--fs-kicker);
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-700);
}
.campo label span[aria-hidden] { color: var(--live); }

.form-legal {
  margin: var(--s-4) 0 var(--s-4);
  font-size: var(--fs-meta);
  color: var(--ink-500);
}
.form-legal a { text-decoration: underline; }

/* Aviso de resultado do envio. A cor da borda é o sinal; o texto diz o resto,
   porque cor sozinha não é informação para quem não a distingue. */
.form-aviso {
  margin-bottom: var(--s-4);
  padding: var(--s-4);
  border-left: 3px solid var(--ink-300);
  background: var(--paper-cool);
  font-size: var(--fs-small);
}
.form-aviso p { margin: 0; }
.form-aviso p + p { margin-top: var(--s-2); }
.form-aviso--ok   { border-left-color: var(--accent); background: var(--brand-soft); }
.form-aviso--erro { border-left-color: var(--live); background: #fdf2f3; }

/* --- Modal de contato ---------------------------------------------------
   z-index acima do menu móvel (200) e do botão de topo (90): quando o modal
   está aberto, ele é a única coisa clicável. */
.modal {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: var(--gutter);
}
.modal[hidden] { display: none; }
.modal__fundo {
  position: absolute;
  inset: 0;
  background: rgba(28, 43, 58, 0.72);
}
.modal__caixa {
  position: relative;
  width: 100%;
  max-width: 640px;
  max-height: calc(100vh - var(--s-6));
  overflow-y: auto;
  padding: var(--s-5) var(--s-5) var(--s-6);
  background: var(--paper);
  border-radius: var(--radius);
  box-shadow: var(--shadow-3);
}
.modal__topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-4);
  border-bottom: 3px solid var(--ink-900);
}
.modal__topo h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 900;
  margin: 0;
}
.modal__fechar {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: grid; place-items: center;
  color: var(--ink-700);
}
.modal__fechar:hover { color: var(--ink-900); }
.modal__fechar svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 2; }
.modal__corpo { padding-top: var(--s-4); }

/* --- Convite para instalar o aplicativo (13/09/2026) --------------------
   Reaproveita o componente `.modal` inteiro — só o que é próprio daqui tem
   classe nova. A caixa é mais estreita que a do contato: aqui é um convite
   de três linhas e dois botões, não um formulário. */
.instalar .modal__caixa { max-width: 460px; }
.instalar__cabecalho { display: flex; align-items: center; gap: var(--s-4); }
.instalar__icone {
  flex: 0 0 auto;
  width: 56px; height: 56px;
  border-radius: var(--radius);
}
.instalar__acoes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}
/* `hidden` PRECISA SER RESGATADO AQUI, e não é zelo — é defeito medido em
   13/09/2026. O `[hidden]` do navegador vale `display: none` com
   especificidade zero, e `.btn` declara `display: inline-flex`: um
   `<button class="btn" hidden>` fica VISÍVEL. Na primeira medição os dois
   botões ("Instalar agora" e "Como instalar") apareciam juntos, que é
   exatamente o que o par existe para não fazer.
   O projeto já tinha tropeçado nisto — `.modal[hidden]` e três regras do
   lightbox existem pelo mesmo motivo. Quando criar componente novo com
   estado escondido, escreva a regra do `[hidden]` junto. */
.instalar__acoes .btn[hidden],
.instalar__passos[hidden],
.instalar__lista[hidden],
.instalar__pronto[hidden],
.instalar__onde[hidden] { display: none; }
/* No telefone os botões empilham e ocupam a largura toda: dois botões lado a
   lado a 390px deixam cada um com ~150px, e "Instalar agora" quebra em duas
   linhas dentro do próprio botão. */
@media (max-width: 460px) {
  .instalar__acoes { flex-direction: column-reverse; }
  .instalar__acoes .btn { width: 100%; text-align: center; }
}
.instalar__passos, .instalar__pronto { margin-top: var(--s-5); }
.instalar__lista {
  margin: 0;
  padding-left: 1.35rem;
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--ink-700);
}
.instalar__lista li { margin-bottom: var(--s-3); }
.instalar__lista li:last-child { margin-bottom: 0; }
.instalar__pronto p { margin: 0 0 var(--s-3); }
.instalar__pronto p:last-child { margin-bottom: 0; }
.instalar__onde { font-size: 0.92rem; color: var(--ink-700); }
.instalar__onde code {
  padding: 1px 5px;
  background: var(--paper-cool);
  border-radius: 4px;
  font-size: 0.9em;
}

/* --- Aviso de cookies ---------------------------------------------------
   Faixa presa embaixo. Abaixo do modal (300) e acima de tudo o mais: se o
   visitante abrir o formulário antes de responder o aviso, o formulário é
   que fica por cima. */
.cookies {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 250;
  background: var(--ink-900);
  color: var(--ink-300);
  box-shadow: 0 -4px 16px rgba(28, 43, 58, 0.22);
}
.cookies__inner {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding-block: var(--s-4);
}
@media (min-width: 800px) {
  .cookies__inner { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--s-5); }
}
.cookies__texto {
  margin: 0;
  font-size: var(--fs-small);
  /* ink-300 sobre ink-900 mede acima de 7:1; ink-400 ficaria em 4,1:1 — o
     mesmo engano já corrigido no .meta e no .field::placeholder. */
  color: var(--ink-300);
}
.cookies__texto a { color: #fff; text-decoration: underline; }
.cookies__acoes {
  display: flex;
  gap: var(--s-3);
  flex: 0 0 auto;
}
.cookies__acoes .btn { flex: 1 1 auto; }
@media (min-width: 800px) { .cookies__acoes .btn { flex: 0 0 auto; } }

/* O botão "voltar ao topo" e a faixa de cookies dividem o canto inferior, e
   no celular um cobre o outro. Enquanto a faixa estiver na tela — o que só
   acontece antes de o visitante responder —, o botão sai. Sem número mágico:
   a altura da faixa muda com o tamanho da tela e do texto.
   Depende de o botão vir DEPOIS da faixa no HTML (ver includes/footer.php):
   `~` só enxerga irmão posterior. */
.cookies ~ .to-top { display: none; }

/* --- Links dentro da bio do apresentador --------------------------------
   O reset do projeto zera link (`a { color: inherit; text-decoration: none }`),
   o que é certo para card e menu, onde o bloco inteiro é clicável. Dentro de
   um parágrafo, porém, isso deixava o endereço com a MESMA aparência do texto
   à volta: clicável e invisível.

   Negrito foi o que o cliente pediu (06/09/2026). A cor vem junto porque peso
   sozinho não identifica link — quem lê rápido não distingue negrito de
   ênfase, e quem enxerga pouco, menos ainda. O sublinhado aparece no hover e
   no foco, que é onde ele informa sem poluir o parágrafo. */
.bio a {
  font-weight: 700;
  color: var(--accent);
}
.bio a:hover,
.bio a:focus-visible { text-decoration: underline; }

/* --- Página legal (política de privacidade) ------------------------------
   Documento longo, para ler do começo ao fim: medida de linha curta, títulos
   com respiro e lista com marcador visível. */
.legal h2 {
  margin-top: var(--s-7);
  margin-bottom: var(--s-3);
  padding-top: var(--s-4);
  border-top: 3px solid var(--ink-900);
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 900;
}
.legal h3 {
  margin-top: var(--s-5);
  margin-bottom: var(--s-2);
  font-size: var(--fs-h4);
  font-weight: 800;
}
.legal p { max-width: 68ch; }
/* Endereço longo (URL do site, e-mail do responsável) não cabe em 390px e
   empurrava o documento inteiro: medido em 06/09/2026, `scrollWidth` dava
   392 num viewport de 390. Palavra sem espaço não quebra sozinha — isto é o
   que autoriza a quebra dentro dela. Ver a invariante "zero rolagem
   horizontal" na skill do projeto. */
.legal, .legal a, .legal code, .form-aviso, .cookies__texto { overflow-wrap: anywhere; }
.legal__lista {
  max-width: 68ch;
  margin: 0 0 var(--s-4);
  padding-left: var(--s-5);
  list-style: disc;
}
.legal__lista li { margin-bottom: var(--s-2); }
.legal code {
  padding: 1px 5px;
  background: var(--ink-100);
  border-radius: var(--radius);
  font-size: 0.9em;
}

/* --------------------------------------------------------------------------
   LIGHTBOX DE GALERIA — 10/09/2026

   Veio de um <style> embutido no noticia.php, que era a única página com
   lightbox. Agora é compartilhado: a marcação do overlay é criada pelo
   módulo 14 do main.js e não existe em template nenhum.

   As cores aqui são LITERAIS de propósito, e é a única exceção à regra dos
   tokens neste arquivo: o overlay é uma superfície fora do documento, preta
   por cima de qualquer página, com texto branco. Não é a paleta editorial do
   site — ligar `--ink-900` aqui faria uma troca de identidade da emissora
   clarear o fundo da foto e apagar o contraste.
   -------------------------------------------------------------------------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;   /* acima da nav fixa e dos modais de contato/pedido */
  display: flex;
  align-items: center;
  justify-content: center;
  /* 0,95 e não 0,9: o assunto é a foto. Com a página legível atrás, o olho
     fica dividido e o visitante não percebe que aquilo é uma camada que se
     fecha. Não é 1 porque o resto de contexto é o que avisa que a página
     continua ali embaixo. */
  background: rgba(0, 0, 0, 0.95);
  /* O padding é o que garante que exista FUNDO CLICÁVEL em volta da foto em
     qualquer tela: com a imagem encostando na borda, "clicar fora" não tem
     onde acontecer no celular. `env(safe-area-inset-*)` mantém o botão de
     fechar longe do notch. */
  padding: calc(var(--s-6) + env(safe-area-inset-top, 0px))
           calc(var(--s-4) + env(safe-area-inset-right, 0px))
           calc(var(--s-6) + env(safe-area-inset-bottom, 0px))
           calc(var(--s-4) + env(safe-area-inset-left, 0px));
}
.lightbox[hidden] { display: none; }
/* O container recebe o foco na abertura (ver módulo 14 do main.js). Anel em
   volta de um retângulo que cobre a tela inteira não informa nada. */
.lightbox:focus { outline: none; }

/* A figura existe para separar a foto do fundo: o handler de "clicar fora"
   testa `e.target === lb`, e sem um elemento no meio a área em volta da
   imagem dentro do flex ainda seria o próprio overlay — clicar na moldura
   branca de uma foto retrato fecharia sem o visitante entender por quê.
   `pointer-events: none` na figura devolve esse cinturão ao fundo, de
   propósito: ali fechar É o comportamento certo. */
.lightbox__figura {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  pointer-events: none;
}
.lightbox__img {
  max-width: min(92vw, 1600px);
  /* `85vh` menos o espaço da legenda e do contador. Sem o desconto, foto em
     pé empurra a legenda para fora da tela. */
  max-height: 78vh;
  object-fit: contain;
  /* A foto volta a receber clique para o visitante poder arrastar/salvar. */
  pointer-events: auto;
}
.lightbox__legenda {
  max-width: min(92vw, 68ch);
  color: #fff;
  font-size: var(--fs-small);
  line-height: 1.4;
  text-align: center;
  text-wrap: balance;
  pointer-events: auto;
}
.lightbox__legenda[hidden] { display: none; }

.lightbox__fechar,
.lightbox__nav {
  position: absolute;
  z-index: 2;
  border: 0;
  background: none;
  color: #fff;
  font: inherit;
  line-height: 1;
  cursor: pointer;
  /* 44px é o alvo de toque mínimo. A seta é um caractere pequeno; sem área
     em volta, errar o toque no celular fecha ou não faz nada. */
  min-width: 44px;
  min-height: 44px;
}
.lightbox__fechar {
  top: calc(var(--s-2) + env(safe-area-inset-top, 0px));
  right: calc(var(--s-2) + env(safe-area-inset-right, 0px));
  font-size: 2.25rem;
}
.lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
  padding: var(--s-3) var(--s-4);
  font-size: 2rem;
}
.lightbox__nav--prev { left: calc(var(--s-1, 4px) + env(safe-area-inset-left, 0px)); }
.lightbox__nav--next { right: calc(var(--s-1, 4px) + env(safe-area-inset-right, 0px)); }
.lightbox__nav[hidden] { display: none; }
.lightbox__fechar:hover,
.lightbox__nav:hover { color: var(--accent); }
.lightbox__fechar:focus-visible,
.lightbox__nav:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

.lightbox__conta {
  position: absolute;
  bottom: calc(var(--s-2) + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  color: #fff;
  font-size: var(--fs-meta);
  letter-spacing: 0.1em;
  opacity: 0.7;
}

/* AS SETAS DESCEM NO CELULAR — medido em 10/09/2026.

   Centradas na vertical junto às bordas, elas ficam EM CIMA da foto em tela
   estreita: a imagem ocupa 92vw (359px de 390px), então sobram ~15px de cada
   lado e o alvo de toque de 44px invade a arte. Duas consequências ruins:
   tocar a beira da foto voltava uma imagem sem o visitante querer, e a faixa
   de fundo que serve para "tocar fora e fechar" simplesmente não existia dos
   lados.

   Embaixo, flanqueando o contador, elas ficam fora da foto e o fundo dos dois
   lados volta a fechar. Vale para telas baixas também (celular deitado), onde
   o problema é o mesmo na horizontal. */
@media (max-width: 700px), (max-height: 480px) {
  .lightbox { padding-bottom: calc(var(--s-9, 4rem) + env(safe-area-inset-bottom, 0px)); }
  .lightbox__nav {
    top: auto;
    bottom: calc(var(--s-1, 4px) + env(safe-area-inset-bottom, 0px));
    transform: none;
    padding: var(--s-2) var(--s-3);
    font-size: 1.75rem;
  }
  /* Longe o bastante do contador para não haver toque ambíguo entre eles. */
  .lightbox__nav--prev { left: 18%; }
  .lightbox__nav--next { right: 18%; }
  .lightbox__img { max-height: 68vh; }
}

/* Miniaturas das galerias do noticia.php. Estavam no <style> embutido; vieram
   para cá quando o lightbox virou compartilhado. `cursor: zoom-in` é o que
   avisa que o clique amplia em vez de navegar. */
.galeria__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
  gap: var(--s-3);
}
.galeria__grid--sm { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
/* Abaixo de 480px o `minmax(420px, ...)` não cabe e a coluna estoura a tela:
   uma faixa mínima maior que a viewport não encolhe. */
@media (max-width: 480px) {
  .galeria__grid { grid-template-columns: 1fr; }
}
.galeria__thumb {
  display: block;
  overflow: hidden;
  padding: 0;
  border: 0;
  border-radius: 8px;
  background: none;
  aspect-ratio: 4 / 3;
  line-height: 0;
  cursor: zoom-in;
}
.galeria__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--t-fast); }
.galeria__thumb:hover img,
.galeria__thumb:focus-visible img { transform: scale(1.05); }

/* O mosaico da /galeria/ também amplia em vez de navegar. */
.mosaic__item { cursor: zoom-in; }

@media (prefers-reduced-motion: reduce) {
  .galeria__thumb img,
  .galeria__thumb:hover img,
  .galeria__thumb:focus-visible img { transition: none; transform: none; }
}
