/* ============================================================
   Fontes self-hospedadas. O publish baixa o que o tema usar.
   ============================================================ */

@font-face {
  font-family: 'Archivo';
  src: url('fonts/display-latin.woff2') format('woff2');
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122;
}

@font-face {
  font-family: 'Instrument Sans';
  src: url('fonts/cuerpo-latin.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122;
}

/* ============================================================
   Tokens.

   Quase-preto empilhado em tres degraus, com acento azul. O preto
   mais escuro do que o grafite que estava aqui antes é o que faz o
   resto parecer caro: com o fundo em #0b0d0f cada superfície acima
   dele se separa sem precisar de borda, e a foto ganha contraste
   sem precisar de véu mais forte.

   Escurecer o fundo levantou TODOS os pares -- e barato: o texto
   passou de #f2f0ed para o off-white #fffcfa, e o mesmo azul que o
   cliente pediu continua no lugar.

     texto / fondo                     19.06:1
     texto-suave / fondo               10.47:1
     texto-suave / superficie           9.93:1
     texto-suave / superficie-alta      9.30:1
     texto-tenue / fondo                6.23:1
     acento-claro / fondo               6.53:1
     acento-claro / superficie          6.18:1
     acento-claro / superficie-alta     5.79:1
     branco / acento (botão)            4.82:1   <- o pior par do site
     branco / acento-fuerte (hover)     6.40:1

   O azul da marca (#0866FF) segue com UM papel só: preenchimento de
   botão, com branco puro em cima. Como texto ele dá 4.04:1 no fundo
   e 3.83:1 na superfície -- reprova nos dois. Para isso existe o
   acento-claro.

   Os valores repetem o bloco `theme` do site.json de propósito:
   aqui servem ao primeiro render, lá servem ao que o cliente pode
   escolher no painel. Se divergirem, o preview mente.
   ============================================================ */

:root {
  --fondo: #0b0d0f;
  --superficie: #121417;
  --superficie-alta: #181b1f;

  --texto: #fffcfa;
  --texto-suave: #bebebe;
  --texto-tenue: #8f9296;

  /* Preenchimento. Nunca use como cor de texto sobre o fundo escuro. */
  --acento: #0866ff;

  /* O hover ESCURECE. Este botão tem texto claro em cima: clarear o fundo
     derrubaria o contraste justamente quando o cursor está mirando nele.
     Escurecendo, sobe para 6.40:1 -- e a sensação de "acendeu" vem do
     brilho, não da cor. */
  --acento-fuerte: #0052e0;

  /* O mesmo azul clareado, para quando o acento é TEXTO: link, número,
     etiqueta, versalete. 6.53:1 no fundo, 6.18:1 na superfície. */
  --acento-claro: #5294ff;

  /* Branco puro, não o --texto do tema: #fffcfa sobre o azul dá 4.72:1 e
     passa raspando. Branco dá 4.82:1 e é o piso do site. */
  --sobre-acento: #ffffff;

  /* Fio de cabelo, não linha. Translúcido em vez de cor fixa para valer
     igual sobre o fundo e sobre as duas superfícies -- é o que separa as
     seções no referência sem desenhar uma régua na página. */
  --borde: color-mix(in srgb, var(--texto) 10%, transparent);
  --borde-forte: color-mix(in srgb, var(--texto) 18%, transparent);
  --exito: #4ade80;

  --display: 'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --cuerpo: 'Instrument Sans', system-ui, -apple-system, sans-serif;

  --salida: cubic-bezier(0.23, 1, 0.32, 1);
  --suave: cubic-bezier(0.77, 0, 0.175, 1);

  --sombra-baja: 0 1px 2px rgba(0, 0, 0, 0.5), 0 6px 18px rgba(0, 0, 0, 0.32);
  --sombra-alta: 0 2px 6px rgba(0, 0, 0, 0.55), 0 24px 60px rgba(0, 0, 0, 0.6);

  --ancho: 74rem;
  --aire-lateral: 1.5rem;
  --radio: 10px;
  --radio-interno: 7px;
  --aire: clamp(4rem, 9vw, 7.5rem);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 5.5rem;

  /* SEM ELÁSTICO no topo e no fim. `overscroll-behavior-y: none` tira o
     rubber-banding do documento; é `-y` e não geral porque a faixa de links do
     cabeçalho rola na horizontal em tela estreita, e ali o quique é o único sinal
     de que ela acabou. Custa o pull-to-refresh do Chrome no Android -- é a mesma
     propriedade --, o que num site institucional não é perda. iOS abaixo do 16
     ignora e mantém o elástico; não há saída em CSS. */
  overscroll-behavior-y: none;

  /* O fundo mora aqui e não no body: assim o brilho ambiente abaixo
     (z-index: -1) fica ACIMA da cor e ABAIXO do conteúdo. */
  background: var(--fondo);
}

/*
  O QUE O LENIS EXIGE. Estas regras vêm do `lenis.css` da própria biblioteca, e
  moram aqui em vez de num arquivo à parte porque o site serve UM css.

  `scroll-behavior: auto` quando o Lenis está ligado, e essa é a linha que importa:
  com `smooth` no `html`, a biblioteca e o browser animam a mesma posição ao mesmo
  tempo -- e o sintoma é a página andar um pedaço e travar. Quem passa a cuidar da
  âncora é o `anchors: true` do Lenis.

  É CONDICIONADO A `.lenis`, que a biblioteca põe no `html` ao subir. Sem JS, no
  editor, com o modal aberto ou com `prefers-reduced-motion`, a classe não existe e
  o `smooth` do CSS volta a valer -- o degradê desejado, sem uma linha de JS para
  coordenar.
*/
html.lenis,
html.lenis body {
  height: auto;
}

html.lenis {
  scroll-behavior: auto;
}

.lenis:not(.lenis-autoToggle).lenis-stopped {
  overflow: clip;
}

.lenis [data-lenis-prevent],
.lenis [data-lenis-prevent-wheel],
.lenis [data-lenis-prevent-touch],
.lenis [data-lenis-prevent-vertical],
.lenis [data-lenis-prevent-horizontal] {
  overscroll-behavior: contain;
}

/* Iframe não repassa evento de roda para o documento de fora, então a rolagem
   morreria ao passar o ponteiro sobre um. Regra do próprio `lenis.css`.

   (Esta nota dizia "este site não tem iframe hoje" e ficou velha no dia em que o
   mapa virou um: hoje ela é a regra que faz o mapa logo abaixo não travar a roda.) */
.lenis.lenis-smooth iframe {
  pointer-events: none;
}

/*
  O MAPA NAO RECEBE PONTEIRO, e isto reverte uma excecao que existia aqui ate
  17/08/2026. Ela devolvia `pointer-events: auto` ao quadro para ele poder ser
  arrastado, e o preco era um defeito que o cliente sentiu: com o ponteiro sobre
  o mapa, a roda travava e a pagina so andava depois de um tranco.

  A CAUSA E ESTRUTURAL, nao de ajuste. Com o ponteiro chegando ao quadro, o
  evento de roda acontece DENTRO de um iframe cross-origin -- e evento de
  iframe de outra origem nao propaga para o documento de fora. O Lenis nunca ve
  a roda ali, entao nao ha configuracao que resolva; a regra
  `.lenis.lenis-smooth iframe { pointer-events: none }` do proprio lenis.css
  existe exatamente por isso. Somava-se o `data-lenis-prevent` no involucro, que
  mandava o Lenis ignorar a regiao de proposito: a rolagem caia no nativo
  enquanto o laco do Lenis seguia escrevendo a posicao. Dois donos do mesmo
  scroll, e o tranco era a briga entre eles.

  O QUE SE PERDE: o mapa nao arrasta e nao da zoom. Para negocio local isso nao
  e perda -- e a mesma leitura que o CLAUDE.md ja fazia do mapa em PNG. Quem
  quer navegar clica no "Como llegar" logo abaixo e vai para o app de mapa que
  ja usa, com transito e rota. O quadro aqui responde outra pergunta: "em que
  parte da cidade isso fica?".

  SEM O PREFIXO `.lenis.lenis-smooth`, de proposito. A regra do lenis.css so
  vale com o Lenis ligado, e ele desliga com `prefers-reduced-motion`, no editor
  e com o modal aberto -- nesses tres casos o quadro voltaria a capturar a roda
  e a armadilha voltaria junto, so para quem pediu menos movimento. O
  comportamento aqui e o mesmo em todos os casos.
*/
.mapa-google iframe {
  pointer-events: none;
}

/* Brilho ambiente. Um fundo chapado numa página inteira lê como "sem
   acabamento" -- que é justamente o que este cliente vende. É fixo: não
   acompanha o scroll, então funciona como luz de ambiente e não como mais
   um elemento que se move. */
html::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(58rem 40rem at 82% -12%,
      color-mix(in srgb, var(--acento-claro) 11%, transparent), transparent 68%),
    radial-gradient(46rem 34rem at 4% 12%,
      color-mix(in srgb, var(--acento-claro) 4%, transparent), transparent 62%);
}

/* Grão. Superfície plana demais é a impressão digital de tela feita por
   IA; um ruído de 4% quebra isso sem aparecer. Fica abaixo do cabeçalho
   (z 20) e muito abaixo da régua do editor (z 2147483646). */
html::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0.045;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='r'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23r)'/%3E%3C/svg%3E");
}

body {
  margin: 0;
  color: var(--texto);
  font-family: var(--cuerpo);
  font-size: 1.0625rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ============================================================
   Tipografia.

   O "premium" do referência não vem da fonte -- vem do tratamento:
   caixa alta com entrelinha travada em 0.95, tracking negativo no
   título grande e positivo nos versaletes pequenos. Archivo em 800
   aguenta isso sem trocar de família e sem baixar 30 KB de fonte.

   Caixa alta em h1 e h2 e nada além. O referência põe caps também
   na bajada e no corpo a 13px, e é onde ele erra: bloco de texto em
   versalete se lê letra por letra. Aqui o corpo fica em 1.0625rem,
   caixa mista.
   ============================================================ */

h1, h2, h3 {
  font-family: var(--display);
  margin: 0;
  text-wrap: balance;
}

h1, h2 {
  text-transform: uppercase;
}

h1 {
  font-size: clamp(2.2rem, 5.8vw, 4rem);
  font-weight: 800;
  line-height: 0.95;
  /* Menos negativo que em caixa mista: maiúscula precisa do espaço que a
     minúscula dispensa, e -0.035em em caps encosta o R no A. */
  letter-spacing: -0.015em;
}

h2 {
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.01em;
}

h3 {
  font-size: 1.18rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1rem; text-wrap: pretty; }
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; }

/* O VERSALETE SAIU em 17/08/2026, a pedido do cliente.
   Era `.encima-seccion` -- o traço curto mais o rótulo em caps antes de cada
   título ("EL CATÁLOGO", "PORTAFOLIO", "LO QUE DICEN"). O feedback dele:
   "esa linea y texto en todo. (Es repetitivo)". Estava certo: sete seções com
   a mesma forma de anúncio deixam de anunciar e viram moldura.

   O que ficou no lugar não é nada -- o `<h2>` passou a abrir a seção sozinho, e
   os títulos foram reescritos para carregar o que o rótulo dizia ("Nuestro
   trabajo" em vez de PORTAFOLIO + "Trabajos recientes").

   A DATA DO POST usava esta classe e NÃO é rótulo repetido: virou `.fecha-post`,
   logo abaixo. */

.fecha-post {
  font-family: var(--display);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--texto-suave);
  margin: 0 0 0.85rem;
  display: block;
}

.icono {
  width: 1.15em;
  height: 1.15em;
  flex: none;
}

:focus-visible {
  outline: 2px solid var(--acento-claro);
  outline-offset: 3px;
  border-radius: 3px;
}

.saltar { position: absolute; left: -9999px; }
.saltar:focus {
  left: 1rem; top: 1rem; z-index: 200;
  background: var(--superficie-alta);
  padding: 0.75rem 1rem;
  border-radius: var(--radio);
}

/* ---------- cabecera ---------- */

.cabecera {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--fondo) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms ease, background 200ms ease;
}

.cabecera[data-rolou] {
  border-bottom-color: var(--borde);
  background: color-mix(in srgb, var(--fondo) 92%, transparent);
}

.franja {
  max-width: var(--ancho);
  margin: 0 auto;
  padding: 1rem var(--aire-lateral);
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  transition: padding 260ms var(--salida);
}

/* Compacta depois de 180px de scroll. Cabeçalho fixo alto come a tela do
   celular o tempo todo, não só na primeira dobra -- e é justo na hora em
   que a pessoa está lendo que ele deveria ocupar menos.

   Encolhe padding e a bajada da marca, nunca a área de toque do botão de
   WhatsApp: é a ação principal do site.

   Nada disso dentro do editor. `marca.bajada` é campo editável, e um campo
   que a página colapsa sozinha some do painel sem sair do site -- o mesmo
   problema de z-index negativo numa imagem: o cliente vê e não consegue
   clicar. */
html:not([data-sitekit-editando]) .cabecera[data-compacto] .franja {
  padding-top: 0.6rem;
  padding-bottom: 0.6rem;
}

.marca {
  text-decoration: none;
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}

.marca span {
  font-family: var(--display);
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  transition: font-size 260ms var(--salida);
}

.marca small {
  font-size: 0.62rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--acento-claro);
  /* Some na versão compacta com grid-template-rows em vez de display:none:
     assim a altura anima em vez de saltar. */
  display: grid;
  grid-template-rows: 1fr;
  overflow: hidden;
  transition: grid-template-rows 260ms var(--salida), opacity 200ms ease;
}

html:not([data-sitekit-editando]) .cabecera[data-compacto] .marca span {
  font-size: 1.18rem;
}

html:not([data-sitekit-editando]) .cabecera[data-compacto] .marca small {
  grid-template-rows: 0fr;
  opacity: 0;
}

/* O logo do cliente. Proporção 11,29:1 -- a altura manda e a largura segue,
   senão ele estoura a faixa em tela estreita.

   As duas regras acima seguem no arquivo de propósito: `.marca span` e
   `.marca small` ainda valem no rodapé, onde a marca continua sendo texto. */
.marca-logo {
  height: clamp(0.66rem, 1.05vw, 0.85rem);
  width: auto;
  transition: height 260ms var(--salida);
}

/* Encolhe junto com a faixa, como o texto fazia. Sem isto o cabeçalho compacta
   e o logo fica do mesmo tamanho, empurrando a altura de volta. */
html:not([data-sitekit-editando]) .cabecera[data-compacto] .marca-logo {
  height: 0.58rem;
}

.franja nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.6rem;
  align-items: center;
  font-size: 0.94rem;
}

.franja nav a {
  text-decoration: none;
  color: var(--texto-suave);
  transition: color 180ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .franja nav a:hover { color: var(--texto); }
}

/* A página em que se está.

   Pinta a partir de `aria-current="page"`, que o exportador escreve na
   exportação de cada página -- e não de uma classe escrita à mão. Assim o
   estado visual e o anunciado pelo leitor de tela são o MESMO dado e não têm
   como divergir: uma classe `.ativo` esquecida daria um menu que mente para
   quem vê e para quem escuta em direções diferentes.

   Sublinhado e não só cor: o texto cheio já é a diferença de cor entre link
   ativo e inativo, e distinguir dois estados por tom sozinho falha para quem
   não separa os dois tons.

   DOIS valores, e o mesmo visual para os dois. `page` é a página exata;
   `true` é a seção em que a página está -- é o que marca "Blog" quando se
   está em `/blog/algum-post`. A diferença entre eles não é visual: ela existe
   para o leitor de tela, que anuncia "página atual" só no primeiro. Um post
   não É a página `/blog`, e dizer que é seria mentir para quem escuta. */
.franja nav a[aria-current='page'],
.franja nav a[aria-current='true'] {
  color: var(--texto);
  text-decoration: underline;
  text-decoration-color: var(--acento-claro);
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
}

/* O botão de WhatsApp já é preenchido; sublinhar dentro dele sujaria. Ele nunca
   é a página atual (aponta para fora), mas a regra fica explícita. */
.franja nav a.boton-nav[aria-current='page'],
.franja nav a.boton-nav[aria-current='true'] { text-decoration: none; }

.boton-nav {
  color: var(--sobre-acento) !important;
  background: var(--acento);
  border-radius: 999px;
  padding: 0.5rem 1.15rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  transition: background 180ms ease, transform 160ms var(--salida);
}

@media (hover: hover) and (pointer: fine) {
  .boton-nav:hover { background: var(--acento-fuerte); }
}

.boton-nav:active { transform: scale(0.97); }

/* No celular a marca fica numa linha e a navegação inteira na outra --
   links E botão juntos. Deixar o botão descer sozinho comia 150px dos
   812px da tela, e o cabeçalho é fixo: esse espaço é perdido o tempo todo. */
@media (max-width: 32rem) {
  html { scroll-padding-top: 7rem; }
  .franja { padding: 0.7rem 1.15rem 0.75rem; gap: 0.45rem; }
  .marca span { font-size: 1.2rem; }

  html:not([data-sitekit-editando]) .cabecera[data-compacto] .franja {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
  }

  .franja nav {
    flex: 1 1 100%;
    flex-wrap: nowrap;
    justify-content: space-between;
    gap: 0.5rem;
    font-size: 0.85rem;
  }

  .boton-nav { padding: 0.42rem 0.85rem; font-size: 0.82rem; gap: 0.35rem; }
}

/* ---------- portada ---------- */

