/* ===================================================
   RESET
=================================================== */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

/* Las capas de ancho fijo del broche (1440/1920px, sistema de coordenadas
   de su clip-path) sobresalen unos px del viewport cuando hay scrollbar
   vertical (clientWidth ≈ ancho − 15px) → scroll horizontal fantasma.
   `clip` lo corta sin crear un scroll container (a diferencia de `hidden`),
   así los timelines de scroll-driven animations siguen en el viewport.
   El overflow-x:hidden previo queda como fallback para navegadores viejos.

   `scroll-behavior: smooth` (2026-08-17, pedido explícito: "al hacer click
   en uno de los nav bar, quiero que haga una animación de ir allá, no un
   salto instantáneo") — cubre CUALQUIER salto a un ancla (`href="#..."`):
   los 4 links del nav (header y footer, mismos destinos), los CTA de Hero/
   Banner que apuntan a `#contacto`/`#areas`, etc. No hace falta JS: ninguno
   de esos `<a>` lleva `preventDefault` (ver `main.js`, el único que toca
   estos links solo cierra el panel off-canvas al hacer clic, no intercepta
   la navegación). `.site-header` nunca es `position:fixed`/`sticky` en
   ningún breakpoint (siempre `relative`), así que no hace falta compensar
   con `scroll-margin-top` en las secciones — el destino no queda tapado.
   Respeta `prefers-reduced-motion` automáticamente: el bloque ACCESIBILIDAD
   de abajo ya fuerza `scroll-behavior:auto !important` bajo esa preferencia
   (puesto ahí de antemano, antes de que este `smooth` existiera, ver el
   comentario de ese bloque). */
html {
  overflow-x: hidden;
  overflow-x: clip;
  scroll-behavior: smooth;
}

body {
  background: #18140f;
}

/* Bloquea el scroll del body mientras el panel está abierto */
body.nav-active {
  overflow: hidden;
}

/* ===================================================
   FUENTES (autoalojadas 2026-07-24, ver CLAUDE.md)
   Antes: 3 <link> a fonts.googleapis.com + 2 preconnect (html→css→woff2,
   2 saltos de red en el critical path) + un preload apuntando a un woff2
   de Google identificado por hash de versión que rota sin aviso. Ahora:
   los archivos viven en assets/fonts/, un solo salto (preload del woff2
   de Gloock en index.html, ver ahí).
   Subset "latin" (no la fuente completa): verificado carácter por
   carácter contra TODO el texto visible de index.html — el único
   contenido es español (á/é/í/ó/ú/ñ/Á/É/Í/Ó/Ñ/¿/¡/·/©/–/—) + ASCII,
   dentro del rango U+0000-00FF/U+2000-206F que cubre este subset (mismo
   unicode-range que ya declaraba el @font-face que servía Google). Full
   charset (cirílico/griego/vietnamita, que este sitio no usa) pesaría
   6.3× más (72.9 KB → 457.5 KB) — no vale la pena sin necesidad real.
   Inter: Google sirve un ÚNICO archivo variable-font (confirmado: tablas
   fvar/gvar, eje wght 100-900) para los 3 pesos 400/500/700 — no 3
   estáticos — así que se autoalojó igual, un solo archivo con
   font-weight:400 700 en vez de 3 @font-face por peso.
   Licencia: SIL OFL 1.1 ambas, sin "Reserved Font Name" activo — ver
   assets/fonts/OFL-Gloock.txt / OFL-Inter.txt.
=================================================== */
@font-face {
  font-family: 'Gloock';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../assets/fonts/gloock-latin-400.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: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../assets/fonts/inter-latin-variable.woff2') format('woff2-variations'), url('../assets/fonts/inter-latin-variable.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;
}

/* ===================================================
   VARIABLES
=================================================== */
:root {
  --color-bg:      #18140f;
  --color-gold:    #9d7733;
  --color-cta:     #f1b853;
  --color-cta-hover: #cd9c47; /* 2026-07-26: variante ~15% más oscura de --color-cta, para que el hover de los CTA sólidos se note por CAMBIO DE COLOR real, no solo por un filter:brightness sutil (pedido explícito: "hazle un hover... con cambio de color... para ver bien el cambio") */
  --color-white:   #ffffff;
  --color-black:   #000000;
  --color-cream:   #f6f0ea;

  /* Grilla */
  --margin-mobile:    16px;
  --margin-tablet:    40px;   /* 744px — tablet vertical */
  --margin-laptop:    64px;   /* 1024px — tablet horizontal / laptop pequeña */
  --margin-desktop:   96px;
  --margin-ultrawide: 192px;
}

/* ===================================================
   ACCESIBILIDAD — prefers-reduced-motion (2026-07-24)
   Antes de esto SOLO apertura.js respetaba esta preferencia (su propio
   check de JS antes de aplicar el clip-path animado). El resto del sitio
   (menú off-canvas, hover de botones/flechas, acordeón de Sobre mí,
   scroll-behavior:smooth del carrusel de Áreas, badge-spin del hero) no
   tenía ningún guard. En vez de ir selector por selector, snippet
   universal (mismo patrón recomendado por MDN/web.dev): acorta cada
   transición/animación del sitio a ~0 en vez de quitarla — la FUNCIÓN se
   conserva intacta (el acordeón sigue abriendo/cerrando, el menú sigue
   deslizando a su posición final, badge-spin sigue centrando el anillo,
   solo que llega ahí casi instantáneo en vez de animado). "Sin animación,
   no sin función".

   `!important` necesario: `.hero__badge-ring{animation:...}` (ver ese
   selector más abajo) se REDECLARA tal cual en 3 breakpoints distintos
   más adelante en el archivo (mismo valor, por la regla de resets 744/
   1024 de este proyecto) — sin `!important` esas redeclaraciones
   posteriores ganan la cascada (mismo orden de fuente, sin más
   especificidad) y anulan este bloque en cualquier viewport ≥744px.
   Mismo patrón que ya usa este archivo en `.hero__badge-ring text{font-size}`
   más abajo, no es un `!important` nuevo/aislado en el proyecto.

   `animation-fill-mode:forwards` es imprescindible, no cosmético: sin él,
   al terminar la única iteración (duration casi 0) el fill-mode por
   defecto (`none`) hace que la propiedad animada (`transform`) REVIERTA a
   como estaría sin la animación — y `.hero__badge-ring` no tiene ningún
   `transform` estático fuera de los keyframes (`translate(-50%,-50%)`
   viene SOLO de `from`/`to`, ver nota en `@keyframes badge-spin`), así que
   el anillo quedaría descuadrado — el mismo bug ya documentado en
   CLAUDE.md para el caso de `@keyframes` anidado en `@media`. Con
   `forwards`, el `to` final (`rotate(360deg)`, visualmente idéntico a 0deg
   por ser una vuelta completa) se queda aplicado, anillo centrado y
   estático.

   `carousel.js` usa `scrollTo({behavior:'smooth'})` en JS — un `behavior`
   explícito en `scrollTo()` gana sobre `scroll-behavior` de CSS (spec), así
   que este bloque no lo alcanza aunque `scroll-behavior:auto` esté aquí;
   el guard equivalente vive en ese archivo (matchMedia + 'auto' en vez de
   'smooth' al navegar con las flechas).
=================================================== */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-fill-mode: forwards !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ===================================================
   HEADER — BASE MOBILE (375px · 4 col · margin 15px)
=================================================== */

/*
 * z-index móvil:
 *   .site-header  → z 200  (stacking context; contiene overlay, nav y hamburger)
 *   .header__nav  → z 101  (panel off-canvas; encima del overlay)
 *   .menu-overlay → z 100  (fondo oscuro)
 *   .header__hamburger → z 150 (siempre encima del overlay y del panel)
 *
 * Nota: con position:fixed, overlay y nav posicionan contra el viewport,
 * pero al pertenecer al stacking context del header (z 200) pintan encima
 * de cualquier contenido del body con z-index inferior.
 */

.site-header {
  background: var(--color-bg);
  height: 67px;
  padding: 0 var(--margin-mobile);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  position: relative;
  z-index: 200;
}

/* --- Logo ---------------------------------------- */
.header__logo {
  text-decoration: none;
  font-family: 'Gloock', serif;
  font-size: 18px;
  font-weight: 400;
  line-height: 150%;
  letter-spacing: -0.03em;
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.logo__text      { color: var(--color-white); }
.logo__ampersand { color: var(--color-gold); }

/* --- Panel off-canvas (mobile) ------------------- */
.header__nav {
  position: fixed;
  top: 0;
  right: 0;
  height: 100vh;
  width: 80%;
  max-width: 300px;
  background: var(--color-bg);
  /* padding-top compensa la barra del header (67px) + aire visual */
  padding: 87px var(--margin-mobile) 32px;
  z-index: 101;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 0.4s cubic-bezier(0.25, 1, 0.5, 1);
}

/* Estado abierto: desliza el panel a la vista */
.header__nav.is-open {
  transform: translateX(0);
}

/* Nav list: vertical dentro del panel */
.nav__list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0;
  align-items: center;
  width: 100%;
}

.nav__link {
  display: block;
  text-decoration: none;
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 500;
  line-height: 1;
  padding: 16px 0;
  width: 100%;
  text-align: center;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
  transition: color 0.2s ease;
}

/* Hover con color, no opacity (2026-07-26, pedido explícito: "dale un
   hover al nav del header, que sea del color amarillo de los botones
   principales... que resalte") — antes solo atenuaba (`opacity:0.7`),
   apenas se notaba sobre el fondo oscuro. `--color-cta` es el mismo
   dorado de `.btn-cta`/`.hero__btn--primary`, mismo lenguaje de acento
   que ya usa el resto del sitio para "esto es interactivo". */
/* @media (hover:hover) and (pointer:fine) (2026-07-30, pedido explícito:
   "en mobile, las card y muchas cosas tienen hover, arregla eso") — sin
   esto, en touch el `:hover` se dispara con el TAP y queda "pegado" hasta
   el próximo tap en otro lado (no hay evento real de "quitar el mouse de
   encima" en touch) — se ve como un bug visual, no una animación. Este
   guard hace que TODOS los `:hover` del sitio solo apliquen en
   dispositivos con puntero fino real (mouse/trackpad); en touch los
   elementos quedan con su estado normal siempre. Mismo criterio en las
   demás reglas `:hover` de este archivo (ver `## HOVER SOLO EN DISPOSITIVOS
   CON PUNTERO` en CLAUDE.md). */
@media (hover: hover) and (pointer: fine) {
  .nav__link:hover {
    color: var(--color-cta);
  }
}

.nav__list li:last-child .nav__link {
  border-bottom: none;
}

/* --- CTA base (compartido por desktop y mobile) -- */
.btn-cta {
  display: none;
  text-decoration: none;
  background: var(--color-cta);
  color: var(--color-black);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  white-space: nowrap;
  border-radius: 8px;
  padding: 16px 22px;
  gap: 16px;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

/* Hover "primario" (2026-07-26, pedido explícito: "quiero que los
   botones del hero y header tengan hover... notorios y bonitos [pero]
   no descabellados") — lift sutil + sombra, mismo lenguaje de "lift +
   sombra" que ya usan las tarjetas (Áreas/Por qué elegirme), adaptado a
   un botón sólido. Mismos valores que `.hero__btn--primary`/
   `.contacto__submit` (los otros 2 CTA "sólidos" del sitio) para que se
   sienta un único lenguaje de botón, no 3 estilos de hover distintos.
   **Actualizado el mismo día** (pedido explícito: "hazle un hover con
   cambio de color... para ver bien el cambio"): el `filter:
   brightness(1.05)` original era demasiado sutil para notarse a simple
   vista — se reemplazó por un cambio de `background-color` real a
   `--color-cta-hover` (~15% más oscuro que `--color-cta`), mucho más
   perceptible sin dejar de ser el mismo dorado (no un color ajeno). */
@media (hover: hover) and (pointer: fine) {
  .btn-cta:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 20px -8px rgba(24, 20, 15, 0.45);
    background-color: var(--color-cta-hover);
  }
}

.btn-cta__icon {
  display: block;
  flex-shrink: 0;
  width: 16px;
  height: 16px;
}

/* CTA dentro del panel off-canvas */
.btn-cta--mobile {
  display: flex;
  margin-top: 24px;
  width: 100%;
  justify-content: center;
  white-space: normal;
  flex-shrink: 0;
}

/* --- Botón hamburguesa --------------------------- */
.header__hamburger {
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  flex-shrink: 0;
  /* z-index 150 dentro del stacking context del header:
     encima del overlay (100) y del nav (101) */
  position: relative;
  z-index: 150;
}

/* SVG contenedor */
.hamburger-icon {
  display: block;
  width: 26px;
  height: 18px;
  overflow: visible;
}

/* Las 3 barras del icono */
.hamburger-line {
  transform-box: fill-box;
  transform-origin: center;
  transition:
    transform 0.35s cubic-bezier(0.25, 1, 0.5, 1),
    opacity   0.25s ease;
}

/*
 * Animación hamburguesa → X
 *
 * Barras en el SVG (viewBox 0 0 26 18):
 *   barra 1: y=0,   h=3  → centro en y=1.5
 *   barra 2: y=7.5, h=3  → centro en y=9   (centro del icono)
 *   barra 3: y=15,  h=3  → centro en y=16.5
 *
 * translateY mueve cada barra al centro (y=9):
 *   barra 1: +7.5px  →  1.5 + 7.5 = 9 ✓
 *   barra 3: -7.5px  → 16.5 - 7.5 = 9 ✓
 */
body.nav-active .hamburger-line:nth-child(1) {
  transform: translateY(7.5px) rotate(45deg);
}

body.nav-active .hamburger-line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

body.nav-active .hamburger-line:nth-child(3) {
  transform: translateY(-7.5px) rotate(-45deg);
}

/* --- Overlay ------------------------------------- */
.menu-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s ease;
  z-index: 100;
  cursor: pointer;
}

body.nav-active .menu-overlay {
  opacity: 1;
  visibility: visible;
}

/* Fix 2026-07-23 (pedido del usuario: la sombra dorada de .hero__photo se cortaba
   contra el header): el header solo necesita su stacking elevado (z-index:200) para
   que el panel off-canvas/overlay/hamburguesa (position:fixed, ver arriba) pinten
   encima de TODA la página cuando el menú está abierto. Mientras está cerrado, no
   hace falta — de hecho el bloque 1440px de abajo YA pone `z-index:auto` en desktop
   por este mismo motivo ("Elimina el stacking context del header"), solo que ahí no
   hace falta el caso "menú abierto" (nav vuelve a flujo normal, sin off-canvas).
   Aquí, con `nav-active`, restauramos el z-index alto SOLO mientras el menú está
   realmente abierto — el resto del tiempo el header no compite con el z-index:10 de
   .hero, y la sombra que sube desde la foto (tablet) ya no queda tapada por un header
   que, cerrado, no necesita pintar por encima de nada. */
body.nav-active .site-header {
  z-index: 200;
}

/* ===================================================
   BREAKPOINT TABLET VERTICAL (min-width: 744px)
   Figma AutoHTML 744px: .header padding 24px 40px, logo
   Gloock 27.65px (= --desktop-h4-font-size). El panel
   off-canvas + hamburguesa se mantienen hasta 1440px (mismo
   mecanismo de main.js) — solo cambia la barra y el logo.
=================================================== */
@media (min-width: 744px) {
  .site-header {
    /* 24 (padding-top) + 36 (logo Gloock 27.65px, line-height normal)
       + 24 (padding-bottom) = 84px — medido contra Recursos/Imagenes
       de referencia/744.png (bloque #18140f: 84px de alto) */
    height: 84px;
    padding: 0 var(--margin-tablet);
    /* z-index:auto (2026-07-23): la sombra dorada de .hero__photo (ver ese bloque)
       sube por encima del borde del header y quedaba cortada por su fondo opaco
       (header seguía con z-index:200 del bloque mobile). Mismo criterio que ya usa
       el bloque 1440px más abajo — el `body.nav-active .site-header{z-index:200}`
       de arriba restaura la elevación completa mientras el menú off-canvas está
       realmente abierto, así que el panel/overlay/hamburguesa no pierden nada. */
    z-index: auto;
  }

  .header__logo {
    font-size: 27.65px;
  }

  /* Compensa la nueva altura de barra (84px) + el mismo "aire visual"
     de 20px que usa el bloque mobile (87 = 67 + 20) */
  .header__nav {
    padding-top: 104px;
  }
}

/* ===================================================
   BREAKPOINT TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   Mismo lenguaje que 768 (referencia real del export 744px), solo
   con más aire — el logo NO encoge de vuelta (27.65px se mantiene
   igual en 768/1024; en 1440 el propio Figma lo baja a 25px, es un
   valor congelado, no una progresión lineal — ver header 768).
=================================================== */
@media (min-width: 1024px) {
  .site-header {
    height: 92px;
    padding: 0 var(--margin-laptop);
  }

  .header__logo {
    font-size: 27.65px;
  }

  /* Compensa la altura de barra (92px) + mismo "aire visual" de 20px */
  .header__nav {
    padding-top: 112px;
  }
}

/* ===================================================
   BREAKPOINT DESKTOP (min-width: 1256px)
   12 col · margin 96px (1440+) / 64px (1256-1439) · gutter 24px
   El corte a escritorio (header con nav completo, hero, y el resto de
   secciones) se bajó de 1440px a 1280px, y LUEGO se corrigió a 1256px
   (2026-07-23, mismo día, 2 pedidos seguidos del usuario):
     1. "arregla como se ve el hero en 1280, quiero que sea como en
        desktop, adaptalo" — confirmado que el alcance real era TODO
        el sitio, no solo el hero (para no mezclar header-tablet con
        hero-escritorio). Corte inicial: `min-width:1280px`.
     2. "la curva en 1280 esta quedando mal, hazla de nuevo" — el
        corte de 1280px NUNCA se activaba en un navegador real: un
        `--window-size=1280` (o una pantalla real de 1280px) da un
        `window.innerWidth` de solo **1256px** (24px menos, reservados
        por el scrollbar/chrome del navegador) — el MISMO fenómeno ya
        documentado para tablet (`--window-size=768` → viewport real
        744px, ver más abajo). Con el corte en 1280px exacto, CUALQUIER
        ancho "1280" real caía en el tier de 1024 (tablet), mostrando
        el header con hamburguesa y la muesca simple en vez de la
        concéntrica — de ahí que "se viera mal". Verificado con
        `window.matchMedia('(min-width:1280px)').matches` → `false` a
        `--window-size=1280` (`innerWidth` real: 1256). Fix: el corte
        se movió a `min-width:1256px` — mismo criterio que ya usa el
        breakpoint de tablet (usar el ancho REAL, no el nominal).
   La mayoría de las secciones ya usan mecanismos fluidos (grid con
   `1fr`, `clamp()`, carruseles con scroll horizontal) que absorben el
   espacio de menos sin tocar nada — solo 2 tuvieron que adaptarse de
   verdad porque sus medidas eran 100% fijas y sumaban EXACTO el ancho
   de 1440 (ningún margen de sobra): Hero (columna de texto, ver ese
   bloque) y Contacto (las 2 columnas + gap, ver ese bloque — tiene su
   propio tier `min-width:1256px` con valores escalados ×0.9038 y un
   tier NUEVO `min-width:1440px` que restaura los valores reales del
   Figma). `--margin-desktop` baja a 64px en este tier (mismo valor que
   ya usa `--margin-laptop`) para darle aire de más a TODAS las
   secciones por igual — se restaura a 96px en un tier nuevo
   `@media (min-width:1440px)` más abajo. */
@media (min-width: 1256px) {
  :root {
    --margin-desktop: 64px;
  }

  /* GUARD (tools/verify_css_resets.py — ver ese script para el detalle del
     chequeo): `.header__nav{padding-top}` se toca en 744 (104px) y 1024
     (112px) y no aparece re-declarado aquí con ese nombre exacto — SAFE,
     falso positivo: `.header__nav{padding:0}` un poco más abajo es el
     shorthand `padding`, que sí cubre `padding-top` (y las otras 3
     direcciones) aunque el script solo compara nombres de propiedad
     literales y no reconoce la equivalencia shorthand/longhand. */
  /* Elimina el stacking context del header en desktop */
  .site-header {
    height: 108px;
    padding: 0 var(--margin-desktop);
    z-index: auto;
  }

  .header__logo {
    font-size: 25px;
  }

  /* Restaura nav al flujo normal */
  .header__nav {
    position: static;
    height: auto;
    width: auto;
    max-width: none;
    background: transparent;
    padding: 0;
    z-index: auto;
    display: block;
    transform: none;
    transition: none;
  }

  /* Nav list: horizontal */
  .nav__list {
    flex-direction: row;
    gap: 34px;
    align-items: center;
    width: auto;
  }

  .nav__link {
    font-size: 16px;
    padding: 0;
    width: auto;
    text-align: left;
    border-bottom: none;
    white-space: nowrap;
  }

  /* CTA desktop visible */
  .btn-cta {
    display: flex;
  }

  /* CTA mobile oculto */
  .btn-cta--mobile {
    display: none;
  }

  /* Hamburger oculto */
  .header__hamburger {
    display: none;
  }

  /* Overlay nunca visible en desktop */
  .menu-overlay {
    display: none;
  }
}

/* ===================================================
   BREAKPOINT DESKTOP ANCHO (min-width: 1440px)
   Restaura `--margin-desktop` a su valor real de Figma (96px) — el
   tier de 1280 lo bajó a 64px para darle aire a las secciones de
   ancho 100% fijo (ver comentario en ese bloque). Nada más cambia
   aquí: cada sección ya trae sus propios valores reales de 1440 en su
   bloque `@media (min-width:1256px)` (fluidos vía `clamp()`/`1fr`/
   scroll horizontal), salvo Hero y Contacto, que tienen su propia
   restauración puntual en sus respectivos bloques.
=================================================== */
@media (min-width: 1440px) {
  :root {
    --margin-desktop: 96px;
  }
}

/* ===================================================
   BREAKPOINT ULTRA-WIDE (min-width: 1920px)
   12 col · margin 192px · gutter 24px
=================================================== */
@media (min-width: 1920px) {
  .site-header {
    height: 112px;
    padding: 0 var(--margin-ultrawide);
  }

  /* Logo hereda el font-size de 1440px a propósito (ver REGLAS SIEMPRE
     → tipografía congelada en 1920px): no crece más allá de 25px. */
}

/* Global (no anidado en ningún @media): un @keyframes declarado DENTRO
   de un @media solo se registra cuando esa condición aplica — a 768/1024
   (donde el sello ya está visible pero el bloque 1440 aún no matchea)
   quedaría sin registrar y el translate(-50%,-50%) que centra el anillo
   de texto (viene únicamente de la animación, no hay transform estático)
   nunca se aplicaría, descuadrando el SVG entero. */
@keyframes badge-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

/* ===================================================
   HERO — BASE MOBILE (375px · padding 15px)
   z-index stack:
     .hero        → 10  (stacking context sobre el fondo)
     .hero__inner → 1   (grid sobre el overlay ::before)
     .hero__stats → 6   (encima del broche)
     .hero__badge → 7   (encima de todo)
=================================================== */

.hero {
  position: relative;
  z-index: 10;
  background-color: var(--color-bg);
  /* Export de Figma (AutoHTML_375/hero.png → webp): foto + marco dorado +
     glow + degradado inferior ya integrados. Se posiciona igual que en el
     diseño: ~100% del ancho del viewport, figura detrás del título/lead.
     Unidades vw → escala proporcional en otros anchos de teléfono.
     Bug real encontrado y corregido (2026-07-26, pedido explícito del
     usuario: "la imagen de fondo de jesusleika va bajando y
     desapareciendo mientras voy agrandando el ancho"): el offset
     vertical en vw (39.7vw) crece con el ANCHO del viewport, pero el
     alto real de `.hero` no crece al mismo ritmo (lo define el texto,
     que envuelve a MENOS líneas en anchos más generosos — de hecho
     `.hero` se vuelve más BAJO al crecer el ancho, no más alto). El
     resultado: a 375px el offset (148.9px) cae bien dentro de la caja;
     a 743px el mismo cálculo (295.3px) ya cayó fuera de una caja que
     además se achicó, así que la foto queda totalmente tapada por el
     overlay oscuro — confirmado con Edge headless en 375/450/550/650/743
     (la silueta se ve tenue en 450, casi nada en 550, invisible en
     650/743). Fix: `clamp()` deja que tamaño/posición escalen con vw
     igual que antes SOLO hasta ~430px (el rango real de teléfonos,
     donde el diseño de Figma sí fue calibrado) y se CONGELAN ahí para
     el resto del rango mobile (430–743px) — la imagen deja de
     seguir bajando en vez de intentar reescalar sobre una caja que ya
     no crece en la misma proporción.
     Eje X centrado (2026-07-26, mismo día, pedido explícito: "necesito
     que en todo mobile, hasta llegar a tablet, se mantenga centrada la
     imagen en el hero") — el offset fijo en vw (7vw) NO centraba: al
     congelar el ancho de la imagen en 432px arriba de ~430px de
     viewport, ese offset dejaba la imagen pegada hacia la izquierda,
     con todo el sobrante de espacio acumulándose a la derecha en vez de
     repartirse a ambos lados. `center` recalcula el offset como
     (ancho del viewport − ancho de la imagen) / 2 en cada refresco, así
     que se mantiene centrada en cualquier ancho del rango mobile, se
     congele o no el tamaño.
     Eje Y también centrado (2026-07-26, mismo día, pedido explícito:
     "debe estar centrada verticalmente") — el `clamp()` de arriba
     (149–171px desde el borde superior) era un offset FIJO, no un
     centrado real: seguía sin adaptarse al alto real de `.hero` en
     cada ancho (el mismo problema de fondo del bug original, solo que
     acotado a un rango en vez de crecer sin límite). `center` en el eje
     Y calcula (alto de `.hero` − alto de la imagen) / 2 dinámicamente,
     así que la imagen queda centrada verticalmente sin importar cuánto
     mida `.hero` en cada ancho — más robusto que el clamp anterior y
     ya no depende de mantener sincronizados los 2 números mágicos
     (149px/171px) con el alto real de la caja. */
  background-image: url('../assets/img/hero/hero-mobile.webp');
  background-repeat: no-repeat;
  background-size: clamp(377px, 100.5vw, 432px) auto;
  background-position: center;
  overflow: visible;
  display: flex;
  flex-direction: column;
  /* padding-top 40px (2026-07-23, corregido contra export real
     Recursos/AutoHTML/375/Hero.html — traía 50px, un valor viejo sin
     export propio; el literal de Figma es 40px). */
  padding: 40px var(--margin-mobile) 0;
}

/* Overlay oscuro sobre la imagen en mobile */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgba(24, 20, 15, 0.76) 0%,
    rgba(24, 20, 15, 0.92) 65%,
    rgba(24, 20, 15, 1) 100%
  );
  z-index: 0;
  pointer-events: none;
}

/* --- Inner: columna en mobile -------------------- */
.hero__inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}

/* --- Columna de texto ---------------------------- */
.hero__content {
  display: flex;
  flex-direction: column;
  gap: 24px;
  align-items: center;
  width: 100%;
}

.hero__title-group {
  display: flex;
  flex-direction: column;
  gap: 22px;
  align-items: center;
  width: 100%;
}

.hero__h1 {
  font-family: 'Gloock', serif;
  font-size: clamp(2.986rem, 1.245vw + 2.519rem, 3.815rem);
  font-weight: 400;
  line-height: calc(1.1em + 4px);
  text-align: center;
}

.hero__h1-gold  { color: var(--color-gold); }
.hero__h1-white { color: var(--color-white); }

.hero__lead {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 1rem;
  font-weight: 500;
  line-height: 24px;
  text-align: center;
}

/* --- Botones ------------------------------------- */
.hero__actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  align-items: center;
  width: 100%;
}

.hero__buttons {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
  width: 258px;
}

.hero__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  font-family: 'Inter', sans-serif;
  font-size: 1rem;
  text-decoration: none;
  padding: 14px 20px;
  width: 100%;
  transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease,
              background-color 0.2s ease, border-color 0.2s ease;
  white-space: nowrap;
}

/* 2 hovers DISTINTOS a propósito (2026-07-26, pedido explícito: "teniendo
   en mente cuál es el botón secundario, por lo cual debe tener un hover
   menos resaltante") — el primario ("Agenda tu consulta gratuita") es la
   acción que el sitio quiere priorizar, así que su hover es el mismo
   lift+sombra que `.btn-cta`/`.contacto__submit` (los otros CTA sólidos
   del sitio, mismo lenguaje). El secundario ("Ver áreas de práctica") es
   un botón "ghost" (fondo transparente, solo borde) — SIN lift: solo un
   relleno tenue del mismo dorado que ya usa su borde, para que se note
   el hover sin competir visualmente con el primario.
   **Actualizado el mismo día** (pedido explícito: "hazle un hover con
   cambio de color... para ver bien el cambio"): `filter:brightness`
   reemplazado por `background-color: var(--color-cta-hover)` (mismo
   fix que `.btn-cta`/`.contacto__submit`, ver esa nota). */
@media (hover: hover) and (pointer: fine) {
  .hero__btn--primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 20px -8px rgba(24, 20, 15, 0.45);
    background-color: var(--color-cta-hover);
  }

  .hero__btn--secondary:hover {
    background: rgba(157, 119, 51, 0.14);
    border-color: var(--color-cta);
  }
}

.hero__btn--primary {
  background: var(--color-cta);
  color: var(--color-black);
  font-weight: 700;
}

.hero__btn--secondary {
  background: transparent;
  color: var(--color-white);
  font-weight: 500;
  border: 0.3px solid var(--color-gold);
  /* En Figma (375) este botón NO se estira: abraza su contenido */
  width: auto;
  padding: 14px 22px;
}

.hero__note {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 400;
  text-align: center;
}

/* Ocultos en mobile */
.hero__media  { display: none; }
.hero__frame  { display: none; }
.hero__broche { display: none; }

/* `.hero__broche-notch` (SVG, exclusivo 1024px — ver bloque 1024/1256 más
   abajo para su geometría/posición): oculto por defecto en mobile/375 y
   744 (ahí sigue el mecanismo de siempre, `.apertura__notch`). */
.hero__broche-notch {
  display: none;
  position: absolute;
  pointer-events: none;
  z-index: 5;
}

/* --- Watermark: oculto en mobile (2026-07-23, pedido explícito del
   usuario) — 744 lo reactiva con `display:block` explícito (regla dura
   de reset, la base ya no es block). --- */
.hero__watermark {
  display: none;
  background:
    linear-gradient(180deg, rgba(24,20,15,0) 0%, rgba(24,20,15,0.47) 86.63%),
    linear-gradient(to left, rgba(255,255,255,0.2), rgba(255,255,255,0.2));
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  font-family: 'Gloock', serif;
  font-size: clamp(1.728rem, 3.134vw + 0.993rem, 3.815rem);
  font-weight: 400;
  line-height: 1.15;
  text-align: center;
  width: 100%;
  /* Figma 375: frame-150 separa contenido y watermark con gap de 22px */
  margin-top: 22px;
  position: relative;
  z-index: 1;
}

