/* =========================================================================
   MUDANZAS EXPRESS GROUP - estilos
   Indice:
   0. Fuentes autoalojadas
   1. Reset y base
   2. Tokens (colores, tipografia, espaciado, radios, sombras, z-index)
   3. Utilidades de layout
   4. Tipografia
   5. Botones
   6. Header (Llamar/WhatsApp fijos arriba)
   7. Hero (foto a pantalla completa + overlay + logo + CTAs + redes)
   8. Menu (boton hamburguesa, panel lateral con Fotos y Resenas)
   9. Galeria
   10. Resenas
   11. Botones flotantes
   12. Footer
   13. Banner de cookies
   14. Scroll-reveal
   15. Accesibilidad y utilidades finales

   Nota sobre versiones anteriores: hubo una landing larga con scroll
   (header sticky con logo, hero con foto, franja de confianza, servicios,
   como trabajamos, contacto, footer) y despues una tarjeta de contacto
   sin foto ni scroll (con una banda "MUY FRAGIL" de acento). Ninguna de
   las dos sigue aqui como CSS comentado (para no arrastrar peso muerto);
   ambas siguen disponibles en el historial de git (ver README).
   ========================================================================= */

/* 0. Fuentes autoalojadas ----------------------------------------------------
   Antes se cargaban desde fonts.googleapis.com/fonts.gstatic.com (preconnect
   + <link> en el <head>): eso enviaba la IP de cada visitante a Google antes
   de que aceptase nada (tratamiento con transferencia internacional previo a
   cualquier consentimiento) y anadia una conexion externa mas al camino
   critico del renderizado. Los archivos viven ahora en assets/fonts/, solo
   en formato woff2 (unico que hace falta: lo soportan todos los navegadores
   actuales) y solo en el subconjunto latino (cubre el espanol -acentos, ene,
   signos de apertura-; no hace falta cirilico ni griego).
   Solo se descargaron los pesos que el CSS usa de verdad, revisando cada
   font-weight del archivo (no familias completas): Saira Condensed en 500
   (segunda linea de la frase de marca, mas ligera), 700 (marca del footer) y
   800 (titulares, primera linea de la frase, puntuacion de resenas); Source
   Sans 3 en 400 (texto de cuerpo) y 600 (botones); JetBrains Mono en 400
   (autor de cada resena) y 600 (badge "4,9 en Google"). El peso 900 de Saira
   Condensed que se cargaba antes no se usa en ningun sitio del CSS, igual
   que el 700 de Source Sans 3: ambos se han dejado fuera.
   Source Sans 3 y JetBrains Mono resultaron ser variable fonts de verdad
   (confirmado descomprimiendo el woff2 y viendo la tabla `fvar` en su
   directorio de tablas, no asumido por el nombre del archivo): Google sirve
   el MISMO archivo para los dos pesos que se piden de cada una, asi que aqui
   solo hace falta descargarlo una vez por familia y declarar dos @font-face
   -uno por peso- apuntando al mismo archivo; el navegador elige el
   corte/instancia correcta dentro de la fuente variable segun el peso de
   cada regla. Saira Condensed, en cambio, sirvio un archivo distinto por
   peso (no es variable en esta subida), de ahi los tres archivos sueltos.
   Antes, con los pesos equivocados cargados, el navegador sustituia el peso
   pedido por el mas cercano disponible (algoritmo de "font matching" del
   CSS): la segunda linea de la frase de marca (weight 500, pensada como
   "ligera") se estaba viendo en realidad a peso 700 (el 500 nunca se cargo),
   y el badge de confianza (mono, weight 600) se veia a peso 700 tambien (solo
   habia 500/700 cargados de esa familia). Con los pesos correctos ahora
   cargados de verdad, las dos se ven ligeramente MAS finas que antes -no es
   un error de descarga, es la correccion de un desajuste que ya existia
   desde que se anadieron esos pesos, enmascarado porque el navegador rellena
   el hueco en silencio en vez de fallar. Verificado con capturas 375/1920
   antes/despues: el resto de la tipografia (titulares, botones, footer,
   autor de resenas) es pixel a pixel identica, solo cambian esos dos sitios,
   y a mejor (mas fieles a la jerarquia "fuerte/ligera" que ya describia el
   comentario de `.hero__slogan` mas abajo). */
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../assets/fonts/saira-condensed-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../assets/fonts/saira-condensed-700.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Saira Condensed";
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url("../assets/fonts/saira-condensed-800.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/source-sans-3.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Source Sans 3";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/source-sans-3.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/jetbrains-mono.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../assets/fonts/jetbrains-mono.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* 1. Reset y base ---------------------------------------------------------*/
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}
body {
  margin: 0;
  background: var(--color-blanco);
  color: var(--color-negro);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }
button { font: inherit; cursor: pointer; }
h1, h2, h3, p { margin: 0; }
svg { display: block; }
/* Sin esto, una regla de autor con la misma especificidad que el
   selector [hidden] del navegador (p.ej. ".algo { display: flex }")
   le gana en la cascada al atributo hidden, porque los estilos de
   autor tienen prioridad sobre los del user-agent aunque la
   especificidad sea igual. Con esta linea, hidden vuelve a ganar
   siempre. (Asi se corrigio el fallback de texto del logo que se veia
   duplicado junto a la imagen real). */
[hidden] { display: none !important; }

/* 2. Tokens ----------------------------------------------------------------
   Paleta muestreada del logo real (@mudanzasexpressgroup). Ajustar al
   milimetro con el logo vectorial definitivo cuando llegue del cliente. */