main {
  /* A foto da portada passa da caixa de propósito. Sem isto ela viraria
     scroll lateral. `clip` e não `hidden`: hidden criaria um contêiner de
     scroll e mataria o `position: sticky` do cabeçalho. */
  overflow-x: clip;
}

main > section {
  max-width: var(--ancho);
  margin: 0 auto;
  padding: var(--aire) var(--aire-lateral) calc(var(--aire) * 1.05);
}

/* ============================================================
   A portada é uma foto de fundo com o texto por cima.

   O contraste aqui NÃO se verifica com dois hex: a foto pode ser
   qualquer coisa, e o cliente troca por outra pelo painel. O véu é
   dimensionado pelo PIOR caso possível -- foto branca estourada.

   Com o fundo mais escuro, o véu pôde AFROUXAR: o piso para o
   texto-suave passar caiu de 82% para 74%, e a 78% ele dá 5.00:1
   (o título dá 9.10:1). Quatro pontos de véu a menos é foto mais
   visível de graça -- a mesma leitura, com mais foto na tela.

   O azul-claro do chip reprova em qualquer véu que ainda deixe a
   foto aparecer, por isso ele ganha fundo próprio a 88%.
   ============================================================ */

.portada {
  max-width: none;
  padding: 0;
  position: relative;
  isolation: isolate;
  /* Segura a foto quando o parallax a desloca. Não contém o cabeçalho
     sticky, então clipar aqui é seguro. */
  overflow: clip;
  display: grid;
  align-items: center;
  /* svh e não vh: no celular o vh conta a barra do navegador que some ao
     rolar, e a portada dá um salto no primeiro toque. */
  min-height: min(88svh, 46rem);
}

/* ---------- a portada ocupa a tela inteira na versão larga ---------- */

/* `100svh - var(--alto-cabecera)` e não `100svh`, porque o cabeçalho é
   `position: sticky` -- e sticky fica NO FLUXO, ocupando os seus 73px no topo.
   Com `100svh` cravado, a portada terminaria 73px abaixo da dobra: o objetivo
   (nada além dela visível antes de rolar) até seria cumprido, mas o conteúdo
   centrado ficaria 36px fora de eixo e o indicador do rodapé cairia fora da
   tela, que é justamente onde ele precisa estar.

   O NÚMERO É MEDIDO, não estimado: 73,1px em headless a 1280 e a 1440. Ele é
   todo derivado de rem (1rem de padding em cima e embaixo na `.franja` mais a
   altura do botão), então acompanha o root font-size -- por isso está em rem e
   não em px. SE O CABEÇALHO MUDAR DE ALTURA, este valor tem que mudar junto:
   não há como o CSS descobrir sozinho a altura de um irmão fora do contêiner, e
   a alternativa seria JS escrevendo a variável, o que deixaria o layout errado
   justamente sem JS.

   SÓ ACIMA DE 62rem, e por dois motivos. É o mesmo ponto em que a portada vira
   duas colunas, e abaixo dele a `.franja` embrulha em duas linhas (medido:
   96,4px a 500px) -- então o valor acima estaria errado. Em tela estreita,
   ocupar a altura toda também empurraria tudo para baixo num aparelho alto,
   sem ganho nenhum. */
@media (min-width: 62rem) {
  .portada {
    --alto-cabecera: 4.57rem;
    min-height: calc(100svh - var(--alto-cabecera));
  }

  /* O respiro vertical encolhe quando a altura é a da tela: quem centra é o
     `align-items`, e `--aire` (até 7,5rem em cada ponta) comeria 240px de uma
     janela de 700px de altura -- o cartão do formulário sozinho tem 401px. */
  .portada .portada-caja {
    padding-block: clamp(2rem, 6vh, 5rem);
  }
}

/* Sem `z-index: -1`. Ele resolvia o empilhamento e criava um problema pior: o
   que fica atrás de tudo não recebe clique, e a foto da portada deixava de ser
   selecionável no editor visual -- o único campo do site sem como ser clicado.
   A ordem no DOM já basta, com a caixa de texto declarada acima. */
.portada-figura {
  margin: 0;
  position: absolute;
  inset: 0;
}

.portada-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Numa portada larga o `cover` corta na vertical. Este valor é afinado para
     a foto que está aqui: se o cliente trocar por outra com o assunto noutra
     altura, o recorte sai errado. É o limite de usar foto arbitrária como
     fundo de tela cheia, e não tem como o CSS adivinhar sozinho. */
  object-position: center 58%;
}

/* O véu. Vive na figura e não na seção para o `data-cms-section` continuar
   podendo pintar o fundo da portada sem brigar com ele. */
.portada-figura::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* No celular o texto ocupa a largura toda, então o véu tem que ser forte em
     qualquer altura onde ele caia. 78% dá 5.00:1 na bajada contra foto branca;
     o piso é 74% e abaixo dele reprova. */
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--fondo) 78%, transparent) 0%,
    color-mix(in srgb, var(--fondo) 88%, transparent) 55%,
    var(--fondo) 100%
  );
}

.portada-caja {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--ancho);
  margin-inline: auto;
  padding: calc(var(--aire) * 0.9) var(--aire-lateral);
  /* A caixa cobre a seção inteira só para posicionar o conteúdo. Se ela
     capturasse clique, a foto atrás ficaria inalcançável no editor -- então o
     vazio dela deixa passar e só as duas colunas respondem. Isto é o que faz o
     campo de texto do formulário receber foco: sem o `pointer-events: auto` em
     `.portada-form`, o clique morreria na caixa. */
  pointer-events: none;
  display: grid;
  gap: clamp(2.2rem, 4vw, 4rem);
}

/* Empilhado abaixo de 62rem: texto e depois o cartão. É a mesma ordem do DOM,
   então nada precisa ser reordenado e a leitura por teclado continua igual. */
@media (min-width: 62rem) {
  .portada-caja {
    grid-template-columns: 1.15fr 0.85fr;
    align-items: center;
  }
}

.portada-texto {
  max-width: 34rem;
  pointer-events: auto;
}

@media (min-width: 62rem) {
  /* Na largura cheia o texto ocupa só a esquerda, então o véu pode ser forte
     ali e leve do outro lado -- é o que deixa a foto respirar sem custar
     legibilidade. O ponto de 88% cobre folgado a coluna de texto. */
  .portada-figura::after {
    background: linear-gradient(
      100deg,
      var(--fondo) 0%,
      color-mix(in srgb, var(--fondo) 88%, transparent) 46%,
      color-mix(in srgb, var(--fondo) 38%, transparent) 78%,
      color-mix(in srgb, var(--fondo) 20%, transparent) 100%
    );
  }

  .portada-texto { max-width: 40rem; }
}

/* O CHIP "● YANAHUARA, AREQUIPA" SAIU em 17/08/2026, com o resto dos versaletes.
   Era `.encima`, e o cliente o listou no que eliminar. A cidade não sumiu do
   site: ela está no endereço da seção Visítanos, no rodapé e no JSON-LD, que
   são os três lugares onde ela responde uma pergunta em vez de decorar. */

.bajada {
  font-size: clamp(1.05rem, 1.6vw, 1.18rem);
  color: var(--texto-suave);
  max-width: 40ch;
  margin-top: 1.6rem;
}

/* ---------- pedido de cotização na portada ---------- */

/* FUNDO PRÓPRIO A 92%, e isso não é escolha de estilo. O bloco cai sobre a foto
   da portada, que o cliente pode trocar por uma clara -- o véu da figura é
   dimensionado para TEXTO, e um campo de formulário precisa de mais: a borda do
   input, o rótulo e o aviso são todos tons que reprovam contra foto branca
   estourada com qualquer véu que ainda deixe a foto aparecer. A 92% o cinza
   claro dá 5,47:1; a 80% cai para 3,64 e reprova. Mesma conta da caixa de
   endereço. */
.portada-form {
  /* Sem `margin-top`: o espaço entre as duas colunas é o `gap` da grade. Com os
     dois, o empilhado a 500px ganharia o dobro de respiro. */
  max-width: 26rem;
  width: 100%;
  pointer-events: auto;
  display: grid;
  gap: 0.85rem;
  padding: 1.5rem;
  border: 1px solid color-mix(in srgb, var(--texto) 14%, transparent);
  border-radius: var(--radio);
  background: color-mix(in srgb, var(--fondo) 92%, transparent);
  backdrop-filter: blur(10px);
  /* O cartão flutua sobre a foto, e sem sombra ele encosta nela como um recorte
     colado. A sombra é larga e fraca -- profundidade, não caixa em relevo. */
  box-shadow: 0 24px 60px -20px rgb(0 0 0 / 0.65);
}

@media (min-width: 62rem) {
  /* Encostado à direita da própria coluna: é o que deixa a faixa clara do véu
     aparecer entre o texto e o cartão, em vez de o cartão colar no título. */
  .portada-form { justify-self: end; }
}

.portada-form-titulo {
  margin: 0;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--texto);
  letter-spacing: -0.01em;
}

.portada-campo {
  display: grid;
  gap: 0.35rem;
  font-size: 0.82rem;
  color: var(--texto-suave);
}

.portada-campo input {
  font: inherit;
  font-size: 0.95rem;
  color: var(--texto);
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  padding: 0.6rem 0.75rem;
  width: 100%;
  /* `appearance: none` tira o estilo nativo do iOS, que ignora o border-radius
     e desenha o próprio fundo claro -- num tema escuro isso é um campo branco
     no meio do bloco. */
  appearance: none;
  transition: border-color 160ms ease, background 160ms ease;
}

/* 70% e não 62%: a 62 o placeholder dá exatamente 4,50:1 sobre a superfície --
   passa, e passa em cima da linha. Qualquer ajuste futuro em `--texto-suave` ou
   em `--superficie` o derruba sem ninguém medir de novo. A 70% sobra margem
   (5,41:1) e o texto continua se lendo como exemplo, não como valor digitado. */
.portada-campo input::placeholder { color: color-mix(in srgb, var(--texto-suave) 70%, transparent); }

.portada-campo input:focus-visible {
  outline: 2px solid var(--acento-claro);
  outline-offset: 1px;
  border-color: transparent;
}

/* O par `[hidden]` é obrigatório: `hidden` é `display: none` na folha do UA e
   QUALQUER regra de autor com `display` vence dele. Sem esta linha o aviso
   ocuparia altura invisível desde o primeiro paint -- o defeito que já apareceu
   duas vezes neste site (`.agenda-campo` e `.agenda-resumen`). */
.portada-aviso {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  /* Não é vermelho: o tema não tem cor de erro e um vermelho solto aqui seria a
     única cor fora da paleta na página inteira. O acento-claro dá 6,53:1 e o
     texto já diz o que falta. */
  color: var(--acento-claro);
}

.portada-aviso[hidden] { display: none; }

/* No editor ele volta. É um texto que o cliente precisa poder reescrever, e
   escondido ele não existe para o painel -- a mesma regra das quatro mensagens
   condicionais do agendamento, logo abaixo neste arquivo. */
html[data-sitekit-editando] .portada-aviso[hidden] { display: flex; }

.portada-form .boton { width: 100%; justify-content: center; margin-top: 0.2rem; }

.portada-secundario { margin: 1.3rem 0 0; }

/* ---------- indicador de que há mais abaixo ---------- */

/* SÓ EXISTE ONDE A PORTADA OCUPA A TELA INTEIRA (mesmo breakpoint), porque é
   só ali que ele responde uma pergunta. Abaixo de 62rem a portada tem 88svh e
   a seção seguinte já aparece cortada na dobra -- a borda cortada é o melhor
   indicador que existe, e um segundo por cima dela seria redundante. */
.portada-baja {
  display: none;
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 3vh, 2.25rem);
  translate: -50% 0;
  z-index: 2;
  width: 2.75rem;
  height: 2.75rem;
  align-items: center;
  justify-content: center;
  border: 1px solid color-mix(in srgb, var(--texto) 20%, transparent);
  border-radius: 999px;
  /* Fundo próprio a 70%: o círculo cai sobre a foto, e no lado direito o véu
     diagonal deixa a foto quase limpa. Contra foto branca estourada o cinza dá
     6,14:1 assim; sem o fundo cairia para 4,3 e, embora ícone de interface
     passe com 3:1, a seta ficaria lavada justamente na foto clara. */
  background: color-mix(in srgb, var(--fondo) 70%, transparent);
  backdrop-filter: blur(8px);
  color: var(--texto-suave);
  transition: color 200ms ease, border-color 200ms ease, background 200ms ease;
}

@media (min-width: 62rem) {
  .portada-baja { display: flex; }
}

.portada-baja-icono {
  width: 1.1rem;
  height: 1.1rem;
  /* O chevron do sprite aponta para a direita; 90° o põe para baixo. */
  rotate: 90deg;
}

/* Bob curto e lento: chama o olho sem competir com o título. Fica atrás de
   `no-preference` e de `:not([data-sitekit-editando])` como todo movimento
   daqui -- o realce do painel é posicionado por `getBoundingClientRect` e não
   se reposiciona, então elemento que anda deixa a marcação no lugar errado. */
@media (prefers-reduced-motion: no-preference) {
  html:not([data-sitekit-editando]) .portada-baja-icono {
    animation: baja-bob 1.9s var(--salida) infinite;
  }
}

/* `translate: 0 4px` -- no eixo Y, e NÃO `4px 0` contando com a rotação.
   As propriedades individuais `translate`, `rotate` e `scale` são aplicadas
   sempre nesta ordem (translate, depois rotate, depois scale), qualquer que
   seja a ordem em que foram declaradas. Ou seja: o `translate` acontece no
   espaço NÃO rotacionado, e `4px 0` empurra a seta para a direita na tela.

   Só o atalho `transform: rotate(90deg) translateX(4px)` faria o translate
   herdar o giro, porque ali as funções compõem na sequência escrita. Como
   `rotate` já vive na regra base, o keyframe anima só o `translate` e a
   rotação segue valendo. */
@keyframes baja-bob {
  0%, 100% { translate: 0 0; }
  50%      { translate: 0 4px; }
}

@media (hover: hover) and (pointer: fine) {
  .portada-baja:hover {
    color: var(--texto);
    border-color: color-mix(in srgb, var(--acento-claro) 55%, transparent);
    background: color-mix(in srgb, var(--fondo) 85%, transparent);
  }
}

.portada-baja:active { scale: 0.94; }

.portada-secundario a {
  font-size: 0.92rem;
  color: var(--texto-suave);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-color: color-mix(in srgb, var(--texto-suave) 40%, transparent);
  transition: color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .portada-secundario a:hover { color: var(--texto); }
}

.boton {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  background: var(--acento);
  color: var(--sobre-acento);
  text-decoration: none;
  padding: 0.9rem 1.7rem;
  border-radius: var(--radio);
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.88rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  box-shadow: 0 10px 30px color-mix(in srgb, var(--acento) 22%, transparent);
  transition: background 180ms ease, transform 160ms var(--salida), box-shadow 220ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .boton:hover {
    background: var(--acento-fuerte);
    box-shadow: 0 16px 44px color-mix(in srgb, var(--acento) 34%, transparent);
  }
}

.boton:active { transform: scale(0.97); }

.boton.secundario {
  background: transparent;
  color: var(--texto);
  border: 1px solid var(--borde-forte);
  box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
  .boton.secundario:hover {
    border-color: var(--texto-tenue);
    background: var(--superficie);
    box-shadow: none;
  }
}

.boton.grande { padding: 1.05rem 2.1rem; font-size: 0.95rem; }

/* ---------- cabeçalho de seção ---------- */

.servicios { border-top: 1px solid var(--borde); }

.cabecera-seccion {
  display: grid;
  gap: 1rem;
  margin-bottom: 3rem;
}

@media (min-width: 50rem) {
  .cabecera-seccion {
    grid-template-columns: auto 1fr;
    align-items: end;
    gap: 2.5rem;
  }

  /* Alinha a base do subtítulo com a do h2, não com o versalete acima dele. */
  .cabecera-seccion .subtitulo { padding-bottom: 0.2rem; }
}

/* Teto na coluna do título: com `auto` um h2 longo em caixa alta ocupa a
   largura toda numa linha só e empurra o subtítulo para uma tira de quatro
   linhas na borda direita. Aqui ele quebra em duas linhas e o subtítulo fica
   com coluna de leitura. */
.titulo-seccion { min-width: 0; max-width: 32rem; }
.subtitulo { color: var(--texto-suave); margin: 0; max-width: 46ch; }

/* ---------- servicios ---------- */

.grilla {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(2, 1fr);
}

/* Duas colunas em 375px dão 163px por card: a descrição vira uma coluna de
   palavras quebradas. Abaixo disso o card ocupa a largura toda. */
@media (max-width: 34rem) {
  .grilla { grid-template-columns: 1fr; }
}