/* --- Ex-"barra de estadísticas": los 4 stats se quitan de mobile
   (pedido explícito 2026-07-23, "elimina el bar stats y pon el broche
   y su curva") — en su lugar, .hero__stats pasa a ser solo el
   contenedor de anclaje del sello. El export real (Hero.html) muestra
   el sello asomando solo 4.54px (sin muesca) — se probó fiel a ese
   número, pero el usuario pidió explícitamente la muesca de todos
   modos ("vale pero hazle la curva"), o sea el mismo hundimiento
   profundo que 744/1024 (offset "11px congelado" de GEOMETRÍA CLAVE,
   ver .apertura::before/.areas::before más abajo, mismos R/f que 744
   porque el badge ya es el mismo tamaño — ver .hero__badge). height =
   112−45 = 67px (45 = r_badge−11, mismo cálculo que 744); con el
   sello en `top:0`, sobresale 45px bajo `.hero`. */
.hero__stats {
  position: relative;
  z-index: 3;
  height: 67px;
  margin-top: 20px;
  width: 100%;
}

/* display:none SOLO en este bloque mobile — 744 la reactiva
   explícitamente (regla dura de RESET, ver CLAUDE.md), heredando el
   resto de estas propiedades (flex-direction/gap/justify-content/width)
   sin tener que repetirlas allá. */
.hero__stats-group {
  display: none;
  flex-direction: row;
  gap: 18px;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 77px;
}

.hero__stat {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
  justify-content: center;
  flex: 1;
}

.hero__stat-value {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  text-align: center;
}

.hero__stat-label {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 22px;
  letter-spacing: 0.5px;
  text-align: center;
}

.hero__stat-divider {
  width: 2px;
  height: 77px;
  background: rgba(157, 119, 51, 0.75);
  flex-shrink: 0;
}

/* Sello: círculo 112px (2026-07-23, pedido explícito del usuario,
   RECALIBRADO contra el export real que llegó el mismo día —
   Recursos/AutoHTML/375/Hero.html: `.ellipse-1{width:111px;height:113px}`,
   se redondea a círculo 112 igual que ya hace 744 con el MISMO óvalo
   — este export confirma que 375 usa el badge de TAMAÑO IDÉNTICO al de
   744, no uno escalado hacia abajo. Por eso el resto de sub-medidas del
   sello (inner circle/ring/arrow) son las MISMAS que el bloque 744 de
   abajo, ver ese bloque para el porqué de cada número. `position:absolute`
   dentro de `.hero__stats`, `top:0` — con `.hero__stats{height:67px}`
   (ver esa regla arriba) el sello sobresale 45px bajo `.hero`, para
   nestarse en la muesca de `.apertura::before` (pedido explícito del
   usuario de mantener la curva pese a que el export literal solo pedía
   4.54px de asome — ver esa regla arriba). */
.hero__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: 112px;
  height: 112px;
  background: var(--color-gold);
  border-radius: 50%;
  overflow: visible;
  flex-shrink: 0;
  z-index: 7;
}

.hero__badge::before {
  content: '';
  position: absolute;
  width: 76px;
  height: 76px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--color-bg);
  border-radius: 50%;
  z-index: 0;
}

.hero__badge-ring {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 50%;
  left: 50%;
  animation: badge-spin 22s linear infinite;
}

.hero__badge-ring text {
  font-size: 9.3px !important;
  letter-spacing: 1.2px;
}

.hero__badge-arrow {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -18px);
  display: block;
  width: 3px;
  height: 28px;
  background: white;
  z-index: 1;
}

.hero__badge-arrow::after {
  content: '';
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-top: 9px solid white;
}

/* ===================================================
   HERO — TABLET VERTICAL (min-width: 744px)
   Reescrito 2026-07-22 contra el export Figma real
   (Recursos/AutoHTML/744/Hero.html + 744.png): a diferencia de lo
   asumido antes (extender el layout mobile columna-centrada), el
   diseño de tablet SÍ tiene fila texto|foto como desktop, pero con
   el sello simple en flujo (sin la muesca/broche curvo, que sigue
   siendo exclusivo de 1440+ por su clip-path de ancho fijo).
   Reutiliza el mismo marcado hero__* de desktop (media/frame/badge),
   solo que aquí el badge queda EN FLUJO dentro de .hero__stats en
   vez de position:absolute sobre una curva.
=================================================== */
@media (min-width: 744px) {

  .hero {
    background-image: none;
    overflow: visible;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 42px;
    padding: 30px var(--margin-tablet) 0;
  }

  .hero::before { display: none; }

  /* --- Fila texto | foto (frame-164) ---------------- */
  .hero__inner {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between; /* fix 2026-07-23: la columna de texto y la de foto son
       ambas de ancho FIJO (ver .hero__media abajo) — el aire sobrante entre ambas al crecer
       el viewport (hasta 1023px) debe ir al GAP, no estirar la foto (ver bug de abajo). */
    gap: 10px;
    align-self: stretch;
  }

  /* width 445px (revertido 2026-07-23): un intento anterior lo bajó a 380px para
     dejarle más sitio a la foto, pero el usuario pidió explícitamente mantener la
     columna de texto como estaba — la foto vuelve a su tamaño anterior en vez de
     seguir robándole ancho a esta columna (ver .hero__media abajo). */
  .hero__content {
    width: 445px;
    flex-shrink: 0;
    align-items: flex-start;
  }

  .hero__title-group {
    width: 100%;
    align-items: flex-start;
  }

  .hero__h1 {
    font-size: 39.81px;
    text-align: left;
  }

  .hero__lead { text-align: left; }

  .hero__actions {
    width: 258px;
    align-items: flex-start;
  }

  /* .hero__buttons/.hero__btn: se mantiene la columna/gap/anchos de la base
     mobile (ver comentario histórico abajo) — el override que SÍ hacía falta
     aquí era `align-items`. Bug real encontrado 2026-07-23 (el usuario: "el
     segundo botón está centrado, ponlo a la izquierda"): `.hero__buttons`
     nunca reseteaba el `align-items:center` de la base mobile — invisible en
     el botón primario (258px, llena el ancho del padre, centrado vs.
     izquierda se ve igual) pero el secundario (abraza su contenido, más
     angosto) quedaba centrado bajo el primario en vez de compartir su borde
     izquierdo (medido: 65px vs. 40px del primario, 25px de más). */
  .hero__buttons { align-items: flex-start; }

  /* .hero__btn NO se toca: la base mobile ya reproduce esta fila (columna,
     gap 16px, primario ancho 100% de 258px / secundario abraza su
     contenido) — el override anterior aquí (fila + width:auto) era un
     guess equivocado, se elimina. */

  .hero__note { text-align: left; }

  /* --- Foto + marco (reutiliza el mismo asset que desktop) -------- */
  /* Bug real encontrado y corregido 2026-07-23: con `flex:1`, el ancho de esta caja
     era "lo que sobre" de la fila tras la columna de texto (fija en 445/612px) — un
     ancho que crece SIN LÍMITE junto al viewport dentro de todo el rango 744–1439px.
     Cerca de 1440 la caja se volvía muy ancha y baja (~685×338 a 1024), y `object-fit:
     cover` con un ancho tan desproporcionado respecto al alto recortaba la mitad
     superior de la foto (la cabeza quedaba fuera) — visible sobre todo cerca del techo
     del rango (1300–1439px), no a los anchos "de referencia" 744/1024 exactos donde sí
     se veía bien. Fix: ancho FIJO vía `aspect-ratio` (mismo criterio que desktop, que
     usa un ancho de columna fijo de 396px en vez de flex) — el aire sobrante de
     viewport ahora lo absorbe el gap de `.hero__inner` (space-between), no esta caja. */
  /* aspect-ratio 2026-07-23 (pedido del usuario: "adapta la foto de desktop, con
     cuadro y todo, a tablet — más grande, el cuadro a la altura de los ojos"):
     396/465 es la proporción EXACTA de la caja de la foto en desktop (antes era
     194/245.51, una medida heredada de otra sesión que no correspondía a esta
     composición) — casi idéntica a la proporción NATURAL de la propia foto
     (332×390 del atributo width/height del <img>, 0.851), así que `object-fit:
     cover` ya no necesita recortar tanto para llenar la caja, y la persona se
     ve más grande/llena (antes se veía la foto casi de cuerpo completo con
     mucho aire arriba, por una caja proporcionalmente más angosta/alta). */
  /* height RECALCULADO 2026-07-24 (pedido explícito del usuario: "reduce el
     cuadro amarillo detrás de Jesús Leika, al igual que la imagen" — mismo
     pedido que además elimina el scroll horizontal real que este recorte
     estaba causando, ver abajo): la iteración anterior (335.51px, +50px
     sobre 285.51px) excedía el ancho disponible dentro de los márgenes
     (`.hero__content` 445px + gap 10px + foto 285.71px = 740.71px, contra
     664px de espacio real entre márgenes de 40px) y dependía por completo
     de `html{overflow-x:clip}` para no generar scroll horizontal — ese
     recorte de ~37px SÍ era scrolleable en la práctica (`clip` no lo
     suprimía de forma confiable), y era la causa real del scroll horizontal
     reportado a este ancho. Nueva altura: la que hace que la foto quepa
     EXACTA dentro del presupuesto real (`664 − 445 − 10 = 209px` de ancho
     disponible → `209 × 465/396 = 245.42px` de alto, aspect-ratio 396/465
     intacto) — cero recorte, márgenes simétricos de 40px a ambos lados, sin
     depender de ningún clip para ocultar excedente. */
  .hero__media {
    display: block;
    flex: none;
    width: auto;
    aspect-ratio: 396 / 465;
    height: 245.42px;
    margin-top: 28px;
    position: relative;
    z-index: 2;
  }

  .hero__photo-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: visible;
  }

  /* object-position/top/filter: mismo mecanismo EXACTO que desktop (ver bloque
     1440), escalado por el factor de caja (245.42/465 ≈ 0.5278, recalculado
     2026-07-24 tras la reducción de tamaño de arriba). `object-position:center
     80%` + `top` son los responsables reales de que el marco quede "a la
     altura de los ojos" — sin ellos el navegador centra la foto por defecto y
     deja mucho aire sobre la cabeza. El `top` en desktop es un valor fijo
     (-69px) sobre una caja de 465px — se escala proporcional, no es un %. */
  .hero__photo {
    display: block;
    width: 100%;
    /* height explícito (mismo mecanismo que desktop, ver ese bloque —
       la <img> desborda su wrap vía `overflow:visible`, ya declarado
       arriba, en vez de agrandar el wrap/`.hero__media`). 313.48px = alto
       EXACTO del ancho real de este wrap (209px × 3072/2048) sin recortar
       nada del archivo — antes 298px recortaba parte del cuerpo por abajo
       (mismo pedido "no la cortes" que en desktop, ver CLAUDE.md). `top`
       también se subió (-36.42→-59px) para que el tope quede pegado al
       header en vez de con aire, ver ese pedido explícito ("casi llegando
       al botón del header") — historial completo en CLAUDE.md. */
    height: 313.48px;
    object-fit: cover;
    /* Foto nueva (2026-08-13): cutout a sangre completa, sin mirror —
       ver el mismo comentario en el bloque 1440 para el porqué. */
    object-position: center top;
    position: relative;
    top: -59px;
    z-index: 2;
    /* overflow:visible (2026-08-15, fix "se ve un borde en el header" —
       ver el mismo comentario, con el detalle completo, en el bloque
       1256px de más abajo). */
    overflow: visible;
    filter:
      drop-shadow(1.58px  -1.06px 3.69px rgba(157,119,51,0.10))
      drop-shadow(5.28px  -4.75px 6.86px rgba(157,119,51,0.09))
      drop-shadow(12.14px -10.56px 9.50px rgba(157,119,51,0.05))
      drop-shadow(21.64px -18.47px 11.61px rgba(157,119,51,0.01));
  }

  /* Degradado que funde foto + marco con el fondo oscuro — mismo
     mecanismo que desktop (::after superpuesto, no un `background`
     detrás de la <img>). La foto es webp SIN transparencia real en
     la zona inferior (persona + fondo de estudio, todo opaco), así
     que un `background` puesto en el propio <img> nunca se veía —
     hacía falta una capa aparte por ENCIMA para que el fundido sea
     visible. Bug real encontrado 2026-07-23: la versión anterior
     asumía (por el patrón del AutoHTML de Banner/Sobre mí, donde SÍ
     hay una capa con transparencia real detrás de un `background`)
     que este caso era igual — no lo era.
     `bottom` para cubrir el mismo desborde de la foto de arriba — mismos
     stops corridos que desktop (son porcentajes, no hace falta escalarlos).
     RECALIBRADO 2026-08-14 (pedido explícito: "el borde inferior duro de
     la imagen se disuelva por completo... y quede una separación clara
     respecto a la frase watermark de abajo"): el `0%→78%→99%` anterior
     mantenía la foto a plena vista hasta muy tarde y luego se apagaba de
     golpe en el último 21% — se notaba como un borde recto, no un fundido,
     justo la franja que roza el watermark (ver rounds de "FOTO DEL HERO
     REEMPLAZADA" en CLAUDE.md). Nuevos stops: arranca antes (42%), sube
     gradual por 2 tramos intermedios (60%/70%, 92%/88%) y llega a opaco
     total en 100% (antes 99%, con el último 1% ya siempre sólido) — sin
     tocar `top/left/right/bottom` (offsets ya calibrados, solo cambian los
     stops). Efecto colateral querido: el 12% final del box (88%→100%,
     coincide con la franja que antes solapaba el watermark) queda ya
     sólido/oscuro antes de llegar al borde real de la foto, dando el aire
     visible pedido en (b) sin mover ninguna caja. */
  .hero__photo-wrap::after {
    content: '';
    position: absolute;
    top: -8.44px;
    left: -15.83px;
    right: 2.11px;
    bottom: -68.06px;
    background: linear-gradient(180deg, rgba(24,20,15,0) 0%, rgba(24,20,15,0) 28%, rgba(24,20,15,0.15) 48%, rgba(24,20,15,0.4) 64%, rgba(24,20,15,0.65) 78%, rgba(24,20,15,0.85) 90%, rgba(24,20,15,1) 100%);
    pointer-events: none;
    z-index: 3;
  }

  /* Marco dorado 3 lados. `top`/`left`/`right`/`height` escalados
     proporcional al de 1440 (factor 245.42/465 ≈ 0.5278, recalculado
     2026-07-24 junto con la reducción de tamaño de `.hero__media` de
     arriba — mismo método ya usado en la recalibración 2026-07-23,
     `left`/width_foto = -30/396 = -7.576%; `right`/width_foto = 4/396 =
     1.010%, `top`/alto_foto = -16/465, `height`/alto_foto = 372/465 = 0.8 —
     el export de Figma no trae este elemento en el CSS, solo es visible en
     744.png, así que se deriva por escala en vez de leerse directo). El
     marco queda DETRÁS de la foto (z-index:1, igual que 1440/1920) y asoma
     por la izquierda, mismo criterio en los 3 anchos — geometría/relación
     con la foto sin cambios, solo la escala (ver CLAUDE.md, sección HEADER/
     HERO/APERTURA — TABLET, para el historial completo del bug de z-index
     que fijó este criterio).
     `left`/`right` AJUSTADOS 2026-07-24 (pedido explícito del usuario:
     "hazlo menos ancho, unos 20px menos"): el ancho total del marco
     (`ancho_foto − right + |left|`) baja ~20px repartido simétricamente
     entre los 2 lados (10px cada uno) en vez de solo en un lado — el
     asome visible por la izquierda se reduce de 15.83px a 5.83px (sigue
     asomando, no se oculta del todo detrás de la foto — eso fue
     exactamente el bug de z-index de 2026-07-23 referenciado arriba, no
     repetirlo) y el lado derecho, que ya estaba oculto bajo la foto
     (`right` positivo = marco inset detrás de ella), se hunde 10px más
     sin cambio visual (seguía invisible antes y después). **1024 recibe
     el mismo ajuste** (sigue reusando literal los valores de 744, mismo
     criterio del 2026-07-24 en REGLAS SIEMPRE).
     **Segunda vuelta, mismo día ("reducelo 50px más")**: mismo reparto
     simétrico (25px cada lado): `left` pasa de −5.83 a **19.17** — cruza a
     positivo, o sea el borde izquierdo del marco queda ahora ENTERAMENTE
     detrás de la foto (ya no asoma por ese lado, a diferencia del ajuste
     anterior que sí dejaba un resto visible). El marco sigue siendo
     visible por ARRIBA (`top:-8.44px`, sin cambios — esa franja sigue
     sobresaliendo por encima de la foto) aunque los 2 lados queden
     tapados. `right` sube a **37.11** — sin efecto visible, ya estaba
     oculto bajo la foto antes y sigue estándolo (ver ajuste anterior).
     **Tercera vuelta, mismo día ("ponlo 30px más ancho, centrado
     horizontalmente en base a Jesús Leika")**: se abandona el reparto
     asimétrico anterior (19.17/37.11, corrido ~9px hacia la izquierda
     respecto al centro real de la foto) — ahora `left`/`right` usan el
     MISMO valor (`13.14px` cada uno), lo que centra el marco exacto sobre
     el ancho de la foto en vez de simplemente resetear un offset viejo.
     Ancho total = `209 (ancho real de la foto a 744) − 13.14 − 13.14 =
     182.72px`, +30px sobre los 152.72px de la vuelta anterior. Sigue
     enteramente detrás de la foto en ambos lados (13.14 > 0, mismo
     criterio que la vuelta anterior) — visible solo por arriba, ahora más
     ancho y centrado. */
  .hero__frame {
    display: block;
    position: absolute;
    top: -8.44px;
    left: 13.14px;
    right: 13.14px;
    height: 196.34px;
    border: 2px solid rgba(157, 119, 51, 0.5);
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    pointer-events: none;
    z-index: 1;
  }

  /* --- Watermark: pasa a `position:absolute` 2026-07-24 (pedido explícito
     del usuario: "la frase ponla justo debajo de Jesús Leika, pegada").
     Hasta ahora vivía EN FLUJO (sibling de `.hero__inner` dentro de
     `.hero`, flex-column), con un `margin-top` negativo que cancelaba el
     gap de `.hero` — eso pegaba el watermark contra el borde inferior de
     lo que fuera MÁS ALTO dentro de `.hero__inner` (columna de texto o
     foto), no específicamente contra la foto. Tras la reducción de
     tamaño de `.hero__media` (2026-07-24, ver esa regla), la columna de
     texto pasó a ser más alta que la foto — el watermark, calculado
     contra el alto de la FILA completa, quedaba lejos del borde real de
     Jesús Leika en vez de pegado a ella. Fix: anclarlo directo a la foto
     con coordenadas absolutas (`.hero` ya es `position:relative` desde la
     regla base) — `top` = padding-top de `.hero` (30px) + margin-top de
     `.hero__media` (28px) + su alto (245.42px) = **303.42px**, sin
     depender del alto de la columna de texto. `left`/`right:
     var(--margin-tablet)` reproduce el mismo ancho de caja que
     `.hero__inner` (mismo padding lateral), así que `text-align:right`
     sigue alineando el texto contra el mismo borde derecho que antes
     (el de la foto, ya que `.hero__media` queda a ras de ese borde por
     el `justify-content:space-between` de `.hero__inner`) — mismo look
     horizontal, ahora con el anclaje vertical correcto.
     font-size 2026-07-23 (pedido del usuario: "ampliá el texto, ponlo como un
     h3 de la versión de tablet, o sea, como los títulos de Áreas de
     práctica"): 19.2px → 27.65px, el mismo token que usa `.areas__card-title`
     a este ancho (ver esa regla — 33.18px "congelado" del resto de anchos, con
     un achique literal a 27.65px solo en este breakpoint).
     font-size 2026-07-24 (pedido explícito del usuario: bajarlo a 22px). */
  .hero__watermark {
    display: block;
    position: absolute;
    top: 303.42px;
    left: var(--margin-tablet);
    right: var(--margin-tablet);
    width: auto;
    margin-top: 0;
    text-align: right;
    white-space: nowrap;
    font-size: 22px;
    line-height: normal;
    background:
      linear-gradient(180deg, rgba(24,20,15,0) 0%, rgba(24,20,15,0.95) 86.63%),
      linear-gradient(to left, rgba(255,255,255,0.2), rgba(255,255,255,0.2));
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    z-index: 3;
  }

  /* --- Barra de estadísticas: fila en flujo, sello centrado simple
     (sin muesca/curva — esa sigue siendo exclusiva de 1440+) -------- */
  /* padding-bottom: 2026-07-23, pedido explícito del usuario ("dale un espacio de
     abajo, como en desktop") — en desktop la barra (104px) deja ~22px de aire oscuro
     entre el texto y la línea plana/muesca (el texto no toca el borde). Aquí el texto
     tocaba directo el borde inferior (0 aire). El sello se desplaza la misma cantidad
     (ver `top` en .hero__badge abajo) para seguir asomando lo mismo sobre la curva. */
  .hero__stats {
    position: relative;
    display: flex; /* la base mobile ya no es flex (ver ese bloque, contenedor del solo-broche) */
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0;
    height: auto;
    margin-top: 0;
    width: 100%;
    padding: 0 0 20px;
  }

  /* Bug real encontrado y corregido 2026-07-23 (el usuario: "arregla el bar stats,
     con referencia a desktop"): con el sello EN FLUJO como 3er ítem flex entre los 2
     grupos, no había ningún gap real — medido con getBoundingClientRect, el borde del
     grupo izquierdo tocaba el borde del sello (0px) y lo mismo del lado derecho, muy
     distinto al aire generoso que deja desktop (33px de holgura real). Además, al ser
     `width:auto` (hug-content) cada grupo se dimensiona a su propio contenido, y como
     el texto del grupo izquierdo ("De experiencia legal y contable") es más largo que
     el derecho, el sello terminaba ~16px descentrado del eje real (no de la vista, del
     contenedor) — invisible a simple vista pero medible.
     Fix: mismo mecanismo que desktop — el sello sale del flujo (`position:absolute;
     left:50%`, ver abajo) y los 2 grupos pasan a `flex:1` (mitades EXACTAS del ancho
     disponible, inmune a que su contenido difiera en longitud), con padding-right/left
     dando la holgura hacia el centro. `min-width:0` evita que el texto más largo fuerce
     su mitad más allá del 50% (mismo problema/fix que ya usa desktop en este selector). */
  .hero__stats-group {
    display: flex;
    flex: 1;
    min-width: 0;
    height: auto;
    align-items: center;
  }

  .hero__stats-group--left  { justify-content: flex-end;   padding-right: 74px; }
  .hero__stats-group--right { justify-content: flex-start; padding-left:  74px; }

  .hero__stat { gap: 8px; }

  .hero__stat-value { font-size: 14px; }
  .hero__stat-label { font-size: 12.64px; }

  .hero__stat-divider { height: 72px; }

  /* Sello: círculo simple, diámetro ≈ el óvalo 111×113 del export, mismo criterio de
     escala que el marco (factor ~0.778 contra los 143.89px de 1440).
     `position:absolute` (2026-07-23, reemplaza el mecanismo "en flujo + margin-bottom
     negativo" — ver bug de arriba): al salir del flujo, .hero__stats ya no necesita
     compensar su alto con un margen negativo — su alto queda determinado solo por los
     2 grupos de texto (72px) + el padding-bottom de aire (20px), y el sello (112px)
     sobresale por debajo de forma natural, mismo resultado visual de siempre. `top:
     20px` (= el padding-bottom nuevo de .hero__stats) reproduce el mismo offset
     vertical que tenía el sello contra el TEXTO antes de añadir ese aire (tope del
     sello a la altura del tope del texto) — sin este ajuste el sello se quedaría
     pegado al texto y asomaría de más sobre la curva, ya que el aire nuevo solo
     empuja hacia abajo la línea plana/muesca, no el propio sello. `left:50%;
     transform:translateX(-50%)` centra el sello en el eje real de .hero__stats sin
     depender del ancho de cada grupo (ver bug de arriba). */
  .hero__badge {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    left: 50%;
    top: 20px;
    transform: translateX(-50%);
    width: 112px;
    height: 112px;
    background: var(--color-gold);
    border-radius: 50%;
    overflow: visible;
    flex-shrink: 0;
    z-index: 7;
  }

  .hero__badge::before {
    content: '';
    position: absolute;
    width: 76px;
    height: 76px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--color-bg);
    border-radius: 50%;
    z-index: 0;
  }

  .hero__badge-ring {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 50%;
    left: 50%;
    animation: badge-spin 22s linear infinite;
  }

  .hero__badge-ring text {
    font-size: 9.3px !important;
    letter-spacing: 1.2px;
  }

  .hero__badge-arrow {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -18px);
    display: block;
    width: 3px;
    height: 28px;
    background: white;
    z-index: 1;
  }

  .hero__badge-arrow::after {
    content: '';
    position: absolute;
    bottom: -8px;
    left: 50%;
    transform: translateX(-50%);
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-top: 9px solid white;
  }
}

/* ===================================================
   HERO — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (pedido explícito del usuario: "como se ve en
   1024 debe ser más parecido a tablet que a desktop"): este bloque
   escalaba cada medida de 744 ×1.376 (=1024/744), lo que hacía que
   1024 se leyera como un paso intermedio HACIA escritorio (foto/sello/
   textos más grandes) en vez de "tablet con más aire". Se revierte a
   reusar los valores YA calibrados de 744 tal cual — la única
   diferencia real que queda entre 744 y 1024 es el margen lateral de
   página (`--margin-laptop` 64px vs `--margin-tablet` 40px), no el
   tamaño del contenido. Donde el valor de 1024 coincidía con el que ya
   cae en cascada desde 744, se borra la declaración en vez de
   repetirla (mismo criterio que ya usa el proyecto).

   FOTO + MARCO — EXCEPCIÓN 2026-07-24 (pedido explícito del usuario:
   "en 1024 quiero que sea diferente, que tenga su propia [versión] — pon
   la imagen de Jesús Leika más grande y el cuadro de detrás también,
   casi como en desktop"): rompe el criterio de arriba SOLO para
   `.hero__media`/`.hero__frame`/`.hero__photo`/`.hero__photo-wrap::after`
   — el resto de Hero (sello, watermark, stats) sigue reusando 744 tal
   cual, sin cambios.
   **Vuelta atrás, mismo día ("hiciste la imagen muy grande, hazla más
   pequeña, que quede en relación a como se ve en 1440")**: el primer
   intento reutilizaba el tamaño LITERAL de escritorio (396×465, sin
   escalar) — resultó demasiado grande para 1024. Ahora se escala la caja
   de 1440 por la razón real de viewport `1024/1440 ≈ 0.71111` (mismo
   criterio ×factor que ya usa 744 desde el 2026-07-22/24, solo que aquí
   el factor es el de ancho de VIEWPORT, no uno derivado del presupuesto
   de layout): **330.67px de alto** (=465×0.71111, ancho resultante vía
   `aspect-ratio` ≈ 281.6px) — proporcional a cómo se ve en 1440, ni el
   tamaño completo de escritorio ni el chico de 744. `.hero__frame`/
   `.hero__photo`(`top`+filter)/`.hero__photo-wrap::after` se re-escalan
   con el mismo factor (0.71111) sobre los valores de 1440, mismo método
   ya usado para derivar los de 744. Espacio disponible de sobra en
   cualquier caso: `.hero__content`(550, ver bug real #11 abajo)+gap(10)+
   foto(≈282)=842 contra 896px (1024−2×64 `--margin-laptop`) — el resto
   lo absorbe el gap de `.hero__inner` (`justify-content:space-between`).

   COLUMNA DE TEXTO — BUG REAL #11 (2026-07-24, pedido explícito: "que el
   párrafo sea más largo... una línea menos, teniendo en cuenta el layout
   respecto a 744 y 1440"): `.hero__content` heredaba el ancho fijo de 744
   (445px, "no tocar" — ver esa regla, pensado para NO robarle ancho a la
   foto en ESE breakpoint) — a este ancho el `.hero__lead` envolvía en 3
   líneas. Medido en el navegador (`Range.getClientRects().length` sobre
   el nodo de texto, variando `.hero__content.width` en vivo): el punto
   exacto donde pasa a 2 líneas es **540px** — se usa **550px** (10px de
   margen de seguridad sobre ese punto justo, por si el render varía
   ligeramente entre navegadores/fuentes). Cabe de sobra sin tocar la foto
   (ver arriba): 550+10(gap)+282(foto)=842 < 896 disponibles. H1/botones
   comparten la misma columna (viven dentro de `.hero__content`) así que
   también ganan aire, efecto esperado, no un problema. */