:root {
  /* -- Marca -- */
  --color-marca: #E83223;        /* rojo-naranja del logo: CTAs y acentos */
  --color-marca-oscuro: #B8241A; /* hover / pressed */
  --color-negro: #0C0E12;        /* negro del logo: titulares, boton de menu */

  /* -- Familia plata (fria, del disco del logo) -- */
  --color-plata-700: #6B6E72;
  --color-plata-500: #B3AEAF;
  --color-plata-300: #DEDCDD;
  --color-plata-100: #F2F1F1;
  --color-blanco: #FFFFFF;

--header-tint-rgb: 255, 255, 255;
/* Subida de 0.70 a 0.75 (iteracion 31): contra la foto nueva del hero
   (cielo claro donde antes habia techo oscuro, mas el rojo de marca
   mas vivo en estas fotos), los iconos ya no llegaban a 3.5:1 ni con
   --color-marca-oscuro (3.30-3.45:1 en escritorio a 0.70). Antes de
   tocar la opacidad se probo bajar solo el color: no basto (ver esa
   regla en .site-header__icons). 0.75 es el minimo redondeado que deja
   margen real sobre 3.5:1 en las 4 medidas (peor caso 3.52:1 a 0.73,
   demasiado al filo; 3.71-3.72:1 a 0.75), no el minimo exacto que
   raspa el umbral -mismo criterio que la iteracion 24 con el 0.80 de
   entonces-. Ver .site-header__icons para los numeros completos. */
--header-opacity: 0.75;

  /* -- Barra superior: gris plano exacto pedido por el cliente, sin
     degradado ni brillos. Distinto de la familia plata de arriba
     (que es mas clara) a proposito: es el gris de esta barra en
     concreto, no un tono generico de la paleta. -- */
  --color-barra: #ACACAC;

  /* -- Tipografia -- */
  --font-display: "Saira Condensed", "Arial Narrow", Arial, sans-serif;
  --font-body: "Source Sans 3", "Segoe UI", Arial, sans-serif;
  --font-mono: "JetBrains Mono", "Consolas", monospace;

  /* -- Espaciado (una unica escala, en rem) -- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;

  /* -- Radios: md (12px) para botones y superficies -sustituyo al pill
     de iteraciones anteriores, que en los botones leia a "chicle"-;
     pill solo para las formas realmente redondas (los flotantes). -- */
  --radius-md: 12px;
  --radius-pill: 999px;

  /* -- Sombra tenida hacia el negro de marca -- */
  --shadow-sm: 0 2px 10px rgba(12, 14, 18, 0.08);
  --shadow-md: 0 12px 32px rgba(12, 14, 18, 0.14);

  /* -- Receta de cristal compartida (barra + tarjeta de contacto): mismo
     blur/saturate/tinte en las dos para que se sientan de una misma
     familia integrada con la foto (calida, no blanco frio/neutro que
     desentona con un atardecer). El tinte se guarda como canales RGB
     sueltos (sin alpha) para poder componer rgba(var(--glass-tint-rgb), X)
     con la opacidad que le toque a cada superficie. -- */
  --glass-blur: 14px;
  --glass-saturate: 1.15;
  --glass-tint-rgb: 255, 246, 235;

  /* -- Layout -- */
  --content-max: 1200px;
  --menu-width: 460px;

  /* -- Z-index (escala documentada, no numeros sueltos) -- */
  --z-menu-backdrop: 200;
  --z-menu-panel: 210;
  --z-header: 220; /* por encima del menu: el boton de menu vive dentro del header y debe poder cerrarlo siempre */
  --z-floating: 250;
  --z-cookie: 300;
}

/* 3. Utilidades de layout ---------------------------------------------------*/
.wrap {
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}
@media (min-width: 768px) {
  .wrap { padding-inline: var(--space-7); }
}

/* 4. Tipografia --------------------------------------------------------------*/
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.05;
  text-wrap: balance;
}
h2 { font-size: clamp(1.5rem, 3vw + 1rem, 1.9rem); }
h3 { font-size: 1.35rem; letter-spacing: -0.005em; }

.lede {
  font-size: 1.1rem;
  color: var(--color-plata-700);
  max-width: 52ch;
}

.tag-mono {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-plata-700);
}

/* 5. Botones ------------------------------------------------------------------
   Lenguaje plano y minimalista a proposito (corregido tras una version
   con gradiente + highlight + sombra de color que leia a "boton Web
   2.0"): relleno solido de un unico tono, sin degradado, sin brillo
   biselado, sombra neutra y muy sutil solo para una micro-elevacion de
   contacto, hover como oscurecimiento (`filter: brightness()`), nunca
   elevacion ni intensificacion de sombra. Radio moderado (`--radius-md`,
   12px) en vez de pill: mas "boton de interfaz" que "chicle", y unico
   para todo elemento interactivo del sitio (cookies incluido). */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0.9rem 1.6rem;
  border-radius: var(--radius-md);
  font-weight: 600;
  font-size: 1.2rem;
  line-height: 1;
  white-space: nowrap;
  border: 2px solid transparent;
  transition: transform 0.15s cubic-bezier(0.16, 1, 0.3, 1), filter 0.15s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}
.btn:active { transform: scale(0.97); }
.btn svg { width: 1.15rem; height: 1.15rem; flex-shrink: 0; }

/* Con peso 600 (no negrita) y un tamano mas comedido (.btn--lg mas
   abajo), el texto de estos botones ya NO cuenta como "texto grande"
   segun WCAG (eso exigiria negrita real >=700 o >=24px), asi que el
   umbral que aplica es el normal, 4.5:1, no el 3:1 de antes. Por eso
   el rojo de marca brillante (--color-marca, #E83223) ya no sirve aqui
   -con blanco solo da 4.27:1, por debajo de 4.5:1, verificado con la
   formula de contraste, no a ojo-: se usa directamente
   --color-marca-oscuro (#B8241A, 6.36:1), la misma familia de rojo,
   sin degradado, un unico tono solido. */