/* O MOSAICO, na forma que o cliente desenhou:

       1111111111 2222 3333
       1111111111 2222 3333
       1111111111 4444 5555
       1111111111 4444 5555

   Um card grande à esquerda ocupando as duas fileiras, três serviços menores à
   direita e o "ver todos" na peça inferior direita. Colunas 10 / 4 / 4.

   A LISTA VIRA `display: contents` porque as peças se intercalam -- o cartão
   fica ENTRE os serviços na grade, então a lista não pode ser uma sub-grade.
   Os `<li>` passam a ser peças da grade externa. O `role="list"` no HTML é o
   par obrigatório disso: `display: contents` faz alguns navegadores largarem a
   semântica de lista.

   TODAS AS PEÇAS TÊM DESCRIÇÃO, e todas em NO MÁXIMO DUAS LINHAS. Ela chegou a
   sair das peças pequenas para a seção caber na tela; o que devolveu o espaço
   foi a foto mais baixa e o corte em duas linhas, não esconder texto. Esconder
   era pior por dois motivos: o campo sumia do painel nesta página e a peça
   virava só uma foto com rótulo.

   Cortada em duas linhas ela ainda diz o que o serviço é, e o texto inteiro
   está a um clique na `/servicios` -- que é justamente o que o cartão ao lado
   oferece.

   O corte é 68rem. Abaixo dele o mosaico não existe: a lista volta a ser duas
   colunas e o cartão vira uma faixa embaixo. Aí não cabe numa tela, e é o certo
   -- numa janela estreita, espremer é pior que rolar. */
@media (min-width: 68rem) {
  .servicios-mosaico {
    display: grid;
    grid-template-columns: 10fr 4fr 4fr;
    grid-template-rows: repeat(2, 1fr);
    gap: 1.25rem;
  }

  .grilla { display: contents; }

  /* Duas linhas em todas as descrições. `line-clamp` sem prefixo já existe, mas
     o par `-webkit-` continua sendo o que a maioria dos motores implementa --
     os dois juntos. */
  .servicio .servicio-texto p {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
  }

  .servicio:nth-child(1) { grid-column: 1; grid-row: 1 / 3; }

  /*
    NO CARD GRANDE QUEM ESTICA É A FOTO, não o texto.

    Ele ocupa duas fileiras, mas o conteúdo (foto + título + duas linhas) é mais
    curto que isso. Como `.servicio-texto` é `flex: 1`, era ele que absorvia a
    sobra -- e o resultado era um respiro morto embaixo do parágrafo, do tamanho
    de meia foto.

    Invertendo, a folga vira imagem: o card em destaque ganha a foto maior, que é
    o que justifica ele ser o destaque. `min-height: 0` porque item de flex não
    encolhe abaixo do conteúdo sem isso, e `aspect-ratio: auto` para a proporção
    parar de mandar na altura -- aqui manda o espaço que sobra, e o `cover`
    recorta.
  */
  .servicio:nth-child(1) .servicio-foto {
    /* `1 1 0` e não `1`: com base `auto` a foto ainda mede o próprio conteúdo e
       volta a EMPURRAR a altura do card -- foi o que aconteceu na primeira
       tentativa, em que ela retomou o 4:3 natural (455px) e a seção deixou de
       caber na tela. Com base 0, ela só ocupa o que sobra. */
    flex: 1 1 0;
    min-height: 0;
  }

  /* ABSOLUTA dentro da moldura, pelo mesmo motivo: em fluxo, a imagem contribui
     com a altura intrínseca dela para o cálculo do flex, e nenhum `flex-basis`
     impede isso. Fora do fluxo, quem manda na altura é a fileira da grade --
     que é o que se quer: o card grande acompanha os pequenos. */
  .servicio:nth-child(1) .servicio-foto img {
    position: absolute;
    inset: 0;
    aspect-ratio: auto;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .servicio:nth-child(1) .servicio-texto { flex: 0 0 auto; }
  .servicio:nth-child(2) { grid-column: 2; grid-row: 1; }
  .servicio:nth-child(3) { grid-column: 3; grid-row: 1; }
  .servicio:nth-child(4) { grid-column: 2; grid-row: 2; }
  .servicio-todos        { grid-column: 3; grid-row: 2; }

  /* Um quinto card (se o teto mudar) cairia fora do desenho e empurraria o
     cartão. Ele fica escondido em vez de quebrar a forma -- e o teto do `<ul>`
     é quem manda de verdade: isto é só a rede. */
  .servicio:nth-child(n + 5) { display: none; }

  /* PANORÂMICA NAS CINCO PEÇAS. Quem manda na altura do mosaico é a foto do
     card grande: a 3/2 ela tinha 406px e a seção passava da tela em 1366x768 e
     1440x700 -- medido. A 16/9 são 343, e as peças pequenas ficam com espaço
     para o título de duas linhas. */
  .servicio .servicio-foto img { aspect-ratio: 16 / 9; }

  /* O RESPIRO PASSA A SER RELATIVO À ALTURA, não à largura. `--aire` é
     `clamp(..., 9vw, 7.5rem)` e sozinho come 240px -- num laptop de 700px de
     altura são justamente os pixels que faltam para a seção caber. Largura não
     diz nada sobre altura disponível: 1440x700 e 1440x1080 têm o mesmo `vw` e
     380px de diferença no que cabe. */
  .servicios { padding-block: clamp(2.25rem, 6svh, 4.5rem); }
}

/* TELA BAIXA: a foto encolhe mais um passo.

   Um laptop de 768px de altura tem ~695px úteis depois do cabeçalho, e a 16/9 o
   mosaico ainda passava. A 2/1 a foto do card grande cai de 343 para 304 e a
   seção entra com folga. Largura não diz nada sobre isto -- 1440x700 e
   1440x1080 têm o mesmo `vw` e 380px de diferença no que cabe, e é por isso que
   a consulta é por ALTURA. */
@media (min-width: 68rem) and (max-height: 900px) {
  .servicio .servicio-foto img { aspect-ratio: 12 / 5; }
  .servicios { padding-block: clamp(1.5rem, 4svh, 2.5rem); }

  .servicio-texto { padding: 0.3rem 1rem 1rem; }

  /* Em tela baixa quem passou a mandar na altura da fileira é o CARTÃO, não a
     foto: os seis marcadores somam mais que a peça de imagem com a descrição
     cortada. Então é ele que aperta -- entrelinha, respiro entre marcadores e
     recuo interno. Medido: devolve ~35px por fileira, que é o que faltava em
     1440x700. */
  /* `.servicios-mosaico` na frente NÃO é enfeite: as regras base do cartão
     aparecem DEPOIS deste bloco no arquivo e empatam em especificidade (0,1,0),
     então venceriam por ordem. Com o ancestral, (0,2,0) ganha de onde estiver. */
  .servicios-mosaico .servicio-todos { padding: 1rem; gap: 1rem; }
  .servicios-mosaico .servicio-todos-lista { gap: 0.2rem; line-height: 1.25; }
}

/* Abaixo do corte o mosaico não existe: a lista é a grade de sempre e o cartão
   vira uma faixa embaixo, largura inteira. */
.servicios-mosaico { display: grid; gap: 1.25rem; }

/*
  O CARTÃO DE "VER TODOS". Superfície e borda de card, como os vizinhos, mas com
  o acento na borda e no chevron: ele pertence à fileira e não é um deles.

  Sem foto de propósito. Uma imagem aqui teria de afirmar alguma coisa sobre um
  serviço específico, e este cartão é sobre os que faltam.

  `position: relative` porque o link é esticado por cima dele pelo `::after` --
  é o que deixa o cartão inteiro clicável sem enfiar os seis nomes dentro do
  nome acessível do link.
*/
.servicio-todos {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.35rem 1.2rem;
  min-height: 7rem;
  text-decoration: none;
  background: var(--superficie);
  border: 1px solid color-mix(in srgb, var(--acento-claro) 32%, var(--borde));
  border-radius: var(--radio);
  transition:
    transform 300ms var(--salida),
    border-color 300ms ease,
    background 300ms ease,
    box-shadow 300ms ease;
}

/* Os nomes que os cartões não mostram, um por linha.

   O marcador é um ponto desenhado no `::before` e não `list-style`: assim ele
   fica na cor de acento e alinhado ao topo da linha mesmo quando um nome
   quebra em duas ("Barras y portaequipajes" numa peça estreita). */
.servicio-todos-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.3rem;
  font-size: 0.82rem;
  line-height: 1.35;
  color: var(--texto-suave);
}

.servicio-todos-lista li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.5rem;
  align-items: start;
}

.servicio-todos-lista li::before {
  content: '';
  width: 0.3rem;
  height: 0.3rem;
  margin-top: 0.42em;
  border-radius: 50%;
  background: var(--acento-claro);
}

.servicio-todos-enlace {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  text-decoration: none;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--texto);
}

/* O link esticado: a área clicável é o cartão, o nome acessível é só "Ver todos
   los servicios". Sem isto, ou o cartão não é clicável, ou o link engole os
   seis nomes no nome dele. */
.servicio-todos-enlace::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--radio);
}

.servicio-todos-icono {
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  color: var(--acento-claro);
  fill: none;
  stroke: currentColor;
  transition: transform 300ms var(--salida);
}

@media (hover: hover) and (pointer: fine) {
  .servicio-todos:hover {
    transform: translateY(-4px);
    background: var(--superficie-alta);
    border-color: var(--acento-claro);
    box-shadow: var(--sombra-alta);
  }

  .servicio-todos:hover .servicio-todos-icono { transform: translateX(4px); }
}

.servicio-todos:active { scale: 0.985; }

.servicio {
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition:
    transform 300ms var(--salida),
    border-color 300ms ease,
    box-shadow 300ms ease;
}

.servicio-foto {
  position: relative;
  overflow: hidden;
}

.servicio-foto img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
  width: 100%;
  transition: transform 700ms var(--suave);
}

/* A foto morre dentro do card em vez de terminar num corte reto. */
.servicio-foto::after {
  content: '';
  position: absolute;
  inset: auto 0 -1px 0;
  height: 38%;
  pointer-events: none;
  background: linear-gradient(
    to top,
    var(--superficie),
    color-mix(in srgb, var(--superficie), transparent 100%)
  );
}

.servicio-texto {
  padding: 0.35rem 1.2rem 1.35rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  flex: 1;
}

.servicio-texto p {
  color: var(--texto-suave);
  font-size: 0.93rem;
  margin: 0;
}

/* Etiqueta em vidro sobre a foto. Sobrou UM uso desde 17/08/2026: a categoria
   de cada trabalho na galeria, que é o que alimenta o filtro. A dos serviços
   ("Mismo día", "Con cita") saiu junto com os versaletes, a pedido do cliente.

   O fundo a 88% é contra foto branca estourada -- a foto é conteúdo do cliente
   e pode ser clara. Abaixo disso o azul-claro reprova; a 88% dá 4.83:1. */
.etiqueta {
  position: absolute;
  top: 0.7rem;
  right: 0.7rem;
  z-index: 1;
  font-family: var(--display);
  font-size: 0.66rem;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--acento-claro);
  background: color-mix(in srgb, var(--fondo) 88%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid color-mix(in srgb, var(--acento-claro) 26%, transparent);
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
}

@media (hover: hover) and (pointer: fine) {
  .servicio:hover {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--acento-claro) 40%, var(--borde));
    box-shadow: var(--sombra-alta);
  }

  .servicio:hover .servicio-foto img { transform: scale(1.045); }
}

/* ---------- trabajos ---------- */

.trabajos { border-top: 1px solid var(--borde); }

/* O FILTRO DA GALERIA SAIU em 17/08/2026, a pedido -- a galeria mostra sempre
   tudo. As regras dele (`.filtros`, `.filtro` e os estados) saíram junto: CSS de
   elemento que não existe é lixo que o próximo leitor tenta entender.

   O `reveal.js` não foi tocado. Ele é byte a byte igual nos três sites e o
   contrato compara; sem `[data-filtros]` no HTML ele simplesmente não monta
   nada. */

.galeria {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  align-items: start;
}

.galeria li { display: flex; flex-direction: column; gap: 0.7rem; }
.galeria li[data-oculto] { display: none; }

.galeria-foto {
  position: relative;
  overflow: hidden;
  border-radius: var(--radio);
}

/* Todas na mesma altura e na mesma linha de base. O `cover` cuida de
   qualquer proporção que o cliente subir depois. */
.galeria-foto img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  transition: transform 700ms var(--suave);
}

@media (hover: hover) and (pointer: fine) {
  .galeria-foto:hover img { transform: scale(1.04); }
}

@media (min-width: 54rem) {
  .galeria { gap: 1.5rem; }
}

.galeria p { color: var(--texto-suave); font-size: 0.92rem; margin: 0; }

.nota-galeria { margin: 2.5rem 0 0; }