@media (min-width: 1024px) {

  .hero {
    gap: 42px;
    padding: 30px var(--margin-laptop) 0;
  }

  .hero__content { width: 550px; }

  /* --- Foto + marco: escalados ×0.71111 (=1024/1440) sobre los valores
     de escritorio (ver comentario de arriba) --- */
  .hero__media { height: 330.67px; margin-top: 28px; }

  .hero__frame {
    top: -11.38px;
    left: -21.33px;
    right: 2.84px;
    height: 264.53px;
  }

  .hero__photo {
    /* -49.07 → -59px: mismo pedido que 744/desktop ("casi llegando al
       botón del header") — sube el tope para que quede pegado al header
       en vez de con aire. */
    top: -59px;
    /* height propio (744 ya no fija `height:100%`, ver ese bloque).
       422.37px = alto EXACTO del ancho real de este wrap (281.58px ×
       3072/2048) sin recortar nada del archivo — mismo pedido "no la
       cortes" que en desktop/744, ver CLAUDE.md. */
    height: 422.37px;
    /* overflow:visible — mismo fix que 744/1256px, ver el comentario
       completo junto a `.hero__photo` en el bloque 1256px. */
    overflow: visible;
    filter:
      drop-shadow(2.13px  -1.42px 4.98px rgba(157,119,51,0.10))
      drop-shadow(7.11px  -6.40px 9.24px rgba(157,119,51,0.09))
      drop-shadow(16.36px -14.22px 12.80px rgba(157,119,51,0.05))
      drop-shadow(29.16px -24.89px 15.64px rgba(157,119,51,0.01));
  }

  .hero__photo-wrap::after {
    top: -11.38px;
    left: -21.33px;
    right: 2.84px;
    /* Mismo criterio que el `height` de arriba */
    bottom: -91.7px;
  }

  /* `top`/`left`/`right` RECALCULADOS 2026-07-24 (ya no reusan literal los
     de 744, ver comentario "FOTO + MARCO — EXCEPCIÓN" arriba): el
     watermark quedó anclado (`position:absolute`, ver bloque 744) al
     borde inferior real de `.hero__media` — como el margen lateral aquí
     es `--margin-laptop` (64px, no `--margin-tablet` 40px), hay que
     recalcular `left`/`right` aunque el `top` dependa solo del alto de la
     foto. `top` = 30 (padding-top de `.hero`) + 28 (margin-top de
     `.hero__media`) + 330.67 (alto de la foto, ver "Vuelta atrás" arriba)
     = **388.67px**. font-size ya NO se resetea a 27.65px (valor viejo,
     previo al pedido 2026-07-24 de bajarlo a 22px) — hereda el 22px de
     744, que sí aplica aquí (el usuario no pidió un tamaño distinto para
     1024). */
  .hero__watermark {
    top: 388.67px;
    left: var(--margin-laptop);
    right: var(--margin-laptop);
  }

  /* value/label: NO se revierten a 744 (14px/12.64px) — son el valor
     "grande" congelado que ya comparten mobile Y 1440 (18px/16px), no
     forman parte del patrón ×1.376 que se está revirtiendo aquí. Sin
     esto caerían en cascada al valor más chico de 744. */
  .hero__stat-value { font-size: 18px; }
  .hero__stat-label { font-size: 16px; }

  /* --- Sello: mismo tamaño que 744 (antes 140px) --- */
  .hero__badge {
    width: 112px;
    height: 112px;
    top: 20px;
  }

  .hero__badge::before {
    width: 76px;
    height: 76px;
  }

  .hero__badge-ring text {
    font-size: 9.3px !important;
    letter-spacing: 1.2px;
  }

  .hero__badge-arrow {
    width: 3px;
    height: 28px;
    transform: translate(-50%, -18px);
  }

  .hero__badge-arrow::after {
    bottom: -8px;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-top: 9px solid white;
  }

  /* z-index:6 en `.hero__stats` (2026-07-24, ver `.hero__broche-notch`
     más abajo): sin esto, el rectángulo OPACO del nuevo broche (z-index:5,
     pero con capa de apilamiento PROPIA por ser `position:absolute`) tapaba
     el texto de los stats ("+10 años", etc.) — esos `<span>` no tienen
     z-index propio, así que sin este ajuste quedaban en la capa "auto",
     por DEBAJO de cualquier hermano con z-index explícito, sin importar
     el orden real en el DOM. Mismo criterio que ya usa `.hero__stats` en
     el bloque 1256px (z-index:6 ahí también, por el mismo motivo frente a
     `.hero__broche`). */
  .hero__stats {
    z-index: 6;
  }

  /* --- Broche 1024 — bug real #12 (2026-07-24, pedido explícito: "la
     curva que está en el hero, la que está antes de apertura, como en
     las demás resoluciones — cuidado con la línea amarilla"): a este
     ancho el sello era un círculo simple flotando sobre `.hero__stats`
     sin ninguna curva propia detrás — el "collar" lo daba `.apertura__notch`
     desde el LADO de Apertura (ver esa regla), un mecanismo distinto al
     de escritorio (>=1256px, `.hero__broche`: rectángulo del ancho del
     viewport con una muesca recortada + `filter:drop-shadow` corriendo a
     lo largo de TODA la línea plana, no solo cerca del sello). El usuario
     pidió específicamente ESE mecanismo (el del hero, con el brillo/sombra
     completo), no el de 744.
     Por qué SVG y no `clip-path` (como si usa 1256+): el usuario advirtió
     de entrada sobre la "línea amarilla" — el mismo defecto de
     antialiasing que ya obligó a abandonar `clip-path:path()` en
     `.apertura__notch` (ver esa regla, y CLAUDE.md). El `clip-path` de
     escritorio nunca mostró ese bug reportado, pero para no arriesgarlo
     aquí se construye igual que `.apertura__notch`: un `<svg><path fill>`
     con LA MISMA forma que dibujaría el clip-path (rectángulo + muesca,
     no solo la muesca aislada) — así se conserva el brillo de borde a
     borde (el `filter:drop-shadow` de abajo traza el contorno del PATH
     completo, incluida la línea recta de los costados, igual que en
     escritorio) sin depender de `clip-path`.
     Geometría: mismo R=66.89/f=15.56/dx=78.05 que ya usa `.apertura__notch`
     (idéntico badge, 112px, en los 2 lados). Canvas 1024×108 (ancho =
     viewport de este breakpoint, igual criterio que `.hero__broche` en
     1256/1440/1920 — nunca mayor al viewport real de este tier, sin
     riesgo de scroll horizontal). `flat_y`=50 dentro del canvas (línea
     plana, con margen arriba para el rectángulo — banda redundante con
     el fondo oscuro que YA pone `.hero`, inofensiva).
     Posición real (medido con `getBoundingClientRect`, badge `top:20px`
     + radio 56px dentro de `.hero__stats`, que mide 92px de alto): la
     línea plana debe caer **6px por encima** del borde inferior real de
     `.hero` (centro del sello 17px sobre ese borde, menos el offset "11"
     de la fórmula = 6px). Con `flat_y=50` dentro del canvas, el TOPE del
     canvas debe quedar en `borde_inferior − 6 − 50 = borde_inferior − 56`,
     y su FONDO en `borde_inferior − 56 + 108 = borde_inferior + 52` →
     `bottom: -52px`. **Primer intento (revertido en la misma sesión):
     `-63px`** — arrastraba un error de signo (usaba "+5" en vez de "−6"
     para la posición de la línea plana) que dejaba el canvas 11px más
     abajo de lo debido; corregido tras medir el resultado real con
     `getBoundingClientRect` y comparar contra el valor esperado. */
  /* Muesca RECALIBRADA una 5ta vez, causa raíz encontrada (2026-07-25,
     "fíjate en como está en 1440"): los 4 intentos anteriores (2 ajustando
     sombra, 2 agrandando el ring hasta calzar el ancho de Áreas) tocaban
     el SÍNTOMA sin tocar la causa real. Comparando a 1440 con el mismo
     recorte/escala real (`.apertura` forzada a `clip-path:none` para ver
     el estado de reposo — su animación scroll-driven no aplica aquí), su
     línea plana coincide EXACTO con el borde inferior real de `.hero`
     (flat_y = hero_bottom, offset 0). A 1024 en cambio la línea plana se
     calculaba 6px POR ENCIMA del borde real (ver comentario histórico
     arriba, "línea plana 6px sobre el borde") — esos 6px caen DENTRO de la
     propia caja oscura de `.hero`, redundantes e invisibles, pero como el
     fillet completo (radio 15.56) mide apenas ~10.5px de profundidad, esos
     6px "comidos" se tragaban más de la MITAD del fillet — justo el tramo
     que dibuja la transición visible de plano→curvo. Lo que quedaba
     expuesto bajo el borde real era solo la cola final del fillet + parte
     del arco grande: se leía como un quiebre casi recto, no como una curva
     (con cualquier ring, por eso agrandarlo solo tapaba el síntoma).
     Fix real: en vez de derivar flat_y desde el offset "11" congelado
     (`badge_center + 11`), se fuerza flat_y = borde inferior real de
     `.hero` (0px de holgura, igual que 1440) y se recalculan R/f/dx con el
     offset REAL que resulta (badge_center queda 17px sobre flat_y, no 11 —
     misma foto de siempre, el sello no se movió, solo se resolvió la
     muesca para que sea tangente a ESE offset real en vez de al "11"
     teórico). ring=10.94/f=15.57 (misma proporción que 1440: ring/r_badge
     ≈0.1954), R=66.94, dx=75.81. Canvas 1024×104 (antes 108), `bottom:-54px`
     (antes -52px). El filter vuelve a sus 5 drop-shadow ORIGINALES sin
     reducir — con la muesca completa expuesta (no truncada) no hace falta
     aligerar la sombra, igual que en 1440. Verificado con captura lado a
     lado a la misma escala real contra 1440 con `.apertura` forzada a
     `clip-path:none`. */
  .hero__broche-notch {
    display: block;
    bottom: -54px;
    left: 50%;
    transform: translateX(-50%);
    width: 1024px;
    height: 104px;
    filter:
      drop-shadow(0   0   20px rgba(24,20,15,0.55))
      drop-shadow(0   2px  5px rgba(24,20,15,0.10))
      drop-shadow(0   9px  9px rgba(24,20,15,0.09))
      drop-shadow(0  20px 12px rgba(24,20,15,0.05))
      drop-shadow(0  36px 14px rgba(24,20,15,0.01));
  }

  .hero__broche-notch path {
    fill: var(--color-bg);
  }
}

/* ===================================================
   HERO — DESKTOP (min-width: 1256px)
   Medidas exactas del export de Figma (AutoHTML_1440).
   Sistema de coordenadas local al .hero:
     línea plana del broche (base del hero) = 611px
     stats-bar: 507–611  ·  foto/texto: banda 48–438
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): 744/1024 tocan varias propiedades de
     Hero que no se re-declaran en este bloque con el mismo nombre — revisado
     una por una contra el CSS real, veredicto por selector:
     - `.hero{align-items,flex-direction,gap}` (744/1024): INERTE — aquí
       `.hero` pasa a `display:block`, esas props son de flex/grid y no
       aplican a un contenedor de bloque.
     - `.hero__inner{flex-direction:row,align-self}` (744): INERTE — aquí
       `.hero__inner` es `display:grid` (flex-direction no aplica) y su
       padre (`.hero`) es `display:block` (align-self no aplica, no hay
       flex/grid container por encima).
     - `.hero__content{flex-shrink}` / `.hero__media{flex:none}` (744):
       INERTE — el padre (`.hero__inner`) es grid, no flex; flex-shrink/flex
       no tienen efecto en grid items.
     - `.hero__badge{flex-shrink}` (744): INERTE — el badge es
       `position:absolute` aquí (fuera de flujo), flex-shrink no aplica a
       elementos fuera de flujo.
     - `.hero__media{position:relative}` (744): OJO, NO TOCAR — esto no es
       una fuga sino una DEPENDENCIA real: `.hero__frame` y
       `.hero__photo-wrap::after` (más abajo, `position:absolute`) necesitan
       que `.hero__media` sea su ancestro posicionado más cercano, y este
       bloque nunca declara su propio `.hero__media{position}` — depende
       por completo de que ese valor siga viniendo de 744. Si se "limpia"
       la regla de 744 pensando que es redundante, el marco/degradado de la
       foto se desalinean.
     - `.hero__media{width:auto,aspect-ratio}` (744): SAFE — `width:auto`
       es justo lo que permite que el grid item se estire a la columna de
       396px de `.hero__inner`; con width y height (465px) ya definidos,
       `aspect-ratio` se ignora (spec: solo rellena una dimensión que falte).
     - `.hero__photo-wrap{overflow:visible}` (744): SAFE — coincide con el
       valor inicial de `overflow`, 100% inerte se declare o no.
     - `.hero__actions{width}` (744:258px/1024:355px): sin efecto visible
       hoy — `.hero__buttons` es `width:auto` dentro (fila que abraza su
       propio contenido, sin `overflow:hidden` en el padre que la recorte),
       así que el ancho heredado de tablet no cambia el render. Frágil: si
       `.hero__actions` gana `overflow` o algo que dependa de su ancho en el
       futuro, este valor heredado puede volverse visible y no ser el
       correcto para 1440.
     - `.hero__buttons{align-items:flex-start}` (744): sin efecto visible
       hoy (los 2 botones miden el mismo alto, flex-start y center dan
       idéntico resultado en la fila) — frágil si algún botón cambia de
       alto.
     - `.hero__stat-value/.hero__stat-label{font-size}` (744 y 1024 con
       valores distintos): SAFE — 1024 restaura explícitamente el valor
       final (18px/16px) antes de llegar a este bloque, no hace falta
       repetirlo aquí.
     - `.hero__stats-group{min-width}` (744): FALSO POSITIVO del script —
       SÍ se resetea aquí, pero vía los selectores modificadores
       `.hero__stats-group--left/--right{min-width:0}` (texto de selector
       distinto al de 744, el script compara selectores literalmente).
     - `.hero__stats{padding-bottom}` (1024: 25px): sin verificar con
       captura — aquí `.hero__stats` usa `height:104px` fijo en vez de
       padding-bottom para su geometría, así que probablemente es inerte,
       pero revisar visualmente antes de tocar este selector.
     - `.hero__title-group{width:100%}` / `.hero__watermark{width:100%}`
       (744): SAFE — mismo valor que ya trae la base mobile, constante que
       nunca cambió, no es una fuga real. */
  .hero {
    background-image: none;
    min-height: 706px;          /* 611 + 95 (45 + 30 + 20 delta adicional foto) */
    display: block;
    padding: 0;
    overflow: visible;
  }

  .hero::before { display: none; }

  /* `.hero__broche-notch` (SVG, exclusivo 1024px — ver ese bloque): se
     oculta aquí porque desde 1256px entra el mecanismo real de escritorio
     (`.hero__broche`, clip-path animado) — sin esto quedarían los 2
     dibujados a la vez. */
  .hero__broche-notch { display: none; }

  /* Fila de 2 columnas EN FLUJO (texto | foto), sin position:absolute: si el
     H1/lead crecen (más líneas), la fila crece con ellos en vez de desbordar
     una caja de altura fija. `justify-content:space-between` reproduce el
     mismo resultado que antes lograban 2 anclas absolutas independientes
     (texto en `left:margin`, foto en `right:margin`): ambas columnas quedan
     pegadas a los bordes del padding y el aire sobrante crece ENTRE ellas
     — por eso a 1600px "respira" en vez de solo estirar un hueco muerto:
     la columna de texto también crece (ver clamp en .hero__content). El
     broche/badge/stats-bar (más abajo) siguen siendo la única pieza que
     se queda position:absolute: su curva es un `clip-path: path()` con
     coordenadas SVG fijas — no admite % ni calc(), así que no hay forma
     elástica de expresar esa geometría sin cambiar la curva. */
  .hero__inner {
    display: grid;
    /* Piso del clamp bajado de 718px a 653.6px (2026-07-23, corte a
       escritorio movido a 1256px — el ancho REAL de un ancho "1280"
       una vez descontado el scrollbar/chrome del navegador, mismo
       motivo que el ajuste 768→744 de tablet, ver "BREAKPOINT DESKTOP"
       más abajo): 718px era el valor que la fórmula "214px+35vw" da
       EXACTO en vw=1440 (214+35×14.4=718) — por eso hacía de piso fijo
       para cualquier ancho MENOR a 1440, incluido 1256. Con
       `.hero__inner{padding:...var(--margin-desktop)}` en 64px + la
       foto fija en 396px, ese piso de 718px desbordaba el contenedor
       bastante antes de llegar a 1440. 653.6px es el valor que la
       MISMA fórmula da en vw=1256 (214+35×12.56=653.6) — con el piso
       ahí en vez de en 718, la columna fluye sin quiebre desde 1256
       (653.6px) hasta 1920 (886px), pasando por 1440 (718px, mismo
       valor exacto de siempre — cero cambio visual en 1440/1920). */
    grid-template-columns: clamp(653.6px, 214px + 35vw, 886px) 396px;
    justify-content: space-between;
    align-items: start;
    gap: 0;                     /* resetea el gap:22px del stack mobile — el aire entre columnas lo da justify-content:space-between */
    padding: 68px var(--margin-desktop) 0;
  }

  /* --- Texto: columna elástica (718px en 1440 → 886px en 1920, fluida
         entre medias), centrada verticalmente dentro de la misma banda
         que la foto (altura 465px, igual que AutoHTML .texto top:50%).
         width:auto resetea el `width:612px` fijo que usa el bloque 1024
         (ahí .hero__content no vive en un grid con columna elástica) —
         sin este reset el texto quedaba encajado en 612px en vez de
         estirarse al ancho real de la columna (clamp 718–886px de
         .hero__inner), envolviendo el H1 en 3 líneas en vez de 2. --- */
  .hero__content {
    width: auto;
    height: 465px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 22px;
  }

  .hero__title-group {
    align-items: flex-start;
    gap: 22px;
  }

  .hero__h1 {
    font-size: 61.04px;
    line-height: calc(1.1em + 4px);
    text-align: left;
  }

  .hero__lead {
    font-weight: 400;
    line-height: 22px;
    text-align: left;
    max-width: 612px;
  }

  .hero__actions {
    align-items: flex-start;
    gap: 18px;
  }

  .hero__buttons {
    flex-direction: row;
    width: auto;
    gap: 10px;
  }

  .hero__btn {
    font-size: 18px;
    padding: 16px 22px;
    width: auto;
  }

  .hero__note {
    text-align: left;
    font-size: 12.8px;
  }

  /* --- Foto: 2ª columna de la grid (396px, ratio 332:390 agrandado
         +75px alto → 396×465). El ancho ya lo fija grid-template-columns
         en .hero__inner; aquí solo se redeclara la altura para que la
         fila tenga banda 465px consistente con el texto. --- */
  .hero__media {
    display: block;
    height: 465px;
    margin-top: 0; /* resetea el margin-top de 744/1024 (ver ese bug) — aquí la foto
      no necesita bajar del header, .hero usa su propio sistema (min-height:706px). */
    z-index: 2;
  }

  .hero__photo-wrap {
    position: relative;
    width: 396px;
    height: 465px;
  }

  .hero__photo {
    display: block;
    width: 100%;
    /* height explícito en vez de 100% — la <img> se hace más alta que su
       wrap (`.hero__media`/`.hero__photo-wrap`, que se queda fijo en
       465px a propósito, ver ese bloque) y desborda hacia abajo vía
       `overflow:visible` (heredado del bloque 744). Así crece la foto sin
       mover NADA que dependa del tamaño del wrap (la fila de la grid, el
       watermark). Historial completo de esta secuencia de ajustes en
       CLAUDE.md, sección "FOTO DEL HERO REEMPLAZADA".
       594px = alto EXACTO del ancho real (396px) sin recortar NADA del
       archivo (396 × 3072/2048 = 594 exacto) — antes eran 588px (6px de
       recorte deliberado por miedo a invadir el header) y la mano/anillo
       de abajo quedaba visiblemente cortada (pedido explícito 2026-08-14:
       "la mano abajo se ve cortada... que se vea completa"). Ese miedo
       era infundado: el botón "Consulta gratuita" del header termina en
       y=81 (medido con `getBoundingClientRect`), bastante antes del borde
       real del header (y=112) — hay de sobra para que el `top` de abajo
       suba sin acercarse siquiera al botón. */
    height: 594px;
    object-fit: cover;
    /* Foto nueva (2026-08-13): cutout a sangre completa (sujeto toca casi
       los 4 bordes del archivo), a diferencia de la anterior que tenía
       aire alrededor — object-position sube al tope para no recortar el
       birrete/borla (lo único que se recorta con `cover` es el margen
       inferior, ya cubierto por el degradado). Sin mirror: la pose ya
       queda centrada/ligeramente hacia la izquierda de forma natural. */
    object-position: center top;
    position: relative;
    /* -69 → -80px: junto con el `height` de arriba, deja el borde inferior
       de la foto en y=694 (5px de margen antes de `.hero__stats`, y=699)
       y el superior en y=100 (19px bajo el botón del header, y=81) — sin
       invadir ninguno de los 2, mostrando el archivo COMPLETO. */
    top: -80px;
    z-index: 2;
    /* overflow:visible (2026-08-15, pedido explícito: "la sombra de la
       imagen del hero está haciendo que se vea un borde en el header
       porque se corta la luz"). Causa real: Chrome trae `overflow:clip`
       por defecto en el UA stylesheet para `<img>` (`overflow-clip-margin:
       content-box`) — nadie lo había puesto a propósito, pero tampoco
       hacía falta: nunca se notó mientras `top`/`height` dejaban toda la
       sombra dentro del propio recuadro de la foto. Al subir `top` a
       -80px (ver comentario de arriba, "casi llegando al botón del
       header"), el `drop-shadow` de abajo (offsets negativos en Y, sube
       y se difumina hacia arriba/derecha) empezó a proyectarse POR ENCIMA
       del borde superior real de la foto — y ese `overflow:clip` implícito
       lo corta en seco justo ahí, en una línea recta, en vez de dejarlo
       desvanecerse: como `.hero` pinta por encima del header (z-index 10
       vs `auto`), esa línea de corte quedaba visible sobre el propio
       header. `overflow:visible` (el valor inicial de CSS, aquí forzado
       porque el UA stylesheet ya no lo es) deja que el filtro se extienda
       y se atenúe con naturalidad más allá del borde de la `<img>` — el
       comentario en `.hero__photo-wrap{overflow:visible}` (arriba, "la
       foto desborda vía overflow:visible") asumía que esa declaración ya
       cubría la propia `<img>` también, pero `overflow` no se hereda: cada
       elemento tiene el suyo, y la imagen nunca tuvo el suyo puesto
       explícito hasta ahora. Mismo fix aplicado en los bloques 744px/
       1024px (mismo motivo, mismo `filter` con offsets negativos en Y). */
    overflow: visible;
    filter:
      drop-shadow(3px  -2px  7px rgba(157,119,51,0.10))
      drop-shadow(10px -9px 13px rgba(157,119,51,0.09))
      drop-shadow(23px -20px 18px rgba(157,119,51,0.05))
      drop-shadow(41px -35px 22px rgba(157,119,51,0.01));
    /* mask-image (2026-08-15, pedido explícito: "la línea derecha se ve
       más larga que la izquierda, quiero que estén iguales de alta" —
       causa real encontrada comparando ambos lados con zoom: NO era el
       `mask-image` de `.hero__frame` [ya fundía parejo, confirmado
       comparando franjas verticales aisladas a la izquierda/derecha del
       marco] — era el propio `drop-shadow` de ESTA regla, desplazado
       hacia +X/-Y [derecha y arriba] en sus 4 capas, cayendo casi encima
       del borde derecho del marco (a solo 4px, contra 30px del lado
       izquierdo) y sin fundirse con nada, así que se mantenía visible
       mucho más abajo que el borde real del marco — se leía como "la
       línea derecha no se apaga". El `filter` no admite degradados
       propios, pero `mask-image` SÍ enmascara el resultado YA compuesto
       del filtro (imagen + sombra juntas) — mismos stops que
       `.hero__frame` de abajo (ambos "aumentados" al mismo tiempo, ver
       ese comentario) para que la sombra se apague en sincronía con el
       borde del marco. Coincide además con los mismos puntos del
       degradado de `.hero__photo-wrap::after` (10%→70%, ahora 20%→85%),
       así que el efecto se ve como UNA sola transición, no 2 fundiéndose
       en momentos distintos.

       Capa 2 + `mask-clip:no-clip` (2026-08-15, mismo día, pedido
       explícito: "la sombra de la foto está haciendo que se vea el borde
       del header... que el brillo suba en el header"). Causa real: por
       default `mask-clip` es `border-box` — SIN excepción para lo que un
       `filter` pinte fuera de esa caja. El `overflow:visible` de arriba
       resolvía el `overflow:clip` del UA stylesheet (ver ese comentario),
       pero `mask-image` trae SU PROPIO recorte implícito a `border-box`,
       independiente de `overflow` — confirmado forzando un
       `drop-shadow(0px -50px 0px red)` de prueba: invisible con el mask
       de una sola capa (aun con `overflow:visible`), visible en cuanto se
       agrega `mask-clip:no-clip`. Con una sola capa de gradiente, quitar
       el clip a secas NO alcanza: el propio `mask-image`, sin
       `mask-repeat:no-repeat`, se repite en mosaico verticalmente (hacia
       arriba Y abajo) más allá de su caja — por eso esta regla usa 2
       capas explícitas, ambas `no-repeat`: la 1ª es EXACTAMENTE el
       degradado de siempre (sin tocar, seguirá apagando la sombra que se
       sale por la derecha/abajo tal como ya hacía) y la 2ª es un relleno
       opaco de 90px de alto, colocado con `mask-position` NEGATIVO para
       que caiga JUSTO ENCIMA de la caja de la foto (0 a -90px, fuera del
       propio `border-box`) — 90px cubre de sobra el alcance real de la
       capa más ancha del `filter` (offset -35px + blur 22px ≈ 57px).
       Compuestas con `mask-composite` en su valor inicial (`add`, unión):
       dentro de la caja manda la capa 1 (comportamiento IDÉNTICO a antes,
       incluida la sombra que se apaga a la derecha/abajo); los 90px de
       encima quedan opacos (la sombra ya no se corta ahí); cualquier otra
       zona fuera de ambas cajas (a los lados, más abajo del borde, o más
       de 90px arriba) sigue en alpha 0, igual que con el `mask-clip`
       original — ninguna otra zona quedó "desprotegida" por accidente
       (verificado con un `drop-shadow` de prueba desplazado también en
       X: el fundido a la derecha se ve igual que antes). */
    -webkit-mask-image:
      linear-gradient(0deg, transparent 0%, transparent 20%, rgba(0,0,0,0.3) 40%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0.8) 70%, #000 85%),
      linear-gradient(#000, #000);
    mask-image:
      linear-gradient(0deg, transparent 0%, transparent 20%, rgba(0,0,0,0.3) 40%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0.8) 70%, #000 85%),
      linear-gradient(#000, #000);
    -webkit-mask-size: 100% 100%, 100% 90px;
    mask-size: 100% 100%, 100% 90px;
    -webkit-mask-position: 0 0, 0 -90px;
    mask-position: 0 0, 0 -90px;
    -webkit-mask-repeat: no-repeat, no-repeat;
    mask-repeat: no-repeat, no-repeat;
    -webkit-mask-clip: no-clip;
    mask-clip: no-clip;
  }

  /* Degradado que funde foto + marco con el fondo oscuro (cubre ambos).
     `bottom` negativo para cubrir el mismo desborde que `.hero__photo` de
     arriba — el propio `::after` también desborda el wrap, sin tocar su
     tamaño. RECALIBRADO 2026-08-14 (pedido explícito: "el borde inferior
     duro de la imagen se disuelva por completo... y quede una separación
     clara respecto a la frase watermark de abajo" — este bloque es el que
     el pedido llamaba "min-width:1440px"; en el CSS real es este,
     `min-width:1256px`, ver nota de nomenclatura en CLAUDE.md, sección
     "FOTO DEL HERO REEMPLAZADA"): los stops viejos (0%→78%→99%) dejaban la
     foto a plena vista hasta muy tarde y la apagaban de golpe en el último
     21%, leyéndose como un corte recto en vez de un fundido — justo la
     franja que roza el watermark. Nuevos stops: arranca antes (42%), sube
     gradual en 2 tramos (60%/70%, 92%/88%) y llega a opaco total en 100%
     (antes 99%) — mismos `top/left/right/bottom` de siempre, solo cambian
     los stops. El 12% final del box (88%→100%, la franja que antes
     solapaba el watermark) queda ya sólido antes del borde real de la
     foto — es lo que da el aire pedido en (b) sin mover ninguna caja. */
  /* RECALIBRADO 2026-08-15 (pedido explícito: "con o sin degradado se ve
     igual" — el usuario probó el toggle en su navegador real sin ver
     ningún cambio). Causa real, encontrada al recalcular la geometría:
     este box (`top:-16/bottom:-129`, alto total 610px sobre un
     `.hero__photo-wrap` de 465px) es MÁS ALTO que el punto donde termina
     la foto de verdad (`.hero__photo{top:-80px;height:594px}` → borde
     real a 514px del wrap, 530px de este box = 86.9% de sus 610px). Los
     stops anteriores solo llegaban a opacidad 1 exactamente en el 100%
     del box — 13 puntos DESPUÉS del borde real de la foto (86.9%) — así
     que justo donde la imagen termina de verdad, el overlay todavía
     rondaba ~0.9 de opacidad, no 1: quedaba una franja fina de la foto
     original sin cubrir del todo, además de la subida gradual real
     siendo previa a eso. Fix: los stops ahora llegan a opacidad 1 en el
     **85%** del box (antes del 86.9% real, con margen) y arrancan antes
     (15%, no 28%) para que la caída sea más notoria de entrada. Unificado
     en ESTE bloque compartido (1256px+, cubre también 1920 — se eliminó
     el override exclusivo de 1920 que existía antes: si el ancho real del
     navegador del usuario no llegaba a tocar exactamente 1920px CSS
     [común con escalado de Windows 125%/150%], ese override nunca se
     activaba y el toggle no mostraba ningún cambio real — con un solo
     degradado en el bloque de 1256px+ esto ya no depende de acertarle al
     ancho exacto). */
  /* `85%→86%`: tramo mínimo ya establecido como límite seguro tras varias
     vueltas de "más abajo" (2026-08-15) — el borde real de la foto cae
     en 86.9% del box, ver historial completo arriba. Se probó
     desactivarlo del todo y también forzarlo más allá de este límite;
     esta es la versión final, restaurada a como estaba justo antes de
     desactivarlo (pedido explícito: "ponlo como estaba antes"). */
  .hero__photo-wrap::after {
    content: '';
    position: absolute;
    top: -16px;
    left: -30px;
    right: 4px;
    bottom: -129px;
    background: linear-gradient(180deg, rgba(24,20,15,0) 0%, rgba(24,20,15,0) 85%, rgba(24,20,15,0.5) 85.5%, rgba(24,20,15,1) 86%);
    opacity: 1;
    pointer-events: none;
    z-index: 3;
  }

  /* Marco dorado de 3 lados (sin borde inferior), esquinas sup. redondeadas,
     detrás de la foto y ligeramente más ancho por la izquierda.
     `mask-image` (2026-08-15, pedido explícito: "dale el mismo degradado
     al cuadro amarillo de detrás pero que esté en 90 grados, empezando
     desde abajo"): **primer intento con `background` revertido** — el
     marco no tiene relleno propio (solo el borde dorado), y el color del
     degradado (`--color-bg`) es EXACTAMENTE el mismo que el fondo real
     detrás del marco, así que un `background` que funde a ese color no
     cambia nada visible (confirmado ocultando la foto y viendo el marco
     solo: sin diferencia con/sin el degradado). El efecto real que sí se
     ve es desvanecer el BORDE mismo — `border` no admite gradientes
     directos, así que se usa `mask-image` con los mismos stops (alpha en
     vez de color: negro=visible, transparente=oculto) para que el propio
     trazo dorado se disuelva. Vertical "desde abajo" (`0deg` = de abajo
     hacia arriba en CSS): el 0% (oculto) arranca en el borde INFERIOR
     del marco y sube, llegando a visible total (`black`) en el 85% de la
     altura (antes 70% — "aumenta el degradado", mismo ajuste aplicado en
     conjunto con el `mask-image` nuevo de `.hero__photo` de arriba, ver
     ese comentario para la causa real de la asimetría izq/der) — mismos
     puntos de corte que el degradado de la foto y el nuevo mask de la
     sombra dorada, solo que aquí controlan visibilidad del trazo, no
     color de relleno. */
  .hero__frame {
    display: block;
    position: absolute;
    top: -16px;
    left: -30px;
    right: 4px;
    height: 372px;
    -webkit-mask-image: linear-gradient(0deg, transparent 0%, transparent 20%, rgba(0,0,0,0.3) 40%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0.8) 70%, #000 85%);
    mask-image: linear-gradient(0deg, transparent 0%, transparent 20%, rgba(0,0,0,0.3) 40%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0.8) 70%, #000 85%);
    border: 2px solid rgba(157, 119, 51, 0.5);
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    pointer-events: none;
    z-index: 1;
  }

  /* --- Watermark: en flujo, siguiente hermano de .hero__inner. Se solapa
         18px con el final de la banda foto/texto (533px de alto real) vía
         margin-top negativo — mismo resultado visual que el `top:515px`
         absoluto anterior (533−18=515), pero como parte del flujo: si la
         fila de arriba creciera, el solape se recalcula solo. El padding
         reproduce el mismo borde derecho que antes daba `right:margin`
         (alineado con el borde derecho de la foto); se extiende hacia la
         izquierda por ser un H2 mayor (`text-align:right` + `nowrap`).
         `position:relative` es solo para ganar el z-index frente a
         .hero__broche (absoluto) donde llegan a solaparse en 1920.
         left/top:auto resetea el `left:488px/top:338px` del bloque 1024
         (ahí el watermark es position:absolute) — como offset de un
         position:relative esos valores desplazaban el texto fuera de su
         lugar en vez de no hacer nada. --- */
  .hero__watermark {
    position: relative;
    left: auto;
    right: auto; /* resetea el `right` que fijan 744/1024 (ver ese bloque) — sin esto
      se filtraría como offset de position:relative y correría el watermark hacia la
      izquierda en vez de dejar que el `padding` de abajo controle el borde derecho. */
    top: auto;
    margin-top: -18px;
    padding: 0 var(--margin-desktop);
    text-align: right;
    white-space: nowrap;
    font-size: 48.83px;
    line-height: 1.15;
    background:
      linear-gradient(180deg, rgba(24,20,15,0) 0%, rgba(24,20,15,0.95) 86.63%),
      linear-gradient(to left, rgba(255,255,255,0.2), rgba(255,255,255,0.2));
    background-clip: text;
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    z-index: 3;
    pointer-events: none;
  }

  /* --- Broche (curva inferior): muesca CONCÉNTRICA al badge. Línea plana
         en y=127 (base del hero, top 579 + 127 = 706). El badge (r 71.945)
         tiene su centro 11px sobre la línea → centro del arco en (719.5,116).
         Arco principal R = 86 (anillo de 14px alrededor del círculo, baja
         hasta y=202) y hombros con fillets r = 20 tangentes a la línea y al
         arco (centros a ±101.366 del eje; tangencia en ±82.24, y=141.15).
     width/clip-path RECALCULADOS 2026-07-24 (bug real: scroll horizontal
     real en el rango 1256–1439px, "1280" nominal — mismo patrón que
     `.apertura::before`, ver CLAUDE.md sección TABLET, bug #5): el canvas
     de ancho FIJO (sistema de coordenadas del `clip-path: path()`, ver
     GEOMETRÍA CLAVE) estaba hardcodeado a 1440px incluso en este tier, que
     cubre viewports reales tan angostos como 1256px — a cualquier ancho
     real entre 1256 y 1439, el canvas se salía ~85–99px de cada lado del
     viewport, y a diferencia de lo que `html{overflow-x:clip}` debería
     evitar, ese excedente SÍ era scrolleable en la práctica (confirmado con
     `scrollTo`/`scrollWidth` real, no solo captura visual). Fix: canvas
     achicado a **1256px** (el ancho MÁS ANGOSTO real de este tier — nunca
     mayor que el viewport real en ningún punto del rango 1256–1439, así
     que nunca desborda) en vez de 1440. La muesca NO se recalculó — mismos
     R/fillet/offsets del badge (idéntico en 1256–1439, ver CONFIG_1440 de
     `apertura.js`) — solo se re-centraron los mismos puntos restando 92px
     (=(1440−1256)/2, el corrimiento del nuevo centro 628 vs el viejo 720)
     a las coordenadas X de los hombros/fillets; los tramos planos (0↔hombro
     izq., hombro der.↔1256) se acortan pero no se nota: `.hero__broche` y
     `.hero` comparten el mismo `background-color:var(--color-bg)`, así que
     el espacio entre el borde del canvas angosto y el borde real del
     viewport (cuando este es más ancho que 1256, hasta 1439px) se rellena
     con el mismo color sólido de `.hero` — sin costura visible, mismo
     truco que ya usa este canvas para esconder SU propio borde recto en el
     centro (la parte visible es solo la muesca, el resto es relleno liso).
     Restaurado a 1440px/path original en el bloque `min-width:1440px`
     dedicado (ver "HERO — DESKTOP ANCHO" más abajo, mismo patrón que ya
     usa Contacto para su propio tier 1256→1440). */
  .hero__broche {
    display: block;
    position: absolute;
    top: 579px;                    /* 484 + 95 */
    left: 50%;
    transform: translateX(-50%);   /* la muesca del path() queda centrada en el viewport */
    width: 1256px;                  /* ancho fijo = sistema de coordenadas del path (0–1256) */
    height: 280px;
    background: var(--color-bg);
    clip-path: path('M 0,0 L 1256,0 L 1256,127 L 728.866,127 A 20,20 0 0 0 709.74,141.15 A 86,86 0 0 1 545.26,141.15 A 20,20 0 0 0 526.134,127 L 0,127 Z');
    filter:
      drop-shadow(0   0   20px rgba(24,20,15,0.55))
      drop-shadow(0   2px  5px rgba(24,20,15,0.10))
      drop-shadow(0   9px  9px rgba(24,20,15,0.09))
      drop-shadow(0  20px 12px rgba(24,20,15,0.05))
      drop-shadow(0  36px 14px rgba(24,20,15,0.01));
    pointer-events: none;
    z-index: 5;
  }

  /* --- Barra de estadísticas: FLEX de 3 columnas ---
         [grupo-izq flex:1] · [badge centrado] · [grupo-der flex:1]
         Sin offsets absolutos "mágicos": los grupos se centran solos
         en cada mitad y se adaptan entre 1440 y 1920. --- */
  .hero__stats {
    position: absolute;
    left: 0;
    top: 602px;                /* 507 + 95 */
    width: 100%;
    height: 104px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    padding: 0 var(--margin-desktop);
    gap: 0;
    z-index: 6;
  }

  .hero__stats-group {
    position: static;
    flex: 1;
    width: auto;
    height: 104px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 22px;
  }

  /* Separan los grupos del badge central en 1440px con padding simétrico
     desde el centro real de la pantalla. min-width:0 es la pieza clave:
     sin ella, flex:1 + min-width:auto deja que el texto más largo de la
     izquierda expanda su caja más allá del 50%, corriendo el punto de
     anclaje (flex-end) más allá del centro real y rompiendo la simetría
     con el lado derecho. Con min-width:0 ambas cajas miden exactamente
     50% siempre.
     Restricción dura: contenido_izq + gap_visual_al_badge + radio_badge(71.945) ≤ ancho_grupo.
     OJO: a 1440×900 reales el documento excede los 900px de alto, así que
     el scrollbar vertical del navegador resta ~15px al ancho útil del
     viewport (clientWidth ≈ 1425, no 1440) → cada grupo mide ~616.5px en
     vez de 624px "ideales". El padding tiene que caber en ese peor caso,
     no en el ideal sin scrollbar. Con gap interno 22px, el grupo
     izquierdo (el más ancho, con "De experiencia legal y contable") mide
     ~508.6px de contenido, así que padding-right/left de 105px deja
     616.5 − 105 = 511.5px disponibles (≥ 508.6px, ~3px de holgura real)
     y dista 105 − 71.945 ≈ 33.06px del borde del badge en ambos lados por
     igual. El grupo izquierdo arranca en x ≈ 100.7px, dentro del margen
     de 96px (--margin-desktop) con ~4.7px de holgura real (verificado con
     getBoundingClientRect a 1440×900 incluyendo el scrollbar).
     En 1920px se resetea todo (ver bloque ultra-wide) para no tocar el
     centrado ya calibrado con translateX. */
  .hero__stats-group--left,
  .hero__stats-group--right {
    min-width: 0;
  }

  .hero__stats-group--left {
    justify-content: flex-end;
    padding-right: 105px;
    transform: none;
  }

  .hero__stats-group--right {
    justify-content: flex-start;
    padding-left: 105px;
    transform: none;
  }

  .hero__stat {
    flex: 0 0 auto;
    height: 100%;
    padding: 22px 0;
    align-items: center;
    gap: 16px;
  }

  .hero__stat-label { white-space: nowrap; }

  .hero__stat-divider { height: 60px; }

  /* --- Badge: círculo dorado centrado horizontalmente, montado sobre el
         pico de la curva. Posicionado respecto al BORDE INFERIOR de la
         stats-bar (= línea plana del broche), de modo que su centro queda
         11px por encima de la curva en AMBOS anchos sin números por
         breakpoint. Texto circular + rotación: sin cambios. --- */
  .hero__badge {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    top: 21.05px;               /* 93 − 71.95 (radio badge) → centro a 11px exactos sobre la curva */
    width: 143.89px;
    height: 143.89px;
    background: var(--color-gold);
    border-radius: 50%;
    overflow: visible;
    z-index: 7;
  }

  /* Hueco oscuro central del badge (aloja la flecha) */
  .hero__badge::before {
    content: '';
    position: absolute;
    width: 98.01px;
    height: 98.01px;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--color-bg);
    border-radius: 50%;
    z-index: 0;
  }

  .hero__badge-ring {
    position: absolute;
    width: 100%;
    height: 100%;
    top: 50%;
    left: 50%;
    animation: badge-spin 22s linear infinite;
  }

  /* radio del path (63.61) = (longitud del texto + 1 "hueco de palabra" extra
     en la costura) / 2π → el remanente entre el último · y la S de SIERRA
     queda igual de ancho que cualquier otro espacio de la frase */
  .hero__badge-ring text {
    font-size: 12px !important;
    letter-spacing: 1.5px;
  }

  .hero__badge-arrow {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -23.09px); /* Centrado matemático: (35.81px cuerpo + 10.37px punta) / 2 = 23.09px */
    display: block;
    width: 3.77px;
    height: 35.81px;
    background: white;
    z-index: 1;
  }

  .hero__badge-arrow::after {
    content: '';
    position: absolute;
    bottom: -10.37px;
    left: 50%;
    transform: translateX(-50%);
    border-left: 10.37px solid transparent;
    border-right: 10.37px solid transparent;
    border-top: 11.31px solid white;
  }
}