.btn--primary {
  background: var(--color-marca-oscuro);
  color: var(--color-blanco);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.btn--primary:hover { filter: brightness(0.92); }

/* WhatsApp del hero: mismo lenguaje plano que .btn--primary, un unico
   tono solido. El verde "oficial" mas conocido (#25D366) con texto
   blanco da SOLO ~2:1 de contraste -falla incluso el 3:1 de texto
   grande, y esto no cambia por estar sobre foto, cristal o lo que sea:
   es el propio fill del boton contra su propio texto, verificado con
   la formula de contraste antes de escribir una linea de CSS-. Se usa
   en su lugar #075E54, el teal oscuro que la propia marca WhatsApp usa
   en su app (uno de sus tres colores oficiales, no un tono inventado):
   7.67:1, de sobra para el 4.5:1 de texto normal que aplica ahora que
   el boton ya no es "grande" segun WCAG. */
.btn--whatsapp {
  background: #075E54;
  color: var(--color-blanco);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
}
.btn--whatsapp:hover { filter: brightness(0.92); }

/* El fondo semitransparente (no del todo transparente) es lo que
   garantiza el contraste AA del WhatsApp del cuerpo del hero pase lo
   que pase debajo: combinado con el overlay del hero (seccion 7,
   nunca por debajo de ~0.32 donde estan los CTAs), la cobertura total
   sobre la foto llega a >=0.56, el minimo para 4.5:1 con texto blanco
   incluso contra un pixel de foto blanco puro. Así el overlay general
   puede quedarse suave (para que la foto se vea) sin sacrificar la
   legibilidad de este boton en concreto. */
.btn--outline {
  background: rgba(12, 14, 18, 0.35);
  border-color: currentColor;
  color: var(--color-blanco);
}
.btn--outline:hover { background: rgba(12, 14, 18, 0.48); }

.btn--sm {
  padding: 0.55rem 1rem;
  font-size: 0.95rem;
}
/* Tamano comedido a proposito: los CTAs del hero deben acompañar la
   composicion (foto + rotulo), no dominarla. Bajado desde 1.5rem/2.5rem
   de padding y 1.4rem de texto de la iteracion anterior. */
.btn--lg {
  padding: 0.875rem 1.75rem; /* 14px 28px */
  font-size: 1.0625rem; /* 17px */
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

:focus-visible {
  outline: 3px solid var(--color-marca);
  outline-offset: 3px;
  border-radius: 4px;
}

/* 6. Header ----------------------------------------------------------
   La pantalla inicial son dos zonas apiladas: la barra gris (esta
   seccion) y el cuerpo con la foto (seccion 7), que juntas llenan
   exactamente el alto de la pantalla (.hero-screen, min-height:100svh,
   flex-direction:column). La barra ocupa su alto natural
   (flex-shrink:0) y el cuerpo se lleva el resto (flex:1 en .hero). */
.hero-screen {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}

/* Logo circular a la izquierda, Llamar/WhatsApp + boton de menu
   agrupados a la derecha. La barra ya NO es un gris solido: es cristal
   esmerilado (fondo blanco translucido + backdrop-filter: blur) para
   que se sienta hecha de la propia foto en vez de una tira pegada
   encima. Para que la foto pueda verse A TRAVES del cristal, .hero
   (seccion 7) pasa a ocupar el alto COMPLETO de .hero-screen (position:
   absolute; inset:0) en vez de "flex:1" despues del alto propio de la
   barra: asi la foto arranca en el borde superior real del viewport,
   por detras de esta barra, y no justo debajo. --color-barra (#ACACAC)
   se conserva como variable por si algun dia hay que volver al solido,
   pero ya no se usa aqui. Recetas de cristal (blur/saturate)
   compartidas con el badge de confianza del hero (seccion 7) para que
   se sientan de la misma familia; solo cambia el tinte (claro aqui,
   oscuro en el badge) porque cada uno lleva texto de un color distinto
   y necesita su propio contraste. position:sticky: ocupa su propio
   espacio en el flujo normal (la foto, ahora absoluta, no cuenta para
   ese flujo) y se queda fija arriba si se hace scroll. z-index por
   encima del menu (backdrop y panel, seccion 8) para que el boton de
   menu de aqui dentro siga siendo alcanzable y pueda cerrar el menu en
   cualquier momento. */
.site-header {
  flex-shrink: 0;
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  /* padding-block estaba roto (`var(--space-)`, sin el numero: un ajuste
     manual anterior para achicar la barra y despejar el rotulo bajo
     ella con la foto vieja -el rotulo tocaba el borde superior del
     archivo, ver iteraciones 21/22-). Con la variable invalida,
     padding-block se descartaba entero (solo queda valido el
     padding-top explicito de la linea de abajo, que SI lo pisa), asi
     que en la practica la barra ya tenia padding-bottom:0. Con la foto
     nueva (mucho cielo por encima del rotulo, ver `.hero__media img`)
     ese achique ya no hace falta -verificado con captura: la barra a
     su alto normal deja de sobra el rotulo debajo, sin solape-, asi
     que se corrige a `var(--space-3)` (el valor simetrico normal, el
     mismo que ya usa el padding-top de al lado) en vez de dejarlo roto
     o de reintroducir el achique con un valor valido. */
  padding-block: var(--space-3);
  padding-inline: calc(env(safe-area-inset-left, 0px) + var(--space-4)) calc(env(safe-area-inset-right, 0px) + var(--space-4));
  padding-top: calc(env(safe-area-inset-top, 0px) + var(--space-3));
  /* Blanca semitransparente CON CUERPO: 0.90, de vuelta al valor de la
     iteracion 23 (paso por 0.80 en la 24 y 0.25 en la 25, ambas descartadas, ver
     mas abajo). Mismo tinte calido `--glass-tint-rgb` (255,246,235) y
     mismo `backdrop-filter: blur(...) saturate(...)`, sin tocar en
     ninguna de estas idas y vueltas.
     LA ITERACION 25 SE CIERRA EN LA 26, Y EL CAMINO QUEDA DESCARTADO DE
     RAIZ, no solo el valor concreto que se probo: se intento bajar la
     barra a 0.25 (mucho mas transparente, para "ver mas foto") con una
     franja oscurecida de refuerzo en `.hero__scrim`, y el resultado
     medido en los iconos (rojo vivo) fue 1.39-2.02:1 en las 4 medidas
     -no se leian bien, confirmado en capturas reales, no solo en el
     numero-. La causa es estructural, no un mal ajuste: con un tinte
     CLARO como este (255,246,235), bajar la opacidad reduce lo que el
     propio tinte aporta de luminancia, pero ese tinte es precisamente
     lo unico que puede EMPUJAR el compuesto hacia el extremo brillante
     de la escala (el unico lado donde un icono rojo de luminancia
     media consigue contraste real). Oscurecer la foto detras no
     compensa esa perdida -de hecho la empeora, porque acerca aun mas
     el compuesto a la zona ciega de contraste del propio rojo-. Es
     decir: **con este tinte claro, el suelo de luminancia que hace
     falta para los iconos rojos solo lo puede dar la propia opacidad
     del tinte, no ningun oscurecido de la foto**. Bajar de verdad la
     opacidad y mantener el contraste de iconos rojos son, con este
     diseno, mutuamente excluyentes: no es un rango que se pueda seguir
     afinando, es un techo real. **0.90 es el punto elegido**: da
     margen de sobra (los iconos vuelven a leerse limpios, ver capturas
     de esta iteracion) sin ser mas opaco de lo necesario.
     Los iconos se quedan en `--color-marca` (`#E83223`, el vivo): a
     0.90 esto YA NO es indiferente como parecia en la 24 -aquella
     iteracion bajo a `--color-marca-oscuro` porque a 0.80 el oscuro
     media mejor-, pero el cliente pidio expresamente el vivo para esta
     vuelta a la 23, y a 0.90 el vivo tambien da contraste de sobra
     (ver `.site-header__icons`): no hizo falta elegir entre ambos por
     medicion, el vivo cumple igual. */
 background: rgba(var(--header-tint-rgb), var(--header-opacity));
backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
-webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  color: var(--color-negro);
  overflow: hidden;
}
/* Barra mas baja SOLO en paisaje (iteracion 32): con la foto actual del
   hero, el rotulo no se puede despejar con `object-position` en las
   proporciones de escritorio pedidas (1366x768, 1600x900, 1920x1080:
   todas ~16:9 como la propia foto, `object-fit:cover` no recorta
   verticalmente nada ahi, es un techo matematico, no un ajuste
   pendiente -ver iteracion 30 y el hallazgo de esta misma iteracion-).
   La unica palanca que queda es la barra: el rotulo se desplaza con la
   escala del viewport (47px/55px/66px de arriba en 1366/1600/1920)
   mientras la barra se quedaba fija en 75px, así que cuanto MAS BAJA
   la barra, menos solapa. No se toca en retrato: ahi el bloque de
   contacto vive abajo del todo (`align-items:flex-end`) y la barra
   nunca fue el problema.
   padding-block baja de `--space-3` (12px) a `--space-1` (4px): con los
   iconos como suelo real de 44px (zona tactil, no negociable), bajar
   mas el padding no reduce mas la barra sin tocar ese suelo. `padding-top`
   se reescribe entero (no solo se hereda) porque si no, la regla base
   de mas arriba (`env(...) + var(--space-3)`) seguiria mandando en el
   lado de arriba, dejando la barra asimetrica (4px abajo, 12px arriba). */
@media (orientation: landscape) {
  .site-header {
    padding-block: var(--space-1);
    padding-top: calc(env(safe-area-inset-top, 0px) + var(--space-1));
  }
}
/* Navegadores sin backdrop-filter (muy pocos hoy): sin blur, una barra
   casi transparente dejaria el logo/iconos ilegibles sobre foto, asi
   que el respaldo aqui es mas opaco que la propia barra normal -aunque
   con la barra normal de vuelta a 0.90, la diferencia entre ambas ya
   no es grande-. Revisado con el rojo vivo (`--color-marca`): contra
   un pixel en negro puro (peor caso posible) da 3.06:1, sobre blanco
   3.70:1, de sobra sobre el 3:1 minimo de contenido grafico. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header { background: rgba(245, 236, 222, 0.92); }
}

.site-header__logo { flex-shrink: 0; min-width: 0; }
/* max-height (no height fija): el logo nunca se escala grande, solo
   ocupa lo que necesita hasta este tope. Con la barra opaca (0.90) el
   logo no depende de ninguna franja oscurecida detras para su
   contraste: se apoya directamente en el fondo claro de la propia
   barra, igual que el texto/iconos.
   La iteracion 25 (barra al 0.25) hizo caer el trazo negro del logo a
   2.80-3.09:1 y el naranja a 1.44-1.67:1 -ninguno de los dos llegaba a
   3:1-. Al volver a 0.90 en la 26 esa tension desaparece para el trazo
   negro (de sobra otra vez, mismo orden que en la 24: 12+:1). El trazo
   NARANJA ("EXPRESS", `rgb(255,48,20)`, no es un token del sistema)
   sigue siendo el mismo caso ya documentado en la 24: no llega a 3:1
   ni siquiera con la barra completamente opaca (techo teorico 3.45:1),
   y el logo no se recolorea (pedido expreso). Ver la iteracion 24 para
   el detalle completo de ese hallazgo, que sigue sin resolverse -no
   es nuevo de esta iteracion, ni la 25 lo empeoro de forma permanente,
   ya que no se aplico-. */
.site-header__logo-img {
  max-height: clamp(38px, 7vh, 50px);
  width: auto;
  display: block;
}
/* En paisaje el logo baja de tope 50px a 40px (iteracion 32), junto con
   el padding-block de `.site-header` de arriba: por debajo de 44px el
   logo deja de ser el que manda la altura de contenido de la barra
   (los iconos, 44x44 de zona tactil, pasan a mandar), asi que bajarlo
   mas alla de eso no reduciria mas la barra -es el punto justo, no un
   numero arbitrario-. 40px se sigue leyendo con margen (no roza los
   bordes de la barra, verificado con captura). */
@media (orientation: landscape) {
  .site-header__logo-img {
    max-height: 40px;
  }
}

/* Solo iconos (Llamar, WhatsApp, Instagram, Facebook, menu), sin
   botones ni cajas alrededor: cada enlace/boton es su propia zona
   tactil de 44x44 (minimo recomendado), con el icono visual mas
   pequeno y centrado dentro. gap entre zonas tactiles para que la
   separacion se vea uniforme sin que las zonas se pisen.
   Los 5 en `--color-marca-oscuro` (`#B8241A`), no el vivo `--color-marca`
   (iteracion 31): con la foto actual del hero (cielo claro donde antes
   habia techo oscuro, y un rojo de marca mas vivo en el propio archivo)
   el vivo se quedaba en 2.22-2.64:1 en escritorio, muy por debajo del
   3.5:1 exigido -umbral mas estricto que el 3:1 de la norma a proposito,
   porque la foto del hero ya ha cambiado cinco veces y un valor al filo
   se rompe en silencio con la siguiente-. El oscuro, contra un fondo
   claro, contrasta mas que el vivo (`currentColor` en los 5 iconos: un
   unico punto de cambio), pero tampoco bastaba solo: 3.30-3.45:1 en
   escritorio a la opacidad de entonces (0.70). Se probo primero SOLO el
   cambio de color, tal y como se pidio, antes de tocar la opacidad -no
   llego-, asi que se subio tambien `--header-opacity` a 0.75 (ver esa
   variable en `:root`). Con los dos cambios juntos: **3.30-5.10:1
   segun icono y medida, minimo 3.71:1 en escritorio** (el peor caso),
   medido con la misma metodologia de las iteraciones 22-26 (icono/logo
   ocultos, captura real, pixel exacto bajo cada uno). El logo (colores
   propios del archivo, no tokens) se remidio aparte: el trazo negro
   sigue de sobra (14.21-15.99:1), pero el trazo naranja propio
   (`rgb(255,48,20)`) da **2.52-3.01:1, sin llegar ni al 3:1 base** en
   ninguna medida -no es un problema de opacidad ni de color de icono,
   es el propio naranja del archivo del logo contra este fondo; no se
   recolorea porque no se pidio, sigue pendiente de decision del
   cliente, igual que en las iteraciones 21/24/26-.
   WhatsApp y Facebook en silueta rellena (`fill="currentColor"`), sin
   cambios en esta iteracion. */
.site-header__icons {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-shrink: 0;
}
.site-header__icons a,
.site-header__icons .menu-toggle {
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  color: var(--color-marca-oscuro);
  flex-shrink: 0;
  transition: transform 0.15s cubic-bezier(0.16, 1, 0.3, 1);
}
.site-header__icons a:active,
.site-header__icons .menu-toggle:active {
  transform: scale(0.9);
}
.site-header__icons svg {
  width: 24px;
  height: 24px;
}

/* 7. Hero --------------------------------------------------------------
   Foto real a pantalla completa (object-fit: cover, no se deforma) con
   un overlay oscuro degradado encima para que los botones lean con
   contraste AA sobre cualquier zona de la imagen. position:absolute;
   inset:0 (dentro de .hero-screen, que ya es position:relative,
   seccion 6): ocupa TODO el alto de la pantalla, de borde a borde,
   pasando por detras de la barra superior (que ahora es cristal
   translucido en vez de solida) en vez de empezar justo debajo de
   ella. Antes era "flex:1" y arrancaba debajo de la barra; se cambio
   a proposito para que la foto se vea A TRAVES del cristal de la
   barra. Misma estructura en las dos orientaciones (sin ramas por
   orientation): se probo un "flex:1" solo en paisaje en la iteracion
   21 para despejar el rotulo de la barra, y se revirtio en la 22
   porque dejaba la barra sin foto detras (el 20% de tinte se veia
   blanco, sin blur real) -ver esa iteracion en el historial para el
   porque y la consecuencia aceptada-. */
.hero {
  position: absolute;
  inset: 0;
  /* isolation:isolate obliga a .hero a crear su propio contexto de
     apilamiento. Sin esto, position:relative/absolute por si solo NO
     crea uno (solo lo hace junto con z-index != auto), asi que el
     z-index negativo de hero__media/hero__scrim (mas abajo) quedaba
     evaluado en un contexto de apilamiento distinto y mas alto en el
     arbol (el de .hero-screen o superior); en ese contexto, el fondo
     negro solido de este mismo .hero pintaba por encima de la foto y
     del overlay en vez de por debajo, y por eso solo se veia negro.
     Con isolation:isolate, la foto y el overlay quedan garantizados
     por encima del fondo negro de respaldo y por debajo del contenido
     (los CTAs), que es el orden correcto. */
  isolation: isolate;
  display: flex;
  background: var(--color-negro); /* respaldo solido si la foto no llega a cargar */
  color: var(--color-blanco);
  overflow: hidden;
}
.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Dos fotos con direccion de arte distinta segun la ORIENTACION real
     del viewport (ver <picture> en index.html): en retrato el camion
     queda abajo, en paisaje a la derecha. object-position sigue al
     rotulo/camiones en cada caso para que el recorte de object-fit:cover
     nunca se los coma. Recalculado por completo para las fotos nuevas
     (mismo nombre de archivo, composicion distinta: mucho mas cielo
     encima del rotulo, rotulo mas abajo y mas a la derecha) midiendo
     por pixeles donde cae el panel en cada foto -no a ojo-, igual
     metodologia que la iteracion 18: se escaneo la imagen real en busca
     del rectangulo blanco puro (RGB>230, canales casi iguales) que
     forma el panel, verificado ademas con capturas ampliadas con
     rejilla superpuesta antes de fiarse del numero. Los valores de la
     ronda anterior (92% 32% en un unico juego para las dos
     orientaciones, ya sin distincion real por un ajuste manual previo)
     quedaron atados a la foto vieja y ya no significan nada con esta. */
}
@media (orientation: portrait) {
  .hero__media img {
    /* Foto retrato 900x1600. Panel medido: x=372-793 (41%-88% del
       ancho), y=395-628 (25%-39% del alto). Camiones: y=655-940
       (41%-59%), truncados por el propio borde derecho del archivo
       (el camion derecho ya sale cortado en la foto original, no es
       un recorte nuestro) -el panel es la prioridad si entran en
       conflicto, tal y como se decidio en la iteracion 18-.
       X=60%: a 375-390px de ancho SI hay recorte horizontal real (el
       contenedor es mas estrecho que la foto en esa proporcion).
       Calculado para que el borde derecho del panel (793px) quede
       dentro de la ventana visible con margen de sobra (no al filo),
       aceptando cortar la punta izquierda del camion izquierdo -mismo
       compromiso que la iteracion 18, con estos numeros nuevos-.
       Y=30%: a 768x1024 (tablet en vertical) SI hay recorte vertical
       real; con estos margenes tan amplios (panel y camiones ocupan
       solo el 25%-59% del alto, hay mucho cielo y mucho suelo de
       sobra) cualquier Y entre 0%-33% deja panel Y camiones completos,
       30% dentro de ese rango con margen a ambos lados. */
    object-position: 60% 30%;
  }
}
@media (orientation: landscape) {
  .hero__media img {
    /* Foto paisaje 1600x900, EXACTAMENTE 16:9 -igual que 1920x1080 y
       casi igual que 1366x768-: a esas dos resoluciones de escritorio
       (las que pide este encargo) object-fit:cover no recorta casi
       nada en ningun eje, asi que object-position apenas tiene efecto
       real ahi; el valor de abajo es para cuando la ventana del
       navegador se redimensiona a otras proporciones (no verificado
       para proporciones muy anchas tipo 21:9, fuera del alcance de
       esta verificacion). Panel medido: x=735-1305 (46%-82% del
       ancho), y=55-305 (6%-34% del alto, con el borde derecho un poco
       mas alto que el izquierdo, panel ligeramente inclinado en la
       foto). Camiones: x=195-1490, y=335-800. */
    object-position: 55% 35%;
  }
}
  
/* Vinieta. La franja oscurecida en los primeros ~120px que llevaba
   esta regla base ya no hace falta para el logo/iconos: eran para dar
   contraste a traves de una barra casi transparente (20% de opacidad,
   iteraciones 13-22), y la barra es blanca semitransparente CON CUERPO
   (0.90) desde la 23, que se basta sola. La iteracion 25 la volvio a
   necesitar brevemente al bajar la barra a 0.25, pero ese camino se
   cerro en la 26 (ver `.site-header` y `.site-header__icons`): la
   barra vuelve a 0.90 y esta franja inicial vuelve a sobrar, igual que
   en la 23-24. La regla base de aqui abajo (sin franja inicial) ya NO
   se usa en ningun caso real -retrato y paisaje la sobrescriben los
   dos, ver ambos `@media`-, se deja solo como valor de respaldo por si
   algun dia falta la orientacion real.
   El badge y los botones no dependen de este oscurecido para su propio
   contraste (texto con su propio text-shadow el uno, rellenos solidos
   opacos los otros), pero la FRASE si -es texto plano sin caja, igual
   que el badge, pero mas grande y con una sombra mas suave-, asi que
   cada orientacion refuerza el degradado justo en la franja donde vive
   la frase ahora (ver mas abajo): el resto se mantiene suave para que
   la foto se vea luminosa donde no hace falta oscurecer. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(
    180deg,
    rgba(12, 14, 18, 0.35) 0px,
    rgba(12, 14, 18, 0.12) 120px,
    rgba(12, 14, 18, 0.08) 45%,
    rgba(12, 14, 18, 0.15) 65%,
    rgba(12, 14, 18, 0.26) 85%,
    rgba(12, 14, 18, 0.3) 100%
  );
}
/* Retrato: con el bloque anclado ABAJO, la frase vive ahora en el
   tramo final de la foto (justo encima de badge+botones), no en el
   cielo de arriba. La primera version de esta franja (una rampa de
   40%-58%-78%-100%) parecia de sobra en la mayoria de alturas, pero a
   568px de alto la frase cae justo en el tramo de rampa todavia sin
   terminar y solo llegaba a 4.33:1 -por debajo del 4.5:1 exigido,
   detectado midiendo el pixel real de la foto (con el texto oculto)
   en las 4 alturas probadas, no asumido-. Calculando el peor caso
   posible (un pixel de foto blanco puro, no solo el actual): hace
   falta >=55% de opacidad negra para que el blanco calido del texto
   (`--glass-tint-rgb`) siga dando >=4.5:1 incluso contra blanco puro,
   verificado con la formula de contraste antes de escribir esto. Se
   usa 60% piano (sin rampa larga: sube directo de 40% a 55% y se
   queda ahi hasta el final) para que TODA la franja donde puede caer
   la frase segun la altura del viewport (comprobado en
   568/667/700/844px) tenga la misma proteccion completa, sin zonas
   intermedias a medio oscurecer. El tramo de arriba (rotulo y
   camiones) no se toca.
   NO lleva franja oscura inicial: en retrato nada mas vive ahi -el
   bloque esta anclado ABAJO, la frase no llega hasta arriba-, y con la
   barra en 0.90 (iteracion 26, de vuelta al valor de la 23) el
   logo/iconos no necesitan ninguna ayuda de la foto. La iteracion 25
   habia anadido aqui una franja de 0.75 en los primeros 100px para
   compensar una barra mucho mas transparente (0.25); esa barra se
   descarto por completo en la 26 (ver `.site-header`), asi que la
   franja se retira con ella, no se deja a medias. El resto del
   degradado (0.08 desde arriba hasta el 40%) es el mismo valor "claro"
   de siempre en la franja media. */
@media (orientation: portrait) {
  .hero__scrim {
    background: linear-gradient(
      180deg,
      rgba(12, 14, 18, 0.08) 0%,
      rgba(12, 14, 18, 0.08) 40%,
      rgba(12, 14, 18, 0.6) 55%,
      rgba(12, 14, 18, 0.6) 100%
    );
  }
}
/* Paisaje: la frase sigue en su sitio de siempre (arriba a la
   izquierda, sin cambios de posicion), pero ahora es mas grande y con
   una sombra mas suave, asi que necesita el mismo refuerzo que arriba:
   la franja oscura inicial se extiende de 120px a 260px (cubre
   header + badge + botones + frase con margen en las alturas de
   escritorio probadas, 768-1080px).
   ESTA franja NO se pudo quitar ni reducir cuando se intento en la 23
   (a diferencia de la de retrato): aqui SI vive algo mas en esas
   mismas filas -la frase, el badge y los botones, todos por encima de
   los primeros 260px en las alturas de escritorio probadas-, y el
   degradado es vertical a todo el ancho: no distingue la barra (a la
   derecha, sobre el rotulo) de la frase (a la izquierda), asi que no
   se puede aliviar una sin desproteger la otra.
   La OPACIDAD de esta franja subio de 0.58 a 0.75 en la iteracion 25
   -no por la frase (que ya estaba de sobra protegida con 0.58,
   calculado contra el peor caso posible de un pixel blanco puro, ver
   el historial de la iteracion 20), sino porque esa misma franja tenia
   que hacer tambien el trabajo de la barra (entonces al 0.25) para dar
   contraste a los iconos-. La iteracion 26 descarta por completo el
   camino de bajar la opacidad de la barra (ver `.site-header`), asi
   que esa razon desaparece: **vuelve a 0.58**, el valor exacto de
   antes de la 25, calculado unicamente para proteger la frase. Sigue
   sin poder quitarse ni reducirse por la misma razon de siempre (un
   degradado vertical no distingue la barra de la frase en la misma
   fila), pero ya no necesita ser mas fuerte de lo que la frase pide
   por si sola, porque la barra a 0.90 vuelve a bastarse sola para el
   logo/iconos. */
@media (orientation: landscape) {
  .hero__scrim {
    background: linear-gradient(
      180deg,
      rgba(12, 14, 18, 0.58) 0px,
      rgba(12, 14, 18, 0.58) 260px,
      rgba(12, 14, 18, 0.08) 45%,
      rgba(12, 14, 18, 0.15) 65%,
      rgba(12, 14, 18, 0.26) 85%,
      rgba(12, 14, 18, 0.3) 100%
    );
  }
}

/* El bloque (frase + badge + botones) cambia de anclaje segun la
   orientacion, a proposito -ya NO es "arriba en las dos" como en la
   iteracion 14-.
   RETRATO: se ancla ABAJO (align-items: flex-end). Con la foto y el
   object-position actuales, el rotulo y los camiones caen en la mitad
   superior/media (medido: rotulo 22%-42% de la foto, camiones
   48%-68%), dejando la explanada de hormigon del tercio inferior
   (68%-100%) totalmente despejada -y es ademas la zona MEJOR para el
   bloque: oscura, sin detalle, con reflejos calidos, en la zona del
   pulgar-. Esto YA se probo en la iteracion 11 y se deshizo en la 14
   porque el cliente cambio la foto y los camiones subieron a esa
   franja; con la foto actual vuelve a estar libre, verificado con
   captura antes de aplicarlo, no asumido. Si la foto cambia otra vez,
   HAY QUE volver a verificar esto con capturas reales.
   PAISAJE: sin cambios de posicion respecto a la iteracion anterior
   (sigue anclado arriba, `padding-top` con el mismo clamp de siempre):
   ahi los camiones ocupan el centro/abajo y la fachada oscura de
   arriba a la izquierda sigue siendo la zona despejada fiable. */
.hero__content {
  flex: 1;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  width: 100%;
  padding-inline: var(--space-5);
  padding-top: var(--space-6);
  /* padding-bottom con clamp(vh): mas generoso en un viewport alto,
     mas ajustado (pero nunca cero) en uno bajo, siempre por encima del
     borde fisico/la home indicator via env(safe-area-inset-bottom). */
  padding-bottom: calc(env(safe-area-inset-bottom, 0px) + clamp(1.5rem, 5vh, var(--space-7)));
}
@media (orientation: landscape) {
  .hero__content {
    align-items: flex-start;
    justify-content: flex-start;
    text-align: left;
    padding-left: clamp(2.5rem, 8vw, 8rem); /* ~8% del ancho en viewports intermedios */
    /* El minimo del clamp tiene que quedar por encima de la altura REAL
       de la barra (acoplado a mano, no independiente: si la barra
       cambia de alto, este numero hay que revisarlo con ella).
       Bajado de 5rem (80px) a 3.75rem (60px) en la iteracion 32: la
       barra bajo de 75px a 53px en paisaje (para despejar el rotulo,
       ver `.site-header`), asi que el margen de 80px se habia quedado
       de sobra -no roto, pero desacoplado, dejando un hueco de mas
       entre la barra y el bloque de contacto sin necesidad-. 60px deja
       ~7px de margen real sobre los 53px de la barra, verificado con
       `getBoundingClientRect()` sin solape en 1366x768, 1600x900,
       1920x1080 y en paisaje bajo (812x375, 926x428). */
    padding-top: calc(env(safe-area-inset-top, 0px) + clamp(3.75rem, 9vh, 6.5rem));
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-7));
  }
}