.nota-galeria a {
  color: var(--acento-claro);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.nota-galeria a::after {
  content: '↗';
  transition: transform 200ms var(--salida);
}

@media (hover: hover) and (pointer: fine) {
  .nota-galeria a:hover::after { transform: translate(2px, -2px); }
}

/* ---------- ¿por qué elegir Autocar? ---------- */

/* A SEÇÃO CONTINUA SE CHAMANDO `taller` no protocolo (`data-cms-section`) e no
   `id`, e só a CLASSE mudou para `.porque`. O nome da seção indexa
   `styles.order` e `styles.sections` no Firestore, e o `id` é o destino de
   `nav.taller` no cabeçalho e no rodapé -- os dois são compartilhados pelas
   páginas. Classe de CSS não é contrato com nada disso. */

.porque {
  border-top: 1px solid var(--borde);
}

/*
  A NARRATIVA POR ROLAGEM.

  TUDO AQUI DEPENDE DE `[data-narrativa]`, que o `inicio.js` põe na seção. Sem
  JS, no editor e com `prefers-reduced-motion` o atributo não existe e nada
  disto vale: a pista tem altura natural, nada prende e as CINCO razões ficam
  visíveis ao mesmo tempo.

  Isso é requisito e não cortesia. O título e o texto de cada razão são campos
  `data-cms`: escondê-los sem essa saída tiraria quatro campos do alcance do
  painel e quatro blocos do HTML servido, que é a premissa de SEO do site. É a
  mesma regra pela qual o modal de agendamento fica aberto no editor.

  A ALTURA DA PISTA é o único número solto, e é o que regula quanto se rola por
  razão: 420svh dá ~80svh de rolagem para cada uma das cinco. O JS NÃO conhece
  este valor -- ele lê a geometria da própria pista, então mexer aqui muda o
  ritmo sem tocar em código.
*/
/* O respiro vertical da seção não faz sentido preso: `--aire` chega a 7,5rem em
   cada ponta e come 240px do bloco fixo, que é justamente a altura que falta
   numa tela baixa. Quem dá o ar aqui é o `align-content: center`. */
.porque[data-narrativa] {
  padding-block: 0;
}

.porque[data-narrativa] .porque-pista {
  height: 420svh;
}

.porque[data-narrativa] .porque-fixo {
  position: sticky;
  /* Mesma altura do cabeçalho medida na portada. Sem isto o bloco prende por
     baixo da faixa e o título da seção fica escondido atrás dela. */
  top: var(--alto-cabecera, 4.57rem);
  height: calc(100svh - var(--alto-cabecera, 4.57rem));
  display: grid;
  align-content: center;
}

/* TETO DE ALTURA NO MOSTRADOR enquanto preso. Ele tem 444px soltos, e num
   laptop de 700px de altura o bloco fixo sobra ~540 -- com o cabeçalho da seção
   junto, o conjunto estourava e o título saía por cima da faixa. Medido em
   1440x700, que é a tela baixa típica.

   `max-height` num SVG com proporção intrínseca encolhe a largura junto, então
   o desenho não distorce: ele só fica menor onde não cabe. */
.porque[data-narrativa] .medidor {
  max-height: 46svh;
  margin-inline: auto;
}

/* As cinco ocupam o MESMO lugar e só a ativa aparece. `position: absolute`
   dentro de uma lista com altura própria: sem a altura reservada, a caixa
   colapsaria e o bloco fixo saltaria a cada troca. */
.porque[data-narrativa] .razones {
  position: relative;
  min-height: 11rem;
  display: block;
  margin-top: 1.75rem;
}

/* As razões não têm `data-revela` -- ver o comentário no HTML. Enquanto tinham,
   nenhuma regra daqui as alcançava: o `reveal.js` as animava para `opacity: 1`
   com a WAAPI, e animação da WAAPI vence a cascata inteira, qualquer
   especificidade. Não adianta subir seletor; o atributo é que tem de sair. */
.porque[data-narrativa] .razon {
  position: absolute;
  inset: 0 0 auto 0;
  padding-bottom: 0;
  border-bottom: 0;
  opacity: 0;
  /* Só a ativa recebe ponteiro: empilhadas, as outras roubariam o clique de
     qualquer link que venha a existir aqui dentro. */
  pointer-events: none;
  transition: opacity 380ms var(--salida);
}

.porque[data-narrativa] .razon[data-activo] {
  opacity: 1;
  pointer-events: auto;
}

/* O leitor de tela continua lendo as cinco: elas saem por opacidade, não por
   `visibility` nem `display`. Quem rola vê uma de cada vez; quem ouve recebe a
   lista inteira, que é a informação completa e na ordem certa. */

/* Duas colunas: o mostrador e a coluna de texto. A leitura é irmã do SVG e
   posicionada POR CIMA dele, não um `<text>` dentro do SVG: o número tem
   contagem animada e é campo editável, e texto de SVG não reflui nem herda a
   tipografia da página. */
.porque-cuerpo {
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  margin-top: 2.5rem;
}

/* O mostrador é alto e estreito desde o quarto de volta (viewBox 108x200).
   `max-width` pequena de propósito: em 20rem ele teria 740px de altura e
   empurraria a coluna de texto para fora do campo de visão. */
.porque-medidor {
  position: relative;
  width: 100%;
  max-width: 15rem;
  justify-self: center;
}

/*
  EM TELA ESTREITA O MOSTRADOR VAI PARA TRÁS DO TEXTO.

  Empilhado, ele custaria uma tela inteira antes de a primeira razão aparecer --
  444px de instrumento que ninguém rola para ver. Atrás, ele vira o fundo da
  seção e o ponteiro aponta para a razão que está sendo lida, que é a mesma
  informação sem custo de altura nenhum.

  `pointer-events: none` porque ele passa por baixo do texto: sem isso roubaria
  o toque de qualquer coisa que caia por cima. Não há `data-cms` aqui dentro --
  o SVG é `aria-hidden` e gerado --, então pôr o desenho atrás não esconde campo
  nenhum do painel. Fosse imagem editável, isto seria a regra do `z-index: -1`
  que este projeto proíbe.

  A 26% as linhas do arco (38% de branco) chegam ao texto como ~10% -- abaixo do
  fio do `--borde` do site. O texto por cima segue nos mesmos 19:1 de sempre.
*/
@media (max-width: 61.99rem) {
  .porque-cuerpo { position: relative; }

  .porque-medidor {
    position: absolute;
    inset: 0;
    max-width: none;
    display: grid;
    place-items: center;
    opacity: 0.26;
    pointer-events: none;
    z-index: 0;
  }

  .porque-medidor .medidor {
    width: auto;
    height: 100%;
    max-width: 100%;
  }

  .porque-texto {
    position: relative;
    z-index: 1;
  }
}

@media (min-width: 62rem) {
  .porque-cuerpo {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
  }
}

.medidor {
  display: block;
  width: 100%;
  height: auto;
}

.medidor-arco {
  fill: none;
  stroke: color-mix(in srgb, var(--texto) 28%, transparent);
  stroke-width: 1.5;
}

.medidor-marca {
  stroke: color-mix(in srgb, var(--texto) 38%, transparent);
  stroke-width: 1.5;
  stroke-linecap: round;
}

/* As cinco que recebem ícone ganham traço mais longo e a cor de acento: são as
   marcas que o ponteiro percorre, e é o que amarra o desenho às cinco razões.

   75% e não 55%: medido, a 55 elas davam 2,71:1 contra o fundo. Marcação de
   mostrador é decoração e não carrega informação -- quem informa são os ícones
   (4,98:1) e o texto ao lado --, então o piso de 3:1 da WCAG para objeto gráfico
   não a alcança formalmente. Mas passar custa uma linha, e a 75% dá 3,89:1.

   As marcações FINAS ficam onde estão, a 16%: são da mesma família do `--borde`
   do site (10% do texto), que é fio de contorno e não elemento gráfico. Levá-las
   a 3:1 acenderia o mostrador inteiro e mataria a hierarquia entre as vinte e um
   traços e os cinco que importam. */
.medidor-marca.es-fuerte {
  stroke: color-mix(in srgb, var(--acento-claro) 75%, transparent);
  stroke-width: 2.2;
}

.medidor-icono {
  color: var(--acento-claro);
  opacity: 0.5;
  transition: opacity 300ms ease;
}

/* O ícone para onde o ponteiro aponta. É o "exibir sobre cada um": o arco diz
   qual das cinco está sendo lida, e a razão correspondente se realça ao lado. */
.medidor-icono.es-activo { opacity: 1; }

/* Sem JS nenhum ícone é o ativo -- e aí todos ficam legíveis por igual, em vez
   de a faixa inteira ficar apagada esperando um realce que nunca vem. */
.medidor:not([data-seguindo]) .medidor-icono { opacity: 0.85; }

/*
  O ALVO DE CLIQUE de cada ícone: leva a rolagem até a razão dele. Pedido em
  17/08/2026. O círculo é criado pelo `inicio.js`, um por âncora, com raio 16 --
  ~66px na tela, acima dos 44 que um alvo de toque exige (o ícone sozinho dá 39).

  `fill: none` + `pointer-events: all`: área que recebe clique sem pintar nada.
  Só `fill: transparent` deixaria o hit-test na dependência do valor herdado de
  `pointer-events`, que é detalhe que varia entre navegadores.

  SÓ EXISTE COM JS. Sem script o `inicio.js` não roda, nenhum círculo é criado, e
  o mostrador continua sendo o desenho de sempre. E abaixo de 62rem ele nem é
  alcançável: ali o `.porque-medidor` é fundo atrás do texto, com
  `pointer-events: none` -- ver a regra mais abaixo. O clique é atalho de
  desktop, e o caminho de todo mundo continua sendo rolar.

  NÃO HÁ FOCO POR TECLADO AQUI, e é deliberado. O SVG é `aria-hidden`, e
  elemento focável dentro de `aria-hidden` é defeito de acessibilidade: o teclado
  para num alvo que o leitor de tela não anuncia. Como o clique só ADIANTA uma
  leitura que rolar também alcança, nada fica inacessível por não ter foco --
  fosse a única forma de chegar à razão 5, isto seria uma barreira e não um
  atalho.
*/
.medidor-toque {
  fill: none;
  pointer-events: all;
  cursor: pointer;
  transition: fill 200ms ease;
}

/* O realce de hover é o próprio círculo ganhando um disco fraco -- é o que
   mostra o tamanho real do alvo, que é maior que o ícone. A 10% ele não disputa
   com o ícone que fica por baixo. */
@media (hover: hover) and (pointer: fine) {
  .medidor-toque:hover { fill: color-mix(in srgb, var(--acento-claro) 10%, transparent); }
}

.medidor-aguja-haste {
  stroke: var(--texto);
  stroke-width: 2.6;
  stroke-linecap: round;
}

.medidor-aguja-eje {
  fill: var(--fondo);
  stroke: var(--texto);
  stroke-width: 2.6;
}

/*
  O PONTEIRO SOBE UMA VEZ, quando a seção entra na tela.

  Ele descansa na marca de baixo (162°) e assenta na de cima (18°). É a metáfora
  inteira da seção dita sem palavra: o mostrador enche.

  DESDE 17/08/2026 A VARREDURA DESEMBOCA NA RAZÃO 1, e não na 5: a ordem foi
  invertida e o topo do arco passou a ser o começo da leitura. O gesto não mudou
  -- ele continua enchendo de baixo para cima --, mudou onde ele para. Que os
  dois coincidam é o que faz a narrativa começar sem o ponteiro pular.

  Quem dispara é o `.visivel` que o `reveal.js` já põe em `[data-revela]`. Não
  há JS novo: o gancho é o mesmo que anima todo o resto da página, e sem script
  a classe nunca chega -- o ponteiro fica parado no fim do curso, que é o estado
  correto e não um mostrador vazio.

  `transform-box: view-box` porque o padrão (`fill-box`) tomaria a caixa do
  próprio traço como referência, e o pivô sairia do eixo desenhado.
*/
.medidor-aguja {
  transform-box: view-box;
  transform-origin: 16px 100px;
  /* O curso vai de 162° (a marca de baixo) a 18° (a de cima). Sem JS vale este
     valor: mostrador CHEIO, não vazio -- vazio permanente leria como defeito. */
  rotate: 18deg;
}

/* Com JS o ponteiro nasce no comeco do curso e espera a secao aparecer. Sem JS
   `html` nunca recebe `data-anima`, entao vale a regra acima e ele ja fica no
   fim -- mostrador cheio, que e o estado correto, e nao um vazio permanente. */
/* `:not([data-seguindo])` AQUI TAMBEM, e não só na regra do `.visivel`.

   Esta é (0,4,0) e a que dá o ponteiro à rolagem (`.medidor[data-seguindo]`) é
   (0,2,0): sem a exclusão, esta vence sempre e o ponteiro fica cravado em 162°
   enquanto o ícone aceso muda ao lado. O defeito só aparece com o `reveal.js`
   carregado — é ele que põe `data-anima` na raiz —, então uma página de prova
   sem ele passa e o site real não. */
html[data-anima] .medidor[data-revela]:not([data-seguindo]) .medidor-aguja {
  rotate: 162deg;
}

/* `:not([data-seguindo])` porque as duas regras mandariam na MESMA propriedade:
   a varredura de uma vez so vale enquanto a rolagem nao assumiu o ponteiro. */
html[data-anima] .medidor[data-revela].visivel:not([data-seguindo]) .medidor-aguja {
  rotate: 18deg;
  transition: rotate 1400ms var(--salida) 120ms;
}

/*
  O PONTEIRO SEGUINDO A ROLAGEM. O `inicio.js` escreve `--aguja` com a rotacao
  da marca correspondente à razão que está no meio da tela.

  700ms é mais lento que um scroll rápido de propósito: o ponteiro chega
  atrasado e assenta, como agulha de painel de verdade. Instantâneo ele lê como
  um valor sendo trocado, não como um instrumento respondendo.
*/
.medidor[data-seguindo] .medidor-aguja {
  /* O padrão é 18° -- a razão 1, que desde 17/08/2026 é a de CIMA. Ele só vale
     no intervalo entre `data-seguindo` entrar e o primeiro `medir()` escrever
     `--aguja`; com o valor antigo (162°) o ponteiro daria um pulo do fim ao
     começo do curso nesse instante. */
  rotate: var(--aguja, 18deg);
  transition: rotate 700ms var(--salida);
}

/* A LEITURA SAIU em 17/08/2026, a pedido: eram os dois números do taller no
   miolo do arco. As regras dela (`.porque-leitura`, `.medidor-valor`,
   `.medidor-rotulo`, `.medidor-pie`) saíram junto -- CSS de elemento que não
   existe é lixo que o próximo leitor tenta entender.

   `.porque-medidor` mantém o `position: relative`: não há mais nada absoluto
   dentro dele hoje, mas o invólucro continua sendo o que limita a largura do
   mostrador, e é onde uma sobreposição futura iria. */

/* As cinco razões: linha com ícone à esquerda e texto à direita, empilhadas.
   Grade de cartões seria a terceira grade de cartões da mesma página (serviços,
   trabalhos, e esta) -- e o que diferencia estas cinco é que se leem em
   sequência, como uma lista de motivos, não em paralelo como um catálogo. */
.razones {
  list-style: none;
  margin: 1.75rem 0 0;
  padding: 0;
  display: grid;
  gap: 1.35rem;
}

.razon {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: auto auto;
  column-gap: 0.9rem;
  row-gap: 0.3rem;
  padding-bottom: 1.35rem;
  border-bottom: 1px solid var(--borde);
}

.razon:last-child { padding-bottom: 0; border-bottom: 0; }

.razon-icono {
  grid-row: 1 / 3;
  width: 1.55rem;
  height: 1.55rem;
  flex: none;
  margin-top: 0.15rem;
  color: var(--acento-claro);
  fill: none;
  stroke: currentColor;
  /* 70% dá 3,74:1 -- acima do piso de 3:1 para elemento gráfico mesmo quando
     não é o ativo. O realce é ganho de ênfase, nunca perda de legibilidade:
     quem não está no meio da tela continua perfeitamente visível. */
  opacity: 0.7;
  transition: opacity 300ms ease, scale 300ms var(--salida);
}

/*
  A RAZÃO ATIVA. O título vai para o acento (6,53:1) e o ícone acende.

  O TEXTO DAS OUTRAS NÃO É APAGADO, e isso é deliberado: escurecer o que não
  está ativo é o reflexo fácil e transforma um realce em perda de contraste --
  quatro parágrafos ilegíveis para destacar um. Aqui só o ativo GANHA; nenhum
  perde.
*/
.razon[data-activo] .razon-icono { opacity: 1; scale: 1.08; }

.razon h3 { transition: color 300ms ease; }
.razon[data-activo] h3 { color: var(--acento-claro); }

.razon h3 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--texto);
  letter-spacing: -0.01em;
}

.razon p {
  margin: 0;
  color: var(--texto-suave);
  font-size: 0.95rem;
  max-width: 46ch;
}

.prosa { max-width: 58ch; color: var(--texto-suave); margin-top: 1.4rem; }
.prosa p:first-child { color: var(--texto); font-size: 1.12rem; }

.datos {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1.5rem;
  margin: 2.25rem 0 0;
  padding-top: 1.75rem;
  border-top: 1px solid var(--borde);
}

.datos > div { display: flex; flex-direction: column; gap: 0.4rem; }
.datos dt { font-size: 0.82rem; color: var(--texto-suave); order: 2; line-height: 1.3; }

.datos dd {
  margin: 0;
  font-family: var(--display);
  font-size: clamp(2rem, 4vw, 2.6rem);
  font-weight: 800;
  color: var(--acento-claro);
  line-height: 1;
  letter-spacing: -0.03em;
  /* Obrigatório aqui: o número conta de zero até o valor, e sem largura fixa
     por dígito o rótulo abaixo dele dança durante a contagem. */
  font-variant-numeric: tabular-nums;
}

/* ---------- visítanos: foto do local + dados ---------- */

/* A foto e os três dados ficam abaixo do mapa, na mesma seção. O cliente pediu
   "mapa, foto de fachada, horarios y contacto" como um bloco só -- e ler o
   endereço a uma rolagem do mapa que o mostra é ler duas vezes a mesma coisa. */
.visita {
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
  margin-top: 2.5rem;
}

@media (min-width: 52rem) {
  .visita { grid-template-columns: 1.1fr 0.9fr; align-items: center; }
}

.visita-figura {
  margin: 0;
  position: relative;
}

.visita-figura img {
  border-radius: var(--radio);
  width: 100%;
  aspect-ratio: 11 / 8;
  object-fit: cover;
  box-shadow: var(--sombra-alta);
}

/* Moldura deslocada atrás da foto: profundidade sem sombra genérica. */
.visita-figura::before {
  content: '';
  position: absolute;
  inset: 1.5rem -1.5rem -1.5rem 1.5rem;
  z-index: -1;
  border: 1px solid color-mix(in srgb, var(--acento-claro) 32%, transparent);
  border-radius: var(--radio);
}

@media (max-width: 51.99rem) {
  .visita-figura::before { inset: 1rem -1rem -1rem 1rem; }
}

.visita-datos {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 1.1rem;
}

.visita-dato {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  color: var(--texto-suave);
  font-size: 0.98rem;
  line-height: 1.5;
}

.visita-dato .icono {
  color: var(--acento-claro);
  margin-top: 0.22em;
}

.visita-dato a {
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--texto-suave) 40%, transparent);
  transition: color 160ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .visita-dato a:hover { color: var(--texto); }
}

/* ---------- opiniones ---------- */

.opiniones { border-top: 1px solid var(--borde); }

.opiniones-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 1.25rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

@media (min-width: 54rem) {
  .opiniones-lista { gap: 1.5rem; }
}

.opinion {
  position: relative;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  padding: 2.6rem 1.5rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  transition: border-color 300ms ease, transform 300ms var(--salida);
}

/* Aspas como marca d'água, não como decoração de citação. Fica atrás do
   texto, no canto, e some do leitor de tela. */
.opinion::before {
  content: '\201C';
  position: absolute;
  top: 0.35rem;
  left: 1.1rem;
  font-family: var(--display);
  font-size: 4rem;
  font-weight: 800;
  line-height: 1;
  color: color-mix(in srgb, var(--acento-claro) 22%, transparent);
  pointer-events: none;
}

.opinion blockquote {
  margin: 0;
  flex: 1;
  font-size: 1.02rem;
  line-height: 1.55;
  color: var(--texto);
}

.opinion-firma {
  border-top: 1px solid var(--borde);
  padding-top: 1rem;
}

.opinion-autor {
  margin: 0;
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
}

.opinion-detalle {
  margin: 0.25rem 0 0;
  font-size: 0.84rem;
  color: var(--texto-tenue);
}

@media (hover: hover) and (pointer: fine) {
  .opinion:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--acento-claro) 34%, var(--borde));
  }
}

/* ---------- contacto ---------- */

.contacto { border-top: 1px solid var(--borde); }

/* O telefone e o horario sairam desta secao (estao no rodape), e com eles o
   `.contacto-datos` e os `.dato-*`. Regra de CSS orfa e pior que codigo morto:
   ela parece que ainda desenha algo, e o proximo redesenho a copia. */

/*
  O MAPA E UM IFRAME DO GOOGLE desde 05/08/2026, a pedido do cliente, escuro por
  `filter`. Substituiu o PNG do OpenStreetMap. O motivo esta no comentario do
  index.html; o resumo e que o raster padrao do OSM desenha toda rua nomeada e o
  estilo do Google filtra por importancia.

  O COMANDO QUE GERAVA O PNG fica gravado, e nao apagado com ele -- e a regra do
  projeto, e aqui ela e a rota de volta:

    node scripts/gerar-mapa.mjs autocar \
      --lat -16.393183 --lon -71.543927 \
      --zoom 17 --colunas 4 --linhas 2 --tema escuro

  ESTES NUMEROS SAO OS DO BRIEFING (§ Mapa), e a primeira versao deste comentario
  trazia OUTROS -- eu escrevi de cabeca `-16.3907 / -71.5537 / zoom 16` ao remover o
  PNG. Comando gravado que nao reproduz a imagem e pior que comando nenhum: ele
  parece a receita e nao e. Se voltar a precisar do PNG, use o de cima.

  O ponto e o do LOGRADOURO, verificado por Nominatim e conferido por reverse-geocode
  -- o OSM nao conhece o nº 204 da Av. Emmel (o unico numero mapeado no trecho e o
  128). Por isso o alfinete era um HALO e nao uma ponta: halo diz "por aqui", ponta
  diz "exatamente aqui", e a segunda seria afirmacao que ninguem checou. A coordenada
  exata segue como pendencia no briefing.

  DUAS COISAS QUE O FILTRO EXIGE, e as duas sao a licao que este site ja pagou com
  os oito tiles montados por grid: camada filtrada dentro de um pai com `transform`
  E `overflow: hidden` aparece com pedacos faltando no Safari, e no Chrome nunca.

  - `overflow: hidden` nao esta aqui. O arredondamento e do proprio iframe.
  - `data-revela` nao esta no involucro (ver o HTML). Era ele que punha `transform`.

  `invert(92%)` e nao 100%: inverter tudo leva branco puro a preto puro e o mapa
  perde a separacao entre via e quadra. `hue-rotate(180deg)` junto e obrigatorio --
  sem ele o verde vira magenta e a agua vira laranja. E a mesma dupla que o
  `gerar-mapa.mjs` usa nos tiles.

  O FILTRO INVERTE TAMBEM O LOGO E A ATRIBUICAO do Google dentro do quadro. Iframe
  e caixa opaca; nao da para excluir parte do conteudo. Risco aceito, nao resolvido.
*/
.mapa-google {
  margin: 0 0 1.5rem;
  /* 8/3 e o corte de 2/3 sobre o 16/9 que havia aqui: a altura de uma caixa com
     aspect-ratio e largura/razao, entao deixa-la em 2/3 pede a razao crescer 3/2.
     16/9 da altura 0,5625 x largura; 2/3 disso e 0,375, que e 8/3. */
  aspect-ratio: 8 / 3;
  background: var(--superficie);
  border-radius: var(--radio);
}