/* ===================================================
   HERO — DESKTOP ANCHO (min-width: 1440px)
   Restaura `.hero__broche` a su ancho/path real de 1440 (el tier de
   1256 lo achica a un canvas de 1256px para no desbordar en el rango
   1256–1439, ver comentario en ese bloque) — a partir de aquí el
   canvas vuelve a coincidir exacto con el ancho real del breakpoint,
   mismo patrón que ya usa Contacto para su propio tier 1256→1440. Debe
   ir ANTES del bloque ULTRA-WIDE de abajo (que redeclara `.hero__broche`
   con sus propios 1920px) — si quedara después, ganaría por orden de
   cascada a igual especificidad y rompería el broche a 1920+.
=================================================== */
@media (min-width: 1440px) {
  .hero__broche {
    width: 1440px;
    clip-path: path('M 0,0 L 1440,0 L 1440,127 L 820.866,127 A 20,20 0 0 0 801.74,141.15 A 86,86 0 0 1 637.26,141.15 A 20,20 0 0 0 618.134,127 L 0,127 Z');
  }
}

/* ===================================================
   HERO — ULTRA-WIDE (min-width: 1920px)
   Corrección de alto: la foto se reduce de 526×618 a 396×465
   (misma proporción, factor ×0.75) para que stats + badge quepan
   dentro de un viewport de 1080px de alto. Ancho de márgenes/columnas
   sin cambios (var(--margin-ultrawide) = 192px).
     línea plana del broche (base del hero) = 691px
     stats-bar: 587–691  ·  foto/texto: banda 68–533
     gap watermark→stats: 587 − 571.15 (fin watermark) ≈ 16px
     badge bottom = 587 + 16.16 + 176 = 779.16 → 88.16px bajo el hero
     total real = header(112) + hero(691) + 88.16 ≈ 891px  (189px de margen bajo 1080px)
=================================================== */
@media (min-width: 1920px) {

  .hero {
    min-height: 691px;          /* stats(587+104) = broche(521.667+169.333) = 691 */
  }

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): el H1 hereda el
     61.04px de 1440px en vez de escalar a 77.76px. El clamp() de
     .hero__inner (definido en el bloque 1440) llegaría a 886px de columna
     de texto exactamente a 1920px de viewport — con la fuente más chica
     eso alargaría demasiado la línea del H1, así que aquí se congela la
     columna de texto al mismo 718px que mide a 1440px (mismo criterio que
     .apertura__text/.areas__intro más abajo); el espacio sobrante entre
     texto y foto lo absorbe justify-content:space-between. La foto sigue
     con ancho constante (396px) en ambos anchos — no necesita su propio
     override aquí. */
  .hero__inner {
    grid-template-columns: 718px 396px;
    padding: 68px var(--margin-ultrawide) 0;
  }

  /* Sin foto/marco/photo-wrap/degradado propios: a 1920px la foto sigue
     midiendo 396×465 y el degradado es el mismo del bloque 1256px (ver
     ese bloque — 2026-08-15, unificado ahí para no depender de un
     override exclusivo de 1920 que no se activaba si el ancho real del
     navegador no llegaba a tocar 1920px CSS exactos). */

  .hero__watermark {
    padding: 0 var(--margin-ultrawide);
    z-index: 6;                 /* por encima de .hero__broche (z-index:5), que ahora la invade verticalmente (521.667–691px) */
  }

  /* Broche: solo sube el `top` (−62px, misma cantidad que .hero__stats)
     para mantener la curva alineada con la barra de stats.
     Muesca concéntrica al badge 1920 (r 82.93, centro 11px sobre la línea
     plana y=169.333 → centro del arco en (959.5,158.333)): arco R = 99.07
     (anillo de ~16px, escala ×1.153 del de 1440; baja hasta y=257.4) y
     fillets r = 23 (centros a ±117.24; tangencia en ±95.15, y=185.93).
     Base del hero = 521.667 + 169.333 = 691 */
  .hero__broche {
    top: 521.667px;
    height: 373px;
    width: 1920px;                  /* ancho fijo = sistema de coordenadas del path (0–1920) */
    clip-path: path('M 0,0 L 1920,0 L 1920,169.333 L 1076.74,169.333 A 23,23 0 0 0 1054.65,185.93 A 99.07,99.07 0 0 1 864.35,185.93 A 23,23 0 0 0 842.26,169.333 L 0,169.333 Z');
  }

  .hero__stats {
    top: 587px;                 /* 571.15 (fin watermark) + 16px de gap; stats bottom (587+104=691) al ras del fin del hero */
    padding: 0 var(--margin-ultrawide);
  }

  /* Mismo mecanismo que 1440px (justify-content:flex-end/flex-start +
     padding, no transform): el grupo izquierdo tiene contenido más ancho
     ("De experiencia legal y contable" vs. las etiquetas del grupo
     derecho), así que sin corrección su gap visual al badge queda más
     angosto que el del grupo derecho. padding-right/padding-left = radio
     del badge (82.93px) + el gap real deseado (55.21px) ambos lados =
     138.14px — mismo criterio que el padding:105px de 1440 (71.945+33.06),
     solo que aquí, al ser gap constante ya no hace falta romper el
     reparto 50/50 con un transform: `min-width:0` (heredado de 1440, ver
     ese comentario) es lo que evita que el texto ancho expanda la caja
     más allá del 50% real. gap:40px sigue siendo el espaciado INTERNO
     entre los 2 stats de cada grupo (no relacionado con este ajuste). */
  .hero__stats-group {
    gap: 40px;
  }

  .hero__stats-group--left {
    justify-content: flex-end;
    padding-right: 138.14px;
  }

  .hero__stats-group--right {
    justify-content: flex-start;
    padding-left: 138.14px;
  }

  /* Badge: reescalado proporcionalmente (k≈0.94, mismo factor que en el
     bloque 1440) para envolver el nuevo círculo de texto. El `top` se
     redeclara (no hereda el de 1440) porque el radio cambia: misma fórmula
     93 − radio, así el centro queda siempre 11px exactos sobre la curva. */
  .hero__badge {
    top: 10.07px;               /* 93 − 82.93 (radio badge) */
    width: 165.86px;
    height: 165.86px;
  }

  .hero__badge::before {
    width: 114.77px;
    height: 114.77px;
  }

  /* letter-spacing sin font-size: el tamaño (12px) hereda igual de 1440px
     (ya era el mismo valor, redeclarado sin necesidad). */
  .hero__badge-ring text {
    letter-spacing: 1.5px;
  }

  .hero__badge-arrow {
    transform: translate(-50%, -28.27px); /* Centrado matemático: (43.35px cuerpo + 13.19px punta) / 2 = 28.27px */
    width: 4.71px;
    height: 43.35px;
  }

  .hero__badge-arrow::after {
    bottom: -13.19px;
    border-left: 12.25px solid transparent;
    border-right: 12.25px solid transparent;
    border-top: 14.14px solid white;
  }
}

/* ===================================================
   APERTURA — BASE MOBILE (375px)
   Banda crema entre el hero y Áreas de práctica. Tiene la muesca
   concéntrica superior (.apertura::before, ver esa regla abajo) donde
   se nesta el sello del hero — pedido explícito del usuario pese a
   que el export real de Hero no la exigía (ver nota en esa regla).
=================================================== */

.apertura {
  position: relative;
  /* Bajo el hero (z 10): el broche/badge oscuros del hero cuelgan sobre la
     banda crema. Sobre las secciones siguientes: la muesca colgante del
     canto (clip-path de apertura.js + ::after, ver bloque 1440) debe
     pintar encima del fondo oscuro de Áreas de práctica. */
  z-index: 5;
  background: var(--color-cream);
  /* padding 75px/75px (2026-07-23, segunda vuelta — el usuario notó que
     con 96px/40px arriba se veía con MÁS espacio que abajo). La muesca
     (`.apertura::before`) es un recorte OSCURO angosto centrado — fuera
     de esa franja angosta el crema empieza en y=0, así que lo que el
     ojo percibe como "espacio sobre el texto" es básicamente el
     padding-top COMPLETO (no solo el sobrante tras la profundidad de
     la muesca, 56px) en la mayor parte del ancho. Por eso 96/40 se veía
     descentrado pese a que 96−56=40 igualaba el buffer "de sobra" con
     el padding-bottom — la resta solo aplica exactamente bajo el pico
     de la muesca, una franja angosta, no el ancho completo del párrafo.
     Fix: padding-top = padding-bottom (75px ambos, con margen de 19px
     bajo el pico de la muesca — de sobra para no chocar). */
  padding: 75px 16px 75px;
}

.apertura__text {
  font-family: 'Gloock', serif;
  font-size: 23.04px;
  font-weight: 400;
  line-height: 28px;
  text-align: center;
}

/* Lavado sutil de Figma sobre los colores planos: aclara apenas la parte
   superior del bloque de texto (gradiente clipeado al texto) */
.apertura__text--dark {
  background:
    linear-gradient(0deg, rgba(250, 250, 248, 0) 0%, rgba(236, 238, 241, 0.5) 100%),
    linear-gradient(to left, var(--color-black), var(--color-black));
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.apertura__text--gold {
  background:
    linear-gradient(0deg, rgba(250, 250, 248, 0) 0%, rgba(236, 238, 241, 0.5) 100%),
    linear-gradient(to left, var(--color-gold), var(--color-gold));
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

/* Muesca CONCÉNTRICA superior (2026-07-23): el export real
   (Recursos/AutoHTML/375/Hero.html) muestra el sello asomando solo
   4.54px bajo `.hero`, sin necesidad de una muesca tallada — se
   probó fiel a ese número, pero el usuario pidió explícitamente la
   curva de todos modos ("vale pero hazle la curva"). Como el badge de
   este ancho ya es del MISMO tamaño que el de 744 (ver .hero__badge,
   confirmado por el export: `.ellipse-1` 111×113 ≈ el óvalo de 744),
   se reutiliza la MISMA geometría R/f que 744 sin recalcular nada
   (R=66.89, f=15.56, dx=78.05 — ver el comentario completo de la
   fórmula en el bloque `@media (min-width:744px)` de esta misma
   regla, un poco más abajo).
   **Bug real encontrado, 2 intentos de fix (2026-07-23, "hay una línea
   que sigue amarilla, quita eso" — confirmado con captura del usuario,
   una línea fina asomando desde el fillet hacia afuera)**:
   - Intento 1: el `.apertura::before` (clip-path, copiado del patrón
     "canvas ANCHO" de 744) iba hasta el borde del box (`L 1100,0`) y
     volvía sobre la MISMA línea hacia el hombro (`L 628.05,0`), una
     franja de área cero recorrida dos veces — se simplificó a un path
     mínimo sin esa retraza. En Edge desktop headless el fix se veía
     perfecto, pero el usuario probó en un TELÉFONO REAL y la línea
     seguía — o sea, no era (solo) el patrón "ida y vuelta": el propio
     `clip-path: path()` con arcos rendeiza distinto en el navegador
     móvil real (antialiasing distinto en la tangencia fillet↔arco).
   - Intento 2 (el que queda activo): en vez de seguir peleando con
     `clip-path`, la muesca de MOBILE se reconstruyó como un `<svg>`
     real en el HTML (`.apertura__notch`, ver index.html) con un
     `<path>` de relleno normal — el relleno de paths en SVG es una
     tecnología mucho más madura/consistente entre navegadores que
     `clip-path: path()` (relativamente nuevo). Mismas coordenadas que
     el intento 1 (viewBox 200×57, mismo path minimal sin retraza).
     `.apertura::before` (el mecanismo clip-path) se ELIMINÓ en mobile
     — 744+ sigue con su propio `::before` de clip-path sin tocar (no
     reportado ahí; si aparece el mismo problema en tablet/desktop,
     aplicar este mismo mecanismo de SVG en vez de clip-path). */
.apertura__notch {
  display: block;
  position: absolute;
  top: -1px;
  left: 50%;
  transform: translateX(-50%);
  width: 200px;
  height: 57px;
  z-index: 0;
  pointer-events: none;
}

.apertura__notch path {
  fill: var(--color-bg);
}

/* ===================================================
   APERTURA — TABLET VERTICAL (min-width: 744px)
   Reescrito 2026-07-22 contra 744.png: a diferencia de lo asumido
   antes (banda plana, canto/lengüeta exclusivos de 1440+), el
   diseño de tablet SÍ tiene las 2 muescas — pero resueltas SIN
   clip-path de ancho fijo (ese sigue siendo exclusivo de 1440+,
   apertura.js no se activa aquí):
     - Arriba: un semicírculo OSCURO (::before, mismo color que el
       fondo) montado sobre el borde superior de .apertura — no es
       un recorte del propio cream, es una pieza aparte encima,
       más simple y funciona con cualquier ancho fluido.
     - Abajo: la lengüeta cream de siempre (.areas::before, ya
       usada en 1440+) con sus propias medidas para este ancho.
   Medidas de 744.png (semicírculo sup. radio≈78px, lengüeta
   inf. ancho≈165px/radio≈87px) — ver notas en cada regla.
=================================================== */
@media (min-width: 744px) {

  /* `.apertura__notch` (SVG) YA NO se oculta aquí (2026-07-24, ver bug
     real más abajo en `.apertura::before`) — sigue activo hasta el
     bloque `1256px`, donde SÍ se oculta (ahí entra el mecanismo real de
     escritorio, `apertura.js` + `.hero__broche`). */

  /* overflow-x:hidden (2026-07-24, fix scroll horizontal a 744/1024): el
     canvas ANCHO de `.apertura::before` (1100px, ver esa regla abajo) se
     extiende a propósito ~170px más allá de cada borde del viewport para
     que su línea plana se pierda fuera de pantalla (evita el escalón recto
     visible que dejaba un canvas angosto, ver comentario de esa regla) —
     pero `.apertura` es el único ancestro con el mismo ancho que el
     viewport, así que contener el desborde AQUÍ (en vez de dejar que
     llegue hasta `html`) evita que ese excedente cuente para el
     scrollWidth de toda la página. Antes dependía por completo de
     `html{overflow-x:clip}` (ver comentario junto a esa regla) para no
     generar scrollbar horizontal fantasma — insuficiente en la práctica
     (el recorte no aplicaba de forma consistente y el excedente seguía
     siendo scrolleable). Mismo efecto visual, el desborde ahora se recorta
     un nivel más adentro. Reseteado a `visible` en el bloque 1256px (ver
     GUARD) porque desktop no genera este pseudo-elemento (`content:none`)
     y no lo necesita. */
  .apertura {
    padding: 95px var(--margin-tablet) 60px;
    overflow-x: hidden;
  }

  .apertura__text {
    width: min(600px, 100%);
    margin: 0 auto;
    font-size: clamp(1.44rem, 0.686vw + 1.1107rem, 1.7281rem);
    line-height: 30px;
  }

  /* Muesca CONCÉNTRICA real (arco + fillets tangentes), no un semicírculo
     liso — mismo mecanismo que el broche de 1440/1920 (ver GEOMETRÍA
     CLAVE), escalado al badge de este ancho (r=56, factor ×0.778 sobre
     1440: ring 14→10.89, fillet 20→15.56; el offset "11" del centro del
     badge sobre la línea NO escala, igual que entre 1440 y 1920).
     R=66.89 (=56+10.89), f=15.56, dx=√((R+f)²−(f+11)²)=78.05.
     **Bug real #10 (2026-07-24, pedido explícito: "la curva del broche en
     1024 tiene una línea amarilla que sigue de largo, problema que
     habíamos tenido anteriormente")**: el mecanismo `clip-path:path()`
     de este pseudo-elemento (canvas ANCHO de 1100px con un tramo "ida y
     vuelta" — `L 1100,0` y vuelta por la misma línea al hombro, ver
     historial completo en CLAUDE.md) es EXACTAMENTE el mismo patrón que
     ya causó la "línea fina" en mobile (375px, 2026-07-23) — ahí se
     probó primero acortar el path (sin la retraza) y AÚN ASÍ la línea
     seguía apareciendo en un teléfono real (el `clip-path:path()` con
     arcos rendeiza distinto entre navegadores/dispositivos reales que en
     Edge headless) — la única solución que funcionó de verdad fue
     abandonar `clip-path` por completo y usar un `<svg>` con `<path>` de
     relleno normal (`.apertura__notch`, ver esa regla arriba y
     `index.html`) — tecnología mucho más madura y consistente. Este
     pseudo-elemento (`.apertura::before`) NUNCA se migró a ese mecanismo
     en 744/1024 — CLAUDE.md ya advertía que el mismo patrón "probablemente
     seguía latente" aquí, sin confirmar. Fix: se DESACTIVA por completo
     (`content:none`) y se reutiliza el `.apertura__notch` SVG que ya
     existía para mobile — mismas coordenadas (viewBox 200×57, mismo path
     minimal), porque el badge de 744/1024 es IDÉNTICO en tamaño al de
     mobile (112px, confirmado en HERO/APERTURA — TABLET), así que la
     geometría R/f/dx no cambia, no hace falta derivar una nueva. Ver
     arriba: `.apertura__notch` ya no se oculta en este bloque, sigue
     activo hasta 1256px. */
  .apertura::before {
    content: none;
  }
}

/* ===================================================
   APERTURA — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar
   ×1.376 sobre 744 — la muesca superior reusa la MISMA geometría que
   744 (R=66.89, f=15.56, idéntica al `.apertura__notch` SVG heredado de
   744 — ver bug real #10 en ese bloque, ya no es un `clip-path`), ya que
   el sello del hero también volvió a su tamaño de 744 (112px). `padding`
   solo cambia el margen lateral (`--margin-laptop`).

   `.apertura__notch{display:none}` (bug real #12, ver HERO 1024): a este
   ancho el collar del sello ya NO lo pone Apertura — pasa a ponerlo el
   propio Hero (`.hero__broche-notch`, pedido explícito del usuario:
   "la curva que está en el hero, la que está antes de apertura, como en
   las demás resoluciones"). Dejar los 2 activos a la vez dibujaría el
   collar DOS VECES superpuesto. **744 NO se toca** — ahí sigue el
   mecanismo de siempre (`.apertura__notch` visible, sin `.hero__broche-
   notch`, que solo existe 1024–1255).
=================================================== */
@media (min-width: 1024px) {

  .apertura {
    padding: 95px var(--margin-laptop) 60px;
  }

  .apertura__notch { display: none; }
}

/* ===================================================
   APERTURA — DESKTOP (min-width: 1256px)
   Banda de 321px: 80px superiores = zona donde cuelgan
   la lengüeta y el badge del hero (Figma: gap entre la
   línea plana del broche y el frame de texto de 241px),
   + frame de texto con padding 30/60.
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): 2 hallazgos en Apertura.
     - `.apertura__text{margin:0 auto}` (744): este bloque usa
       `margin-inline:auto` (solo horizontal) en vez de resetear el
       shorthand `margin` completo — el `margin-top/bottom:0` que trae el
       `margin:0 auto` de 744 sigue colándose hasta aquí. Hoy es inofensivo
       (no se necesita margen vertical en ningún ancho), pero si en el
       futuro este bloque necesitara un margen vertical propio, hay que
       declararlo explícito — `margin-inline` NO lo cubre.
     - `.apertura::before` (744 Y 1024 — content/position/top/left/
       transform/width/height/background/clip-path/pointer-events/z-index,
       la muesca superior de tablet): CONFIRMADO, no un falso positivo.
       `min-width:744px`/`min-width:1024px` SIGUEN aplicando por encima de
       esos anchos (son aditivos, no rangos exclusivos), así que el pseudo-
       elemento con la geometría calibrada para 1024 se seguía generando
       también a 1440/1920 — invisible hoy solo porque `.hero` (z-index:10)
       lo tapa físicamente en la misma "zona de intrusión" documentada en
       GEOMETRÍA CLAVE (CLAUDE.md), pero frágil ante cambios futuros de
       alto/z-index de `.hero`. ARREGLADO 2026-07-23: `.apertura::before {
       content: none; }` más abajo en este bloque destruye el pseudo-
       elemento en 1440/1920 sin afectar 744/1024 (`content` no es
       heredable entre breakpoints en cascada normal — cada `min-width`
       posterior puede resetearlo). Verificado sin cambio de render (el
       elemento ya era invisible, tapado por `.hero`). NOTA: el script
       seguirá listando las otras props de este selector (position/top/
       left/transform/width/height/background/clip-path/pointer-events/
       z-index) como "no re-declaradas en 1440" — es la limitación de
       heurístico documentada en su propia cabecera (no entiende que
       `content:none` vuelve inertes las demás propiedades del
       pseudo-elemento). Ya no es un hallazgo real, es ruido esperado. */
  .apertura {
    min-height: 321px;             /* 80 (intrusión del hero) + 241 (frame Figma) */
    padding: 110px var(--margin-desktop) 60px;   /* 110 = 80 + 30 (padding-top del frame) */
    overflow-x: visible;   /* reset del overflow-x:hidden de 744 (ver esa regla) — no
      hace falta aquí, `.apertura::before` no existe en desktop (content:none abajo). */
  }

  /* GUARD fix: destruye el pseudo-elemento de la muesca de tablet en
     desktop (ver comentario GUARD arriba) — sin esto queda generado e
     invisible solo por casualidad (tapado por .hero). */
  .apertura::before {
    content: none;
  }

  /* `.apertura__notch` (SVG, ver bug real #10 en el bloque 744): activo
     desde mobile hasta 1023px — aquí entra el mecanismo real de
     escritorio (`apertura.js` + `.hero__broche`), así que se oculta para
     no duplicar la muesca. */
  .apertura__notch { display: none; }

  /* Ancho elástico en vez de un salto fijo 1036px→1274px en 1920: crece
     linealmente con el viewport entre ambos extremos (mismos valores de
     diseño, ahora como límites del clamp en vez de 2 números sueltos por
     breakpoint) — a 1600px el bloque de texto ya no se queda pegado en
     1036px con aire muerto a los lados, respira con el viewport. */
  .apertura__text {
    width: clamp(1036px, 322px + 49.583vw, 1274px);
    margin-inline: auto;
    font-size: 27.65px;
    line-height: 37px;
  }

  /* --- El canto curvo que se despega del broche del hero al hacer scroll
         lo recorta apertura.js aplicando clip-path: path(...) inline en
         cada frame (ver ese archivo) — path() con coordenadas fijas tiene
         soporte amplio (incluido Safari), a diferencia de clip-path:
         shape() interpolado + animation-timeline: scroll(root) que usaba
         la versión anterior (misma geometría/fórmulas, solo cambia el
         mecanismo). El shape sobresale bajo la caja (muesca colgando
         hasta H+~15px); el cream de esa zona lo pinta .apertura::after
         (rect estático bajo la banda, mismo rol que en la versión CSS
         original). Mientras el JS está activo, oculta la lengüeta
         estática .areas::before (ver regla `.has-apertura-clip` en el
         bloque ÁREAS) para no duplicar la muesca. Sin JS, con
         prefers-reduced-motion, o en mobile: banda plana + esa lengüeta
         estática son el fallback.
     **Bug real encontrado y corregido (2026-07-23, "la curva de áreas se
     ve mal en 1280" — en realidad preexistente también a 1440, solo se
     hizo visible al revisar 1280 con prefers-reduced-motion): este rect
     no estaba condicionado a que el JS estuviera realmente activo —
     se generaba SIEMPRE que el viewport matcheaba este bloque (>=1280),
     con o sin `prefers-reduced-motion`. Su `z-index:-1` solo lo manda
     atrás DENTRO del stacking context de `.apertura` (z-index:5) — ese
     contexto completo sigue pintando ENCIMA de `.areas` (z-index:1), así
     que el rect liso (sin la muesca) tapaba la lengüeta curva de
     `.areas::before` cada vez que el JS no corría, mostrando un
     rectángulo crema flotante en vez de la curva. Fix: `display:none`
     por defecto, solo se muestra cuando `apertura.js` agrega
     `has-apertura-clip` a `<html>` (JS realmente activo y controlando
     el recorte) — mismo gate que ya usa `.areas::before` a la inversa. --- */
  .apertura::after {
    content: '';
    position: absolute;
    z-index: -1;
    top: calc(100% - 12px);
    left: calc(50% - 101.366px);
    width: 202.732px;
    height: 87px;
    background: var(--color-cream);
    display: none;
  }

  html.has-apertura-clip .apertura::after {
    display: block;
  }

  /* Bug real encontrado y corregido (2026-07-26, "quiero que el zoom de
     las letras vaya acorde a donde está la barra... no quiero que se
     mueva si ya paró la barra, que no sea tan smooth"): `.apertura__text`
     lleva la clase `.reveal` (para su fallback sin scroll-zoom, ver
     ANIMACIONES) — esa clase le pone una `transition: opacity 1s
     cubic-bezier(...), transform 1s cubic-bezier(...)` que sigue activa
     aunque `apertura.js` esté fijando `opacity`/`transform` inline en
     cada frame de scroll. Con esa transition puesta, cada cambio de
     valor inline NO se aplica al instante — el navegador interpola hacia
     el nuevo valor durante 1s completo, así que el texto queda
     "arrastrando" detrás de la posición real del canto y sigue
     moviéndose solo hasta 1s después de que el scroll ya se detuvo.
     Confirmado con un test aislado (forzar `opacity` de 1→0 y muestrear
     `getComputedStyle` cada pocos ms): a los 306ms seguía en 0.775 en
     vez de 0, recién se asienta cerca de 1s — la MISMA duración que la
     transition de `.reveal`. Fix: anular la transition mientras
     `apertura.js` controla el elemento (`html.has-apertura-clip`) para
     que el valor inline se aplique de inmediato, en sincronía 1:1 con el
     scroll — sin este gate se perdería también el fallback normal de
     `.reveal` cuando el mecanismo de scroll NO está activo (mobile,
     reduced-motion), que si necesita su transition de entrada. */
  html.has-apertura-clip .apertura__text {
    transition: none;
  }
}

/* ===================================================
   APERTURA — ULTRA-WIDE (min-width: 1920px)
   Banda de 386.5px: 104.5px de intrusión del hero
   (muesca 1920) + frame de texto hug (30 + 192 + 60).
=================================================== */
@media (min-width: 1920px) {

  .apertura {
    min-height: 386.5px;
    padding: 134.5px var(--margin-ultrawide) 60px;  /* 134.5 = 104.5 + 30 */
  }

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): hereda el
     27.65px/37px de 1440px en vez de escalar a 37.9px/48px. El clamp()
     de 1440px llegaría a 1274px de ancho exactamente a 1920px de
     viewport — con la fuente más chica esa línea quedaría demasiado
     larga, así que aquí se congela el ancho al mismo 1036px que mide a
     1440px (extremo inferior del clamp original) en vez de a su extremo
     superior. */
  .apertura__text {
    width: 1036px;
  }

  .apertura::after {
    left: calc(50% - 117.24px);
    width: 234.48px;
    height: 100.07px;
  }
}