/* Bloque anclado: badge + botones, apilados con un espaciado fijo
   (multiplo de 8) para leerse como una sola pieza diseniada. Sin
   tarjeta/cristal contenedor: badge y botones flotan sueltos
   directamente sobre la foto, cada uno con su propia sombra de
   contacto (text-shadow en el badge, box-shadow tinido en los
   botones, ver mas abajo) en vez de una caja compartida -menos
   elemento visual entre el usuario y la foto, y bastante menos alto
   ocupado en movil que la tarjeta de la iteracion anterior-. */
/* align-items: flex-start en las dos orientaciones (antes solo en
   paisaje, retrato centraba): con dos botones ocupando buena parte del
   ancho, un bloque alineado a la izquierda -frase, badge y botones
   compartiendo el mismo borde- se lee como una unidad intencionada, no
   como piezas centradas por separado. El bloque en si sigue centrado
   en la pantalla en retrato (`justify-content: center` en
   `.hero__content`); esto solo cambia como se alinean SUS hijos entre
   si. */
.hero__anchor {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2); /* 8px: compacto entre badge y botones */
}

/* Badge "4,9 en Google": texto blanco flotando directamente sobre la
   foto (sin caja/cristal detras), con su propio text-shadow como
   "sombra de contacto" -el equivalente del box-shadow de un boton
   cuando no hay una caja de la que colgarlo-. El icono SVG no hereda
   text-shadow (no es texto), asi que lleva su propio drop-shadow a
   juego. Facil de quitar: es un unico div, ver comentario en
   index.html. */