.mapa-google iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  filter: invert(92%) hue-rotate(180deg);
}

/* Em tela estreita 16/9 vira uma faixa baixa demais para se achar num mapa. O
   valor TEM de casar com o `ESTREITO` do mapa.js, que troca o zoom na mesma
   largura: se um mudar sozinho, o zoom passa a trocar antes ou depois da forma. */
@media (max-width: 620px) {
  .mapa-google { aspect-ratio: 2 / 1; }
}

/*
  A FICHA abaixo do mapa. Ela guarda os tres campos que a caixa sobre o PNG
  carregava (`contacto.mapa.link`, `contacto.direccion`, `contacto.mapa.texto`), e
  e por ela existir que a troca do mapa nao orfanou chave nenhuma -- o que importa
  neste site, que TEM historico de edicao pelo painel.
*/
.mapa-ficha {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1rem;
  padding: 0.9rem 1.1rem;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  text-decoration: none;
  color: var(--texto);
}

.mapa-ficha strong { font-weight: 600; }

.mapa-ir {
  color: var(--acento-claro);
  font-size: 0.9rem;
  margin-left: auto;
}

@media (hover: hover) and (pointer: fine) {
  /* A ficha abaixo do mapa e o que recebe hover agora -- o `<a class="mapa">`
     que envolvia o PNG inteiro deixou de existir. */
  .mapa-ficha:hover { border-color: var(--borde-forte); }
  .mapa-ficha:hover .mapa-ir { color: var(--texto); }
}

/* ---------- llamado ---------- */

/* Faixa de largura cheia, um pedido só. Escapa da caixa de 74rem como a
   portada, e é o último bloco antes do rodapé -- o lugar onde quem leu tudo
   ainda não escreveu. */
.llamado {
  max-width: none;
  padding: 0;
  border-top: 1px solid var(--borde);
  background: var(--superficie);
  position: relative;
  overflow: hidden;
}

.llamado::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(40rem 26rem at 78% -20%,
      color-mix(in srgb, var(--acento) 26%, transparent), transparent 70%),
    radial-gradient(30rem 22rem at 8% 120%,
      color-mix(in srgb, var(--acento-claro) 10%, transparent), transparent 68%);
}

.llamado-caja {
  position: relative;
  max-width: var(--ancho);
  margin: 0 auto;
  padding: clamp(3rem, 7vw, 5.5rem) var(--aire-lateral);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.llamado-caja h2 { max-width: 24ch; }

.llamado-caja p {
  color: var(--texto-suave);
  max-width: 46ch;
  margin: 1.25rem 0 0;
}

.llamado-caja .boton { margin-top: 2.25rem; }

/* ============================================================
   Página /servicios.

   Ela NÃO tem portada com foto. As cinco fotos dos serviços entram
   logo abaixo do título, e uma sexta imagem em cima delas seria
   repetição sem informação -- além de mais uma foto de banco.

   O título é o <h1> desta página. O `auditSeo` do exportador cobra
   exatamente um <h1> por página, então os nomes dos serviços aqui
   são <h2> (na home os mesmos campos são <h3>, porque lá o <h1> é
   o da portada). Mesmo campo, nível diferente: a hierarquia é da
   página, o conteúdo é do site.
   ============================================================ */

.catalogo { border-top: 1px solid var(--borde); }

.catalogo-portada {
  max-width: 44rem;
  margin-bottom: clamp(3rem, 6vw, 4.5rem);
}

.catalogo-bajada {
  color: var(--texto-suave);
  font-size: 1.12rem;
  max-width: 52ch;
  margin: 1.5rem 0 0;
}

.catalogo-lista {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: clamp(3rem, 6vw, 5rem);
}

/* Uma fileira por serviço, foto e texto lado a lado, alternando o lado a cada
   item. É o oposto da grade da home de propósito: lá cinco cartões iguais dão o
   panorama, aqui cada serviço tem a página inteira de largura para si.

   O corte é em 54rem, o mesmo da grade da home -- abaixo disso a foto fica em
   cima e o texto embaixo, e a alternância não existe. */
.catalogo-item {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  align-items: center;
}

@media (min-width: 54rem) {
  .catalogo-item { grid-template-columns: 1.1fr 0.9fr; }

  /* Só a figura troca de coluna. Mover os dois com `direction` ou `order` no
     contêiner inverteria também a ordem de leitura, e a ordem do documento é o
     que o leitor de tela percorre -- foto antes do nome do serviço em metade
     dos itens e nome antes da foto na outra metade seria leitura embaralhada. */
  .catalogo-item:nth-child(even) .catalogo-figura { grid-column: 2; grid-row: 1; }
  .catalogo-item:nth-child(even) .catalogo-texto { grid-column: 1; grid-row: 1; }
}

.catalogo-figura {
  margin: 0;
  position: relative;
  overflow: hidden;
  border-radius: var(--radio);
  border: 1px solid var(--borde);
  background: var(--superficie);
}

.catalogo-figura img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  transition: transform 700ms var(--suave);
}

@media (hover: hover) and (pointer: fine) {
  .catalogo-item:hover .catalogo-figura img { transform: scale(1.035); }
}

.catalogo-texto {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.85rem;
}

.catalogo-texto p {
  color: var(--texto-suave);
  font-size: 1.02rem;
  max-width: 42ch;
  margin: 0;
}

/* A etiqueta da home é `position: absolute` sobre a foto. Aqui ela é um chip no
   fluxo, acima do nome -- por isso precisa desfazer o posicionamento. Mesma
   chave, mesmo texto, outro lugar na página. */
.etiqueta-suelta {
  position: static;
  top: auto;
  right: auto;
  backdrop-filter: none;
}

/* ---------- proceso ---------- */

.proceso { border-top: 1px solid var(--borde); }

/* Esta seção é centrada, ao contrário das outras (título à esquerda, subtítulo
   à direita). É a forma da referência que o cliente mandou, e ela cabe aqui
   por um motivo estrutural e não só estético: o que vem abaixo é uma linha do
   tempo simétrica de cinco colunas, e um cabeçalho alinhado à esquerda sobre
   uma figura centrada desalinha a seção inteira.

   Especificidade 0,2,0 -- ganha da regra de duas colunas dentro do `@media`
   sem depender da ordem em que os dois aparecem no arquivo. */
.cabecera-seccion.centrada {
  grid-template-columns: 1fr;
  justify-items: center;
  text-align: center;
  max-width: 48rem;
  margin-inline: auto;
}

.proceso .cabecera-seccion { margin-bottom: 1.25rem; }

.proceso-intro {
  margin: 0 auto 3.5rem;
  max-width: 58ch;
  text-align: center;
  color: var(--texto-suave);
  font-size: 1.05rem;
}

/* LINHA DO TEMPO, não mais cartões. É a forma da referência que o cliente
   mandou: círculo numerado, título e texto centrados embaixo, e um fio ligando
   um círculo ao seguinte.

   O `--raio` e o `--vao` são declarados aqui porque o fio é posicionado a
   partir dos dois. Escritos à mão nos dois lugares, mudar o tamanho do círculo
   deixaria o fio saindo de dentro dele ou com folga -- e isso não quebra nada,
   só fica torto na tela de alguém. */
.pasos {
  --raio: 2.125rem;
  --vao: 1.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: 2.5rem var(--vao);
  /* O contador do CSS numera, e não um campo `numero` no conteúdo: número
     digitado à mão fica errado no instante em que o cliente apaga um passo pelo
     painel, e nada avisaria. */
  counter-reset: paso;
}

@media (min-width: 40rem) {
  .pasos { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 56rem) {
  .pasos { grid-template-columns: repeat(3, 1fr); }
}

/* CINCO colunas só a partir de 72rem. Cada passo fica com ~13rem e os textos do
   cliente têm ~30 palavras, então abaixo disso viravam colunas altas e finas --
   foi por isso que a primeira versão parou em três. O fio horizontal só faz
   sentido com a fileira inteira na tela, e é aqui que ele aparece. */
@media (min-width: 72rem) {
  .pasos { grid-template-columns: repeat(5, 1fr); }
}

.paso {
  counter-increment: paso;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.5rem;
}

/* `decimal-leading-zero` dá 01, 02... e não 1, 2. Dois dígitos mantêm todos os
   círculos com o mesmo peso visual; com um dígito o "1" parece menor que o
   "10" no dia em que a lista crescer. */
.paso::before {
  content: counter(paso, decimal-leading-zero);
  width: calc(var(--raio) * 2);
  height: calc(var(--raio) * 2);
  display: grid;
  place-items: center;
  border: 1px solid color-mix(in srgb, var(--acento-claro) 45%, transparent);
  border-radius: 50%;
  font-family: var(--display);
  font-size: 1.3rem;
  font-weight: 700;
  /* acento-claro dá 6.53:1 no fundo. O acento puro daria 3.83 e reprovaria --
     número decorativo grande continua sendo texto. */
  color: var(--acento-claro);
  margin-bottom: 1rem;
  /* Fundo próprio e acima do fio: o cálculo abaixo faz o fio parar na borda do
     círculo, mas arredondamento de subpixel deixa um fio de cabelo encostando.
     Com fundo, não há como aparecer. */
  background: var(--fondo);
  position: relative;
  z-index: 1;
}

/* O FIO ENTRE UM CÍRCULO E O SEGUINTE.

   Com L = largura do item, V = vão e R = raio: o fio vai da borda direita
   deste círculo (L/2 + R) até a borda esquerda do próximo (L + V + L/2 − R).
   Em `left`/`right` sobre o próprio item isso vira:

     left  = 50% + R
     right = −50% + R − V

   Só existe a partir de 72rem porque só ali os cinco estão na mesma fileira. */
@media (min-width: 72rem) {
  .paso::after {
    content: '';
    position: absolute;
    top: var(--raio);
    left: calc(50% + var(--raio));
    right: calc(-50% + var(--raio) - var(--vao));
    height: 1px;
    background: color-mix(in srgb, var(--acento-claro) 30%, transparent);
  }

  /* O último não liga a nada -- e `5n` cobre o fim de CADA fileira, para o dia
     em que o cliente acrescentar um sexto passo pelo painel: sem isso o quinto
     esticaria um fio para a direita apontando para o vazio. */
  .paso:last-child::after,
  .paso:nth-child(5n)::after { display: none; }
}

.paso h3 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--texto);
  letter-spacing: -0.01em;
}

.paso p {
  color: var(--texto-suave);
  font-size: 0.93rem;
  margin: 0;
}

/* A chamada ao fim do processo, como na referência. Reusa
   `contacto.whatsapp.*` -- a MESMA chave da faixa final, com o mesmo texto.
   Chave nova aqui daria dois campos dizendo a mesma coisa, e eles divergiriam
   na primeira correção do cliente. */
.proceso-cta {
  margin: 3.5rem 0 0;
  display: flex;
  justify-content: center;
}

/* ---------- marcas con las que trabajamos ---------- */

.marcas {
  border-top: 1px solid var(--borde);
  /* `clip` e não `hidden`: hidden criaria um contêiner de rolagem e mataria o
     `position: sticky` do cabeçalho -- a mesma razão já escrita na regra do
     `main`. */
  overflow-x: clip;
}

/* A PISTA: recorta, sangra até a borda da seção e esmaece as pontas.

   A sangria é `margin-inline` negativo do respiro da seção e NÃO `width: 100vw`.
   A versão com 100vw foi escrita e medida: dá scroll lateral na página inteira,
   porque `100vw` conta a largura da barra de rolagem, que não é área visível. */
.marcas-pista {
  position: relative;
  margin: 2.5rem calc(-1 * var(--aire-lateral)) 0;
  /* Sem JS a fileira rola na mão -- é o estado que existia antes do auto-scroll,
     e continua sendo o certo para quem não executa script. */
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
}

.marcas-pista::-webkit-scrollbar { display: none; }

/* Com a esteira ligada não há o que rolar na mão: quem move é a animação. */
.marcas-pista[data-rolando] { overflow-x: hidden; }