/* ===================================================
   ÁREAS DE PRÁCTICA — BASE MOBILE (375px)
   Stack vertical a sangre completa (sin carrusel ni flechas).
   La lengüeta cream de la apertura cuelga sobre el borde superior de
   esta sección vía .areas::before (pedido explícito del usuario,
   "vale pero hazle la curva" — el export real de Hero no la exigía,
   ver nota en APERTURA/.hero__badge, pero se agregó de todos modos);
   por eso el fondo oscuro y el padding-top hacen de "colchón" para
   que no choque con la cabecera.
   z-index: por debajo de .apertura (5) para que su lengüeta
   pinte encima.
=================================================== */

.areas {
  position: relative;
  z-index: 1;
  background: var(--color-bg);
  /* padding-top 92px (2026-07-23, pedido explícito del usuario: "dale
     más espacio... ya que la curva está muy pegada del título" — el
     intento anterior, 60px, seguía dejando la cabecera casi pegada a
     la muesca). Mismo valor literal que usa 744 para esta misma
     profundidad de muesca (~56px, idéntica geometría) — buffer ≈36px,
     igual que allá. */
  padding: 92px 0 50px;
  display: flex;
  flex-direction: column;
  gap: 48px;
}

/* Lengüeta cream con muesca CONCÉNTRICA, misma geometría que 744
   (R=66.89, f=15.56 — el badge de este ancho ya es del mismo tamaño,
   ver .hero__badge) — canvas angosto (156.1px, con collar de 12px)
   porque es crema-sobre-crema, mismo criterio que 744. */
.areas::before {
  content: '';
  position: absolute;
  top: -12px;
  left: 50%;
  transform: translateX(-50%);
  width: 156.1px;
  height: 67.89px;
  background: var(--color-cream);
  clip-path: path('M 0,0 L 156.1,0 L 156.1,12 A 15.56,15.56 0 0 0 141.38,22.55 A 66.89,66.89 0 0 1 14.72,22.55 A 15.56,15.56 0 0 0 0,12 Z');
  pointer-events: none;
}

/* --- Cabecera --- */
.areas__head {
  padding: 0 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.areas__intro {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.areas__label {
  color: var(--color-gold);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 700;
  text-align: center;
}

.areas__title {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
  text-align: center;
}

/* Flechas: solo en desktop */
.areas__arrows { display: none; }

/* --- Pista: stack vertical en mobile --- */
.areas__track {
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* Imprescindible en desktop: sin esto, la pista (flex-item de .areas en
     columna) toma su min-width:auto = ancho del contenido (~2520px) y
     desborda en vez de hacer scroll → overflow-x:auto queda inerte y las
     flechas creen que no hay recorrido. Con min-width:0 respeta el ancho
     del viewport y el scroll-snap funciona. */
  min-width: 0;
}

/* --- Tarjeta --- */
.areas__card {
  position: relative;
  /* Mobile: sin radius (a sangre). Desktop lo pone en 8px (ver bloque 1440). */
  border-radius: 0;
  overflow: hidden;
  padding: 32px 16px;
  display: flex;
  flex-direction: column;
  gap: 42px;
  align-items: center;
  /* El overlay oscuro va en el shorthand junto a la foto (cada tarjeta
     ajusta su opacidad para legibilidad; ver modificadores). */
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Overlay + foto por área (opacidad calibrada por imagen en Figma) */
.areas__card--empresarial   { background-image: linear-gradient(rgba(0,0,0,.75), rgba(0,0,0,.75)), url('../assets/img/areas/empresarial.webp'); }
.areas__card--inmobiliario  { background-image: linear-gradient(rgba(0,0,0,.75), rgba(0,0,0,.75)), url('../assets/img/areas/inmobiliario.webp'); }
.areas__card--penal         { background-image: linear-gradient(rgba(0,0,0,.55), rgba(0,0,0,.55)), url('../assets/img/areas/penal-y-litigio.webp'); }
.areas__card--administrativo{ background-image: linear-gradient(rgba(0,0,0,.75), rgba(0,0,0,.75)), url('../assets/img/areas/administrativo-y-publico.webp'); }
.areas__card--civil         { background-image: linear-gradient(rgba(0,0,0,.77), rgba(0,0,0,.77)), url('../assets/img/areas/civil-y-familia.webp'); }
.areas__card--migratorio    { background-image: linear-gradient(rgba(0,0,0,.75), rgba(0,0,0,.75)), url('../assets/img/areas/migratorio.webp'); }

.areas__card-text {
  display: flex;
  flex-direction: column;
  gap: 22px;
  align-items: center;
  width: 100%;
}

.areas__card-title {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 33.18px;
  font-weight: 400;
  line-height: 1.1;
  text-align: center;
}

.areas__card-desc {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
  text-align: center;
}

/* --- Botón "Solicitar asesoría" --- */
.areas__btn {
  align-self: center;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  background: var(--color-cta);
  border-radius: 8px;
  padding: 4px 4px 4px 22px;
  color: var(--color-black);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  white-space: nowrap;
  box-shadow:
    0px 1px 1px 0px rgba(158,158,158,0.05),
    0px 3px 3px 0px rgba(158,158,158,0.04),
    0px 6px 3px 0px rgba(158,158,158,0.03),
    0px 10px 4px 0px rgba(158,158,158,0.01);
}

/* Sin hover a propósito (2026-07-26, pedido explícito: "los botones de
   áreas de práctica no deben tener hover") — antes tenía `opacity:0.9`
   al pasar el mouse; se quitó junto con la `transition` que ya no hace
   falta. El hover de la TARJETA completa (`.areas__card:hover`, lift +
   sombra) NO se toca — el pedido era específico del botón "Solicitar
   asesoría" dentro de cada tarjeta, no de la tarjeta misma. */
.areas__btn-icon {
  display: flex;
  flex-shrink: 0;
}

.areas__btn-icon svg { display: block; }

/* ===================================================
   ÁREAS DE PRÁCTICA — TABLET VERTICAL (min-width: 744px)
   Reescrito 2026-07-22 contra AreasDePractica.html (744.png):
   sigue siendo el mismo mecanismo de carrusel (flechas + scroll-
   snap horizontal, con tarjetas más angostas) que ya traía este
   bloque — el export solo confirma medidas exactas (padding,
   tarjeta 324px, título 27.65px, flechas 38px con sombra propia)
   en vez de las aproximaciones previas. La pista SIGUE sangrando
   por la derecha (.areas sin padding lateral, .areas__track con
   su propio margin/padding) aunque el frame de Figma muestre
   exactamente 2 tarjetas encajadas sin sangrado — así se conserva
   la afordancia de "hay más para scrollear" que ya daba el
   carrusel; Figma no puede mostrar ese estado interactivo.
   Todas las propiedades que toca este bloque las vuelve a declarar
   el bloque 1440px por completo — cero riesgo de fuga hacia desktop.
=================================================== */
@media (min-width: 744px) {

  .areas {
    padding: 92px 0 40px;
    gap: 48px;
  }

  .areas__head {
    padding: 0 var(--margin-tablet);
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
  }

  .areas__intro {
    align-items: flex-start;
    width: 441px;
    gap: 8px;
  }

  .areas__label,
  .areas__title { text-align: left; }

  .areas__title { font-size: 33.18px; }

  .areas__arrows {
    display: flex;
    flex-direction: row;
    gap: 16px;
    flex-shrink: 0;
  }

  /* Sombra propia de 4 capas del export (filter dddd de frame-83/84),
     distinta de la de 1440 — convertida de feDropShadow a box-shadow:
     offset(0,0)/blur.5/.2 · (2,1)/1/.17 · (4,3)/1.5/.1 · (7,5)/1.5/.03 */
  .areas__arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border: none;
    border-radius: 50%;
    background: var(--color-cream);
    cursor: pointer;
    box-shadow:
      0px 0px 0.5px rgba(0,0,0,0.2),
      2px 1px 1px rgba(0,0,0,0.17),
      4px 3px 1.5px rgba(0,0,0,0.1),
      7px 5px 1.5px rgba(0,0,0,0.03);
    transition: opacity 0.2s ease, transform 0.2s ease;
  }

  .areas__arrow:hover { transform: translateY(-1px); }

  .areas__arrow[disabled] {
    opacity: 0.4;
    cursor: default;
    transform: none;
  }

  /* Bug real encontrado y corregido (2026-07-26, "hay un scroll vertical
     en las cards"): `overflow-x:auto` + `overflow-y:visible` no hace lo
     que parece — spec de CSS Overflow: si un eje NO es `visible` y el
     otro SÍ lo es, el `visible` se fuerza al valor computado del otro
     (aquí, `auto`). Confirmado con `getComputedStyle(track).overflowY`
     → `"auto"`, no `"visible"` como está escrito. Con `overflow-y` real
     en `auto`, cualquier tarjeta aún no revelada (las que siguen fuera
     del recorte horizontal, con el `transform:translateY(40px)` inicial
     de `.reveal` sobre `.areas__card`, ver CSS de animaciones) empuja el
     `scrollHeight` del track por encima de su `clientHeight` — el
     navegador habilita scroll vertical real dentro del carrusel
     (confirmado: `scrollHeight` 418px vs `clientHeight` 390px). Fix:
     `overflow-y: hidden` en vez de `visible` — como ninguno de los 2
     ejes queda en `visible`, la regla de forzado no aplica y el eje Y
     se queda tal cual se escribe (sin scroll, sin scrollbar). Mismo
     bug/fix aplica en el bloque 1256px (mismo patrón `overflow-x:auto`
     + `overflow-y:visible`), ver esa regla.

     2° bug real, reportado por el usuario tras el fix de arriba ("al
     hacerle hover a las cards... se ve que se levanta pero se corta
     arriba"): con `overflow-y:hidden` puesto directo, el `padding-box`
     del track quedaba a ras del borde superior real de las tarjetas
     (`padding-top:0`) — el hover-lift (`.areas__card:hover{transform:
     translateY(-6px)}`) mueve la tarjeta 6px por ENCIMA de ese borde, y
     `overflow:hidden` recorta en las 4 direcciones, no solo hacia abajo.
     Fix: `padding-top:8px` (buffer > 6px de sobra) + `margin-top:-8px`
     en la MISMA regla — el padding empuja el contenido 8px hacia abajo
     DENTRO de la caja, y el margen negativo sube la caja completa esos
     mismos 8px, cancelándose entre sí (posición visual de las tarjetas
     sin cambio, confirmado con `getBoundingClientRect` antes/después:
     mismo `top`) — pero el borde de recorte (`padding-box`) ahora queda
     8px más arriba de las tarjetas, dándole espacio de sobra al
     hover-lift de 6px sin tocar el borde de abajo (que sigue clippeando
     el `scrollHeight` extra de las tarjetas no reveladas, el bug
     original, sin reabrirlo). */
  .areas__track {
    flex-direction: row;
    gap: 16px;
    margin-left: var(--margin-tablet);
    margin-top: -8px;
    padding: 8px var(--margin-tablet) 0 0;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }

  .areas__track::-webkit-scrollbar { display: none; }

  .areas__card {
    flex: 0 0 324px;
    width: 324px;
    height: 365px;
    scroll-snap-align: start;
    border-radius: 8px;
    padding: 32px 14px;
    gap: 0;
    justify-content: space-between;
    align-items: flex-start;
  }

  .areas__card-text {
    align-items: flex-start;
    gap: 22px;
  }

  /* Título de tarjeta más chico que el resto del sitio a este ancho
     específico (27.65px vs los 33.18px "congelados" en el resto de
     breakpoints, ver memoria del proyecto) — valor literal del
     export, no una progresión: 1024 vuelve al tamaño estándar. */
  .areas__card-title { font-size: 27.65px; }

  .areas__card-title,
  .areas__card-desc { text-align: left; }

  .areas__btn { align-self: flex-start; }

  /* Lengüeta cream colgando desde .apertura, con la muesca CONCÉNTRICA
     real (arco + fillets) en vez del domo elíptico liso de antes —
     misma geometría que .apertura::before de este mismo ancho (ver
     esa regla arriba: R=66.89, f=15.56, canvas 156.1×67.89), reusando
     el mismo criterio de siempre (top:-12px + collar de 12px en el
     path, solapa bajo la banda como anti-seam) que ya usa 1440. El
     padding-top de 92px de arriba ya deja aire de sobra para que no
     choque con el título. */
  .areas::before {
    content: '';
    position: absolute;
    top: -12px;
    left: 50%;
    transform: translateX(-50%);
    width: 156.1px;
    height: 67.89px;
    background: var(--color-cream);
    clip-path: path('M 0,0 L 156.1,0 L 156.1,12 A 15.56,15.56 0 0 0 141.38,22.55 A 66.89,66.89 0 0 1 14.72,22.55 A 15.56,15.56 0 0 0 0,12 Z');
    pointer-events: none;
  }
}

/* ===================================================
   ÁREAS DE PRÁCTICA — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar
   ×1.376 sobre 744 — la muesca inferior reusa la MISMA geometría que
   744 (R=66.89, f=15.56, canvas 156.1×67.89, top:-12px). `padding`/
   `margin-left` de .areas__head/.areas__track solo cambian el margen
   lateral (`--margin-laptop`), el resto queda igual que 744.
=================================================== */
@media (min-width: 1024px) {

  .areas {
    padding: 92px 0 40px;
    gap: 48px;
  }

  .areas__head { padding: 0 var(--margin-laptop); }

  /* `padding-top:8px` se re-declara aquí a propósito (no "sin override"
     como el resto de este bloque): el shorthand `padding` de abajo solo
     cambia el margen lateral, pero como es shorthand completo resetearía
     el `padding-top:8px`/buffer de recorte del hover-lift heredado de
     744 de vuelta a 0 si no se repite — ver el bug real documentado
     junto a `.areas__track` en el bloque 744px. `margin-top:-8px` SÍ se
     hereda sin problema (no es parte de este shorthand). */
  .areas__track {
    margin-left: var(--margin-laptop);
    padding: 8px var(--margin-laptop) 0 0;
  }

  /* .areas::before: sin override — hereda de 744 la misma geometría
     (top:-12px, canvas 156.1×67.89, R=66.89/f=15.56). */

  /* Título de tarjeta: se mantiene en el tamaño "congelado" del resto
     del sitio (33.18px) — NO se revierte al 27.65px de 744, ese achique
     era literal del export 744px (no forma parte del patrón ×1.376 que
     se está revirtiendo en el resto de este bloque). */
  .areas__card-title { font-size: 33.18px; }
}

/* ===================================================
   ÁREAS DE PRÁCTICA — DESKTOP (min-width: 1256px)
   Carrusel horizontal scroll-snap. Cabecera con márgenes
   (label/H2 a la izq · flechas a la der); la pista sangra
   por la derecha (padding-right 0 del contenedor) para que
   las tarjetas se salgan del viewport y se naveguen con flechas.
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): `.areas__card-title{font-size}` se
     toca en 744 (27.65px) y 1024 (33.18px) y no aparece re-declarado aquí
     — SAFE: 1024 restaura explícitamente el valor "congelado" (33.18px)
     que este bloque también usa, antes de llegar a 1440 — no hace falta
     repetirlo. */
  .areas {
    padding: 105px 0 60px;
    gap: 48px;
  }

  /* Lengüeta cream estática con la muesca del broche (arco R 86 + fillets
     r 20; franja recta de 12px solapada bajo la banda como anti-seam →
     top: −12px), colgando en reposo sobre Áreas. clip-path: path() con
     geometría fija: ampliamente soportado (incluido Safari). Es el
     fallback para cuando apertura.js NO está activo (JS deshabilitado,
     prefers-reduced-motion, o mobile): mientras SÍ está activo, .apertura
     recorta su propia muesca animada y esta lengüeta se oculta (ver regla
     `.has-apertura-clip` justo abajo) para no duplicar la curva. */
  .areas::before {
    content: '';
    position: absolute;
    top: -12px;
    left: 50%;
    transform: translateX(-50%);
    width: 202.732px;
    height: 87px;
    background: var(--color-cream);
    clip-path: path('M 0,0 L 202.732,0 L 202.732,12 A 20,20 0 0 0 183.606,26.15 A 86,86 0 0 1 19.126,26.15 A 20,20 0 0 0 0,12 Z');
    pointer-events: none;
  }

  /* apertura.js añade esta clase al <html> cuando toma control del clip
     de .apertura (desktop + sin reduced-motion) — evita el doble recorte. */
  html.has-apertura-clip .areas::before { display: none; }

  .areas__head {
    padding: 0 var(--margin-desktop);
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
  }

  .areas__intro {
    align-items: flex-start;
    width: 718px;
    gap: 8px;
  }

  .areas__label { text-align: left; }

  .areas__title {
    font-size: 48.83px;
    text-align: left;
  }

  /* Flechas: círculos cream con chevron oscuro */
  .areas__arrows {
    display: flex;
    flex-direction: row;
    gap: 22px;
    flex-shrink: 0;
  }

  .areas__arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border: none;
    border-radius: 50%;
    background: var(--color-cream);
    cursor: pointer;
    box-shadow:
      0px 1px 1px 0px rgba(0,0,0,0.2),
      0px 3px 1.5px 0px rgba(0,0,0,0.1),
      0px 5px 1.5px 0px rgba(0,0,0,0.03);
    transition: opacity 0.2s ease, transform 0.2s ease;
  }

  .areas__arrow:hover { transform: translateY(-1px); }

  /* Extremo alcanzado: la flecha se atenúa (estado lo pone carousel.js) */
  .areas__arrow[disabled] {
    opacity: 0.4;
    cursor: default;
    transform: none;
  }

  /* Pista → scroller horizontal con snap. Sangra a la derecha:
     primer card alineado con el H2 (margin-left = margen, NO padding-left:
     el padding de un scroll container se recorre, y al scrollear las
     tarjetas invadían el margen izquierdo hasta el borde del viewport;
     con margin el scrollport empieza en la línea del margen y las
     tarjetas se recortan ahí), padding-right = margen para que el
     último card cierre en el margen.
     `overflow-y: hidden` (no `visible`) — mismo bug/fix que el bloque
     744px de arriba: con `overflow-x:auto` + `overflow-y:visible`, el
     eje Y se fuerza a `auto` por spec (regla de CSS Overflow: un eje
     `visible` junto a otro que no lo es se recomputa al valor del otro),
     habilitando scroll vertical real cuando una tarjeta aún no revelada
     (transform `translateY(40px)` inicial de `.reveal`) empuja el
     `scrollHeight` del track por encima de su alto.
     `padding-top:8px` + `margin-top:-8px` — mismo fix del 2° bug real
     (reportado tras el de arriba: el hover-lift de -6px se recortaba
     contra el borde superior del track, `overflow:hidden` clippea en
     las 4 direcciones) — ver el comentario completo en el bloque 744px,
     misma técnica: el padding le da margen de recorte hacia arriba y el
     margin negativo cancela el desplazamiento visual que ese padding
     introduciría. */
  .areas__track {
    flex-direction: row;
    gap: 24px;
    margin-left: var(--margin-desktop);
    margin-top: -8px;
    padding: 8px var(--margin-desktop) 0 0;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;             /* Firefox */
    -ms-overflow-style: none;          /* IE/Edge viejo */
  }

  .areas__track::-webkit-scrollbar { display: none; }  /* WebKit */

  .areas__card {
    flex: 0 0 400px;
    width: 400px;
    height: 390px;
    scroll-snap-align: start;
    border-radius: 8px;
    padding: 32px 22px;
    gap: 0;
    justify-content: space-between;
    align-items: flex-start;
  }

  .areas__card-text {
    align-items: flex-start;
    gap: 22px;
  }

  .areas__card-title,
  .areas__card-desc { text-align: left; }

  .areas__btn { align-self: flex-start; }
}

/* ===================================================
   ÁREAS DE PRÁCTICA — ULTRA-WIDE (min-width: 1920px)
   Hereda del bloque 1440: solo cambian márgenes, tamaño de
   H2 y de las flechas. Tarjetas iguales (400×390).
=================================================== */
@media (min-width: 1920px) {

  .areas {
    padding: 130px 0 90px;
  }

  /* Lengüeta estática con la muesca 1920 (arco R 99.07 + fillets r 23) */
  .areas::before {
    width: 234.48px;
    height: 100.07px;
    clip-path: path('M 0,0 L 234.48,0 L 234.48,12 A 23,23 0 0 0 212.39,28.6 A 99.07,99.07 0 0 1 22.09,28.6 A 23,23 0 0 0 0,12 Z');
  }

  .areas__head {
    padding: 0 var(--margin-ultrawide);
  }

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): el H2 hereda el
     48.83px de 1440px en vez de escalar a 55.34px. .areas__intro crecía
     de 718px (1440) a 1146px aquí — con la fuente más chica esa línea
     quedaría demasiado larga, así que se congela al mismo 718px; el aire
     sobrante contra las flechas lo absorbe justify-content:space-between
     de .areas__head. */
  .areas__intro { width: 718px; }

  .areas__arrow {
    width: 54px;
    height: 54px;
  }

  .areas__arrow svg {
    width: 18px;
    height: 18px;
  }

  .areas__track {
    margin-left: var(--margin-ultrawide);
    padding-right: var(--margin-ultrawide);
  }

  /* Tipografía congelada en 1920px: el H3 de tarjeta hereda el 33.18px de
     1440px (la tarjeta sigue midiendo 400×390 en ambos anchos, así que no
     hace falta tocar ningún ancho aquí). */
}

/* ===================================================
   POR QUÉ ELEGIRME — BASE MOBILE (375px)
   Banda crema (mismo patrón que .apertura: border-radius
   12px arriba para "flotar" sobre el fondo oscuro de Áreas
   que la precede). Intro centrada + 2 tarjetas apiladas a
   sangre completa (foto + overlay oscuro, icono + texto).
=================================================== */

.porque {
  position: relative;
  background: var(--color-cream);
  border-radius: 12px 12px 0 0;
  /* padding-bottom 0 (pedido explícito del usuario, "la última card
     pegala del final del contenedor, solo en mobile") — la última
     tarjeta (a sangre completa, width:100%) queda al ras del borde
     inferior de la sección en vez de dejar aire debajo. 744+ declara
     su propio padding completo (con aire abajo), no se filtra. */
  padding: 50px 0 0;
  display: flex;
  flex-direction: column;
  gap: 38px;
  align-items: center;
}

.porque__intro {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  padding: 0 16px;
  text-align: center;
}

.porque__heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.porque__label {
  color: var(--color-gold);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 700;
}

.porque__title {
  color: var(--color-black);
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
}

.porque__desc {
  color: var(--color-black);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
}

.porque__cards {
  display: flex;
  flex-direction: column;
  gap: 24px;
  width: 100%;
}

/* --- Tarjeta --- */
.porque__card {
  position: relative;
  width: 100%;
  height: 434px;
  border-radius: 0;
  overflow: hidden;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}

/* Overlay + foto por tarjeta (opacidad y sombra calibradas en Figma) */
.porque__card--derecho {
  background-image: linear-gradient(rgba(24,20,15,.9), rgba(24,20,15,.9)), url('../assets/img/porque/derecho.webp');
  box-shadow:
    -3px 2px 7px 0px rgba(0,0,0,0.1),
    -11px 7px 13px 0px rgba(0,0,0,0.09),
    -25px 15px 17px 0px rgba(0,0,0,0.05),
    -44px 27px 21px 0px rgba(0,0,0,0.01),
    -69px 42px 23px 0px rgba(0,0,0,0);
}

.porque__card--contabilidad {
  background-image: linear-gradient(rgba(24,20,15,.92), rgba(24,20,15,.92)), url('../assets/img/porque/contabilidad.webp');
  box-shadow:
    3px 2px 8px 0px rgba(0,0,0,0.1),
    13px 7px 15px 0px rgba(0,0,0,0.09),
    29px 16px 20px 0px rgba(0,0,0,0.05),
    52px 29px 24px 0px rgba(0,0,0,0.01),
    82px 45px 26px 0px rgba(0,0,0,0);
}

/* Contenido centrado sobre la foto (AutoHTML: frame-161/162 absolutos,
   top:63px). Las dos tarjetas comparten el mismo centrado left:50% +
   translateX: en el export de Figma la de Contabilidad usa constraints
   left/right en vez de centrado (asimetría de ~30px, artefacto del auto
   layout) — se normaliza aquí para que ambas tarjetas sean espejo exacto. */