.hero__trust {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-blanco);
  font-family: var(--font-mono, inherit);
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-shadow: 0 2px 10px rgba(12, 14, 18, 0.55), 0 1px 3px rgba(12, 14, 18, 0.4);
}
.hero__trust svg {
  width: 1rem;
  height: 1rem;
  color: var(--color-marca);
  flex-shrink: 0;
  filter: drop-shadow(0 1px 2px rgba(12, 14, 18, 0.5));
}

/* Llamar y WhatsApp, ahora las dos pastillas del mismo tamano/lenguaje
   (ver .btn--whatsapp mas arriba): en una fila cuando caben, apiladas
   (wrap normal de .btn-row) cuando no -a 375px, "Llamar" + "WhatsApp"
   con icono y texto en las dos no caben en una sola linea dentro de la
   tarjeta-. Al apilarse cada boton conserva su propio ancho (no se
   fuerza a ocupar todo el ancho de la tarjeta): se ve como dos CTAs
   igual de importantes, no una jerarquia forzada. */
.hero__ctas { justify-content: center; align-items: center; gap: var(--space-3); }
@media (orientation: landscape) {
  .hero__ctas { justify-content: flex-start; }
}

/* Frase de marca: titular real, ya no una nota de interfaz. Sube a
   Saira Condensed (la display que la pagina ya carga; antes usaba la
   de cuerpo, el mismo registro que los botones, por eso se leia como
   interfaz y no como voz de marca), bastante mas grande, interlineado
   ceñido (1.1) y DOS PESOS dentro de la propia frase -mas fuerte la
   primera linea (`.hero__slogan-strong`), mas ligera la segunda
   (`.hero__slogan-light`)- para que la jerarquia viva en el texto, no
   en una caja. Color: blanco CALIDO reutilizando `--glass-tint-rgb`
   (el mismo tinte que ya comparten la barra y el badge) en vez de
   blanco frio puro, con una unica sombra ancha y suave (no las dos
   capas duras de antes) para que se integre en una foto de atardecer
   en vez de leerse superpuesta. Dos lineas fijas (`<br>` en el HTML,
   no wrap natural). Alineada a la izquierda en las dos orientaciones
   (antes centrada en retrato): con dos botones ocupando buen ancho, un
   bloque alineado a la izquierda -frase, badge y botones a un mismo
   borde- se lee mucho mas intencionado que todo centrado.
   `margin-top` es la pieza que materializa el "margen de seguridad"
   pedido: con el bloque ahora anclado ABAJO, lo que hay que proteger
   es la distancia entre la frase (primer hijo del bloque) y los
   camiones/el rotulo por ENCIMA, asi que el margen vive arriba, no
   abajo como en la iteracion anterior (bloque arriba del todo,
   protegiendo el hueco hacia abajo). clamp(vh): mas margen en un
   viewport alto, menos -pero nunca cero- en uno bajo. */