/*
  O ESMAECIDO DAS PONTAS É UMA MÁSCARA DE ALFA, desde 17/08/2026.

  Antes eram dois véus `::before`/`::after` pintados em `var(--fondo)` -- eles
  não apagavam nada, COBRIAM com a cor do fundo. Funciona só enquanto o que está
  atrás é exatamente aquela cor: qualquer superfície diferente por baixo (uma
  faixa, um gradiente, o tema do cliente mudando `--fondo` pelo painel) e o véu
  vira um retângulo visível de cor errada em cima da fileira.

  A máscara não sabe cor nenhuma. Ela apaga por alfa, então serve sobre qualquer
  fundo e acompanha o tema de graça.

  POR QUE NÃO ERA ASSIM ANTES: `mask-image` cria um contexto de empilhamento, e
  `arb.svg` e `old-man-emu.jpg` dependiam de `mix-blend-mode: screen` para
  descartar o preto que sobrava da inversão. Dentro do contexto novo, o `screen`
  passa a compor contra o vazio do grupo em vez do fundo da página, e a caixa
  escura que o blend existia para sumir voltava.

  O QUE DESTRAVOU FOI O ARQUIVO, e não uma descoberta de CSS. Os dois logos
  foram regravados com alfa (o resultado daquele filtro assado no próprio
  arquivo), o `mix-blend-mode` saiu junto com a exceção que existia para eles, e
  aí a máscara passou a ser possível. É o "conserto de verdade" que o comentário
  da exceção já apontava.

  `-webkit-` junto: o não prefixado só chegou ao Safari 15.4, e sem ele o
  esmaecido simplesmente não acontece -- a fileira corta seca na borda.
*/
.marcas-pista {
  --esmaece: clamp(2.5rem, 9vw, 8rem);
  -webkit-mask-image: linear-gradient(to right,
    transparent, #000 var(--esmaece),
    #000 calc(100% - var(--esmaece)), transparent);
  mask-image: linear-gradient(to right,
    transparent, #000 var(--esmaece),
    #000 calc(100% - var(--esmaece)), transparent);
}

/* O TRILHO é o que anda. `width: max-content` para ele medir o conteúdo inteiro
   em vez do pai, senão a cópia não caberia e não haveria o que deslizar. */
.marcas-trilho {
  display: flex;
  width: max-content;
  padding-inline-start: var(--aire-lateral);
}

/*
  A ESTEIRA.

  `--desloca` é escrito pelo `inicio.js` com a largura MEDIDA de uma lista, e
  não `-50%`: o trilho tem um recuo inicial (`padding-inline-start`), então
  metade dele não é exatamente uma lista e o laço daria um salto a cada volta.
  `--duracao` também vem de lá, derivada da largura -- assim a velocidade fica
  constante quando o cliente acrescentar ou tirar marcas pelo painel. Com um
  número fixo, quinze logos e trinta andariam a velocidades diferentes.

  Só roda com `[data-rolando]`, que o JS só põe fora do editor e fora de
  `prefers-reduced-motion`. O `html:not([data-sitekit-editando])` é o cinto de
  segurança para o caso de o atributo do editor chegar depois -- ele chega, o
  handshake fecha depois deste script.
*/
@media (prefers-reduced-motion: no-preference) {
  html:not([data-sitekit-editando]) .marcas-pista[data-rolando] .marcas-trilho {
    animation: marcas-desliza var(--duracao, 40s) linear infinite;
  }
}

@keyframes marcas-desliza {
  to { transform: translateX(calc(-1 * var(--desloca, 0px))); }
}

/* A cópia sai de cena no editor. O atributo do editor chega DEPOIS deste
   script (o handshake fecha depois), então a cópia já existe quando ele
   aparece: sem esta regra o cliente veria trinta logos parados e acharia que
   editou errado. Ela não tem `data-cms` nenhum, então esconder não tira campo
   do painel -- é a única coisa escondida por CSS neste site que pode ser
   escondida com segurança, justamente por ser invisível ao protocolo. */
html[data-sitekit-editando] .marcas-copia { display: none; }

/* Parar sob o ponteiro é o que deixa alguém LER a marca que passou. Sem isso a
   esteira é decoração que nega a própria informação. */
@media (hover: hover) and (pointer: fine) {
  .marcas-pista:hover .marcas-trilho { animation-play-state: paused; }
}

.marcas-lista {
  list-style: none;
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--vao-marcas, clamp(2.25rem, 4.5vw, 4rem));
  /* O vão à direita separa esta lista da cópia. Fica como padding e não como
     `gap` do trilho para `--desloca` continuar sendo exatamente a largura de uma
     lista -- com gap no trilho, o laço saltaria meio vão a cada volta. */
  padding: 1rem var(--vao-marcas, clamp(2.25rem, 4.5vw, 4rem)) 1.25rem 0;
}

.marca-item {
  flex: none;
  /* OBRIGATÓRIO, e o motivo não é estilo nenhum: o nome da marca é um
     `oculto-visual`, que é `position: absolute`. Sem um ancestral posicionado
     ele se resolve contra o documento e ESCAPA do contêiner de rolagem -- os
     quinze spans ficam espalhados e esticam a página. Medido: a home rolava
     500px de lado, a /servicios (sem esta fileira) não rolava nada.
     `overflow-x: clip` na seção não pega, porque elemento absoluto só é clipado
     por quem é o bloco que o contém. */
  position: relative;
}

/* TODOS BRANCOS. `brightness(0)` achata qualquer cor para preto e `invert(1)`
   devolve tudo em branco -- funciona igual para logo colorido, escuro ou claro,
   e não depende de saber a cor de cada arquivo.

   `max-width: none` porque o reset global põe `max-width: 100%`: numa fileira
   que rola não há "100%" que faça sentido, e ele espremeria os logos largos
   (o Sonax é 4,65:1). Aqui manda a altura e a largura acompanha. */
.marca-item img {
  height: clamp(1.6rem, 2.4vw, 2rem);
  width: auto;
  max-width: none;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: 0.66;
  transition: opacity 200ms ease;
}

/*
  AS DUAS EXCEÇÕES SAÍRAM em 17/08/2026, e o que as tirou foi o arquivo.

  `arb.svg` e `old-man-emu.jpg` eram logos de RECORTE: o nome vazado dentro de
  uma forma cheia (a placa vermelha da ARB, o campo branco da Old Man Emu).
  Achatar isso com `brightness(0) invert(1)` funde o vazado com a forma e sobra
  uma mancha branca. Eles rodavam com outra cadeia --
  `invert(1) grayscale(1) brightness(3)` mais `mix-blend-mode: screen`, onde o
  blend descartava o preto que sobrava da inversão.

  O BLEND ERA O QUE IMPEDIA A MÁSCARA DE ALFA nas pontas da fileira (ver a regra
  da `.marcas-pista`). Os dois arquivos foram regravados COM ALFA -- o resultado
  daquela cadeia assado no próprio arquivo, opaco onde ela deixava branco e
  transparente onde o `screen` descartava. Agora os quinze passam pelo mesmo
  `brightness(0) invert(1)` e não há exceção nenhuma aqui.

  A REGRA QUE ISSO DEIXA: logo de recorte precisa de alfa NO ARQUIVO. Tratamento
  por blend resolve o logo e cobra o preço num lugar que ninguém liga ao logo --
  aqui, a impossibilidade de esmaecer a fileira sem pintar a cor do fundo.

  Continua pendente no BRIEFING pedir à ARB e à Old Man Emu a versão
  monocromática oficial: o que está aqui reproduz o que a tela já mostrava, não
  um arquivo aprovado pela marca.
*/

@media (hover: hover) and (pointer: fine) {
  .marca-item:hover img { opacity: 1; }
}

/* ---------- preguntas frecuentes ---------- */

.preguntas { border-top: 1px solid var(--borde); }

.preguntas-lista {
  list-style: none;
  margin: 2.5rem 0 0;
  padding: 0;
  max-width: 52rem;
}

.pregunta {
  border-bottom: 1px solid var(--borde);
}

.preguntas-lista > li:first-child .pregunta { border-top: 1px solid var(--borde); }

.pregunta summary {
  /* `list-style: none` mais o `::-webkit-details-marker` cobrem os dois motores:
     o triângulo nativo aparece de um jeito no Safari e de outro no Chrome, e
     nenhum dos dois combina com o resto. */
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.15rem 0;
  font-family: var(--display);
  font-size: 1.02rem;
  font-weight: 600;
  color: var(--texto);
  transition: color 160ms ease;
}

.pregunta summary::-webkit-details-marker { display: none; }

.pregunta summary::after {
  content: '';
  flex: none;
  width: 0.62rem;
  height: 0.62rem;
  border-right: 2px solid var(--acento-claro);
  border-bottom: 2px solid var(--acento-claro);
  transform: rotate(45deg) translate(-0.1rem, -0.1rem);
  transition: transform 200ms var(--salida);
}

.pregunta[open] summary::after {
  transform: rotate(225deg) translate(-0.16rem, -0.16rem);
}

.pregunta summary:focus-visible {
  outline: 2px solid var(--acento-claro);
  outline-offset: 3px;
  border-radius: var(--radio-interno);
}

/*
  A RESPOSTA ABRE E FECHA POR `grid-template-rows: 0fr -> 1fr`, a mesma técnica
  do acordeão de serviços do modal e da bajada do cabeçalho. `display` não
  interpola e altura em pixel exigiria medir o conteúdo no JS.

  NADA DE PADDING VERTICAL NEM BORDA DENTRO DA CAIXA QUE COLAPSA. `min-height:
  0` zera a altura do conteúdo, mas padding e borda continuam somando -- foi
  assim que o detalhe fechado do modal media 15px em vez de 0. O espaço vertical
  vem da margem do `<p>` (que o `overflow: hidden` recorta junto) e o divisor
  mora na `.pregunta`, fora do que colapsa.

  `visibility` FECHA O OUTRO LADO: com altura zero o texto continuaria na árvore
  de acessibilidade e o leitor de tela leria as seis respostas. Ela precisa de
  atraso ao FECHAR e de nenhum ao abrir -- `visibility` não interpola, e sem o
  atraso ela vira `hidden` no meio da animação e o texto pisca fora antes de a
  caixa subir.

  QUEM SEGURA O `open` DURANTE O FECHAMENTO é o `inicio.js`: `<details>` fechado
  não renderiza o conteúdo, então sem isso não haveria de onde animar. Sem JS o
  `<details>` abre e fecha na hora, que é o comportamento nativo e continua
  correto -- por isso a marcação segue sendo `<details>` e não botão com
  `aria-expanded`.
*/
.pregunta-caja {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  visibility: hidden;
  transition: grid-template-rows 240ms var(--salida), visibility 0s linear 240ms;
}

.pregunta[data-aberto] .pregunta-caja {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 240ms var(--salida), visibility 0s;
}

/*
  O INVÓLUCRO INTERNO NÃO É REDUNDANTE, e sem ele a caixa fechada mede 21px em
  vez de 0 -- medido.

  `min-height: 0` deixa o item da grade encolher, mas a MARGEM dele continua
  contando no dimensionamento da faixa: a resposta tem `margin-bottom: 1.3rem`,
  que é exatamente os 21px que sobravam. Seis perguntas fechadas deixavam 126px
  de vão morto na seção.

  Com o corpo no meio, a margem do `<p>` passa a viver DENTRO de algo que o
  `overflow: hidden` recorta junto, e a faixa vai a zero de verdade. É a mesma
  razão pela qual não há padding vertical nem borda aqui dentro.
*/
.pregunta-caja > * { min-height: 0; }

.pregunta-corpo { overflow: hidden; }

.pregunta p {
  margin: 0 0 1.3rem;
  padding-right: 2.5rem;
  color: var(--texto-suave);
  font-size: 0.98rem;
  max-width: 68ch;
}

@media (hover: hover) and (pointer: fine) {
  .pregunta summary:hover { color: var(--acento-claro); }
}

/* No editor todas ficam abertas. `<details>` fechado não renderiza o conteúdo,
   e cada resposta é um campo `data-cms` -- fechadas, seis textos existiriam na
   página sem como ser clicados no painel. O `open` em si é posto pelo
   `inicio.js`, porque atributo não se escreve por CSS; estas duas linhas
   garantem que a caixa não fique colapsada quando ele o faz. */
html[data-sitekit-editando] .pregunta-caja {
  grid-template-rows: 1fr;
  visibility: visible;
}

/* ============================================================
   Agendamento.

   O tamanho e travado pela tela MAIS ALTA -- a grade de horas,
   com 21 cupos em 4 colunas. As outras tres sobram espaco, e isso
   e melhor que o contrario: caixa que muda de altura a cada passo
   faz o botao "Continuar" fugir de baixo do cursor, e a pagina
   atras salta junto.

   Em telas curtas o dialogo passa a ocupar tudo e rola por dentro.
   Vinte e um horarios nao cabem em 375x812 sem rolar, e fingir que
   cabem daria fonte de 9px.
   ============================================================ */

.agenda {
  border: 0;
  padding: 0;
  background: transparent;
  max-width: none;
  max-height: none;
  width: 100%;
  height: 100%;
  color: var(--texto);
}

.agenda::backdrop {
  background: color-mix(in srgb, #000 72%, transparent);
  backdrop-filter: blur(3px);
}

/* A página não rola atrás do modal.
   `showModal()` deixa o resto inerte para clique, mas não para a roda do mouse
   nem para o dedo: sem isto o diálogo fica parado sobre uma página que anda.

   O `padding-right` devolve a largura que a barra de rolagem ocupava — sem ele a
   página salta ~15px para o lado no instante em que o modal abre, nas máquinas de
   barra fixa. O valor é medido pelo agenda.js, porque só ele sabe quanto a barra
   mede nesta máquina; o `0px` do fallback é o macOS, onde a barra é sobreposta e
   não ocupa largura nenhuma.

   Os `position: fixed` do html (o brilho e o grão) não se mexem: `inset: 0` é
   relativo à viewport, não à caixa de conteúdo. */
html[data-agenda-abierto] {
  overflow: hidden;
  padding-right: var(--barra-scroll, 0px);
}

.agenda-caja {
  width: min(26rem, 100%);
  /* Trava as quatro na altura da MAIS ALTA. Um valor menor faria tres caberem
     e a quarta crescer, que e justamente o salto que travar a altura existe
     para evitar -- o "Continuar" fugiria de baixo do cursor. O numero e medido,
     nao estimado: veja o teste de alturas no relatorio. */
  min-height: min(40.75rem, 100%);
  margin: auto;
  display: flex;
  flex-direction: column;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: 14px;
  box-shadow: var(--sombra-alta);
  overflow: hidden;
}

/* O <dialog> centraliza pelo margin auto da caixa; para isso ele precisa ser
   um flex container ocupando a viewport. */
.agenda[open] {
  display: flex;
}

.agenda-topo {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.1rem 1.15rem 0;
}

.agenda-topo h2 {
  flex: 1;
  font-size: 0.98rem;
  letter-spacing: 0.06em;
  line-height: 1.2;
}

.agenda-fechar {
  flex: none;
  width: 2rem;
  height: 2rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  border-radius: 8px;
  color: var(--texto-suave);
  cursor: pointer;
  transition: color 160ms ease, background 160ms ease;
}

.agenda-fechar svg { width: 1.1rem; height: 1.1rem; }

@media (hover: hover) and (pointer: fine) {
  .agenda-fechar:hover { color: var(--texto); background: var(--superficie-alta); }
}

/* ---------- passos ---------- */

.agenda-passos {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
  margin: 0.9rem 0 0;
  padding: 0 1.15rem;
}

.agenda-passos li {
  border-bottom: 2px solid var(--borde);
  transition: border-color 200ms ease;
}

/* Cada passo é um botão: dá para voltar a um já preenchido tocando nele. O botão
   ocupa a célula inteira porque o alvo de toque é o passo, não a palavra — em
   375px "Hora" tem 30px de largura, e o resto da célula responderia a nada. */
.agenda-passos button {
  width: 100%;
  font-family: var(--display);
  font-size: 0.72rem;
  font-weight: 600;
  text-align: center;
  color: var(--texto-tenue);
  background: transparent;
  border: 0;
  padding: 0.15rem 0.1rem 0.5rem;
  cursor: pointer;
  transition: color 200ms ease;
}

.agenda-passos li[data-ativo] {
  border-bottom-color: var(--acento-claro);
}

.agenda-passos li[data-ativo] button { color: var(--texto); }

/* Passo ainda inalcançável. Continua clicável de propósito — o clique diz o que
   falta — então o cursor segue sendo de mão: `not-allowed` prometeria que nada
   acontece, e algo acontece. */
.agenda-passos button[aria-disabled='true'] { color: var(--texto-tenue); }

@media (hover: hover) and (pointer: fine) {
  .agenda-passos button:hover { color: var(--texto); }
}

/* ---------- corpo ---------- */

.agenda-corpo {
  flex: 1;
  padding: 1.1rem 1.15rem 0;
  overflow-y: auto;
  /* O scroll de dentro não CONTAGIA o de fora. Ao chegar no fim da lista de
     serviços, o gesto que continua passaria a rolar a página -- o efeito que
     travar o fundo existe para impedir, entrando pela porta de trás. Vale também
     no trackpad, e é a metade do problema que o `overflow: hidden` não pega. */
  overscroll-behavior: contain;
}

.agenda-tela[hidden] { display: none; }

.agenda-ayuda {
  color: var(--texto-suave);
  font-size: 0.88rem;
  line-height: 1.45;
  margin: 0 0 0.95rem;
  max-width: 46ch;
}

.agenda-aviso, .agenda-nota {
  font-size: 0.88rem;
  margin: 0.9rem 0 0;
}

.agenda-aviso { color: var(--acento-claro); }
.agenda-nota { color: var(--exito); }

/* O horário na tela de data. Fica antes do calendário porque é ele que explica
   os dias apagados — sem ler "Dom cerrado", domingo desativado parece bug. */
.agenda-horario {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
  font-size: 0.83rem;
  color: var(--texto-suave);
}

.agenda-horario .icono {
  width: 1rem;
  height: 1rem;
  flex: none;
  color: var(--acento-claro);
}

/* Lista VERTICAL, uma linha por serviço.
   Foi uma grade de três quadrados. No quadrado de 1/3 de largura só cabia o nome
   — e a descrição de cada serviço já existe na página, era informação pronta que
   o modal não mostrava justo na tela onde se decide. Deitada, a linha tem largura
   para o texto, e abre para mostrar a descrição daquele serviço. */
.agenda-servicios {
  display: grid;
  gap: 0.4rem;
}

.agenda-hora, .agenda-dia {
  font-family: var(--display);
  font-weight: 600;
  color: var(--texto-suave);
  background: transparent;
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  cursor: pointer;
  /* Nada de selecionar o texto do botao. Dois cliques rapidos numa grade de
     opcoes marcam a palavra em azul, e o realce de selecao briga com o estado
     "marcado" -- a pessoa fica sem saber o que esta escolhido. */
  user-select: none;
  -webkit-user-select: none;
  transition:
    color 160ms ease,
    border-color 160ms ease,
    background 160ms ease,
    transform 160ms var(--salida);
}

/* A linha é um <div> e não um botão: escolher e abrir são dois botões IRMÃOS
   dentro dela. Botão dentro de botão é HTML inválido, e o clique de dentro não
   chega de forma confiável. */
.agenda-servicio {
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  overflow: hidden;
  transition: border-color 160ms ease, transform 160ms var(--salida);
}

.agenda-servicio-fila {
  display: flex;
  align-items: stretch;
  transition: background 160ms ease;
}

/* O estado "marcado" é lido do FILHO, com `:has()`. Assim o `aria-pressed` do
   botão continua sendo a única fonte de verdade do que está escolhido — um
   atributo espelhado no pai seria um segundo lugar para discordar. */
.agenda-servicio:has(.agenda-servicio-elegir[aria-pressed='true']) {
  border-color: var(--acento);
}

.agenda-servicio-fila:has(.agenda-servicio-elegir[aria-pressed='true']) {
  background: var(--acento);
}

.agenda-servicio-elegir {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.75rem;
  font-family: var(--display);
  font-weight: 600;
  font-size: 0.88rem;
  line-height: 1.25;
  text-align: left;
  color: var(--texto-suave);
  background: transparent;
  border: 0;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  transition: color 160ms ease;
}

.agenda-servicio-elegir svg {
  width: 1.35rem;
  height: 1.35rem;
  flex: none;
  color: var(--acento-claro);
  transition: color 160ms ease;
}

.agenda-servicio-elegir[aria-pressed='true'] {
  color: var(--sobre-acento);
}

.agenda-servicio-elegir[aria-pressed='true'] svg { color: currentColor; }

/* O botão de abrir o detalhe. Separado por uma linha fina do alvo de escolher,
   senão os dois viram uma coisa só e o dedo não sabe onde acerta o quê. */
.agenda-servicio-mas {
  flex: none;
  /* 44px, e não um valor escolhido a olho: é o mínimo de alvo de toque, e medido
     em 2.6rem dava 42 — dois pixels que só aparecem pedindo o número. */
  width: 2.75rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  border-left: 1px solid color-mix(in srgb, var(--borde) 70%, transparent);
  color: var(--texto-tenue);
  cursor: pointer;
  transition: color 160ms ease;
}

.agenda-servicio-mas svg {
  width: 1rem;
  height: 1rem;
  /* O chevron do sprite aponta para a direita (é o do calendário). Girado é o de
     baixo — o mesmo desenho, não uma segunda cópia dele no sprite. */
  transform: rotate(90deg);
  transition: transform 200ms var(--salida);
}

.agenda-servicio-mas[aria-expanded='true'] svg { transform: rotate(-90deg); }

.agenda-servicio-fila:has(.agenda-servicio-elegir[aria-pressed='true']) .agenda-servicio-mas {
  color: color-mix(in srgb, var(--sobre-acento) 70%, transparent);
  border-left-color: color-mix(in srgb, var(--sobre-acento) 30%, transparent);
}

/* O anel de foco POR DENTRO da linha.
   O padrão do site é `outline-offset: 3px`, que aqui cai fora da caixa e o
   `overflow: hidden` da linha corta — sobrava um risco azul vertical na borda do
   divisor, que lê como defeito de render e não como "isto está focado". Offset
   negativo desenha o anel dentro, inteiro. */
.agenda-servicio-elegir:focus-visible,
.agenda-servicio-mas:focus-visible {
  outline: 2px solid var(--acento-claro);
  outline-offset: -3px;
  border-radius: 3px;
}

/* Marcado, o fundo já é o acento e um anel da mesma cor desapareceria nele. */
.agenda-servicio-elegir[aria-pressed='true']:focus-visible {
  outline-color: var(--sobre-acento);
}

/* A caixa que COLAPSA. A altura anima de `0fr` a `1fr` em vez de saltar de
   `display: none` — a mesma técnica da bajada do cabeçalho, e o motivo é o mesmo:
   `display` não interpola, e altura em pixel exigiria medir o conteúdo no JS.

   `visibility` fecha o outro lado: com altura zero o texto continuaria na árvore
   de acessibilidade, e o leitor de tela leria as dez descrições de uma vez. */
.agenda-servicio-detalle {
  display: grid;
  grid-template-rows: 0fr;
  visibility: hidden;
  transition:
    grid-template-rows 200ms var(--salida),
    /* Esconder só DEPOIS de terminar de fechar. `visibility` não interpola: sem o
       atraso ela vira `hidden` no meio da animação e o texto pisca fora antes de a
       caixa acabar de subir. Ao ABRIR o atraso é zero — aparecer tarde seria pior
       que aparecer junto. */
    visibility 0s linear 200ms;
}

.agenda-servicio-detalle[data-abierto] {
  grid-template-rows: 1fr;
  visibility: visible;
  transition:
    grid-template-rows 200ms var(--salida),
    visibility 0s;
}

/* NADA de padding vertical nem borda aqui dentro.
   `min-height: 0` zera a altura do CONTEÚDO, e é obrigatório (o mínimo automático
   de um item de grid é o tamanho do conteúdo dele). Mas padding e borda continuam
   somando na caixa: com `padding: 0.15rem ... 0.75rem` e uma borda de 1px, o
   detalhe fechado media 15px em vez de 0 — dez linhas viravam 150px de sobra e a
   descrição aberta ficava cortada em 15px de altura. O espaço vertical vem das
   margens dos filhos, que o `overflow: hidden` recorta junto. */
.agenda-servicio-detalle-caja {
  display: grid;
  justify-items: start;
  gap: 0.5rem;
  padding: 0 0.8rem;
  min-height: 0;
  overflow: hidden;
}

.agenda-servicio-detalle-caja > :last-child { margin-bottom: 0.75rem; }

.agenda-servicio-detalle-caja p {
  margin: 0.6rem 0 0;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--texto-suave);
}

/* O divisor entre o cabeçalho da linha e o detalhe mora FORA da caixa que
   colapsa, senão o 1px dele seria altura que nunca chega a zero. Aparece só com o
   detalhe aberto — fechado não há o que dividir. */
.agenda-servicio:has(.agenda-servicio-detalle[data-abierto]) .agenda-servicio-fila {
  border-bottom: 1px solid color-mix(in srgb, var(--borde) 70%, transparent);
}

.agenda-servicio-etiqueta {
  font-family: var(--display);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--acento-claro);
  border: 1px solid color-mix(in srgb, var(--acento-claro) 35%, transparent);
  border-radius: 999px;
  padding: 0.14rem 0.5rem;
}