.porque__card-inner {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 63px;
  /* min() en vez de 343px fijo: por debajo de 375px (p.ej. 320px) el
     valor fijo desbordaba la tarjeta (100% del viewport, sin padding
     propio) y .porque__card la recortaba (overflow:hidden), cortando
     el icono/título/descripción por los bordes. calc(100% - 32px)
     reproduce el mismo margen de 16px por lado que da 343px a 375px. */
  width: min(343px, calc(100% - 32px));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.porque__card-icon-svg {
  width: 103px;
  height: auto;
}

.porque__card-title {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 23.04px;
  font-weight: 400;
}

.porque__card-desc {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
  text-align: center;
}

/* Línea dorada inferior (AutoHTML: line-7/72, 200px, top relativo a la
   altura de la tarjeta) — se ancla por bottom, equivalente y más estable
   si el alto de la tarjeta cambia. */
.porque__card-line {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 22px;
  width: 200px;
  height: 0;
  margin: 0;
  border: none;
  border-top: 1px solid var(--color-gold);
}

/* ===================================================
   POR QUÉ ELEGIRME — TABLET VERTICAL (min-width: 744px)
   Reescrito 2026-07-22 contra PorQuElegirme.html (744.png):
   confirma que las tarjetas ya pasan a fila de 2 columnas a este
   ancho (como este bloque ya asumía) — solo corrige medidas.
   El ícono NO encoge a este ancho (103px, igual que mobile): el
   guess anterior (84px) lo achicaba sin motivo — Figma lo deja
   fijo aquí y solo lo reduce recién en 1440 (96px, ver ese bloque).
=================================================== */
@media (min-width: 744px) {

  .porque {
    padding: 40px var(--margin-tablet);
  }

  .porque__intro {
    width: min(610px, 100%);
    padding: 0;
  }

  .porque__heading { width: 100%; }

  .porque__title { font-size: 33.18px; }

  .porque__desc { width: min(610px, 100%); }

  .porque__cards {
    flex-direction: row;
    width: 100%;
    gap: 16px;
  }

  /* Ancho explícito (no flex:1): .porque no resetea `flex` en 1440px,
     así que flex-grow se filtraría y pisaría el width:506/536px
     calibrado de la tarjeta (flex-basis:0% del shorthand `flex`
     gana sobre `width` en el eje principal). */
  .porque__card {
    width: calc(50% - 8px);
    height: 434px;
    border-radius: 8px;
  }

  .porque__card-inner { width: min(300px, calc(100% - 24px)); }

  .porque__card-line { bottom: 18px; }
}

/* ===================================================
   POR QUÉ ELEGIRME — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar
   ×1.376 sobre 744 — solo cambia el margen lateral (`--margin-laptop`).
=================================================== */
@media (min-width: 1024px) {

  .porque { padding: 40px var(--margin-laptop); }

  /* .porque__intro/.porque__desc/.porque__title/.porque__cards/
     .porque__card/.porque__card-inner/.porque__card-icon-svg: sin
     override — heredan de 744 (610px, 33.18px, gap 16px, calc(50% -
     8px)×434px, min(300px, calc(100% - 24px)), 103px). */
}

/* ===================================================
   POR QUÉ ELEGIRME — DESKTOP (min-width: 1256px)
   Intro centrada (870px) + 2 tarjetas en fila (506px c/u,
   1036px total con gutter 24px, igual que el frame de texto
   de la apertura).
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): sin hallazgos en este componente —
     744/1024 no dejan ninguna propiedad sin re-declarar aquí. */
  .porque {
    padding: 60px var(--margin-desktop);
  }

  .porque__intro {
    width: 870px;
    padding: 0;
  }

  .porque__heading { width: 870px; }

  .porque__title { font-size: 48.83px; }

  .porque__desc { width: 610px; }

  .porque__cards {
    flex-direction: row;
    width: 1036px;
    gap: 24px;
  }

  .porque__card {
    width: 506px;
    height: 404px;
    border-radius: 8px;
  }

  .porque__card-inner { width: 446px; }

  /* Normalizado a 96px para ambos iconos (AutoHTML: 96×98 Derecho /
     99×98 Contabilidad — diferencia de ~3px, artefacto del hug de Figma). */
  .porque__card-icon-svg { width: 96px; }

  .porque__card-line { bottom: 18px; }
}

/* ===================================================
   POR QUÉ ELEGIRME — ULTRA-WIDE (min-width: 1920px)
   Hereda del bloque 1440: solo cambian márgenes, tamaño de
   H2, ancho de tarjeta y tamaño de icono.
=================================================== */
@media (min-width: 1920px) {

  .porque {
    padding: 90px var(--margin-ultrawide);
  }

  /* Tipografía congelada en 1920px: el H2 hereda el 48.83px de 1440px.
     Sin ajuste de ancho: .porque__intro/.porque__heading ya miden 870px
     fijos en ambos anchos (no crecen en este bloque), así el salto de
     línea del título no cambia. */

  .porque__cards { width: auto; }

  .porque__card {
    width: 536px;
    height: 434px;
  }

  .porque__card-icon-svg { width: 103px; }

  .porque__card-line { bottom: 22px; }
}

/* ===================================================
   CÓMO TRABAJO — BASE MOBILE (375px)
   Banda crema (mismo fondo que .porque). Grid de una columna:
   heading, foto y pasos son grid-items directos de .como,
   colocados vía grid-template-areas (heading → foto → pasos).
   En desktop cambia solo la plantilla de áreas (ver bloque 1440),
   sin reestructurar el HTML ni usar display:contents.
=================================================== */

.como {
  position: relative;
  /* -1px: solapa 1px con el borde inferior de .porque (misma cream de
     fondo). Sin esto, Chrome renderiza una costura de 1px gris entre
     ambos bloques aunque el gap real (getBoundingClientRect) sea 0 —
     artefacto de antialiasing entre 2 cajas adyacentes del MISMO color
     (con colores distintos no se nota, por eso solo aparece aquí). */
  margin-top: -1px;
  background: var(--color-cream);
  border-radius: 0 0 2px 2px;
  padding: 50px 16px;
  display: grid;
  /* minmax(0,1fr): sin esto, la columna implícita se auto-dimensiona por
     el contenido intrínseco (p.ej. .como__photo) y a <375px "revienta"
     más ancha que el viewport ("grid blowout") — un width:min(343px,100%)
     en el hijo no alcanza porque el % se resuelve indefinido durante ese
     cálculo intrínseco y cae al fallback fijo (343px). */
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "heading"
    "photo"
    "steps";
  gap: 24px;
  justify-items: center;
}

.como > picture { grid-area: photo; }
.como__heading  { grid-area: heading; }
.como__steps    { grid-area: steps; }

.como__heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
  /* width:100% (no solo grid-area): con justify-items:center el item
     por defecto se dimensiona a su max-content (título Gloock sin
     saltos de línea) en vez de encogerse al área de la columna —
     mismo "hug" que ya documentado para el bloque 1440 (.como__title),
     solo que aquí toca al contenedor flex completo. Sin esto, a
     <375px el H2 no envuelve y desborda la sección. */
  width: 100%;
}

.como__label {
  color: var(--color-gold);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 700;
}

.como__title {
  color: #0e0e0c;
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
  /* width:100%: un flex item con align-items:center no se clampea al
     ancho del contenedor (a diferencia de un grid item) — sin esto
     el H2 se dimensiona a su max-content y desborda a <375px. Mismo
     fix que ya usa el bloque 1440 para el mismo problema. overflow-wrap:
     "acompañamiento" (14 letras, Gloock 39.81px) es más ancha que la
     columna a 320px — sin esto la palabra se sale del box en vez de
     partirse. */
  width: 100%;
  overflow-wrap: break-word;
}

.como__photo {
  display: block;
  /* min() + aspect-ratio en vez de 343×363 fijo: por debajo de 375px
     (p.ej. 320px) el ancho fijo desbordaba la columna de la grid
     (.como solo tiene 16px de padding por lado) y empujaba toda la
     sección más ancha que el viewport. */
  width: min(343px, 100%);
  height: auto;
  aspect-ratio: 343 / 363;
  border-radius: 4px;
  object-fit: cover;
  box-shadow:
    -1px 1px 4px 0px rgba(0,0,0,0.1),
    -5px 5px 7px 0px rgba(0,0,0,0.09),
    -11px 11px 9px 0px rgba(0,0,0,0.05),
    -19px 19px 11px 0px rgba(0,0,0,0.01),
    -30px 30px 12px 0px rgba(0,0,0,0);
}

.como__steps {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

/* --- Paso individual --- */
.como__step {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  padding: 18px;
  text-align: center;
}

/* Círculo del icono: incluye la sombra de 4 capas que en el AutoHTML
   viene "horneada" en un filtro SVG sobre el propio rect — se normaliza
   a un único box-shadow CSS reutilizable en los 3 pasos. */
.como__step-icon {
  flex-shrink: 0;
  width: 98px;
  height: 100px;
  border-radius: 50%;
  background: #211c16;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow:
    -1px 0px 1px 0px rgba(0,0,0,0.1),
    -2px 1px 3px 0px rgba(0,0,0,0.09),
    -5px 3px 4px 0px rgba(0,0,0,0.05),
    -10px 5px 4px 0px rgba(0,0,0,0.01),
    -15px 7px 5px 0px rgba(0,0,0,0);
}

/* El dibujo del icono ya ocupa solo ~35% de su viewBox por diseño (Figma):
   el svg debe medir lo mismo que el círculo, NO una fracción de él —
   si se reduce más, el icono queda minúsculo (doble escalado). */
.como__step-icon-svg { width: 98px; height: auto; }
.como__step-icon-svg--handshake { width: 37px; }

.como__step-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.como__step-title {
  color: var(--color-black);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  line-height: 22px;
}

.como__step-desc {
  color: var(--color-black);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
  text-align: center;
}

.como__step-line {
  width: 100%;
  height: 0;
  margin: 0;
  border: none;
  border-top: 2px solid var(--color-bg);
}

/* ===================================================
   CÓMO TRABAJO — TABLET VERTICAL (min-width: 744px)
   Reescrito 2026-07-22 contra CMoTrabajo.html (744.png): a
   diferencia de lo asumido antes (mismo apilado centrado de
   mobile, solo con más aire), el diseño real a este ancho es
   FOTO ARRIBA a sangre completa (301px, asset propio como-768.webp
   — la de mobile es un recorte retrato distinto, no sirve
   ampliada) + heading alineado a la izquierda + pasos en FILA
   (icono izq. + texto der., como ya hacía 1024) en vez del
   ícono-arriba-texto-abajo centrado de mobile.
   Sigue en una sola columna (grid-template-areas propio, orden
   foto→heading→pasos) — el patrón de 2 columnas foto-izq/
   contenido-der arranca recién en 1024 (sin export propio ahí,
   pero ya funcionaba razonablemente con la foto 664×301 no
   pensada para ir angosta en 2 columnas de ese ancho).
=================================================== */
@media (min-width: 744px) {

  .como {
    padding: 40px;
    grid-template-areas:
      "photo"
      "heading"
      "steps";
    gap: 28px;
    /* stretch (no el center heredado de mobile): la foto pasa a sangre
       completa (ver .como__photo) y necesita que su celda de grid mida
       el 100% de la columna, no el ancho intrínseco de la imagen. */
    justify-items: stretch;
  }

  /* Gap real de 38px entre foto y heading (el resto ya usa 28px
     parejo vía `gap` de la grid) — mismo criterio que .sobre__accordion
     (row-gap parejo + margin-top puntual, ver ese bloque). */
  .como__heading {
    align-items: flex-start;
    text-align: left;
    margin-top: 10px;
  }

  .como__title {
    width: 100%;
    font-size: 33.18px;
  }

  .como__photo {
    width: 100%;
    height: 301px;
    aspect-ratio: auto;
  }

  .como__steps { gap: 16px; }

  .como__step {
    flex-direction: row;
    align-items: flex-start;
    text-align: left;
    gap: 24px;
    padding: 18px;
  }

  .como__step-icon { width: 78px; height: 80px; }

  .como__step-icon-svg { width: 78px; }
  .como__step-icon-svg--handshake { width: 32px; }

  .como__step-text { align-items: flex-start; }

  .como__step-title,
  .como__step-desc { text-align: left; }
}

/* ===================================================
   CÓMO TRABAJO — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   Sin export propio a este ancho. La suposición previa (adelantar
   ya el patrón de 2 columnas de 1440) dejó de encajar al calibrar
   768 contra el export real: ahí la foto es un recorte PAISAJE a
   sangre completa (664×301, asset como-768.webp), muy distinto del
   recorte RETRATO angosto (612×647) que sí usa 1440 — meterla en
   una caja retrato de 320×339 la recortaría a una tira vertical
   irreconocible. Se mantiene la misma columna única de 768 (foto
   arriba a sangre completa) escalada — el giro a 2 columnas queda
   como algo exclusivo de 1440, donde si hay un asset retrato real.
=================================================== */
@media (min-width: 1024px) {

  .como {
    padding: 40px var(--margin-laptop);
  }

  /* .como__title/.como__photo/.como__step-icon(-svg): sin override —
     heredan de 744 (33.18px, foto 301px de alto, icono 78×80px). Solo
     cambia el margen lateral (`--margin-laptop`) — ver nota en HERO 1024. */

  .como__step-title,
  .como__step-desc { text-align: left; }
}

/* ===================================================
   CÓMO TRABAJO — DESKTOP (min-width: 1256px)
   Grid de 2 columnas: foto (612×647) a la izq., heading+pasos a
   la der. La foto ocupa las 2 filas ("photo") mientras heading
   (fila auto) y pasos (fila 1fr) se apilan a la derecha; el track
   1fr crece para acomodar la altura real de la foto (comportamiento
   estándar de CSS Grid al expandir tracks flexibles frente a un
   item que abarca varias filas — spec "Expand Flexible Tracks"),
   dejando que .como__steps reparta sus 3 pasos con space-between
   en ese espacio, igual que el Figma (frame-111 height:643 +
   frame-110 flex:1). Un solo track flexible (1fr): sin ambigüedad
   de reparto entre varios fr al mismo tiempo.
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): 5 hallazgos en Cómo Trabajo.
     - `.como{gap:28px}` (744): FALSO POSITIVO — este bloque resetea vía
       longhand `column-gap`/`row-gap` (row-gap:28px, mismo valor) en vez
       del shorthand `gap`; el script compara nombres literales y no
       reconoce la equivalencia.
     - `.como__photo{aspect-ratio:auto}` (744): SAFE — aquí width (612px)
       y height (647px) ya están ambos definidos, aspect-ratio se ignora.
     - `.como__step{padding:18px}` / `.como__steps{gap:16px}` (744): SAFE
       — mismo valor que trae la base mobile, constante que nunca cambió
       entre breakpoints (el `gap` de `.como__steps` además convive bien
       con el `justify-content:space-between` de aquí — actúan como piso
       mínimo, no hay conflicto).
     - `.como__heading{margin-top:10px}` (744): REVISAR — no se resetea
       aquí. A 744 compensa el gap dispar foto→heading dentro de una
       columna apilada; a 1440 heading ya no está debajo de la foto (grid
       de 2 columnas, celdas distintas), así que ese propósito ya no
       aplica. No se verificó con captura si desplaza el heading 10px de
       más dentro de su celda — revisar visualmente antes de tocar este
       selector. */
  .como {
    grid-template-columns: 612px 1fr;
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "photo heading"
      "photo steps";
    justify-items: stretch;
    align-items: stretch;
    column-gap: 24px;
    row-gap: 28px;
    padding: 60px var(--margin-desktop);
    justify-content: center;
  }

  .como__photo {
    width: 612px;
    height: 647px;
  }

  .como__heading {
    align-items: flex-start;
    text-align: left;
  }

  /* width:100% imprescindible: el padre es flex-column align-items:flex-start,
     así que sin ancho explícito el H2 se encoge a su contenido (hug) y el
     texto envuelve en un ancho mucho menor al de la columna (612px). */
  .como__title {
    width: 100%;
    font-size: 48.83px;
  }

  .como__steps {
    justify-content: space-between;
    min-height: 0;
  }

  .como__step {
    flex-direction: row;
    align-items: flex-start;
    text-align: left;
    gap: 24px;
  }

  .como__step-icon { width: 88px; height: 90px; }

  .como__step-icon-svg { width: 88px; }
  .como__step-icon-svg--handshake { width: 31px; }

  .como__step-text { align-items: flex-start; }

  .como__step-title,
  .como__step-desc { text-align: left; }
}

/* ===================================================
   CÓMO TRABAJO — ULTRA-WIDE (min-width: 1920px)
   Hereda del bloque 1440: foto con otro recorte (más
   panorámico) e icono más chico (valores tal cual AutoHTML
   — variación de Figma, no error: a más ancho, el icono
   reduce un poco).
=================================================== */
@media (min-width: 1920px) {

  /* Corregido 2026-07-31, pedido explícito ("la sección Cómo trabajo
     debe estar centrada en desktop"): la columna de contenido ya no es
     "1fr" (crecía hasta ~1090px de ancho real a 1920, muy por encima del
     max-width:612px que ya limitaba el texto — el texto quedaba pegado
     a la izquierda con un vacío enorme a la derecha, rompiendo la
     simetría que sí tiene "¿Por qué elegirme?" arriba). Ahora la columna
     es un 612px fijo (mismo ancho de lectura que ya usaba el texto vía
     max-width, ya no hace falta declararlo aparte) y `justify-content:
     center` (heredado del bloque 1256, sin cambios ahí) centra el par
     foto+contenido (756+24+612=1392px) dentro del contenedor disponible
     (1536px a 1920) — el sobrante se reparte igual a ambos lados en vez
     de quedar todo a la derecha. */
  .como {
    grid-template-columns: 756px 612px;
    padding: 90px var(--margin-ultrawide);
  }

  .como__photo {
    width: 756px;
    height: 611px;
  }

  .como__step-icon { width: 78px; height: 80px; }

  .como__step-icon-svg { width: 78px; }
  .como__step-icon-svg--handshake { width: 32px; }
}

/* ===================================================
   BANNER CTA — BASE MOBILE (375px)
   Franja oro sólido, contenido centrado en columna (título
   + descripción, botón WhatsApp debajo). `.banner__decor` es
   el mazo (PNG con alpha, sin fondo) del AutoHTML posicionado
   como acento decorativo en la esquina — el propio AutoHTML lo
   exporta como "background: url(...) cover" del contenedor,
   pero a esa escala el mazo tapa el texto; se reinterpreta como
   elemento decorativo absoluto de baja opacidad, fiel al efecto
   tenue visible en la captura de referencia.
=================================================== */

.banner {
  position: relative;
  background: var(--color-cta);
  border-radius: 0 0 12px 12px;
  padding: 50px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
  text-align: center;
  overflow: hidden;
}

.banner__decor {
  position: absolute;
  right: -70px;
  bottom: -35px;
  width: 260px;
  height: auto;
  opacity: 0.25;
  pointer-events: none;
}

.banner__content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
}

.banner__title {
  color: #0e0e0c;
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
}

.banner__desc {
  position: relative;
  color: #0e0e0c;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
}

.banner__cta {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  background: #18140f;
  color: var(--color-white);
  border-radius: 8px;
  padding: 16px 22px;
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
  text-decoration: none;
  white-space: nowrap;
  /* La `transition` real de este botón NO se declara aquí — `.banner__cta`
     también lleva la clase `.reveal` (para su entrada al hacer scroll,
     ver ANIMACIONES), y como `.reveal` vive más abajo en el archivo con
     la MISMA especificidad (selector de una sola clase), cualquier
     `transition` puesta en ESTA regla quedaría completamente pisada por
     la de `.reveal` (mismo bug ya documentado para `.areas__card`/
     `.porque__card`) — la `transition` real, que cubre entrada Y hover a
     la vez, se declara junto al hover en la sección ANIMACIONES, después
     de `.reveal`, para que gane por orden de cascada. */
}

/* Hover (2026-07-26, pedido explícito: "hazle un hover al botón de CTA
   banner de WhatsApp") — este botón es oscuro (`#18140f`, mismo tono que
   el fondo del sitio) flotando sobre el banner dorado/foto, al revés de
   los demás CTA (dorado sobre fondo oscuro) — por eso su hover NO puede
   ser "oscurecer" (ya es casi negro, no se notaría) ni tampoco tiene
   sentido copiar `--color-cta-hover` (pensado para aclarar un dorado, no
   para un botón oscuro). Se ilumina a un marrón cálido más claro
   (`#2b2019`, mismo tinte que `--color-bg` pero perceptiblemente más
   claro) + el mismo lift/sombra que el resto de botones sólidos, para
   mantener un cambio de color real y notorio sin salirse de la paleta.
   La `transition` que hace esto suave vive en ANIMACIONES (ver comentario
   arriba) — sin ella, este hover igual APLICA (los valores de propiedad
   no dependen de la transition), solo se vería como un salto brusco en
   vez de una transición de 0.2s.
   El `transform` del lift NO se declara aquí — ver por qué junto a
   `.banner__cta:hover{transform}` en ANIMACIONES (mismo motivo por el
   que la `transition` tampoco vive aquí: `.reveal.is-visible` gana por
   cascada). `box-shadow`/`background-color` sí funcionan bien declarados
   en este punto porque `.reveal`/`.reveal.is-visible` no tocan esas 2
   propiedades, así que no hay ninguna regla más abajo que las pise. */
@media (hover: hover) and (pointer: fine) {
  .banner__cta:hover {
    box-shadow: 0 10px 20px -8px rgba(0, 0, 0, 0.5);
    background-color: #2b2019;
  }
}

.banner__cta-icon {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
}

/* ===================================================
   BANNER CTA — TABLET VERTICAL (min-width: 744px)
   RECALIBRADO 2026-07-23 contra export real (Recursos/AutoHTML/744/).
   La aproximación vieja (fondo oro sólido + mazo, centrado, sin la
   foto) era incorrecta — la captura de referencia confirma que a
   744 ya usa el mismo patrón que 1440/1920: foto de la estatua +
   gradiente diagonal, contenido alineado a la izquierda, esquinas
   cuadradas (sin border-radius, a diferencia del mobile que flota
   con radius sobre la sección siguiente). Mismo mecanismo de 2 capas
   (gradiente + `auto 100%` por altura) ya usado en desktop — la caja
   es más baja/angosta a este ancho pero la fórmula sigue siendo
   agnóstica al aspect ratio (escala por la altura real visible).
   POSICIÓN X AFINADA A MANO 2026-07-24 ("la imagen se ve cortada"):
   `right` (100%) dejaba a Lady Justice pegada al borde y cortada por
   el corte diagonal del gradiente. Se corrió el punto de anclaje
   `calc(100% + 125px)` (más allá del 100%, o sea la imagen se desplaza
   hacia la derecha respecto a la caja) a puro ojo contra capturas
   reales en 744 Y 1024 hasta que la figura completa (balanza, rostro,
   base) quedó centrada dentro de la franja visible junto al corte —
   confirmado que ese mismo valor funciona bien en ambos anchos (no
   hace falta un override aparte en el bloque 1024). Si se retoca el
   ancho de la caja, el gradiente, o el asset, hay que re-verificar
   este offset a ojo — no sale de una fórmula, es el resultado final
   de varias iteraciones visuales.
=================================================== */
@media (min-width: 744px) {

  .banner {
    padding: 40px;
    gap: 22px;
    align-items: flex-start;
    justify-content: flex-start;
    text-align: left;
    border-radius: 0;
    background:
      linear-gradient(99.02deg, rgba(241,184,83,1) 70.18%, rgba(24,20,15,0.75) 70.19%),
      url('../assets/img/banner/banner-desktop.webp') calc(100% + 125px) center / auto 100% no-repeat;
  }

  .banner__decor { display: none; }

  .banner__content {
    align-items: flex-start;
    width: 392px;
  }

  .banner__title {
    width: 100%;
    color: #18140f;
    font-size: 33.18px;
  }

  .banner__title-extra { display: none; }
}

/* ===================================================
   BANNER CTA — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar
   ×1.376 sobre 744 — solo cambia el margen lateral (`--margin-laptop`).
=================================================== */
@media (min-width: 1024px) {

  .banner { padding: 40px var(--margin-laptop); }

  /* .banner__content/.banner__title: sin override — heredan de 744
     (392px, 33.18px). */
}

/* ===================================================
   BANNER CTA — DESKTOP (min-width: 1256px)
   Franja a sangre con foto (estatua de la Justicia) + gradiente
   diagonal oro→oscuro (99.55deg, corte en 70.18%): el gradiente
   va COMO PRIMERA capa de fondo sobre la foto (segunda capa) —
   donde el stop es oro opaco tapa la foto por completo; donde es
   oscuro semitransparente (.75) dejar verla con tinte oscuro. Así
   se reconcilian las 2 fuentes del AutoHTML (el gradiente venía en
   la clase .banner y la foto en un style inline aparte, pero juntas
   son exactamente la mezcla de la captura de referencia). Contenido
   alineado a la izquierda, sin acento del mazo (oculto).
   Tamaño de la foto: `auto 100%` (NO `cover`) + `right center` — la
   caja de la sección es muy ancha y baja (~1464×430); `cover` la
   escala por el ANCHO completo del elemento aunque el 70% quede
   tapado por el oro opaco, así que hace zoom excesivo y recorta la
   estatua casi entera. `auto 100%` escala solo por la ALTURA real
   visible, dejando la estatua completa igual que en la referencia.
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): `.banner{gap:22px}` se toca en 744
     y no se re-declara aquí (este bloque solo tocaba align-items/
     justify-content/text-align/padding/border-radius/background). CONFIRMADO
     — el gap de 22px entre título/descripción/botón que se ve en 1440/1920
     venía de la cascada de 744, no de una declaración propia de este
     bloque (la base mobile usa 32px). ARREGLADO 2026-07-23: se declara
     `gap: 22px` explícito abajo (mismo valor ya heredado — sin cambio de
     render) para que deje de depender de que 744 no cambie el suyo. */
  .banner {
    align-items: flex-start;
    justify-content: flex-start;
    text-align: left;
    padding: 90px var(--margin-desktop);
    border-radius: 0;
    gap: 22px;
    background:
      linear-gradient(99.55deg, rgba(241,184,83,1) 70.18%, rgba(24,20,15,0.75) 70.19%),
      url('../assets/img/banner/banner-desktop.webp') right center / auto 100% no-repeat;
  }

  .banner__decor { display: none; }

  .banner__content {
    align-items: flex-start;
    width: 718px;
  }

  .banner__title {
    width: 100%;
    color: #18140f;
    font-size: 48.83px;
  }

  .banner__title-extra { display: none; }
}

/* ===================================================
   BANNER CTA — ULTRA-WIDE (min-width: 1920px)
=================================================== */
@media (min-width: 1920px) {

  .banner {
    padding: 120px var(--margin-ultrawide);
    background:
      linear-gradient(99.02deg, rgba(241,184,83,1) 70.18%, rgba(24,20,15,0.75) 70.19%),
      url('../assets/img/banner/banner-desktop.webp') right center / auto 100% no-repeat;
  }

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): el H2 hereda el
     48.83px de 1440px en vez de escalar a 55.34px. .banner__content
     crecía de 718px (1440) a 886px aquí — con la fuente más chica el
     título/descripción quedarían demasiado anchos, así que se congela al
     mismo 718px; el resto de la franja (mucho más ancha que a 1440) deja
     ver más foto de fondo a la derecha, que es justamente el efecto
     buscado en un banner a sangre. */
  .banner__content { width: 718px; }
}

/* ===================================================
   SOBRE MÍ — BASE MOBILE (375px)
   Foto + bio + acordeón (Educación/Certificaciones/Membresías).
   Grid de una columna: heading, foto, bio y accordion son
   grid-items directos de .sobre, colocados vía grid-template-areas
   (heading → foto → bio → accordion), sin display:contents ni
   wrappers de agrupación. Gap real del AutoHTML no es uniforme
   (22px entre heading→foto→bio, 32px antes del acordeón): se usa
   `gap:22px` parejo + `margin-top:10px` en el acordeón para
   completar los 32px sin duplicar declaraciones de gap.
=================================================== */

.sobre {
  position: relative;
  background: var(--color-bg);
  padding: 50px 16px;
  display: grid;
  grid-template-areas:
    "heading"
    "photo"
    "bio"
    "accordion";
  justify-items: center;
  gap: 22px;
}

.sobre__heading   { grid-area: heading; }
.sobre__photo     { grid-area: photo; }
.sobre__bio       { grid-area: bio; }
.sobre__accordion { grid-area: accordion; }

/* La foto es un DIV (no <img>) con la foto y el viñeteado apilados
   como capas de `background` — el AutoHTML trae el degradado en la
   clase del nodo y la foto en un <img> aparte, pero es el mismo caso
   que Banner: son 2 capas del mismo fondo, no 2 elementos distintos. */
.sobre__photo {
  flex-shrink: 0;
  width: 215px;
  height: 313px;
  background:
    linear-gradient(154.72deg, rgba(24,20,15,0) 75.23%, rgba(24,20,15,1) 90.02%),
    linear-gradient(185.92deg, rgba(24,20,15,0) 74.89%, rgba(24,20,15,1) 98.68%),
    linear-gradient(90deg, rgba(24,20,15,.25) 75.27%, rgba(24,20,15,1) 95.72%),
    url('../assets/img/sobre/sobre-mobile.webp') center / cover no-repeat;
}

.sobre__heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  text-align: center;
}

.sobre__label {
  color: var(--color-gold);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 700;
}

.sobre__title {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
}

.sobre__bio {
  display: flex;
  flex-direction: column;
  gap: 16px;
  width: 100%;
}

.sobre__para {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
}

/* overflow-x:hidden (2026-08-17, fix icono flotante de WhatsApp "cortado"
   en mobile hasta terminar el scroll): los 3 `.sobre__acc-item` entran con
   `--reveal-x:40px` (ver ANIMACIONES/GEOMETRÍA — entrada lateral desde la
   derecha). Mientras un item no se revela (todavía no cruzó el
   IntersectionObserver, o sea CUALQUIER momento antes de que el usuario
   llegue a Sobre mí con el scroll — incluido el estado inicial de carga),
   su `transform:translateX(40px)` PINTA la caja 40px a la derecha de su
   posición real en el layout — a 375px eso empuja su borde derecho ~9px
   más allá del viewport, generando scroll horizontal REAL (confirmado con
   `scrollTo`/`scrollWidth`: 384 vs 360 sin este fix; `html{overflow-x:clip}`
   NO lo bloquea, mismo bug ya documentado para `.apertura::before`/
   `.hero__broche` en CLAUDE.md). Un mobile real usa ese ancho de documento
   "sobrante" para calcular dónde cae el borde derecho de cualquier
   `position:fixed` (el botón flotante de WhatsApp, `.floating-actions`),
   así que aparecía pegado/cortado contra el borde hasta que el scroll
   llegaba a esta sección y el `transform` se resolvía a `none`
   (`.is-visible`), momento en que el navegador recalculaba y el botón
   "se arreglaba". Fix: contener el desborde en el propio contenedor de los
   3 items en vez de solo confiar en el `clip` global de `html` (mismo
   criterio ya usado en `.apertura`/`.areas__track`) — el layout vertical
   (flex-column, alto de los paneles) no se ve afectado, solo se recorta el
   eje X. */
.sobre__accordion {
  margin-top: 10px;
  display: flex;
  flex-direction: column;
  width: 100%;
  overflow-x: hidden;
}

.sobre__acc-item {
  border-bottom: 0.5px solid rgba(241,184,83,.75);
}

.sobre__acc-item--first {
  border-top: 0.5px solid rgba(241,184,83,.75);
}

.sobre__acc-trigger {
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  padding: 16px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 54px;
}

.sobre__acc-label {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 18px;
  font-weight: 400;
}