.hero__slogan {
  margin: 0;
  margin-top: clamp(0.75rem, 3.5vh, 2.5rem);
  margin-bottom: var(--space-3);
  max-width: 20rem;
  font-family: var(--font-display);
  /* Tope en 1.1rem (17.6px), no 1.5rem: al anadir "en Gran Canaria" la
     SEGUNDA linea paso a ser la que manda (antes era la primera). A
     18.5px ya mide 313px de los 320px disponibles -demasiado justo-, y
     a 20px+ pasa de esos 320px y se parte en dos, rompiendo las "dos
     lineas" pedidas -medido con canvas.measureText() sobre la fuente
     real, no a ojo-. 17.6px deja margen de sobra (~298px de 320). */
  font-size: clamp(0.95rem, 2.6vh, 1.1rem);
  line-height: 1.1;
  color: rgb(var(--glass-tint-rgb));
  text-align: left;
  text-shadow: 0 6px 28px rgba(12, 14, 18, 0.55);
}
/* Acento de marca: UNA regla fina en --color-marca, no una palabra en
   rojo dentro del texto (la otra opcion posible). Decorativa
   (`::before`, no es texto real), asi que no le aplica el umbral de
   contraste de WCAG -a diferencia de una palabra en rojo, que habria
   exigido recalcular el contraste cada vez que la foto de fondo
   cambiase (ya ha pasado varias veces en este proyecto) y que
   --color-marca brillante no siempre pasa (mismo problema ya visto en
   los botones, ver seccion de arriba: ahi hizo falta --color-marca-
   oscuro). La regla ata la tipografia al rojo del logo sin ese riesgo,
   nunca hay que volver a verificarla si la foto cambia. */