.agenda-hora {
  font-size: 0.86rem;
  padding: 0.55rem 0.3rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Marcado. O botao inteiro vira o estado: em multipla escolha, caixinha ao
   lado obriga a olhar duas coisas para saber o que esta ligado. */
.agenda-hora[aria-pressed='true'],
.agenda-dia[aria-pressed='true'] {
  color: var(--sobre-acento);
  background: var(--acento);
  border-color: var(--acento);
}

@media (hover: hover) and (pointer: fine) {
  .agenda-hora:hover, .agenda-dia:hover:not(:disabled) {
    color: var(--texto);
    border-color: var(--borde-forte);
  }
  .agenda-hora[aria-pressed='true']:hover,
  .agenda-dia[aria-pressed='true']:hover {
    color: var(--sobre-acento);
    background: var(--acento-fuerte);
    border-color: var(--acento-fuerte);
  }

  .agenda-servicio:has(.agenda-servicio-elegir:hover) { border-color: var(--borde-forte); }
  .agenda-servicio-elegir:hover { color: var(--texto); }
  .agenda-servicio-mas:hover { color: var(--texto); }

  .agenda-servicio-fila:has(.agenda-servicio-elegir[aria-pressed='true']:hover) {
    background: var(--acento-fuerte);
  }
  .agenda-servicio:has(.agenda-servicio-elegir[aria-pressed='true']:hover) {
    border-color: var(--acento-fuerte);
  }
  .agenda-servicio-elegir[aria-pressed='true']:hover { color: var(--sobre-acento); }
}

.agenda-hora:active, .agenda-dia:active:not(:disabled) {
  transform: scale(0.97);
}

/* A linha inteira afunda um pouco, e nao 3% como os quadrados: numa peca de
   largura cheia o mesmo 0.97 vira um salto de 10px na borda e chama mais atencao
   que o proprio clique. */
.agenda-servicio:has(.agenda-servicio-elegir:active) { transform: scale(0.99); }

.agenda-horas {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.45rem;
}

@media (max-width: 30rem) {
  .agenda-horas { grid-template-columns: repeat(3, 1fr); }
}

/* ---------- calendario ---------- */

/* O calendario e um bloco de largura fixa, nao uma grade que estica.
   Esticado na largura do dialogo, `aspect-ratio: 1` dava celulas de 70px e o
   passo da data ia a 887px -- mais alto que os outros tres somados de folga.
   Travado em 17.5rem cada celula fica em ~36px, o tamanho do calendario do
   shadcn, e o passo cabe na mesma altura dos demais. */
.agenda-calendario {
  max-width: 14.5rem;
  margin-inline: auto;
}

.agenda-mes {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}

.agenda-mes strong {
  font-family: var(--display);
  font-size: 0.92rem;
  font-weight: 700;
  user-select: none;
}

/* Só a primeira letra. O Intl devolve "agosto de 2026" em minúscula, e
   `text-transform: capitalize` maiusculizaria cada palavra -- "Agosto De 2026",
   que em espanhol está errado: a preposição não sobe. */
.agenda-mes strong::first-letter { text-transform: uppercase; }

.agenda-mes-nav {
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  color: var(--texto-suave);
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease;
}

.agenda-mes-nav svg { width: 1rem; height: 1rem; }

.agenda-mes-nav:disabled {
  opacity: 0.35;
  cursor: default;
}

@media (hover: hover) and (pointer: fine) {
  .agenda-mes-nav:hover:not(:disabled) {
    color: var(--texto);
    border-color: var(--borde-forte);
  }
}

.agenda-dias {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 0.25rem;
}

.agenda-dia-semana {
  font-family: var(--display);
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--texto-tenue);
  text-align: center;
  padding-bottom: 0.3rem;
  user-select: none;
}

.agenda-dia {
  aspect-ratio: 1;
  font-size: 0.82rem;
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
  border-color: transparent;
}

/* Hoje ganha um contorno, nao a cor de marcado: se fosse a mesma pintura,
   "hoje" e "o dia que escolhi" seriam indistinguiveis. */
.agenda-dia[data-hoy]:not([aria-pressed='true']) {
  border-color: var(--borde-forte);
  color: var(--texto);
}

.agenda-dia:disabled {
  color: var(--texto-tenue);
  opacity: 0.4;
  cursor: default;
}

.agenda-campo {
  display: block;
  margin-bottom: 0.8rem;
}

/* `display: block` acima vence o `display: none` que o atributo `hidden` traz
   do browser -- regra de autor ganha da folha do UA. Sem isto o campo de data
   escondido pelo agenda.js continuava ocupando 76px de altura invisivel, e o
   passo da data ficava 76px mais alto que os outros sem nada aparecendo ali. */
.agenda-campo[hidden] { display: none; }

.agenda-campo > span {
  display: block;
  font-size: 0.8rem;
  color: var(--texto-suave);
  margin-bottom: 0.4rem;
}

.agenda-campo input, .agenda-campo select {
  width: 100%;
  font: inherit;
  font-size: 0.95rem;
  color: var(--texto);
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  padding: 0.65rem 0.8rem;
}

/* O calendario nativo do WebKit vem com o icone escuro sobre fundo escuro. */
.agenda-campo input[type='date'] { color-scheme: dark; }

.agenda-campo input:focus-visible, .agenda-campo select:focus-visible {
  outline: 2px solid var(--acento-claro);
  outline-offset: 2px;
}

/* ---------- resumo ao vivo ---------- */

.agenda-resumen {
  margin: 0.8rem 1.15rem 0;
  padding: 0.5rem 0.7rem;
  background: var(--fondo);
  border: 1px solid var(--borde);
  border-radius: var(--radio-interno);
  display: grid;
  gap: 0.22rem;
  font-size: 0.76rem;
}

/* O `display: grid` acima é regra de autor e vence o `display: none` que o
   atributo `hidden` traz da folha do UA — a mesma armadilha do `.agenda-campo`.
   Sem esta linha o resumo aparecia VAZIO no primeiro passo: uma caixa com borda
   e nada dentro, que é exatamente o que o `hidden` inicial existe para evitar.
   Só se via abrindo o modal; nenhuma leitura do código mostra isso. */
.agenda-resumen[hidden] { display: none; }

.agenda-resumen-fila {
  display: flex;
  gap: 0.6rem;
  align-items: baseline;
}

.agenda-resumen-fila[hidden] { display: none; }