.sobre__acc-chevron {
  flex-shrink: 0;
  transition: transform 0.3s ease;
}

.sobre__acc-item.is-open .sobre__acc-chevron { transform: rotate(180deg); }

/* Alto animado por JS (sobre.js mide .sobre__acc-panel-inner.scrollHeight
   y lo aplica como height inline al abrir/cerrar) — evita el truco CSS
   grid-template-rows: 0fr → 1fr, que requiere `display: grid` en un
   contenedor cuyo alto real no se conoce de antemano. */
.sobre__acc-panel {
  height: 0px;
  overflow: hidden;
  transition: height 0.3s ease;
}

.sobre__acc-panel-inner { overflow: hidden; }

.sobre__acc-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 18px 18px 18px;
}

.sobre__acc-list li {
  position: relative;
  padding-left: 14px;
  color: rgba(255,255,255,.70);
  font-family: 'Inter', sans-serif;
  font-size: 15px;
  font-weight: 400;
}

.sobre__acc-list li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--color-cta);
}

/* ===================================================
   SOBRE MÍ — TABLET VERTICAL (min-width: 744px)
   RECALIBRADO 2026-07-23 contra export real (Recursos/AutoHTML/744/).
   La aproximación vieja (foto 260×378, heading con clamp()) era una
   extensión sin verificar — el export real confirma que sigue apilado
   en una columna CENTRADA (mismo layout que mobile, no el de 2
   columnas de desktop), pero con una sorpresa: la foto es MÁS CHICA
   que en mobile (178×259 vs 215×313 — mismo aspect-ratio 0.687, así
   que se reusa `sobre-mobile.webp` sin necesidad de un asset nuevo,
   solo se reduce su tamaño de display) — otro caso de tamaño NO
   monótono entre breakpoints, ver REGLAS SIEMPRE.
   Gaps reales del export (no uniformes: 16 heading→foto, 38
   foto→bio, 38 bio→acordeón — confirmado también por medición de
   píxeles contra la captura, ya que el export trae `gap:0` a nivel
   raíz por un artefacto de anidado de Figma que no cuadra con la
   captura real): se resuelve con `gap:16px` parejo en el grid +
   `margin-top:22px` en `.sobre__bio` y en `.sobre__accordion`
   (16+22=38 en ambos casos), mismo patrón que ya usaba el acordeón
   en 375/1440. El título necesita `width:556px` explícito — sin eso,
   al no estar en `justify-items:stretch` (sigue `center` como
   mobile), se estira a fit-content del track completo (~664px) en
   vez de envolver en las 2 líneas más angostas de la referencia.
=================================================== */
@media (min-width: 744px) {

  .sobre {
    padding: 40px;
    gap: 16px;
  }

  .sobre__photo {
    width: 178px;
    height: 259px;
  }

  .sobre__title {
    width: min(556px, 100%);
    font-size: 33.18px;
  }

  .sobre__bio { margin-top: 22px; }

  .sobre__accordion { margin-top: 22px; }
}

/* ===================================================
   SOBRE MÍ — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar
   ×1.376 sobre 744 — sigue en una sola columna centrada, mismos
   tamaños que 744 (foto 178×259, título 33.18px). Solo cambia el
   margen lateral (`--margin-laptop`).
=================================================== */
@media (min-width: 1024px) {

  .sobre { padding: 40px var(--margin-laptop); }

  /* .sobre__photo/.sobre__title/.sobre__bio/.sobre__accordion: sin
     override — heredan de 744 (178×259, min(556px,100%)/33.18px,
     margin-top:22px). */
}

/* ===================================================
   SOBRE MÍ — DESKTOP (min-width: 1256px)
   Cambia solo la plantilla de grid: foto a la izquierda + columna
   de contenido (heading, bio, acordeón) a la derecha.
=================================================== */
@media (min-width: 1256px) {
  /* GUARD (tools/verify_css_resets.py): `.sobre{gap:16px/22px}` (744/1024)
     no se re-declara aquí con ese nombre — FALSO POSITIVO: este bloque
     resetea vía longhand `column-gap:0; row-gap:22px` (mismo valor que ya
     traía 1024, coincidentemente); el script no reconoce la equivalencia
     shorthand/longhand. */

  /* Grid de 2 columnas: foto (424×617) fija + columna de contenido
     (665px). La foto abarca las 5 filas ("photo"); las 2 filas 1fr
     de los extremos son celdas vacías que absorben el espacio sobrante
     por igual (mismo valor de fr en ambas → reparto simétrico
     garantizado por spec, sin ambigüedad) y así centran verticalmente
     el bloque heading+bio+accordion frente a la foto, sin necesitar
     que ese bloque viva en un wrapper propio.
     **Bug real encontrado y corregido (2026-07-23, "al abrir los
     acordeones la sección siguiente se mueve hacia abajo")**: sin
     `height` propio, `.sobre` medía su alto de forma intrínseca (filas
     "auto" + las 2 "1fr" de relleno) — con el acordeón CERRADO el total
     coincidía con los 617px de la foto por apenas ~6.6px de margen
     real (medido con getBoundingClientRect), pero un panel abierto
     necesita ~99px más: al no caber ni con las filas 1fr en 0, `.sobre`
     crecía ~92px de golpe y empujaba Contacto hacia abajo esa misma
     cantidad. Primer fix (revertido): `height` = foto + padding
     (idéntico al alto ya usado con el acordeón cerrado) + scroll
     interno en `.sobre__accordion` para el panel abierto — el usuario
     pidió explícitamente evitar la scrollbar y ver el panel completo.
     **Fix final**: `height` fijo pero MÁS ALTO que la foto — el
     necesario para que quepa el panel MÁS GRANDE ya abierto (heading
     126.40625 + bio 214 + acordeón abierto 265 + margin-top 16 + 4×22
     de row-gap + el padding vertical de este bloque, medido con
     getBoundingClientRect con la transición desactivada para evitar
     falsos negativos — ver nota de medición en `.sobre__accordion`
     abajo). Las 2 filas `1fr` de relleno absorben el sobrante cuando
     el acordeón está cerrado (reparto simétrico, sigue centrando
     heading+bio+accordion contra la foto) — consecuencia visible:
     con todo cerrado, la tarjeta queda ~92px más alta que antes (más
     aire arriba/abajo de la foto), a cambio de que Contacto NUNCA se
     mueva y el panel abierto se vea completo sin scrollbar.
     **Fila superior `1fr` → `auto` (2026-07-26, pedido explícito: "los
     acordeones haz que se abran hacia abajo... que al abrir el segundo
     acordeón la información sube un poco... haz que la sección no se
     mueva")**: con las 2 filas de relleno en `1fr` (reparto simétrico),
     CUALQUIER cambio en el alto del bloque auto (heading+bio+accordion)
     — típicamente al abrir un panel MÁS ALTO que el que estaba abierto,
     o al cerrar uno y abrir otro de distinto tamaño — reparte la
     diferencia entre AMBAS filas de relleno por igual: la fila de
     arriba se achica, y como se achica, el bloque completo (heading
     incluido) sube esa misma cantidad. Es el causante real de "la
     información sube un poco" — el acordeón technically sí abre hacia
     abajo, pero TODO lo de arriba se recoloca hacia arriba al mismo
     tiempo por el recentrado automático. Fix: la fila superior pasa a
     `auto` (sin contenido asignado ahí → 0px, fija, nunca vuelve a
     recalcularse) y la fila inferior se queda como ÚNICA `1fr` — ahora
     es la única que absorbe el sobrante, así que crece/encoge por su
     cuenta sin mover nada por ENCIMA del acordeón. Consecuencia visible
     aceptada: el bloque heading+bio+accordion ya NO queda centrado
     verticalmente contra la foto en reposo (se ve más pegado arriba,
     con más aire abajo) — es el trade-off inherente a "que abra hacia
     abajo como un acordeón normal" en vez de mantener el centrado
     antiguo; el usuario pidió explícitamente el comportamiento de
     acordeón normal, no el centrado. La altura TOTAL de `.sobre` no
     cambió (sigue en 829.40625px/889.40625px) — sigue habiendo margen
     de sobra para el panel más grande sin necesidad de crecer, mismo
     cálculo que ya garantizaba el fix de 2026-07-23. */
  /* SIN `height` fijo (2026-08-15): `.sobre` mide lo que su contenido
     real pide — con el acordeón cerrado eso es la foto (617px, más alta
     que heading+bio+acordeón-cerrado juntos), así que el total en reposo
     queda en `padding + 617 + padding`, igual criterio que el resto de
     secciones (Por qué elegirme/Cómo trabajo/Footer: tamaño = contenido +
     padding, sin colchón). Fila superior `auto` (NO `1fr`, pedido
     explícito de la ÚLTIMA vuelta: "que el contenedor se expanda, pero
     que solo se expanda hacia abajo, no quiero que la información se
     levante" — con `1fr` en AMBOS extremos, como se probó en la vuelta
     anterior, al abrir un panel las 2 filas de relleno se encogen
     PAREJO hasta llegar a 0 antes de que `.sobre` empiece a crecer de
     verdad, así que la fila de arriba (y con ella heading/bio/foto)
     subía un poco cada vez — exactamente el mismo bug ya documentado en
     2026-07-26 para el heading, "al abrir el segundo acordeón la
     información sube", que había obligado a esa fila a `auto` en el
     `height` fijo que existió ANTES de quitarlo del todo esta sesión).
     Con la fila de arriba en `auto` (0px, sin contenido asignado, fija
     para siempre) y SOLO la de abajo en `1fr`, el crecimiento del
     acordeón (al abrir cualquier panel) se absorbe ÚNICA y enteramente
     en esa fila de abajo — nunca en la de arriba — así que heading/bio/
     foto no se mueven ni 1px sin importar qué panel se abra, y `.sobre`
     SÍ crece hacia abajo (empujando Contacto) cuando el panel no cabe en
     el sobrante disponible.
     **2026-08-16**: se probó y se revirtió un `height` fijo (829.40625/
     889.40625px, mismo cálculo que el fix histórico de 2026-07-23) para
     que `.sobre` nunca creciera — el usuario pidió deshacerlo ("olvidalo,
     ponlo como estaba") antes de dar más feedback sobre el resultado, así
     que este es el estado vigente de nuevo: `.sobre` SÍ crece hacia abajo
     al abrir un panel grande, sin colchón de aire de más en reposo. */
  /* 2026-08-31: `.sobre` en reposo mide `padding + foto(617) + padding`
     (737px) — como cualquier otra sección, sin el hueco de ~277px abajo
     que dejaba `auto…1fr` (2026-08-15) ni la inflación a 903px del
     centrado con `auto` en las filas de relleno (mismo día).
     `grid-template-rows: 1fr min-content min-content min-content 1fr`:
     - las 3 filas centrales son `min-content` → heading/bio/acordeón NO
       se estiran aunque la foto que las cruza sea más alta;
     - las 2 filas de relleno `1fr` (áreas vacías arriba y abajo) reparten
       lo que sobra entre el alto de la foto y el del texto → el bloque de
       texto queda CENTRADO verticalmente contra la foto (~25px de aire
       arriba y abajo, además del padding), así el espacio bajo el
       acordeón se ve igual que sobre el heading.
     Al abrir un panel la fila central del acordeón crece, las 2 `1fr`
     se reparten el resto (mínimo movimiento del heading, ~3px) y `.sobre`
     empuja Contacto hacia abajo. `.sobre__accordion{align-self:start}`
     (más abajo) evita que el acordeón se estire dentro de su fila.
     Sin JS de congelado (sobre.js volvió a su forma simple). Este bloque
     solo aplica ≥1256px; mobile/tablet siguen en una sola columna. */
  .sobre {
    grid-template-columns: 424px 665px;
    grid-template-rows: 1fr min-content min-content min-content 1fr;
    grid-template-areas:
      "photo ."
      "photo heading"
      "photo bio"
      "photo accordion"
      "photo .";
    justify-items: stretch;
    align-items: stretch;
    align-content: start;
    column-gap: 0;
    row-gap: 22px;
    justify-content: center;
    padding: 60px var(--margin-desktop);
  }

  .sobre__photo {
    width: 424px;
    height: 617px;
    background:
      linear-gradient(154.72deg, rgba(24,20,15,0) 75.23%, rgba(24,20,15,1) 90.02%),
      linear-gradient(185.92deg, rgba(24,20,15,0) 74.89%, rgba(24,20,15,1) 98.68%),
      linear-gradient(90deg, rgba(24,20,15,.25) 75.27%, rgba(24,20,15,1) 95.72%),
      url('../assets/img/sobre/sobre-desktop.webp') center / cover no-repeat;
  }

  .sobre__heading { align-items: flex-start; text-align: left; }

  /* width:auto — resetea el `width` fijo que usan 768/1024 (ahí el
     heading no está en justify-items:stretch, así que necesita un
     ancho explícito para no estirarse a fit-content del track). Aquí
     .sobre__heading SÍ se estira (justify-items:stretch arriba), así
     que el H2 ya envuelve naturalmente al ancho real de la columna
     (665px) sin necesitar un valor propio. */
  .sobre__title { width: auto; font-size: 48.83px; }

  .sobre__para { text-align: left; }

  /* row-gap uniforme es 22px (heading→bio) — resetea el margin-top
     que 768/1024 usan para separar bio del bloque foto+heading (ahí
     el gap base es más chico y necesita el margin-top extra; aquí
     el row-gap de 22px ya es el valor real, sin extra). */
  .sobre__bio { margin-top: 0; }

  /* el AutoHTML pide 38px antes del acordeón — se completa con
     margin-top (22+16=38), mismo patrón que el ajuste de mobile
     (22+10=32). `align-self:start` — red de seguridad para que el
     acordeón nunca se estire dentro de su fila del grid (su fila es
     `min-content`, así que hoy no hay nada que estirar, pero deja el
     comportamiento explícito por si la plantilla de filas cambia). */
  .sobre__accordion { margin-top: 16px; align-self: start; }
}

/* ===================================================
   SOBRE MÍ — ULTRA-WIDE (min-width: 1920px)
   La foto NO crece (sigue 424×617, igual que el AutoHTML): solo se
   ensancha la columna de contenido y el título.
=================================================== */
@media (min-width: 1920px) {

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): el H2 hereda el
     48.83px de 1440px en vez de escalar a 55.34px. La columna de
     contenido crecía de 665px (1440) a 852px aquí — con la fuente más
     chica el título y la bio quedarían demasiado anchos, así que se
     congela al mismo 665px (la foto ya era ancho fijo, no cambia). El
     grid sigue centrado (`justify-content:center`, heredado de 1440), así
     que el bloque foto+texto solo queda con más aire a los lados. */
  /* Sin `height` propio (2026-08-15, ver el comentario largo del bloque
     1256px — `.sobre` ya no reserva altura fija, mide su contenido real;
     un `height` fijo se probó y se revirtió el 2026-08-16, ver esa nota).
     La foto sigue en 424×617 (sin cambios aquí tampoco), así que el
     total en este ancho es `90 + 617 + 90`, mismo criterio que el resto
     de secciones a este ancho (padding 90px + contenido, sin colchón). */
  .sobre {
    grid-template-columns: 424px 665px;
    padding: 90px var(--margin-ultrawide);
  }
}

/* ===================================================
   CONTACTO — BASE MOBILE (375px)
   Columna izq (info+canales) + columna der (form). Mismo orden de
   DOM en los 3 anchos — a diferencia de .como/.sobre, aquí NO hace
   falta `display:contents`/`order`: el AutoHTML mobile es la MISMA
   jerarquía que desktop, solo cambia flex-direction (row→column) y
   alineación (heading centrado, canales/form a sangre completa).
=================================================== */

.contacto {
  position: relative;
  background: var(--color-bg);
  border-radius: 0 0 12px 12px;
  padding: 50px 16px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
}

.contacto__col-info {
  display: flex;
  flex-direction: column;
  gap: 38px;
  width: 100%;
}

.contacto__heading {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  text-align: center;
}

.contacto__top-text {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}

.contacto__label {
  color: var(--color-gold);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 700;
}

.contacto__title {
  color: var(--color-white);
  font-family: 'Gloock', serif;
  font-size: 39.81px;
  font-weight: 400;
  line-height: 1.1;
}

.contacto__subtitle {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 400;
  line-height: 22px;
}

.contacto__channels {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 22px;
  width: 100%;
}