.hero__slogan::before {
  content: "";
  display: block;
  width: 2.75rem;
  height: 3px;
  margin-bottom: var(--space-2);
  background: var(--color-marca);
}
.hero__slogan-strong { font-weight: 800; }
.hero__slogan-light { font-weight: 500; }
@media (orientation: landscape) {
  .hero__slogan {
    /* Tope en 1.7rem (27.2px), no 2.4rem: con "en Gran Canaria" la
       segunda linea (ahora la mas larga) mide ~650px a 2.4rem/38.4px
       -muy por encima del max-width de aqui abajo-. A 1.7rem mide
       ~456px, con margen dentro de los 480px -medido con
       canvas.measureText(), no a ojo-. */
    font-size: clamp(1.6rem, 3.2vh, 1.7rem);
    max-width: 30rem;
  }
}

/* 8. Menu -------------------------------------------------------------
   Boton hamburguesa (vive dentro de .site-header__icons, seccion 6)
   + panel lateral con Fotos y Resenas. Se cierra con el propio boton
   (el icono se transforma en una X), con Escape o tocando el fondo
   oscurecido. */
.menu-toggle {
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: none;
  background: transparent;
  color: var(--color-negro);
  transition: transform 0.15s cubic-bezier(0.16, 1, 0.3, 1);
}
.menu-toggle:active { transform: scale(0.9); }

.menu-toggle__icon {
  position: relative;
  width: 22px;
  height: 15px;
}
.menu-toggle__icon span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.25s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease, top 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
.menu-toggle__icon span:nth-child(1) { top: 0; }
.menu-toggle__icon span:nth-child(2) { top: 6px; }
.menu-toggle__icon span:nth-child(3) { top: 12px; }
.menu-toggle.is-open .menu-toggle__icon span:nth-child(1) { top: 6px; transform: rotate(45deg); }
.menu-toggle.is-open .menu-toggle__icon span:nth-child(2) { opacity: 0; }
.menu-toggle.is-open .menu-toggle__icon span:nth-child(3) { top: 6px; transform: rotate(-45deg); }