.agenda-resumen-fila > span {
  flex: none;
  min-width: 4.6rem;
  color: var(--texto-tenue);
  font-family: var(--display);
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.agenda-resumen-fila > strong {
  font-weight: 600;
  color: var(--texto);
  line-height: 1.35;
}

/* ---------- pie do dialogo ---------- */

.agenda-pie {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.9rem 1.15rem 1.15rem;
  border-top: 1px solid var(--borde);
  margin-top: 0.9rem;
}

.agenda-pie .boton { padding: 0.75rem 1rem; font-size: 0.8rem; }

.agenda-pie .boton { flex: 1; justify-content: center; }
.agenda-pie [hidden] { display: none; }

/* Verde do WhatsApp no ultimo passo. Fica so aqui: e a unica acao do site que
   sai para outro aplicativo, e vale ser reconhecivel pela cor dele.

   NAO e o #25D366 da marca: com texto branco em cima ele da 1.98:1. O verde
   claro do WhatsApp existe para fundo claro com texto escuro, e aqui o texto e
   branco. Este tom escurecido da 4.80:1 parado e 5.40:1 no hover -- na direcao
   certa. O primeiro valor que tentei (#1f9d55) dava 3.49 parado e 4.80 no
   hover: passava so quando o cursor chegava, que e o mesmo erro invertido que
   o botao azul deste site ja teve. */
.agenda-confirmar {
  background: #198346;
  box-shadow: 0 10px 30px color-mix(in srgb, #198346 22%, transparent);
}

@media (hover: hover) and (pointer: fine) {
  .agenda-confirmar:hover {
    background: #157a3f;
    box-shadow: 0 16px 44px color-mix(in srgb, #198346 32%, transparent);
  }
}

/* Onde ha espaco, o calendario cresce. Em tela cheia a largura sobra e a
   altura tambem: celula de 30px ali e miniatura sem motivo, e dia pequeno num
   toque de dedo e erro de alvo. O teto de 20rem impede que num tablet em tela
   cheia ele viaje para 400px de largura. */
@media (max-height: 43rem), (max-width: 28rem) {
  .agenda-calendario { max-width: 20rem; }
  .agenda-dia { font-size: 0.95rem; }
  .agenda-dia-semana { font-size: 0.72rem; }
}

/* Tela curta ou estreita: ocupa tudo e rola por dentro.
   Os limiares acompanham o tamanho do dialogo (26rem x 35.25rem), com uma
   folga para o backdrop respirar. Ficaram em 44rem/34rem quando ele era maior,
   e nao acompanharam: numa janela de laptop com 700px de altura o modal ia a
   tela cheia sem precisar. */
@media (max-height: 43rem), (max-width: 28rem) {
  .agenda-caja {
    width: 100%;
    min-height: 100%;
    border: 0;
    border-radius: 0;
  }
}

/* ============================================================
   No editor o dialogo fica aberto, estatico e com as QUATRO
   telas a vista.

   <dialog> fechado e display:none, e nada que o JS esconde pode
   conter data-cms: os rotulos dos passos existiriam na pagina sem
   como ser clicados no painel -- o mesmo problema de z-index
   negativo numa imagem editavel. Aqui ele deixa de ser modal e
   passa a ser um bloco no fim da pagina.
   ============================================================ */

html[data-sitekit-editando] .agenda {
  display: block;
  position: static;
  width: auto;
  height: auto;
  padding: 0 var(--aire-lateral) var(--aire);
}

html[data-sitekit-editando] .agenda-caja {
  min-height: 0;
  margin-inline: auto;
}

html[data-sitekit-editando] .agenda-tela[hidden] { display: block; }
html[data-sitekit-editando] .agenda-corpo { overflow: visible; }
html[data-sitekit-editando] .agenda-pie [hidden] { display: inline-flex; }

/* As mensagens condicionais nascem com `hidden`: aparecem quando a pessoa erra
   ou quando hoje ainda da tempo. Sao quatro textos que o cliente precisa poder
   reescrever, e escondidas elas nao existem para o painel. Mesma regra de
   sempre -- nada que o site esconde pode conter data-cms sem uma saida no
   editor. */
html[data-sitekit-editando] .agenda-aviso[hidden],
html[data-sitekit-editando] .agenda-nota[hidden] { display: block; }

/* O campo de data nativo é escondido pelo agenda.js quando o calendário assume
   -- e o rótulo dele (`agenda.fecha.rotulo`) mora dentro. No editor ele volta,
   senão é mais um texto na página sem como ser clicado no painel. */
html[data-sitekit-editando] .agenda-campo[hidden] { display: block; }

/* O resumo nasce escondido e cada linha dele tambem -- sao quatro rotulos
   editaveis que, escondidos, nao existem para o painel. */
html[data-sitekit-editando] .agenda-resumen[hidden],
html[data-sitekit-editando] .agenda-resumen-fila[hidden] { display: flex; }
html[data-sitekit-editando] .agenda-resumen[hidden] { display: grid; }

html[data-sitekit-editando] .agenda-tela + .agenda-tela {
  border-top: 1px solid var(--borde);
  margin-top: 1.4rem;
  padding-top: 1.4rem;
}

/* O token do icone e conteudo editavel que nao deve aparecer na pagina: quem
   desenha e o sprite. Escondido por posicao e nao por display:none, senao o
   campo sairia da arvore e o editor nao teria onde clicar -- e no editor ele
   volta como etiqueta, para o cliente poder trocar o token. */
.oculto-visual {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

html[data-sitekit-editando] .oculto-visual {
  position: static;
  width: auto;
  height: auto;
  clip-path: none;
  display: inline-block;
  margin-top: 0.4rem;
  font-family: var(--display);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--acento-claro);
  border: 1px dashed color-mix(in srgb, var(--acento-claro) 40%, transparent);
  border-radius: 999px;
  padding: 0.15rem 0.5rem;
}

/* ---------- pie ---------- */

/* O respiro lateral vive na caixa interna, não no rodapé.

   Com padding nos dois -- `.pie` e depois a caixa centrada dentro do que
   sobrou -- o texto do rodapé caía 24px à esquerda do resto da página: a
   centragem acontece DEPOIS de descontar o padding externo. Só uma das duas
   caixas pode ter respiro lateral, e tem que ser a que também está limitada
   a `--ancho`. */
/* ============================================================
   Rodapé.

   Três colunas de conteúdo mais a linha de baixo. As redes são
   TRÊS e não quatro: só Instagram, WhatsApp e o mapa existem de
   verdade. Ícone de Facebook ou TikTok apontando para nada é
   afirmação falsa sobre o cliente.
   ============================================================ */

.pie {
  border-top: 1px solid var(--borde);
  background: var(--fondo);
  padding: clamp(2.5rem, 5vw, 4rem) 0 1.75rem;
}

.pie-grade {
  max-width: var(--ancho);
  margin: 0 auto;
  padding-inline: var(--aire-lateral);
  display: grid;
  gap: 2.5rem 2rem;
  font-size: 0.9rem;
  color: var(--texto-suave);
}

/* A coluna da marca é mais larga que as de links: ela tem um parágrafo, e as
   outras têm uma palavra por linha. Três colunas iguais deixariam a descrição
   numa tira estreita ao lado de duas colunas com metade do espaço vazio. */
@media (min-width: 52rem) {
  .pie-grade { grid-template-columns: 1.5fr 1fr 1.2fr; gap: 2rem 3rem; }
}

.pie p { margin: 0; }

/* O logo do rodapé. Mesma altura do cabeçalho de propósito: são a mesma marca
   na mesma página, e dois tamanhos diferentes leem como descuido. */
.pie-marca-logo {
  height: clamp(0.66rem, 1.05vw, 0.85rem);
  width: auto;
  margin-bottom: 1.1rem;
}

.pie-descripcion {
  margin-top: 1.15rem;
  max-width: 34ch;
  line-height: 1.6;
}

.pie-redes {
  display: flex;
  gap: 0.6rem;
  margin-top: 1.5rem;
}

/* 42px de área de toque: abaixo de 44 o alvo fica pequeno para o dedo, e estes
   três são o caminho para o Instagram, que é a vitrine real do cliente. */
.pie-red {
  width: 2.65rem;
  height: 2.65rem;
  display: grid;
  place-items: center;
  border-radius: var(--radio-interno);
  background: var(--superficie);
  border: 1px solid var(--borde);
  color: var(--texto-suave);
  transition: color 200ms ease, background 200ms ease, transform 160ms var(--salida);
}

.pie-red svg { width: 1.15rem; height: 1.15rem; }

@media (hover: hover) and (pointer: fine) {
  .pie-red:hover {
    color: var(--texto);
    background: var(--superficie-alta);
  }
}

.pie-red:active { transform: scale(0.94); }

.pie-titulo {
  font-family: var(--display);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--texto);
  margin-bottom: 1.15rem;
}

.pie-columna {
  display: flex;
  flex-direction: column;
  align-items: start;
  gap: 0.85rem;
}

.pie-columna a {
  color: var(--texto-suave);
  text-decoration: none;
  transition: color 180ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .pie-columna a:hover { color: var(--texto); }
}

/* O título é filho do flex e herdaria o `gap`, somando com a própria margem. */
.pie-columna .pie-titulo { margin-bottom: 0; }

.pie-dato {
  display: flex;
  align-items: start;
  gap: 0.6rem;
  line-height: 1.5;
}

.pie-dato .icono {
  color: var(--acento-claro);
  margin-top: 0.1em;
}

.pie-baixo {
  max-width: var(--ancho);
  margin: clamp(2.5rem, 5vw, 3.5rem) auto 0;
  padding: 1.5rem var(--aire-lateral) 0;
  border-top: 1px solid var(--borde);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  justify-content: space-between;
  font-size: 0.85rem;
  color: var(--texto-suave);
}

/* O crédito "Sitio por ..." saiu em 17/08/2026, a pedido. Era `data-cms-lock`,
   então não orfanou chave nenhuma -- conteúdo travado nunca chegou ao
   Firestore. As regras dele saíram junto; deixar CSS de elemento que não existe
   é lixo que o próximo leitor tenta entender. */

/* ============================================================
   Entrada por scroll, com melhoria progressiva: sem JS o
   conteudo aparece normalmente e o Googlebot le tudo.

   Quando o ./motion.js carrega, o spring dele assume estas
   mesmas propriedades. As duas não brigam: animação da WAAPI
   tem precedência sobre transition na cascata, então a do
   Motion vence enquanto roda e esta fica como rede.
   ============================================================ */

html[data-anima] [data-revela] {
  opacity: 0;
  transform: translateY(14px);
}

html[data-anima] [data-revela].visivel {
  opacity: 1;
  transform: none;
  transition:
    opacity 620ms var(--salida) var(--retraso, 0ms),
    transform 620ms var(--salida) var(--retraso, 0ms);
}

/* Escalonamento da camada sem Motion. Com ele carregado o atraso vem do
   `delay` por índice de lote, que sabe o que entrou junto na tela -- isto
   aqui é o palpite estático que funciona sem JS nenhum além do observer. */
html[data-anima] .servicio:nth-child(2),
html[data-anima] .galeria li:nth-child(2),
html[data-anima] .opinion:nth-child(2) { --retraso: 70ms; }

html[data-anima] .servicio:nth-child(3),
html[data-anima] .galeria li:nth-child(3),
html[data-anima] .opinion:nth-child(3) { --retraso: 140ms; }

html[data-anima] .servicio:nth-child(4) { --retraso: 210ms; }
html[data-anima] .servicio:nth-child(n + 5) { --retraso: 260ms; }

/* Os passos do /servicios entram juntos na tela (quatro numa fileira em telas
   largas), então escalonam como os cartões. As fileiras do catálogo NÃO
   escalonam: cada uma ocupa a altura de uma foto grande e entra sozinha no
   viewport -- atraso ali seria espera sem nada acontecendo. */
html[data-anima] .paso:nth-child(2) { --retraso: 70ms; }
html[data-anima] .paso:nth-child(3) { --retraso: 140ms; }
html[data-anima] .paso:nth-child(n + 4) { --retraso: 210ms; }

/* ============================================================
   Parallax por scroll-driven animation.

   Em CSS e não no Motion de propósito: `animation-timeline` custa
   0 KB, roda no compositor e degrada para foto parada onde não
   existe -- o `scroll()` do Motion custaria 6 KB gzip a mais para
   fazer o mesmo. É o caso em que a plataforma ganha da biblioteca.

   Desligado dentro do editor: o realce do painel é posicionado por
   getBoundingClientRect e não se reposiciona a cada scroll, então
   uma foto que anda deixa a marcação apontando para o lugar errado
   -- justamente na imagem que é a mais difícil de selecionar.
   ============================================================ */

@keyframes portada-deriva {
  from { transform: scale(1.12) translateY(-2.5%); }
  to   { transform: scale(1.12) translateY(2.5%); }
}

/* A foto do taller deriva por `object-position`, não por transform: a moldura
   deslocada atrás dela vive FORA da caixa da figura (inset negativo), então
   clipar para conter um scale cortaria a própria moldura, e não clipar deixaria
   a foto invadir a seção vizinha. Mexer no enquadramento mantém tudo dentro
   dos mesmos pixels. Custa repintura em vez de composição -- aceitável numa
   foto de 400px de altura, não na portada de tela cheia. */
@keyframes figura-deriva {
  from { object-position: center 42%; }
  to   { object-position: center 58%; }
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    html:not([data-sitekit-editando]) .portada-img {
      /* O scale de 1.12 é o que dá margem para os 2.5% de deslocamento em
         cada direção sem descobrir a borda da seção. */
      animation: portada-deriva linear both;
      animation-timeline: scroll(root);
      animation-range: 0 100svh;
    }

    /* `.visita-figura` e não `.taller-figura`: a foto mudou de seção em
       17/08/2026 (do bloco do taller para Visítanos) e a classe foi junto. Um
       seletor que não casa com nada não dá erro -- só desliga o parallax, e
       ninguém nota. */
    html:not([data-sitekit-editando]) .visita-figura img {
      animation: figura-deriva linear both;
      animation-timeline: view();
      animation-range: entry 10% exit 90%;
    }

    /* O indicador some conforme a pessoa rola: depois do primeiro gesto ele já
       disse o que tinha para dizer, e uma seta pulsando sobre o conteúdo vira
       ruído. `visibility` fecha o outro lado -- com opacity 0 ele continuaria
       recebendo clique sobre a seção seguinte. Ela não interpola, então o pulo
       fica em 99%, depois de a opacidade já ter chegado a zero. */
    html:not([data-sitekit-editando]) .portada-baja {
      animation: baja-some linear both;
      animation-timeline: scroll(root);
      animation-range: 0 25svh;
    }
  }
}

@keyframes baja-some {
  0%   { opacity: 1; visibility: visible; }
  99%  { visibility: visible; }
  100% { opacity: 0; visibility: hidden; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html[data-anima] [data-revela] { transform: none; }
  html[data-anima] [data-revela].visivel { transition: opacity 300ms ease; }

  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }

  .boton:active, .boton-nav:active { transform: none; }
  .servicio:hover, .opinion:hover { transform: none; }
  .servicio:hover .servicio-foto img { transform: none; }
  .galeria-foto:hover img { transform: none; }
  .catalogo-item:hover .catalogo-figura img { transform: none; }
  .agenda-servicio:has(.agenda-servicio-elegir:active) { transform: none; }
  /* A regra global zera a DURAÇÃO das transições, não o atraso. O atraso de
     `visibility` existe para esperar a caixa fechar; sem animação não há o que
     esperar, e ele deixaria a borda do detalhe visível por 200ms depois do fim. */
  .agenda-servicio-detalle { transition-delay: 0s; }
  /* A rotação do chevron não é enfeite: ela É a diferença entre aberto e fechado.
     Aqui ela vira instantânea (o `transition-duration` acima cuida disso) em vez
     de sumir — sem ela o ícone mentiria sobre o estado. */
}

/* ==========================================================================
   BLOG
   O indice reaproveita `.catalogo-*` (a mesma fileira dos servicos). O que e
   proprio daqui e o cartao clicavel e a pagina do post.
   ========================================================================== */

/* Ancora do `::after` que estica a area clicavel sobre o cartao inteiro. */
.blog-item { position: relative; }

.blog-cartao {
  color: inherit;
  text-decoration: none;
  /* O sublinhado nasce so no hover: dez titulos sublinhados de uma vez viram
     uma parede de linhas azuis, e o cartao inteiro ja se le como clicavel. */
  text-underline-offset: 4px;
}

/* A area clicavel e o cartao, o NOME ACESSIVEL e so o titulo.

   E o motivo de o link nao envolver o cartao: um `<a>` em volta de foto, data,
   titulo e resumo e anunciado pelo leitor de tela como um link cujo nome tem
   quatro frases. Aqui quem clica acerta em qualquer lugar, e quem escuta ouve
   apenas o titulo. */
.blog-cartao::after {
  content: '';
  position: absolute;
  inset: 0;
  /* Acima da foto (que tem `overflow: hidden` no pai) e abaixo de nada: nao ha
     outro alvo dentro do cartao com que disputar o clique. */
  z-index: 1;
}

@media (hover: hover) and (pointer: fine) {
  .blog-cartao:hover { text-decoration: underline; }
}

/* O foco tem que aparecer no CARTAO, nao num retangulo de uma linha de texto:
   com a area esticada, o anel desenhado no proprio `<a>` marcaria so o titulo
   enquanto o alvo do clique e a fileira toda. */
.blog-item:focus-within {
  outline: 2px solid var(--acento-claro);
  outline-offset: 6px;
  border-radius: var(--radio);
}

.blog-cartao:focus-visible { outline: none; }

/* -------------------------------------------------------------- a pagina do post */

.articulo { border-top: 1px solid var(--borde); }

.articulo-cabeza {
  /* 44rem e a medida das portadas de secao deste site. Aqui ela tambem serve a
     leitura: linha longa demais faz o olho perder a volta. */
  max-width: 44rem;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.articulo-cabeza h1 { margin: 0.6rem 0 0; }

.articulo-bajada {
  color: var(--texto-suave);
  font-size: 1.12rem;
  max-width: 52ch;
  margin: 1.5rem 0 0;
}

.articulo-figura {
  margin: 0 0 clamp(2rem, 5vw, 3.5rem);
  overflow: hidden;
  border-radius: var(--radio);
  border: 1px solid var(--borde);
  background: var(--superficie);
}

.articulo-figura img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  display: block;
}

/* -------------------------------------------------------------- o corpo do post

   Tudo daqui para baixo estiliza markup que o CLIENTE escreve, dentro do campo
   `corpo` (`data-cms-type="rich"`). Por isso os seletores sao por TAG e nao por
   classe: nao existe classe nenhuma no que sai do editor, e a allowlist da
   sanitizacao (`RICH_ALLOWED_TAGS`) e a lista exata do que pode chegar aqui.
   Uma tag da allowlist sem estilo e um paragrafo torto na pagina de alguem. */

.articulo-corpo {
  max-width: 38rem;
  font-size: 1.08rem;
  line-height: 1.75;
  color: var(--texto-suave);
}

.articulo-corpo > :first-child { margin-top: 0; }
.articulo-corpo > :last-child { margin-bottom: 0; }

.articulo-corpo p { margin: 0 0 1.4em; }

/* `h2` e `h3` sao os unicos niveis da allowlist, e e de proposito: o `h1` e o
   titulo do post, e deixar o cliente escrever outro daria dois `h1` na pagina. */
.articulo-corpo h2,
.articulo-corpo h3 {
  font-family: var(--display);
  color: var(--texto);
  line-height: 1.2;
  margin: 2.2em 0 0.7em;
}

.articulo-corpo h2 { font-size: 1.6rem; }
.articulo-corpo h3 { font-size: 1.25rem; }

.articulo-corpo strong { color: var(--texto); font-weight: 600; }

.articulo-corpo a {
  color: var(--acento-claro);
  text-underline-offset: 3px;
}

.articulo-corpo ul,
.articulo-corpo ol {
  margin: 0 0 1.4em;
  padding-left: 1.4em;
}

.articulo-corpo li { margin: 0 0 0.5em; }

.articulo-corpo blockquote {
  margin: 2em 0;
  padding-left: 1.2em;
  border-left: 2px solid var(--acento-claro);
  color: var(--texto);
  font-size: 1.2rem;
}

/* `figure`, `img` e `figcaption` NAO estao na allowlist do `rich` ainda -- eles
   entram com o editor de blocos. O estilo fica escrito desde agora porque o dia
   em que a allowlist crescer, uma foto sem largura maxima estoura a coluna de
   leitura, e ninguem lembra de vir aqui. */
.articulo-corpo figure {
  margin: 2em 0;
}

.articulo-corpo img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--radio);
  border: 1px solid var(--borde);
}

.articulo-corpo figcaption {
  margin-top: 0.7rem;
  font-size: 0.92rem;
  color: var(--texto-tenue);
}

/* -------- os blocos de mídia do editor de post --------

   Estas três regras estilizam markup que o CLIENTE monta pelo painel, então elas
   são por TAG e por classe do protocolo -- nunca por classe que ele possa escrever.
   `RICH_ALLOWED_TAGS` é a lista exata do que pode chegar aqui; tag da allowlist sem
   estilo é um parágrafo torto na página de alguém. */

/* Divisor. Fio de cabelo e respiro generoso: ele separa assuntos, e um separador
   apertado lê como erro de espaçamento em vez de decisão. */
.articulo-corpo hr {
  height: 0;
  margin: clamp(2.5rem, 5vw, 3.5rem) auto;
  border: 0;
  border-top: 1px solid var(--borde);
  /* Mais estreito que a coluna: a linha cheia lê como fim de página. */
  width: 30%;
}

/* Botão dentro do texto. `display: inline-flex` e não `block`: um botão da largura
   da coluna de leitura vira faixa, e faixa no meio de um artigo lê como anúncio. */
.corpo-botao {
  display: inline-flex;
  align-items: center;
  margin: 1.6em 0;
  padding: 0.85rem 1.5rem;
  background: var(--acento);
  color: var(--sobre-acento);
  border-radius: var(--radio-interno, 8px);
  font-family: var(--display);
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background 180ms ease, transform 160ms var(--salida);
}

@media (hover: hover) and (pointer: fine) {
  /* ESCURECE no hover, nunca clareia: o texto em cima é claro, e clarear o fundo
     derrubaria o contraste justamente quando o cursor está mirando nele. É a mesma
     regra do botão da nav. */
  .corpo-botao:hover { background: var(--acento-fuerte); }
}

.corpo-botao:active { transform: scale(0.97); }

/* Caixa de destaque. A cor vem de uma CLASSE-TOKEN (`--ambar`) e não de um valor
   que o cliente escreve: valor livre aqui seria injeção de CSS na página, e é a
   mesma razão pela qual o Firestore guarda token e não hex. */
.corpo-callout {
  margin: 1.8em 0;
  padding: 1.1rem 1.3rem;
  border-left: 3px solid var(--borde-forte);
  border-radius: 0 var(--radio-interno, 8px) var(--radio-interno, 8px) 0;
  background: var(--superficie);
  color: var(--texto);
}

.corpo-callout > :first-child { margin-top: 0; }
.corpo-callout > :last-child { margin-bottom: 0; }

/* As quatro variações. O fundo é o acento a 10%: sobre ele o `--texto` mantém
   contraste de corpo, e a barra lateral é que carrega a cor de verdade. */
.corpo-callout--neutro {
  border-left-color: var(--texto-tenue);
}

.corpo-callout--ambar {
  border-left-color: #e0a437;
  background: color-mix(in srgb, #e0a437 10%, var(--superficie));
}

.corpo-callout--verde {
  border-left-color: var(--exito);
  background: color-mix(in srgb, var(--exito) 10%, var(--superficie));
}

.corpo-callout--azul {
  border-left-color: var(--acento-claro);
  background: color-mix(in srgb, var(--acento-claro) 10%, var(--superficie));
}

.articulo-corpo pre {
  margin: 2em 0;
  padding: 1rem 1.1rem;
  overflow-x: auto;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  font-size: 0.94rem;
}

.articulo-corpo code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.94em;
}

.articulo-volta {
  margin: clamp(3rem, 6vw, 4.5rem) 0 0;
  padding-top: clamp(1.5rem, 3vw, 2rem);
  border-top: 1px solid var(--borde);
}

.articulo-volta a {
  color: var(--acento-claro);
  text-decoration: none;
  font-family: var(--display);
  font-size: 0.86rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

@media (hover: hover) and (pointer: fine) {
  .articulo-volta a:hover { text-decoration: underline; text-underline-offset: 4px; }
}