.contacto__whatsapp {
  width: 100%;
  background: #211c16;
  border: 0.3px solid var(--color-gold);
  border-radius: 8px;
  padding: 22px;
  display: flex;
  align-items: center;
  gap: 16px;
  text-decoration: none;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Hover (2026-07-26, pedido explícito: "el botón de contacto, el de la
   izquierda, donde dice envíame un mensaje a WhatsApp, hazle su hover")
   — mismo lenguaje lift+sombra que el resto de botones/tarjetas, con un
   recorrido intermedio (-4px) entre el de un botón (-2px) y el de una
   tarjeta completa (-6px), acorde a que es una tarjeta-botón más grande
   que un botón normal. El borde dorado (ya presente sin hover) se
   intensifica a `--color-cta` para reforzar la respuesta. */
@media (hover: hover) and (pointer: fine) {
  .contacto__whatsapp:hover {
    transform: translateY(-4px);
    border-color: var(--color-cta);
    box-shadow: 0 14px 26px -10px rgba(0, 0, 0, 0.5);
  }
}

.contacto__whatsapp-icon { flex-shrink: 0; width: 51px; height: 52px; }

.contacto__whatsapp-text {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.contacto__whatsapp-title {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 18px;
  font-weight: 700;
}

.contacto__whatsapp-sub {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 400;
}

.contacto__data {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.contacto__data-item {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 400;
  text-decoration: none;
  transition: color 0.2s ease;
}

/* Mismo lenguaje "color dorado = interactivo" que .nav__link:hover —
   sin esto los 3 (tel/correo/ubicación) no dan ninguna señal de que
   son links, se ven igual que texto plano. */
@media (hover: hover) and (pointer: fine) {
  .contacto__data-item:hover {
    color: var(--color-cta);
  }
}

.contacto__data-icon { flex-shrink: 0; }

.contacto__col-form { width: 100%; }

.contacto__form {
  width: 100%;
  background: #211c16;
  border: 0.3px solid var(--color-gold);
  border-radius: 8px;
  padding: 40px 24px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.contacto__fields {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.contacto__field-row {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

.contacto__field {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.contacto__field-label {
  color: rgba(255,255,255,.5);
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 500;
  line-height: 22px;
}

.contacto__input {
  width: 100%;
  background: var(--color-bg);
  border: 0.3px solid var(--color-gold);
  border-radius: 4px;
  padding: 10px;
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 500;
  height: 42px;
}

.contacto__input::placeholder { color: rgba(255,255,255,.5); }

.contacto__select-wrap { position: relative; }

.contacto__select {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 30px;
  cursor: pointer;
}

/* El value vacío usa `disabled selected hidden` como placeholder real
   (no hay texto placeholder nativo en <select>) — se pinta atenuado
   igual que los demás campos vacíos hasta que el usuario elige algo. */
.contacto__select:invalid { color: rgba(255,255,255,.5); }
.contacto__select option { color: var(--color-white); background: var(--color-bg); }

.contacto__select-chevron {
  position: absolute;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
}

.contacto__textarea {
  height: 129px;
  resize: none;
  font-family: 'Inter', sans-serif;
}

.contacto__submit {
  width: 100%;
  background: var(--color-cta);
  border: none;
  border-radius: 4px;
  padding: 12px 22px;
  color: #18140f;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

/* Hover (2026-07-26, pedido explícito: "al botón de formulario también
   hazle hover") — mismo lenguaje que `.btn-cta`/`.hero__btn--primary`
   (los otros CTA sólidos), un único lenguaje de botón en todo el sitio.
   **Actualizado el mismo día** (pedido explícito: "con cambio de color...
   para ver bien el cambio"): `filter:brightness` → `background-color`
   real a `--color-cta-hover` (mismo fix que los otros 2, ver esa nota). */
@media (hover: hover) and (pointer: fine) {
  .contacto__submit:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 20px -8px rgba(24, 20, 15, 0.45);
    background-color: var(--color-cta-hover);
  }
}

.contacto__privacy {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 500;
  line-height: 22px;
}

/* Envío del formulario sin recargar (js/contacto-form.js) —————————————
   Mismos tokens que .contacto__form (fondo #211c16, borde dorado 0.3px,
   radius 8px). Los selectores [hidden] llevan override EXPLÍCITO de
   `display` porque .contacto__form y .contacto__success fijan
   `display:flex`, y un `display` en el CSS de autor gana sobre el
   `display:none` que el atributo `hidden` aplica desde la hoja del
   navegador (gotcha conocido) — sin esto, `form.hidden = true` no
   ocultaría nada. */
.contacto__form[hidden] { display: none; }

.contacto__form-error {
  color: var(--color-white);
  background: rgba(241, 184, 83, 0.08);
  border: 0.3px solid var(--color-gold);
  border-radius: 4px;
  padding: 10px 12px;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 500;
  line-height: 20px;
}

.contacto__form-error a {
  color: var(--color-cta);
  text-decoration: underline;
}

.contacto__success {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  background: #211c16;
  border: 0.3px solid var(--color-gold);
  border-radius: 8px;
  padding: 40px 24px;
}

.contacto__success[hidden] { display: none; }

.contacto__success:focus { outline: none; }

.contacto__success-icon {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
}

.contacto__success-title {
  color: var(--color-cta);
  font-family: 'Gloock', serif;
  font-size: 24px;
  line-height: 1.2;
}

.contacto__success-text {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 500;
  line-height: 24px;
}

/* ===================================================
   CONTACTO — TABLET VERTICAL (min-width: 744px)
   RECALIBRADO 2026-07-23 contra export real (Recursos/AutoHTML/744/),
   RECENTRADO 2026-07-26 (pedido explícito del usuario, en 2 pasos en
   la misma sesión: primero "en 1024... centrala" y al ver el fix
   dijo "esto de centralizarlo quiero que lo hagas desde tablet, o
   sea, desde que el espacio [lo permita]") — el export de 744 SÍ traía
   el heading/canales alineados a la izquierda (ver detalle que se
   dejó documentado abajo), pero el usuario pidió explícitamente el
   centrado desde este ancho en vez de solo desde 1024 — se prioriza
   el pedido explícito sobre el valor literal del export. 1256+ NO se
   tocó (layout de 2 columnas ya establecido, fuera del pedido).
   El export SÍ seguía siendo la fuente real para 2 cosas que no
   cambiaron con el recentrado: `.contacto__channels` es la que trae
   un ancho FIJO real (626px, del que `.contacto__col-info` hereda por
   hug-to-content, ya que no tiene su propio width), y
   `.contacto__col-form` se estira a sangre completa del contenedor
   (`align-self:stretch`, sin width fijo) — por eso NO lleva `min()`.
=================================================== */
@media (min-width: 744px) {

  .contacto {
    padding: 40px;
    gap: 38px;
    align-items: center;
  }

  .contacto__heading,
  .contacto__top-text {
    align-items: center;
    text-align: center;
  }

  .contacto__title { font-size: 33.18px; }

  /* .contacto__col-info trae `width:100%` desde la base mobile — se
     acota aquí al mismo ancho que `.contacto__channels` (su hijo más
     ancho) para que el heading/subtítulo no se estiren más allá de
     la tarjeta de WhatsApp+datos. */
  .contacto__col-info,
  .contacto__channels { width: min(626px, 100%); align-items: center; }

  /* gap:44px (antes 22px, heredado de mobile) — pedido explícito: "dale
     espacio... separalos más del botón de whatsapp". Solo separa la
     tarjeta de WhatsApp de la fila de datos debajo, no toca nada más
     de `.contacto__channels`. */
  .contacto__channels { gap: 44px; }

  /* Tel/correo/ubicación en FILA (2026-07-26, pedido explícito: "ponlas
     tipo una al lado de otra en horizontal y con el icono arriba") —
     antes era una columna de 3 filas icono-izq/texto-der (patrón
     heredado de mobile). `.contacto__data-item` pasa de fila a columna
     (icono arriba, texto debajo, todo centrado) y `.contacto__data`
     pasa de columna a fila para alinear los 3 ítems uno junto al otro.
     `max-width:170px` por ítem evita que el correo/la dirección (los
     textos más largos) se disparen a lo ancho y rompan el reparto en
     3 columnas parejas — envuelven centrados dentro de ese ancho en
     vez de forzar una sola línea larga. Dato "no monótono": el tamaño
     de fuente sigue en 16px aquí (más grande que los 14px de mobile Y
     de 1440 — el export ya lo confirmaba así) — hay que resetear a
     14px en el bloque 1440 para que no se filtre. */
  .contacto__data {
    flex-direction: row;
    justify-content: center;
    gap: 56px;
    width: 100%;
  }

  .contacto__data-item {
    flex-direction: column;
    gap: 12px;
    text-align: center;
    max-width: 170px;
  }

  .contacto__data-item { font-size: 16px; }

  /* Íconos un poco más grandes (2026-07-26, pedido explícito) — width
     fijo + height:auto conserva la proporción real de cada ícono (no
     son todos cuadrados: el de teléfono es 17×17, el de correo 17×14,
     el de ubicación 14×17), así que un solo valor de ancho escala los
     3 sin deformarlos. */
  .contacto__data-icon { width: 22px; height: auto; }

  .contacto__field-row {
    flex-direction: row;
    gap: 22px;
  }

  /* El form ya usa el botón "grande" (antes exclusivo de 1440) y un
     padding lateral más ancho que el de mobile — no es una progresión,
     es el valor literal de este ancho (ver comentario del reset en
     1440, que vuelve a 40px 24px). */
  .contacto__form { padding: 40px 48px; }

  .contacto__submit {
    padding: 16px 22px;
    font-size: 18px;
  }
}

/* ===================================================
   CONTACTO — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   RECALIBRADO 2026-07-24 (ver nota en HERO 1024): se deja de escalar/
   interpolar sobre 744 — sigue en una sola columna, mismos anchos y
   tamaños que 744 (channels 626px, dato 16px, form 40px 48px). Solo
   cambia el margen lateral (`--margin-laptop`). El centrado y la fila
   de tel/correo/ubicación con ícono arriba ya se adelantaron a 744
   (2026-07-26, ver bloque de arriba) — este breakpoint ya no necesita
   su propio override para eso, lo hereda sin cambios.
=================================================== */
@media (min-width: 1024px) {

  .contacto { padding: 40px var(--margin-laptop); }

  /* .contacto__title/.contacto__col-info/.contacto__data-item/
     .contacto__form: sin override — heredan de 744 (33.18px,
     min(626px,100%), 16px, 40px 48px). */
}

/* ===================================================
   CONTACTO — DESKTOP (min-width: 1256px)
   Fila: columna info + columna form, con gap. A 1440 (AutoHTML real)
   536+100+612 = 1248 = 1440 − 2×96 exacto, sin aire de sobra.
   FLUIDO 1256→1440 (2026-07-26, pedido explícito del usuario: "el
   formulario... se mantiene pequeño y con espacio al lado [al ir
   expandiendo desde 1256], necesito que las cosas... se adapten al
   tamaño hasta 1440px") — hasta ahora col-info/channels/data/gap/
   col-form usaban un valor FIJO escalado ×0.9038 en este tier (484.5/
   458.3/282.1/90.4/553.2) que saltaba de golpe a los reales de 1440
   (536/507/312.17/100/612) justo al cruzar ese breakpoint: entre
   1256 y 1439px de viewport real, esos valores fijos se quedaban
   congelados mientras el espacio disponible seguía creciendo, dejando
   aire muerto a la derecha del formulario/WhatsApp. Fix: cada ancho
   pasa a `clamp(valor_1256, calc(valor_1256 + (100vw − 1256px) ×
   pendiente), valor_1440)` — interpola linealmente entre los 2 valores
   reales a medida que crece el viewport, y el propio `clamp()` lo deja
   fijo en el valor real de 1440 de ahí en adelante (por eso el bloque
   `min-width:1440px` que existía aparte ya no hace falta — el `clamp()`
   ya llega solo a esos 5 valores exactos a partir de 1440px, sin
   duplicar los números mágicos en 2 bloques). Pendientes (Δvalor/184,
   ya que 1440−1256=184): col-info 51.5/184, channels 48.7/184, data
   30.07/184, gap 9.6/184, col-form 58.8/184. Alto fijo 771px (AutoHTML,
   no cambia entre 1256/1440/1920) — hereda a 1920 sin cambios.
=================================================== */
@media (min-width: 1256px) {
  .contacto {
    flex-direction: row;
    align-items: flex-start;
    justify-content: flex-start;
    gap: clamp(90.4px, calc(90.4px + (100vw - 1256px) * 0.052174), 100px);
    padding: 90px var(--margin-desktop);
    border-radius: 0;
    height: 771px;
  }

  /* align-items:flex-start (2026-08-15, pedido explícito: que el título
     de Contacto quede en la MISMA línea vertical que el título del
     Banner CTA — "Toda situación legal tiene una salida..."): resetea
     el `align-items:center` que pone el bloque 744px sin resetear hasta
     ahora — sin esto, `.contacto__heading` y `.contacto__channels`
     quedaban cada uno centrado por separado dentro de este contenedor
     (anchos distintos → bordes izquierdos distintos, título más
     adelantado que el botón/datos). Con `flex-start` ambos caen flush
     contra el borde izquierdo real de `.contacto` — que usa el MISMO
     `padding-left` (`var(--margin-desktop)`/`var(--margin-ultrawide)`)
     que `.banner`, así que el título de Contacto queda exactamente en
     la misma línea que el de Banner (`.banner__title{width:100%}`
     dentro de `.banner__content{align-items:flex-start}`, sin offset
     de centrado). Verificado con `getBoundingClientRect` en 1256-1920:
     mismo `left` en ambos títulos en todo el rango. */
  .contacto__col-info {
    width: clamp(484.5px, calc(484.5px + (100vw - 1256px) * 0.279891), 536px);
    flex-shrink: 0;
    align-items: flex-start;
  }

  /* text-align:left en AMBOS (heading Y top-text): top-text (label+
     título) tiene su propio `text-align:center` puesto en 744+ (ver ese
     bloque) que NO se hereda solo con resetear el del padre `.heading`
     — sin este reset explícito aquí, el label "Contacto" y el título se
     quedaban centrados en desktop pese a que el resto (subtítulo, WhatsApp,
     datos) ya estaba a la izquierda (bug reportado por el usuario). */
  .contacto__heading { align-items: flex-start; text-align: left; }
  .contacto__top-text { align-items: flex-start; text-align: left; }

  .contacto__title { font-size: 48.83px; }

  /* align-items:flex-start resetea el `center` que usa 744+ (2026-07-26,
     ver ese bloque) — a este ancho el layout sigue siendo el de 2
     columnas con todo alineado a la izquierda, sin tocar. gap:22px
     resetea el 44px de 744+ (el pedido de "más espacio" era específico
     de la fila de tablet, que aquí no existe — sigue siendo columna). */
  .contacto__channels {
    width: clamp(458.3px, calc(458.3px + (100vw - 1256px) * 0.264674), 507px);
    align-items: flex-start;
    gap: 22px;
  }

  /* flex-direction:column + gap:16px resetea la FILA de 744+ (ver ese
     bloque, "ponlas... en horizontal") — el pedido de poner tel/correo/
     ubicación en fila con ícono arriba fue específico para tablet, acá
     el layout de escritorio (2 columnas) sigue con la columna vertical
     original de icono-izq/texto-der, sin cambios. */
  .contacto__data {
    width: clamp(282.1px, calc(282.1px + (100vw - 1256px) * 0.163424), 312.17px);
    flex-direction: column;
    justify-content: flex-start;
    gap: 16px;
  }

  /* flex-direction:row + text-align:left + max-width:none resetea el
     ícono-arriba/texto-centrado de 744+ — vuelve al patrón icono-izq/
     texto-der de siempre. Resetea también el 16px/15px que usan
     768/1024 — a este ancho el dato de contacto vuelve a 14px (mismo
     valor que ya traía mobile), otro caso de tamaño NO monótono
     (14→16→14). */
  .contacto__data-item {
    flex-direction: row;
    gap: 12px;
    text-align: left;
    max-width: none;
    font-size: 14px;
  }

  /* width/height:auto resetea el 22px de 744+ (íconos más grandes,
     pedido específico de la fila de tablet) — vuelve al tamaño real
     de cada ícono (los atributos width/height propios del SVG en el
     HTML: 17×17 teléfono, 17×14 correo, 14×17 ubicación). */
  .contacto__data-icon { width: auto; height: auto; }

  .contacto__col-form {
    width: clamp(553.2px, calc(553.2px + (100vw - 1256px) * 0.319565), 612px);
    flex-shrink: 0;
  }

  /* Resetea el padding lateral más ancho (48px) que usan 768/1024 —
     a este ancho el AutoHTML vuelve al valor de mobile (24px); 1920
     lo vuelve a ensanchar a 48px más abajo. */
  .contacto__form { padding: 40px 24px; }

  .contacto__field-row { flex-direction: row; gap: 22px; }

  .contacto__submit { padding: 16px 22px; font-size: 18px; }
}

/* ===================================================
   CONTACTO — ULTRA-WIDE (min-width: 1920px)
   `justify-content:space-between` en vez de gap fijo: a este ancho
   el AutoHTML reparte el sobrante entre las 2 columnas en vez de un
   gap constante (756+757 ya casi llena el espacio disponible).
=================================================== */
@media (min-width: 1920px) {

  .contacto {
    justify-content: space-between;
    gap: 0;
    padding: 90px var(--margin-ultrawide);
  }

  .contacto__col-info { width: 756px; }

  /* Tipografía congelada en 1920px (ver REGLAS SIEMPRE): el H2 hereda el
     48.83px de 1440px (no escala a 55.34px) y el dato de contacto hereda
     el 14px de 1440px (no escala a 16px). .contacto__col-info crece de
     536px (1440) a 756px aquí — con el H2 más chico esa línea quedaría
     demasiado larga, así que se acota el bloque título+subtítulo al mismo
     536px con max-width; el resto de la columna (WhatsApp + canales,
     .contacto__channels) sí aprovecha el ancho completo. */
  .contacto__heading { max-width: 536px; }

  .contacto__channels { width: 626px; }

  .contacto__col-form { width: 757px; }

  .contacto__form { padding: 40px 48px; }
}

/* ===================================================
   FOOTER — BASE MOBILE (375px)
   Marca centrada arriba + nav/contacto en fila propia debajo, luego
   barra legal. `.footer__top` es un grid: mobile agrupa nav+contacto
   en una fila de 2 bajo la marca (grid-template-areas); en desktop
   cambia solo la plantilla de áreas para que sean 3 columnas
   parejas (ver DESKTOP más abajo), sin display:contents.
=================================================== */

.footer {
  position: relative;
  background: var(--color-bg);
  border-top: 0.3px solid var(--color-gold);
  padding: 50px 16px;
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.footer__top {
  display: grid;
  /* minmax(0,1fr) en vez de 1fr: 1fr por sí solo es minmax(auto,1fr), y
     ese mínimo "auto" se resuelve al ancho fijo de .footer__contact
     (el % de su width:min() no puede resolverse en ese cálculo
     intrínseco) — revienta la columna a <375px. Con minmax(0,1fr) el
     track sí respeta el espacio disponible real. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-areas:
    "brand   brand"
    "nav     contact";
  justify-items: center;
  gap: 32px;
}

.footer__brand { grid-area: brand; }
.footer__nav { grid-area: nav; }
.footer__contact { grid-area: contact; }

.footer__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: min(294px, 100%);
}

.footer__logo {
  text-decoration: none;
  text-align: center;
  font-family: 'Gloock', serif;
  font-size: 24px;
  font-weight: 400;
  line-height: 150%;
  letter-spacing: -0.03em;
}

.footer__logo-text { color: var(--color-white); }
.footer__logo-amp   { color: var(--color-gold); }

.footer__tagline {
  color: var(--color-white);
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 400;
}

.footer__nav,
.footer__contact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.footer__nav   { width: 93px; }
/* min() en vez de 183px fijo: en la columna de 2 (grid .footer__top)
   por debajo de 375px el track queda más angosto que 183px y el
   ancho fijo desbordaba fuera del footer. */
.footer__contact { width: min(183px, 100%); }

.footer__col-title {
  color: var(--color-gold);
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 16px;
  font-weight: 700;
}

.footer__nav-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
}

.footer__nav-list a,
.footer__contact-list a {
  color: var(--color-white);
  text-decoration: none;
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 400;
}

/* El correo (jesusleikasierra@gmail.com) es una sola palabra sin espacios:
   sin esto no tiene dónde envolver y desborda a <375px. width:100% para
   que ocupe el ancho real disponible antes de tener que partirse. */
.footer__contact-list a {
  width: 100%;
  overflow-wrap: anywhere;
}

@media (hover: hover) and (pointer: fine) {
  .footer__nav-list a:hover,
  .footer__contact-list a:hover {
    opacity: 0.7;
  }
}

.footer__contact-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
  /* width:100%: sin esto (align-items:center no clampea el ancho de un
     flex item a su contenedor) el texto de contacto no envuelve y
     desborda a <375px — mismo patrón que .como__title. */
  width: 100%;
}

.footer__contact-list p {
  color: var(--color-white);
  font-family: 'Inter', sans-serif;
  font-size: 14px;
  font-weight: 400;
}

.footer__bottom {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.footer__rule {
  width: 100%;
  height: 0;
  margin: -0.5px 0 0 0;
  border: none;
  border-top: 0.5px solid var(--color-white);
}

.footer__legal {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
}

.footer__copyright,
.footer__credit {
  color: var(--color-white);
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-size: 12.8px;
  font-weight: 400;
}

.footer__credit {
  text-decoration: none;
}

/* ===================================================
   FOOTER — TABLET VERTICAL (min-width: 744px)
   RECALIBRADO 2026-07-26 (pedido explícito del usuario: "haz lo mismo
   en tablet como tal, que se ponga como en desktop desde que se pueda
   y se vaya abriendo" — continuación del cambio ya hecho en 1024, ver
   git history/comentario viejo). Ya NO es la fila "brand brand"/"nav
   contact" de 2 filas (marca centrada arriba + nav/contacto debajo) —
   pasa a ser directamente las 3 columnas de escritorio (brand | nav |
   contact en una sola fila), igual que 1024+/1256+, solo que con menos
   aire: a 744, con `padding:40px`, quedan 664px de contenido — ajustado
   pero suficiente para brand (mínimo 220px) + 145px + 183px + 2 gaps
   vía `justify-content:space-between` (~58px cada uno). El resto de
   breakpoints (1024/1256/1920) ya NO repiten esta estructura —la
   heredan de aquí sin cambios (dedupe; solo ajustan padding/gap, que sí
   les es propio). "Se vaya abriendo": los gaps de `space-between`
   crecen solos a medida que el viewport crece (744→1024→1256+), sin
   necesidad de un valor por breakpoint.
=================================================== */
@media (min-width: 744px) {

  .footer { padding: 40px; gap: 28px; }

  /* minmax(220px, auto): mismo piso que ya usa 1024+ (ver comentario
     de esa sección) — sin él, la columna "auto" de la marca colapsa a
     su min-content bajo poca disponibilidad de espacio y el logo se
     parte palabra por palabra ("Sierra"/"Torres"/"&"/"Asoc." cada uno
     en su propia línea). Confirmado con Edge headless a 744: sin el
     piso, mismo bug que se vio primero a 1024. */
  .footer__top {
    width: 100%;
    grid-template-columns: minmax(220px, auto) 145px 183px;
    grid-template-areas: "brand nav contact";
    justify-items: start;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0;
  }

  /* width:min(294px,100%) + align-items:flex-start: la marca deja de
     estar centrada (era el único bloque que ocupaba las 2 columnas,
     "brand brand") y pasa a ser su propia columna angosta alineada a
     la izquierda, igual que 1024+/1256+. */
  .footer__brand { width: min(294px, 100%); align-items: flex-start; }

  /* font-size:24px (antes 27.65px, el mismo "token" que usa el logo
     del header a este ancho): con el footer ya en 3 columnas de
     escritorio, el logo también adopta el tamaño real de 1440
     (AutoHTML) en vez del tamaño de tablet. */
  .footer__logo { font-size: 24px; text-align: left; justify-content: flex-start; }

  .footer__tagline { text-align: left; }

  .footer__nav,
  .footer__contact { align-items: flex-start; }

  /* width:145px (antes 93px) + nowrap: mismo motivo que 1024+/1256+ —
     el nav del footer usa las etiquetas largas del header ("Áreas de
     práctica"/"¿Por qué elegirme?"), que no caben en 93px sin envolver
     a 2 líneas. */
  .footer__nav { width: 145px; }
  .footer__nav-list a { white-space: nowrap; }

  .footer__col-title { text-align: left; }

  .footer__nav-list { align-items: flex-start; }

  .footer__nav-list a,
  .footer__contact-list a { text-align: left; }

  .footer__contact-list {
    align-items: flex-start;
    text-align: left;
    width: auto;
  }

  .footer__contact-list a { width: auto; overflow-wrap: normal; }

  .footer__contact-list p { text-align: left; }

  .footer__legal {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
  }

  .footer__copyright,
  .footer__credit { text-align: left; }
}

/* ===================================================
   FOOTER — TABLET HORIZONTAL / LAPTOP PEQUEÑA (min-width: 1024px)
   La estructura de 3 columnas ya se adelantó a 744px (ver bloque de
   arriba, 2026-07-26) — aquí solo queda lo que sigue siendo EXCLUSIVO
   de este ancho: el margen lateral pasa de `--margin-tablet` (40px,
   plano) a `--margin-laptop` (64px), dándole más aire a los 3 grupos
   sin tocar su geometría (los gaps de `space-between` simplemente
   crecen solos con el espacio de sobra).
=================================================== */
@media (min-width: 1024px) {

  .footer { padding: 40px var(--margin-laptop); }
}

/* ===================================================
   FOOTER — DESKTOP (min-width: 1256px)
   El paso a 3 columnas ya ocurre desde 1024px (ver bloque de arriba,
   2026-07-26) — aquí solo queda lo que sigue siendo EXCLUSIVO de este
   ancho: más aire (padding/gap más generosos, `--margin-desktop`).
=================================================== */
@media (min-width: 1256px) {
  .footer {
    padding: 60px var(--margin-desktop);
    gap: 26px;
  }
}

/* ===================================================
   FOOTER — ULTRA-WIDE (min-width: 1920px)
=================================================== */
@media (min-width: 1920px) {

  .footer {
    padding: 90px var(--margin-ultrawide);
    gap: 32px;
  }
}

/* ===================================================
   LEGIBILIDAD MÓVIL — LINE-HEIGHT DE TÍTULOS GRANDES (2026-07-30)
   Pedido explícito del usuario: en el rango realmente mobile (<744px,
   el que más se usa para ver el sitio) los H2 grandes en Gloock
   (39.81px: .areas__title/.porque__title/.como__title/.banner__title/
   .sobre__title/.contacto__title, y 33.18px: .areas__card-title) usan
   `line-height:1.1` desde su regla base — un valor pensado para texto
   de 1 línea, pero estos títulos envuelven a 3-4 líneas en 375px. Con
   1.1 (≈44px de interlineado sobre 39.81px de fuente) las líneas quedan
   visualmente "pegadas" — el ojo percibe el bloque como un solo choque
   de texto en vez de líneas legibles, más notorio aún con acentos
   (Á/É/Í) que añaden altura. Se sube a un valor más cómodo SOLO en este
   rango — no se toca `line-height` en la regla base (compartida con
   tablet/desktop) para no arriesgar la calibración pixel-perfect ya
   cerrada en 744+/1256+ (ver `## Estado` en CLAUDE.md) ni obligar a
   auditar resets en cada breakpoint intermedio (regla dura del proyecto
   para 744/1024, ver `## MANTENIMIENTO CSS`) — un `@media (max-width)`
   autocontenido evita ese riesgo por diseño, no se filtra hacia arriba.
   `.hero__h1` NO se toca: ya usa `calc(1.1em + 4px)`, más generoso que
   un 1.1 plano y sin el mismo problema.
   Límite 743.98px (no 744px) para no solaparse con el breakpoint real
   de tablet del proyecto (`min-width:744px`, ver CLAUDE.md — el corte
   real, no 768px). */
@media (max-width: 743.98px) {
  .areas__title,
  .porque__title,
  .como__title,
  .banner__title,
  .sobre__title,
  .contacto__title {
    line-height: 1.25;
  }

  .areas__card-title {
    line-height: 1.2;
  }
}

/* ===================================================
   ANIMACIONES DE ENTRADA (2026-07-26, pedido explícito del usuario:
   "quiero mas animado", investigar qué queda bien con UX/UI para un
   sitio de abogados). Deliberadamente RESTRAINED: un despacho legal
   vende confianza/seriedad, no dinamismo — mismo criterio por el que
   ya se descartó cualquier easing "rebote"/bounce en todo el proyecto
   (apertura.js usa cubic-bezier(0.33,1,0.68,1), un ease-out sobrio, no
   un spring). Motion 100% GPU (opacity/transform), CERO propiedades de
   layout — no interfiere con ninguna geometría ya calibrada del sitio
   (hero__broche/badge/stats, grid-template-areas de .como/.sobre,
   scroll-clip de apertura.js).

   `.reveal`: fade + rise sutil (24px por default) al entrar en el
   viewport, UNA sola vez (`js/reveal.js`, IntersectionObserver +
   unobserve — repetir la animación cada vez que se sube/baja el scroll
   se ve juguetón, no profesional, y por eso se descartó a propósito).
   `--reveal-delay` (entero, multiplicado ×90ms vía calc()) permite
   escalonar grupos (tarjetas de Áreas/Por qué elegirme, pasos de Cómo
   trabajo — este último el caso con más justificación: es un proceso
   numerado real, el stagger refuerza que hay una SECUENCIA, no
   decoración). Se optó por una custom property + `calc()` en vez de
   `:nth-child()`: varias secciones intercalan elementos no-`.reveal`
   entre los ítems que sí llevan la clase (p.ej. `.como__step-line` un
   `<hr>` entre cada `.como__step`) — `:nth-child()` cuenta TODOS los
   hermanos sin distinguir clase, así que el índice se corrompe; la
   custom property puesta a mano por elemento no depende de la posición
   entre hermanos. `--reveal-y`/`--reveal-scale` (2026-07-26, pedido
   explícito: "Áreas de práctica" necesitaba una entrada más notoria)
   permiten a una sección específica pedir más recorrido/un toque de
   zoom SIN tocar el comportamiento default de las demás — ver
   `.areas__head`/`.areas__card` más abajo. Se resolvió con custom
   properties (no con un selector más específico tipo
   `.areas__card.reveal`) a propósito: un selector compuesto ganaría por
   especificidad sobre el `.reveal{transform:none}` de
   `prefers-reduced-motion` de más abajo (mismo nivel de especificidad
   que el `.reveal` base) y rompería el respeto a esa preferencia — las
   custom properties no tienen especificidad propia, así que el
   override de reduced-motion sigue ganando igual que con cualquier
   otro `.reveal`.

   El Hero: SOLO `.hero__media` (la foto) lleva `.reveal` (agregado
   2026-07-26, pedido explícito del usuario tras confirmar el riesgo —
   ver detalle junto a `.hero__actions` más abajo). `.hero__h1`/
   `.hero__lead`/`.hero__broche`/`.hero__badge`/`.hero__stats` NO llevan
   `.reveal` a propósito (mismo motivo de siempre: LCP y fragilidad
   calibrada al píxel). `.apertura` tampoco lleva `.reveal` en su
   mecanismo con scroll activo (el texto sí lo lleva como fallback sin
   scroll-zoom, pero `apertura.js` controla su entrada real con estilos
   inline, no este mecanismo). */
/* Duración/easing (2026-07-26, mismo día, pedido explícito: "quiero que
   las animaciones sean más smooth y que no entren tan rápido a
   escena"). Antes: 0.7s con `cubic-bezier(0.16,1,0.3,1)` (ease-out-expo
   — arranca MUY rápido y decelera recién al final, así que casi todo el
   recorrido visual ya pasó en el primer tercio de la duración: se lee
   como un "salto" seguido de un asentamiento lento, no como una entrada
   pareja). Ahora: 1s con `cubic-bezier(0.33,1,0.68,1)` — la misma curva
   sobria que ya usa `apertura.js` (zoom del texto, aterrizaje del canto)
   en vez de inventar una nueva: acelera de forma más gradual y se siente
   más lenta/pareja de principio a fin sin dejar de ser ease-out puro
   (spec del proyecto: nunca bounce/spring). El multiplicador del stagger
   también subió (90ms→120ms) para que el escalonado entre tarjetas se
   note más ahora que cada entrada individual dura más. */
.reveal {
  --reveal-delay: 0;
  --reveal-x: 0px;
  --reveal-y: 24px;
  --reveal-scale: 1;
  opacity: 0;
  transform: translate(var(--reveal-x), var(--reveal-y)) scale(var(--reveal-scale));
  transition: opacity 1s cubic-bezier(0.33, 1, 0.68, 1),
              transform 1s cubic-bezier(0.33, 1, 0.68, 1);
  transition-delay: calc(var(--reveal-delay) * 120ms);
}

/* Áreas de práctica (2026-07-26): entrada más marcada que el default —
   más recorrido vertical + un toque de zoom (0.94→1), tanto en el
   encabezado (label+H2) como en cada tarjeta, para que se note con
   claridad al hacer scroll ("que las tarjetas vayan apareciendo... al
   igual que los títulos", pedido explícito del usuario). */
.areas__head,
.areas__card {
  --reveal-y: 40px;
  --reveal-scale: 0.94;
}

/* Acordeón de "Sobre mí" — entrada lateral (2026-07-26, pedido explícito:
   "quiero que los acordeones vayan entrando uno a uno por la derecha,
   uno detrás de otro"). Se usa `--reveal-x` (nuevo, ver `.reveal` arriba)
   en vez de `--reveal-y`: cada `.sobre__acc-item` entra deslizándose
   desde la derecha (sin desplazamiento vertical) — `.reveal` ya soporta
   ambos ejes a la vez vía `translate()`, así que no hace falta un
   mecanismo aparte. El stagger (`--reveal-delay` inline por ítem, ver
   `index.html`) da el efecto "uno detrás de otro". Reemplaza el
   `.reveal` que antes llevaba `.sobre__accordion` como bloque único
   (fade+rise vertical) — ver ese comentario en la sección Sobre mí. */
.sobre__acc-item {
  --reveal-x: 40px;
  --reveal-y: 0px;
}

.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* `.banner__cta:hover{transform}` — 3° pieza del mismo bug/fix de
   `.banner__cta` (ver comentario junto a la regla en la sección Banner
   CTA): el hover puesto ahí perdía su `transform:translateY(-2px)`
   porque `.reveal.is-visible` (arriba) es un selector de 2 clases
   (especificidad 0-2-0, igual que `.banner__cta:hover` — clase+pseudo)
   y gana por venir después en la cascada, dejando el botón en reposo
   (`translateY(0)`) sin importar el hover. Declarada aquí, DESPUÉS de
   `.reveal.is-visible`, para que el lift sí se note — mismo patrón que
   ya usa `.areas__card:hover` un poco más abajo. */
@media (hover: hover) and (pointer: fine) {
  .banner__cta:hover {
    transform: translateY(-2px);
  }
}

/* Entrada del Hero al cargar (NO scroll-triggered, corre una vez al
   pintar la página): `.hero__actions` (botones CTA + nota) — SIN
   `.reveal` a propósito, aunque el pedido de 2026-07-26 ("animar
   también los botones del Hero") se aceptó: agregarle la clase `.reveal`
   encima de este `@keyframes` haría que 2 mecanismos independientes
   controlen `opacity`/`transform` del mismo elemento (el `animation` de
   abajo Y la `transition` de `.reveal`), compitiendo entre sí sin
   ganador claro. Como este keyframe YA anima la entrada de los botones
   (fade+rise al cargar) y el Hero siempre está sobre el pliegue (un
   trigger "al hacer scroll" dispararía casi de inmediato igual), no
   aporta nada real cambiar el mecanismo — se deja intacto y el pedido
   de "animar los botones" ya queda cubierto por este `@keyframes`.
   `.hero__media` (la foto) sí sumó `.reveal` (arriba) porque no tenía
   ninguna animación de entrada previa, sin ese conflicto.
   Se evaluó (y se sigue descartando) animar `.hero__h1`/`.hero__lead` —
   el propio proyecto marca el H1 (vía preload de Gloock) como el
   elemento LCP del sitio; animar su opacidad puede retrasar cómo Chrome
   mide el LCP real. Se sigue descartando tocar
   `.hero__broche`/`.hero__stats`/`.hero__badge` — CLAUDE.md ya documenta
   ese trío como un sistema "indivisible" calibrado al píxel (el badge
   debe posarse exactamente 11px sobre la curva del broche); el riesgo
   de un efecto visual no deseado en un área ya frágil no vale la pena
   frente al beneficio marginal. */
@keyframes hero-actions-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}
.hero__actions {
  animation: hero-actions-in 1.1s cubic-bezier(0.33, 1, 0.68, 1) 0.35s both;
}

/* Lift sutil en hover de tarjetas clicables (Áreas/Por qué elegirme):
   comunica "esto lleva a algo" sin el efecto "juguetón" de un scale —
   un lift + sombra es el lenguaje de hover más usado en sitios de
   servicios profesionales (vs. scale, más común en e-commerce/consumo).

   Bug real corregido (2026-07-26, "quiero animaciones de entrada más
   smooth" — investigado y encontrado, no reportado por el usuario):
   esta regla y `.reveal` son selectores de UNA sola clase (misma
   especificidad, 0-1-0) — el shorthand `transition` NO se combina entre
   reglas con la misma especificidad, la que aparece DESPUÉS en la
   cascada gana COMPLETA. Como esta regla vive después de `.reveal` en
   el archivo, `.areas__card.reveal`/`.porque__card.reveal` perdían por
   completo la transición de `opacity` de `.reveal` (quedaban con SOLO
   `transform 0.35s`, sin transición de opacity en absoluto) — la
   entrada de esas tarjetas se veía con la opacidad saltando de golpe
   mientras solo el transform interpolaba, más corto y notoriamente
   menos suave que el resto de las secciones (verificado con
   getComputedStyle antes del fix: `transition: "transform 0.35s ..."`
   vs. `"opacity 0.7s ..., transform 0.7s ..."` en una sección no
   afectada). Fix: esta regla ahora declara EXPLÍCITAMENTE las 2
   propiedades (mismo opacity que `.reveal`, transform con su propia
   duración más corta — sigue sirviendo para el hover, que quiere una
   respuesta más ágil que la entrada). Actualizado 2026-07-26 (mismo
   pedido de "más smooth" de arriba, junto a `.reveal`): opacity subió a
   1s/mismo easing sobrio para no desincronizarse del nuevo valor base;
   transform (hover Y el scale-in de `.areas__card`, ver `--reveal-scale`
   arriba) subió de 0.4s a 0.5s — sigue siendo notoriamente más ágil que
   la entrada completa, sin sentirse brusco en el hover.

   2° bug real encontrado en la misma verificación (no reportado, se
   detectó comparando `getComputedStyle(...).transitionDelay` de una
   tarjeta de Áreas/Por qué elegirme contra un `.como__step` con el mismo
   `--reveal-delay`): el shorthand `transition` de ESTA regla también
   resetea `transition-delay` a su valor inicial (`0s`) para ambas capas,
   por el mismo motivo de cascada que ya corrigió el bug de arriba (misma
   especificidad, gana la regla que aparece después) — pero esta vez
   afecta a `transition-delay`, no a qué propiedad transiciona. Resultado
   real: las 6 tarjetas de Áreas y las 2 de Por qué elegirme IGNORABAN
   por completo `--reveal-delay` y entraban las 6/2 SIMULTÁNEAS en vez de
   escalonadas — a diferencia de `.como__step` (sin esta regla encima),
   que sí escalonaba bien. Fix: declarar `transition-delay` explícito
   en esta regla con la MISMA fórmula que usa `.reveal`, para que ninguna
   de las 2 reglas dependa de heredar la del otro selector. */
.areas__card,
.porque__card {
  transition: opacity 1s cubic-bezier(0.33, 1, 0.68, 1),
              transform 0.5s cubic-bezier(0.33, 1, 0.68, 1);
  transition-delay: calc(var(--reveal-delay) * 120ms);
}
/* Hover SOLO en Áreas — quitado de `.porque__card` (2026-07-26, pedido
   explícito: "quítale el hover a las cards de por qué elegirme"). La
   `transition` de arriba (compartida por las 2, necesaria para la
   entrada `.reveal`) se deja intacta en ambos selectores — solo se quita
   la regla `:hover` de Por qué elegirme, no su transición base. */
@media (hover: hover) and (pointer: fine) {
  .areas__card:hover {
    transform: translateY(-6px);
  }
}

/* `.banner__cta` — mismo bug/fix que `.areas__card`/`.porque__card` de
   arriba: también lleva `.reveal` (para su entrada al hacer scroll) Y
   necesita su PROPIA `transition` para el hover agregado 2026-07-26 (ver
   comentario junto a `.banner__cta` en la sección Banner CTA). Declarada
   aquí, DESPUÉS de `.reveal`, para ganar por orden de cascada (misma
   especificidad, selector de una sola clase). `opacity` a 1s (igual que
   `.reveal`, para la entrada) + `transform` a 0.5s (compromiso entre la
   entrada y el hover, mismo criterio que `.areas__card`/`.porque__card`)
   + `box-shadow`/`background-color` a 0.2s (solo las necesita el hover,
   pueden ser más ágiles). */
.banner__cta {
  transition: opacity 1s cubic-bezier(0.33, 1, 0.68, 1),
              transform 0.5s cubic-bezier(0.33, 1, 0.68, 1),
              box-shadow 0.2s ease,
              background-color 0.2s ease;
  transition-delay: calc(var(--reveal-delay) * 120ms);
}

@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .hero__actions {
    animation: none;
  }
  .areas__card,
  .porque__card {
    transition: none;
  }
  .areas__card:hover {
    transform: none;
  }
  /* Hovers de botones (2026-07-26, ver `.btn-cta`/`.hero__btn--primary`/
     `.contacto__submit`/`.contacto__whatsapp`): se anula solo el `transform`
     (el lift) — el resto del bloque `*{transition-duration:0.01ms}` de
     arriba ya vuelve instantáneo cualquier cambio de `box-shadow`/`filter`/
     `border-color` que quede, así que no hace falta resetearlos aparte. */
  .btn-cta:hover,
  .hero__btn--primary:hover,
  .contacto__submit:hover,
  .contacto__whatsapp:hover,
  .banner__cta:hover,
  .floating-whatsapp:hover {
    transform: none;
  }
}

/* ===================================================
   BOTÓN FLOTANTE — WhatsApp (agendar cita/consulta) (2026-08-15,
   pedido explícito del usuario). Fixed, esquina inferior derecha.

   Mismo lenguaje de lift+sombra que ya usa el resto del sitio en
   hover, pero fondo/hover en los colores oficiales de marca de
   WhatsApp (2026-08-16, pedido explícito: "ponlo con los colores
   de whatsapp") — NO el dorado `--color-cta` que usaba antes (ese
   trío sigue intacto para `.btn-cta`/`.hero__btn--primary`/
   `.contacto__submit`, sin tocar). Verde WhatsApp `#25D366` en
   reposo; hover al verde oscuro oficial `#128C7E` (el mismo par
   que usa la propia app/sitio de WhatsApp). Icono en
   `--color-bg` (el oscuro de fondo del sitio, `#18140f`) — se
   probó blanco primero pero el usuario pidió explícitamente
   "algo que vaya más con la página" en vez de blanco puro.

   `z-index:60` — por encima de cualquier sección (.hero es la más alta
   del resto del sitio con z-index:10) pero por DEBAJO del stacking
   context del header (`.site-header`, z-index:200 mientras el menú
   mobile está abierto, ver "z-index móvil" más arriba en este archivo)
   a propósito: como `.floating-actions` vive fuera de `.site-header`
   (hermano suyo en el `<body>`, ver index.html), con un z-index menor
   el overlay oscuro + panel off-canvas del menú lo tapan visualmente
   sin necesitar ninguna regla `body.nav-active` nueva — y como
   `main.js` ya aplica `inert` a todo hijo de `<body>` que no sea
   `.site-header` al abrir el menú (ver ese archivo), el botón también
   queda no-interactivo/fuera del foco mientras tanto, doble garantía
   sin código extra.

   Nota: el mismo día se agregó un 2° botón "volver arriba" (ghost
   style, `js/floating.js`) y se quitó a pedido explícito del usuario
   ("pon solo el de whatsapp, quita el de scroll to top") — `.floating-
   actions` se dejó como contenedor (aunque hoy tenga un solo hijo) por
   si se agrega otro botón flotante más adelante. */
.floating-actions {
  position: fixed;
  right: var(--margin-mobile);
  bottom: var(--margin-mobile);
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}

.floating-whatsapp {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 62px;
  height: 62px;
  border-radius: 50%;
  text-decoration: none;
  background: #25d366;
  color: var(--color-bg); /* currentColor del icono inline — mismo oscuro del fondo del sitio, no blanco, para que combine con la paleta */
  box-shadow: 0 10px 22px -8px rgba(24, 20, 15, 0.55);
  transition: transform 0.2s ease, box-shadow 0.2s ease,
              background-color 0.2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .floating-whatsapp:hover {
    transform: translateY(-3px);
    box-shadow: 0 14px 26px -8px rgba(24, 20, 15, 0.6);
    background-color: #128c7e;
  }
}

@media (min-width: 744px) {
  .floating-actions {
    right: var(--margin-tablet);
    bottom: var(--margin-tablet);
  }

  .floating-whatsapp {
    width: 70px;
    height: 70px;
  }
}