.menu-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu-backdrop);
  background: rgba(12, 14, 18, 0.55);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
}
.menu-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

.menu-panel {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  height: 100dvh;
  width: min(100%, var(--menu-width));
  z-index: var(--z-menu-panel);
  background: var(--color-blanco);
  color: var(--color-negro);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  padding: calc(env(safe-area-inset-top, 0px) + var(--space-9)) var(--space-5) calc(env(safe-area-inset-bottom, 0px) + var(--space-6));
  box-shadow: -12px 0 40px rgba(12, 14, 18, 0.18);
  transform: translateX(100%);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.menu-panel.is-open { transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .menu-toggle__icon span,
  .menu-backdrop,
  .menu-panel {
    transition: none;
  }
}

/* El menu abierto bloquea el scroll de fondo y revela los flotantes
   (ver seccion 11). */
body.menu-open { overflow: hidden; }

.menu-section h2 { margin-bottom: var(--space-5); }
.menu-section__link { margin-top: var(--space-5); }
.menu-section__link a { text-decoration: underline; }

/* 9. Galeria ------------------------------------------------------------------
   8 fotos reales del equipo, elegidas de un lote de 21 que paso el
   cliente (ver README para el listado completo y por que se
   descartaron las otras 13). Dos columnas fijas: el panel del menu
   nunca es mas ancho que --menu-width, asi que mas columnas quedarian
   demasiado apretadas. Con 8 en 2 columnas hay scroll vertical dentro
   del panel (ya lo tenia con 3, `overflow-y:auto` en `.menu-panel`),
   no hace falta nada especial. */
.gallery__grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(2, 1fr);
}
.gallery__item {
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-plata-300);
  aspect-ratio: 3 / 4;
}
.gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* 10. Resenas ------------------------------------------------------------------
   Una sola columna: igual que la galeria, el panel del menu es
   demasiado estrecho para tarjetas de resena en varias columnas. */
.reviews__head { margin-bottom: var(--space-6); }
.rating-badge {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.rating-badge__score {
  font-family: var(--font-display);
  font-size: 2.4rem;
  font-weight: 800;
  line-height: 1;
}
.rating-badge__stars svg { width: 1rem; height: 1rem; color: var(--color-marca); }
.rating-badge__stars { display: flex; gap: 2px; margin-bottom: 0.25rem; }
.rating-badge__meta { font-size: 0.85rem; color: var(--color-plata-700); }

.reviews__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: 1fr;
}
.review-card {
  background: var(--color-plata-100);
  border: 1px solid var(--color-plata-300);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.review-card__stars { display: flex; gap: 2px; }
.review-card__stars svg { width: 0.95rem; height: 0.95rem; color: var(--color-marca); }
.review-card p { color: var(--color-negro); }
.review-card__author {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--color-plata-700);
  letter-spacing: 0.02em;
}

/* 11. Botones flotantes -----------------------------------------------------
   La pantalla principal ya tiene los CTAs grandes de Llamar/WhatsApp,
   asi que estos flotantes se quedan invisibles ahi (evita duplicar).
   Solo se revelan mientras el menu esta abierto (body.menu-open), para
   no perder el contacto de vista al hojear Fotos/Resenas. z-index por
   encima del panel para que no queden tapados por el scroll interno. */
.floating {
  position: fixed;
  z-index: var(--z-floating);
  bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-5));
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  box-shadow: var(--shadow-md);
  color: var(--color-blanco);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.15s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}
body.menu-open .floating {
  opacity: 1;
  pointer-events: auto;
}
.floating:active { transform: scale(0.94); }
.floating svg { width: 1.6rem; height: 1.6rem; }
.floating--call {
  left: var(--space-5);
  background: var(--color-negro);
}
.floating--whatsapp {
  right: var(--space-5);
  background: #25D366;
}
@media (prefers-reduced-motion: reduce) {
  .floating { transition: opacity 0.2s ease; }
}

/* 12. Footer ------------------------------------------------------------------
   Sobrio: nombre, correo de contacto, enlaces legales y credito. Fondo
   oscuro (coherente con el resto de la marca) para que, si el header
   fijo de arriba (seccion 6) queda visible al llegar aqui haciendo
   scroll, el boton outline en blanco siga teniendo contraste. */
.site-footer {
  background: var(--color-negro);
  color: var(--color-plata-300);
  padding-block: var(--space-6);
}
.site-footer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4) var(--space-6);
}
.site-footer__brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.1rem;
  letter-spacing: 0.01em;
  color: var(--color-plata-100);
}
.site-footer__email {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
.site-footer__email svg { width: 1.15rem; height: 1.15rem; flex-shrink: 0; }
.site-footer__email span { text-decoration: underline; }
.site-footer__email:hover { color: var(--color-plata-100); }
.site-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  font-size: 0.9rem;
}
.site-footer__legal a:hover, .site-footer__legal button:hover { color: var(--color-plata-100); }
/* "Configurar cookies" reabre el banner (js/main.js): es un <button>, no un
   enlace real (no navega a ningun sitio), pero tiene que leerse igual que
   "Aviso legal"/"Politica de privacidad" a su lado -mismo tipo, mismo
   tamano, mismo color-, no como un boton de interfaz aparte. */
.site-footer__legal button {
  background: none;
  border: none;
  padding: 0;
  color: inherit;
}
.site-footer__bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.08);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  font-size: 0.8rem;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--space-3);
}

/* 13. Banner de cookies ------------------------------------------------------
   Pendiente de revision legal. Consentimiento minimo antes de disparar gtag. */
.cookie-banner {
  position: fixed;
  left: var(--space-4);
  right: var(--space-4);
  bottom: var(--space-4);
  z-index: var(--z-cookie);
  max-width: 720px;
  margin-inline: auto;
  background: var(--color-negro);
  color: var(--color-plata-100);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
}
.cookie-banner[hidden] { display: none; }
.cookie-banner p { font-size: 0.85rem; color: var(--color-plata-300); flex: 1 1 260px; }
.cookie-banner p a { color: var(--color-blanco); text-decoration: underline; }
.cookie-banner__actions { display: flex; gap: var(--space-3); flex-wrap: wrap; }

/* 14. Scroll-reveal -----------------------------------------------------------
   Infraestructura para IntersectionObserver (main.js). No hay ningun
   [data-reveal] en el markup actual: el menu se anima como panel al
   abrirse, no elemento a elemento. Se deja lista por si hiciera falta
   mas adelante (p.ej. si el panel crece mucho). */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1), transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* 15. Accesibilidad y utilidades finales --------------------------------------*/
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: -100px;
  background: var(--color-marca);
  color: var(--color-blanco);
  padding: 0.75rem 1.25rem;
  border-radius: var(--radius-md);
  z-index: 400;
  transition: top 0.2s ease;
}
.skip-link:focus { top: var(--space-4); }
