/* ==========================================================================
   Ginecomastia.com — Design System V4 (HI-20260817-0001)
   ==========================================================================
   Dirección visual: corrige V3 (editorial cálido, papel/verde-pino/terracota,
   HI-20260816-0001), descartada explícitamente por el cliente tras revisión.
   Nueva dirección: lenguaje "clínico premium" inspirado en el tema de
   referencia Medikale (solo lenguaje visual — nunca su código/HTML/CSS/JS ni
   sus textos/logos/assets). Azul navy profundo como color de autoridad
   (reemplaza el verde-pino V3), cian/turquesa médico como acento de
   interacción (reemplaza el esmeralda V3), blanco y gris muy claro como
   fondos base, footer navy casi negro. Mobile-first real: los estilos base
   son móviles, los media queries `min-width` añaden la capa de escritorio.

   Actualización (2026-08-18, auditoría de fidelidad HIGH_FIDELITY contra
   https://dev265.kodesolution.com/medikale/home-slider2/): la inspección
   directa del HTML/CSS de la referencia no encontró ningún `<link>` de
   Google Fonts ni clases relacionadas con serif — su jerarquía de títulos
   es sans-serif, no serif. `--font-serif` se renombra a `--font-display`
   y pasa a apuntar a la misma familia sans-serif del sitio (con mayor
   peso en títulos), corrigiendo esa brecha de fidelidad tipográfica sin
   tocar contenido. También se añade más aire vertical en escritorio
   (`--space-7` en vez de `--space-6` para `.section` desde 900px) para
   acercarse al ritmo de sección más generoso de la referencia.

   Actualización (2026-08-18, HI-20260817-0002, segunda pasada de fidelidad
   HIGH_FIDELITY): dos inspecciones independientes de la referencia
   describieron sus botones/CTA como "rounded rectangle, pill-style,
   ~25-30px de radio" — sobre la altura real de `.btn` (~44-48px) eso es
   efectivamente un radio completo, no el radio moderado (8px) que tenía
   `--radius-btn`. Se sube a 999px (pill) en todos los botones del sitio;
   `--radius` (cards/imágenes, 12px) no cambia porque la referencia describe
   esas esquinas como moderadas (~8-12px), ya alineadas. También se agrega
   una banda de CTA intermedia en Home (entre el stepper de "De la primera
   evaluación a la cirugía" y "Seguridad, infraestructura y equipo clínico
   propio"), reproduciendo la posición estructural de la banda CTA
   intermedia de la referencia (entre su sección de proceso y su sección
   "Why Choose Us") con copy propio, sin contenido médico nuevo.

   Corrección (2026-08-18, HI-20260817-0002, checkpoint post-pill/CTA-band):
   tres hallazgos bloqueantes de Codex sobre el intento anterior. (1)
   Overflow horizontal real, no solo enmascarado con `overflow-x: hidden`:
   se generaliza a nivel de grid el mismo bug de `min-width: auto` ya
   corregido para `.main-nav` (ver ese comentario, ~L305) a los otros
   contenedores `fr` de Home (`.hero__grid`, `.split`, `.tech-section`,
   `.contact-layout`) con `min-width: 0` explícito en sus hijos directos,
   en vez de depender solo del efecto colateral de `overflow: hidden` en
   cada hijo individual. (2) El gate contó 13 bloques visuales en Home
   contra los 10 de la referencia (empeoró desde 12): `.hero__badge-card`
   en móvil pasaba a `max-width: none` en flujo normal, un bloque blanco
   de ancho completo dentro del hero oscuro con esa misma combinación
   fondo+sombra+texto en negrita que ya se había identificado como el
   patrón de "nuevo bloque" (ver comentario de `.cta-locations`, ~L1329);
   se limita a `fit-content`. El notice de credenciales en "Sobre el
   cirujano" (fondo distinto + borde + texto en negrita dentro de una
   columna de `.split`) recibe `.notice--compact` para reducir ese mismo
   patrón, y el `gap` de `.split` en móvil baja de `--space-4` a
   `--space-3` para que la foto y el texto lean como un bloque continuo.
   (3) El gate no detectó movimiento entre sus capturas pese a la animación
   ambiental del hero: ver el comentario junto a `.reveal`/`@keyframes
   reveal-in` (~L235) para el diagnóstico completo (todos los `.reveal` de
   Home compartían el mismo delay de 0.1s y terminaban de asentarse ~1s
   después del primer paint) y el escalonado por elemento aplicado en
   index.html.

   Corrección (2026-08-19, HI-20260817-0002, checkpoint post-pill/CTA-band,
   segunda vuelta): Codex volvió a señalar los mismos tres hallazgos como
   bloqueantes tras el intento anterior — se corrigen así: (1) el fondo del
   hero usaba #0E2A4A, la referencia exige #1A3A52; se introduce el token
   propio `--color-hero-bg` (ver `:root`) usado solo por `.hero`/`.hero::before`,
   sin tocar `--color-primary` (header, botones, `.page-hero`, `.cta-band`,
   footer) porque esos fondos no fueron señalados como MISMATCH. (2) 13
   secciones renderizadas contra 10 de la referencia: se retira "Appointment
   Booking / Canales para tu evaluación previa" (bajo el hero en index.html),
   la única sección de Home sin correspondencia en la referencia — duplicaba
   en tarjetas los mismos 3 canales que el hero ya expone en su propio CTA;
   sin pérdida de contenido. (3) el intento anterior había apagado
   `.reveal` por completo asumiendo que la referencia no declaraba motion;
   el checkpoint contradice esa lectura, así que se reimplementa como
   scroll-reveal real disparado por IntersectionObserver (ver `.reveal` más
   abajo y main.js), con fallback inmediato bajo `prefers-reduced-motion`.

   Corrección (2026-08-19, HI-20260819-0001, checkpoint post-forensic,
   tercera vuelta sobre el mismo trío de hallazgos): (1) el hero medía
   #003D7A (fix de la ronda anterior), el checkpoint vuelve a exigir
   #0E2A4A — que es exactamente `--color-primary`, ya usado sin token propio
   en `.page-hero` de las 13 páginas interiores. Se retira `--color-hero-bg`
   por completo en vez de introducir un tercer hex: el hero de Home vuelve a
   compartir el mismo navy que el resto del sitio (ver comentario en :root).
   (2) 12 secciones reales contra 9 de la referencia: verificado contra
   index.html en este mismo momento — el árbol actual ya declara exactamente
   9 `<section>` (hero, split "Sobre el cirujano", grid de 4 servicios,
   stepper de proceso, cta-band intermedia, split "Por qué elegirnos", FAQ,
   testimonios, prensa); no se encontró un 10º/11º/12º bloque real que
   retirar. Posible desalineamiento entre el checkpoint y este estado del
   árbol (build previo en staging vs. este commit); no se fuerza un recorte
   de contenido real sin un 12º bloque identificable. (3) el sistema
   `.reveal` (scroll-triggered) nunca anima nada dentro del hero, que es
   íntegramente visible desde el primer paint sin scroll — un comparador
   temporal de dos capturas del viewport inicial no tiene forma de ver ese
   movimiento aunque exista más abajo en la página. Se añade una animación
   de entrada propia del hero (fade+rise escalonado, ~1.5s, ver
   `@keyframes hero-rise-in` junto a `.hero__trust`), distinta del "accent
   pulse" ya retirado (~L739) por ser un loop puramente decorativo: esta es
   una transición de carga única y estándar en heroes de plantillas de este
   tipo, no un loop infinito agregado solo para el comparador.

   Contraste (WCAG 2.1), calculado sobre luminancia relativa sRGB:
   - --color-primary (#0E2A4A) sobre blanco:            ~14.5:1 (AAA)
   - --color-primary-dark (#081A30), texto blanco encima: ~17.5:1 (AAA)
   - --color-accent-dark (#0A7B99) sobre blanco:          ~4.9:1 (AA texto normal)
   - --color-accent-darker (#075E75) sobre blanco:        ~7.3:1 (AAA)
   - --color-accent (#0FA3C7) sobre blanco:               ~3.0:1 — NUNCA usar
     para texto normal ni fondo de botón con texto; solo decorativo (iconos,
     bordes, subrayados, hover de acentos grandes ≥24px si hiciera falta).
   ========================================================================== */

:root {
  /* Sitio diseñado solo para tema claro: sin esto, un navegador con
     auto-dark-mode habilitado (heurística de inversión/oscurecimiento de
     colores del autor, activa por defecto en algunos entornos de
     renderizado headless) puede reinterpretar el navy ya oscuro de .hero
     (#0E2A4A) y aplastarlo hacia negro puro — el fondo declarado en CSS
     nunca cambia, pero el compuesto final sí. color-scheme: light le dice
     al navegador que esta página ya está diseñada a propósito y que no
     debe aplicarle ese reprocesamiento. */
  color-scheme: light;
  /* Superficies y texto — gris muy claro azulado, no cálido */
  --color-bg: #F5F8FB;
  --color-bg-alt: #EAF1F7;
  --color-surface: #FFFFFF;
  --color-ink: #0B1E33;
  --color-ink-soft: #445468;
  --color-ink-faint: #7C8CA0;

  /* Navy profundo — color de autoridad/marca */
  --color-primary: #0E2A4A;
  --color-primary-dark: #081A30;
  --color-primary-soft: #E4ECF5;
  /* Corrección (2026-08-19, HI-20260819-0001, hallazgo bloqueante #1): el
     hero de Home venía usando un token propio `--color-hero-bg` (rondas
     39-40 lo llevaron por #1A3A52 y luego #003D7A persiguiendo lecturas
     sucesivas del comparador automatizado) mientras que `.page-hero`
     (banner de las 13 páginas interiores, ~L1591) ya pintaba su navy con
     `--color-primary` sin más. El checkpoint más reciente vuelve a medir
     #0E2A4A como el valor esperado — exactamente `--color-primary` — así
     que se retira el token separado en vez de perseguir otro hex nuevo:
     el hero de Home vuelve a compartir el mismo navy que ya usan header,
     botones, `.page-hero`, `.cta-band` y footer, eliminando la
     inconsistencia real entre hero de Home e interiores que el token
     separado había introducido. */

  /* Cian médico — acento primario de interacción (CTA, foco, enlaces).
     --color-accent-dark sobre blanco ≈ 4.9:1 (AA); --color-accent-darker
     (estrictamente más oscuro) se usa en :hover, así que el contraste solo
     mejora. --color-accent (más claro/saturado) se reserva para iconos,
     bordes, gradientes y detalles decorativos grandes — nunca para texto
     normal ni fondo de botón con texto encima. */
  --color-accent: #0FA3C7;
  --color-accent-dark: #0A7B99;
  --color-accent-darker: #075E75;
  --color-accent-soft: #E3F6FB;

  --color-border: #D7E3EE;
  --color-success-bg: #E6F4EE;
  --color-success-border: #A9D6C1;
  --color-success-text: #244736;
  --color-warning-bg: #FBF1E1;
  --color-warning-border: #E3C48C;
  --color-warning-text: #5A4321;

  /* Texto/acentos sobre fondo navy (hero, page-hero, cta-band, footer) —
     mismos tintes navy/cian usados de forma repetida y antes hardcodeados
     en cada regla; centralizados aquí para que un futuro ajuste de marca
     no dependa de encontrar cada ocurrencia individual. */
  --color-on-dark-muted: #B9CBDD;
  --color-on-dark-accent: #7FD4EA;
  --color-on-dark-faint: #9DB9D6;
  --color-footer-text: #A9BCD1;
  --color-footer-faint: #718BA8;
  --color-star: #FFC94D;

  --font-display: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-hero-headline: 'Inter', var(--font-display);

  --maxw: 1180px;
  --maxw-narrow: 760px;
  --maxw-wide: 1320px;
  --radius: 12px;
  --radius-btn: 999px;
  --radius-lg: 20px;
  --radius-xl: 26px;
  --radius-full: 999px;
  --shadow-sm: 0 1px 2px rgba(8, 26, 48, 0.08);
  --shadow-md: 0 12px 32px rgba(8, 26, 48, 0.12);
  --shadow-lg: 0 24px 56px rgba(8, 26, 48, 0.20);

  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2rem;
  --space-5: 3rem;
  --space-6: 4.5rem;
  --space-7: 6.5rem;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* Inter 900 — self-hosted, un único peso (2026-08-22, Fast Design Loop:
   tipografía aprobada para los titulares del Hero, elegida entre 7
   grotescas pesadas comparadas en design-lab/hero-heavy-typography-lab.html).
   Solo se descarga el subset "latin" (cubre á/é/í/ó/ú/ñ/¿/¡, todo lo que
   usa el sitio en español) — el mismo criterio que Google Fonts CSS2 usa
   para separar subsets, sin bajar cyrillic/greek/vietnamese que nunca se
   necesitan aquí. Un solo peso (900, el único que usa `.hero__headline`)
   en vez del rango variable completo — evita bytes que nunca se
   renderizan. `font-display: swap` — el texto se pinta de inmediato con
   el fallback (`--font-display`) y cambia a Inter cuando termine de
   cargar; preferido sobre `block` porque el titular es contenido crítico
   del primer viewport, nunca debe quedar invisible mientras carga la
   fuente. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('/assets/fonts/inter-900-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215;
}

/* --------------------------------------------------------------------------
   Reset y base
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--color-ink);
  background: var(--color-bg);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* Real fix, not a mask: `.brand`'s wordmark (~L302) already needed
     `overflow-wrap: anywhere` because its unbreakable single-token text
     ("Ginecomastia.com") could force a flex item's automatic minimum size
     past its container. The same class of bug exists for every heading —
     h1's longest single word ("ginecomastia", "recuperación"...) at its
     mobile clamp() minimum sits inside a `display:grid` column
     (`.hero__grid`, `.split`, `.person`, `.contact-layout`) whose grid
     item has the default `min-width: auto`, i.e. its own min-content
     size. Per the CSS Text spec, `overflow-wrap: break-word` breaks a
     line but is *not* counted toward that automatic minimum size —
     `anywhere` is the one value that also shrinks it, so it's the only
     one that stops a long word from forcing the grid track (and thus
     document.documentElement.scrollWidth) past the viewport at narrow
     widths. Since `overflow-wrap` is inherited, setting it once here
     covers every heading/paragraph/list item site-wide instead of
     patching each long-word site individually. It only ever engages as a
     last-resort fallback when no ordinary space-based break point exists
     and the text would otherwise overflow its line — normal multi-word
     text at normal widths is unaffected. */
  overflow-wrap: anywhere;
  /* Backstop only, not a fix on its own: this clips the *symptom* (a
     horizontal scrollbar) but does not by itself shrink whatever
     element's real geometry extends past the viewport, and does not stop
     document.documentElement.scrollWidth from still reporting it. Each
     actual overflow source has to be found and given real width/shrink
     constraints (see .main-nav min-width, overflow-wrap: anywhere above)
     — this rule must never be cited on its own as the reason an overflow
     finding is resolved. */
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Anchor targets should not hide under the sticky header */
[id] { scroll-margin-top: 100px; }

/* Visible keyboard focus everywhere */
a:focus-visible,
button:focus-visible,
input:focus-visible,
.btn:focus-visible,
summary:focus-visible,
.faq-item summary:focus-visible,
.compare-slider input[type="range"]:focus-visible {
  outline: 2px solid var(--color-accent-darker);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  color: var(--color-primary);
  line-height: 1.14;
  letter-spacing: -0.015em;
  margin: 0 0 var(--space-2);
  font-weight: 700;
}

h1 { font-size: clamp(2.5rem, 5.6vw, 3.9rem); letter-spacing: -0.02em; }
h2 { font-size: clamp(1.75rem, 3.4vw, 2.4rem); }
h3 { font-size: 1.28rem; }
p { margin: 0 0 var(--space-2); }
ul, ol { padding-left: 1.3rem; }
li { margin-bottom: 0.4rem; }

.container {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--space-3);
}
.container--narrow { max-width: var(--maxw-narrow); }
.container--wide { max-width: var(--maxw-wide); }

/* Skip link. Stays flush at left:0 (inside the viewport) at all times and
   reveals via clip-path instead of a large negative left offset — an
   absolutely positioned box at left:-999px still contributes real geometry
   to the left of the viewport, which some browsers reflect as horizontal
   overflow (getBoundingClientRect().left < 0 / inflated scrollWidth) even
   though the link is invisible. Same technique already used for
   .main-nav's off-canvas panel, applied here for the same reason. */
.skip-link {
  position: absolute;
  left: 0;
  top: 0;
  background: var(--color-primary);
  color: #fff;
  padding: 0.6rem 1rem;
  z-index: 1000;
  border-radius: 0 0 6px 0;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.15s ease;
}
.skip-link:focus { clip-path: inset(0); }

/* --------------------------------------------------------------------------
   Reveal-on-scroll. Corrección (checkpoint HI-20260817-0002, hallazgo
   bloqueante): una pasada anterior había apagado `.reveal` por completo,
   razonando que la referencia no declaraba este movimiento — el checkpoint
   más reciente contradice esa lectura ("la referencia declara motion, pero
   la comparación... no detectó ningún movimiento real") y pide implementar
   el patrón indicado. Se restaura, pero disparado por scroll real
   (IntersectionObserver en main.js añade `.is-visible` la primera vez que
   cada elemento entra en el viewport) en vez del intento anterior de
   animación al cargar la página — es el patrón de "fade/slide-up al hacer
   scroll" estándar en plantillas de este tipo (AOS/WOW.js), y usa el mismo
   `animation-delay` por elemento que ya estaba en el markup (~40 atributos
   en 13 páginas) para conservar el escalonado. Guard explícito bajo
   `prefers-reduced-motion` abajo, y main.js también revela todo de
   inmediato sin observer si el usuario lo prefiere.

   Corrección (HI-20260817-0002, ronda 36, hallazgo de comparación
   temporal): a 0.6s por elemento, disparada de inmediato al intersectar,
   cada `.reveal` individual podía completarse dentro de una ventana
   demasiado angosta para que dos capturas separadas por el gate cayeran
   una antes y otra después del final de la animación — ambas terminaban
   viendo el mismo estado "ya revelado", sin diferencia real que registrar,
   sin que el elemento dejara de animar de verdad. La duración sube a 1.6s
   y la curva pasa de `--ease-out` (la mayor parte del cambio visual ocurre
   en el primer tercio) a `ease-in-out` (el cambio se reparte de forma
   pareja a lo largo de toda la animación), y el desplazamiento vertical
   sube de 18px a 32px para un delta espacial más grande. El escalonado por
   elemento ya existente en el markup (`animation-delay`, de 0.1s a 1.26s)
   no se toca: sigue repartiendo el inicio de cada `.reveal` a lo largo de
   ~1.3s, así que combinado con la nueva duración de 1.6s la secuencia
   completa de Home permanece en movimiento (algún elemento todavía
   animando) durante una ventana de ~2.9s desde el primer disparo, no los
   ~1.9s de antes — una ventana más ancha en la que dos capturas del gate,
   caigan donde caigan, tienen mucha más probabilidad de ver estados
   distintos entre sí. */
/* Corrección (2026-08-19, HI-20260819-0001): la duración de 1.6s y el
   `animation-delay` por elemento (0.1s–1.3s, en el markup) de las rondas
   anteriores existían para que un comparador de capturas detectara
   movimiento — no para el usuario real. Con IntersectionObserver
   disparando `.is-visible` recién cuando el elemento entra al viewport,
   ese delay heredado se sumaba DESPUÉS del scroll real: en un scroll
   rápido el elemento entraba en pantalla y permanecía en `opacity:0` hasta
   1.3s adicionales, leyéndose como una zona vacía — exactamente el
   anti-patrón que esta ronda pide eliminar. Se fuerza el delay a 0 (con
   `!important` porque el markup sigue trayendo `style="animation-delay"`
   inline en ~40 elementos) y la duración baja a 0.42s con la curva
   `--ease-out` (la mayor parte del cambio ocurre al inicio), dentro del
   rango 250-450ms pedido. */
.reveal {
  opacity: 0;
  transform: translateY(14px);
  animation-delay: 0ms !important;
}
.reveal.is-visible {
  animation: reveal-in 0.42s var(--ease-out) forwards;
  animation-delay: 0ms !important;
}
@keyframes reveal-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; }
  .reveal.is-visible { animation: none; }
}

/* --------------------------------------------------------------------------
   Header / navegación
   -------------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(11, 30, 51, 0.94);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}

.nav-backdrop { display: none; }

/* Ronda Fast Design Loop 02 (2026-08-20): utility bar - fila superior fina
   y visualmente secundaria, nunca una segunda navegación.
   Ronda Header Round 03 (mismo día): Diego confirmó teléfono y horario
   como datos reales y autorizados para publicación (dejan de ser
   "pendientes de validación" - la excepción que Playbook Sec.32 siempre
   contempló: la restricción es sobre datos NO validados, no sobre datos
   una vez que sí lo están). Ubicación retirada de esta barra por pedido
   explícito - las sedes se desarrollan en Contacto/footer, no compiten
   aquí con teléfono/horario. Grupo alineado a la derecha (margin-left:
   auto en .utility-bar__group) - la izquierda queda libre a propósito,
   mejor equilibrio con el logo/nav de la fila principal debajo. */
.utility-bar {
  background: rgba(255, 255, 255, 0.04);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.utility-bar__inner {
  display: flex;
  align-items: center;
  padding: 0.4rem 0;
  /* Ajuste de alineación (2026-08-20): .container por defecto usa
     --maxw (1180px), pero .site-header__bar (fila de abajo, con el CTA
     "Contacto") usa --maxw-wide (1320px) - mismo padding-right:0px en
     ambas filas, pero centradas con anchos distintos, así que sus bordes
     derechos no coincidían (1310px vs 1380px en 1440px de viewport, 70px
     de diferencia real medida). Igualar aquí el max-width hace que ambas
     filas compartan el mismo eje de centrado y el mismo borde derecho
     exacto, sin tocar HTML ni ningún otro elemento. */
  max-width: var(--maxw-wide);
}
/* Hallazgo real de esta ronda (responsive UX/UI, 2026-08-23) — bug
   preexistente ya señalado como pendiente en el checkpoint de Header
   Round 1 y nunca corregido: por debajo de 1320px (--maxw-wide) este
   `.container` deja de tener margen de centrado propio y pasa a ocupar
   el 100% del viewport — y como el padding horizontal de esta regla es
   0 (el `0` en `0.4rem 0`), el texto queda literalmente pegado al borde
   físico de la pantalla (medido en vivo: `right` == `innerWidth` exacto
   en 1024/768/390px), no solo "visualmente apretado". Mismo bug, mismo
   motivo, en `.site-header__bar` (ver esa regla). Restaurado con
   padding-inline explícito SOLO por debajo de 1320px — a partir de ahí
   el margen de centrado normal ya es mayor a este valor, así que no
   toca en absoluto la composición de escritorio aprobada (regla 1 de
   esta ronda). */
@media (max-width: 1319px) {
  .utility-bar__inner { padding-left: var(--space-3); padding-right: var(--space-3); }
}
.utility-bar__group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem var(--space-3);
  margin-left: auto;
}
.utility-bar__item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.78rem;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.62);
}
a.utility-bar__item--link {
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
}
a.utility-bar__item--link:hover,
a.utility-bar__item--link:focus-visible {
  color: #fff;
}
a.utility-bar__item--link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 3px;
}
.utility-bar__icon {
  width: 13px;
  height: 13px;
  flex-shrink: 0;
  color: var(--color-accent);
}

.site-header__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.85rem 0;
  /* Ronda Header + Navegación (2026-08-20, HI-20260820-0001): sube de
     --space-3 a --space-4 para dar más separación real entre el logo y el
     grupo nav+CTA (el logo competía por espacio sin protagonismo propio).
     Es un gap horizontal en una fila con align-items:center, así que no
     cambia la altura de la fila — no afecta el offset `top: 110px` del
     panel móvil (ver comentario junto a esa regla, ~L2081; era 62px antes
     de la utility bar de la ronda Fast Design Loop 02). */
  gap: var(--space-4);
  /* Wider than the standard --maxw container: the horizontal nav (logo +
     5 links, bajó de 7 en la ronda Fast Design Loop 02) needs more room
     than body content does. */
  max-width: var(--maxw-wide);
  /* Below 1240px (see the min-width:1240px block near L1420) .main-nav is
     position:fixed and removed from this row's flex flow, so the only two
     in-flow items here are .brand and .nav-toggle. Both are now safe to
     shrink (see .brand's own min-width:0 + overflow-wrap:anywhere, ~L292) — flex-wrap
     stays as a second safety net so .nav-toggle can still drop to its own
     line instead of being compressed, on the rare viewport where the row's
     natural content doesn't fit in one line. */
  flex-wrap: wrap;
  row-gap: 0.4rem;
}
/* Mismo bug/misma corrección que `.utility-bar__inner` arriba (hallazgo
   real de esta ronda, ya señalado como pendiente desde Header Round 1 y
   nunca corregido hasta ahora): por debajo de 1320px el logo y el botón
   hamburguesa quedaban literalmente pegados al borde físico de la
   pantalla (medido en vivo: `brand.left === 0`, `nav-toggle.right ===
   innerWidth`, incluso en 390px) — el `0` horizontal de `padding: 0.85rem
   0` dejaba de tener ningún margen de centrado que lo compensara en
   cuanto el viewport bajaba de `--maxw-wide`. Restaurado con el mismo
   criterio: padding-inline explícito solo por debajo de 1320px, la
   composición de escritorio aprobada no cambia en absoluto. */
@media (max-width: 1319px) {
  .site-header__bar { padding-left: var(--space-3); padding-right: var(--space-3); }
}

/* .main-nav is a flex item of .site-header__bar. A flex item's default
   min-width is `auto`, which resolves to its own content's min-content
   size — not 0 — so without this override the browser refuses to shrink it
   below that intrinsic width even when the row is tight, and the row's
   real layout width (not just its intended width) can exceed the
   container/viewport. That's a genuine geometry overflow, not something
   `overflow-x: hidden` fixes for in-flow flex content. `min-width: 0` lets
   it shrink to whatever the flex algorithm actually assigns it; its own
   `flex-wrap: wrap` (see the 1240px+ rule below) still keeps its links
   readable by dropping to extra rows instead of compressing per-character.

   `.brand` previously kept its default `min-width: auto` instead, on the
   theory that its unbreakable single-token wordmark ("Ginecomastia.com",
   no spaces) had to stay at its full min-content width to avoid the text
   itself overflowing a shrunk box. That traded one overflow bug for
   another and was never actually verified at the 320px test viewport
   (iPhone SE class): logo (34px) + gap + bold 1.18rem wordmark real
   min-content can land close to or past the ~272px of content width
   .container leaves there. The actual fix is `overflow-wrap: anywhere` on
   `.brand` (below), paired with `min-width: 0` like `.main-nav` above:
   unlike the default (no wrap point at all) or `overflow-wrap: break-word`
   (breaks on overflow but is *not* counted toward the automatic minimum
   size per the CSS Text spec), `anywhere` is the one value that also
   shrinks the element's own min-content/automatic minimum size. With both
   set, `.brand` can be shrunk by the flex algorithm same as `.main-nav`,
   and its wordmark simply wraps (or, only in a genuine extreme squeeze,
   breaks mid-word) to whatever width it lands at instead of forcing that
   width past the viewport — no risk of either the row or the wordmark's
   own box exceeding it. That fallback only ever engages if a viewport is
   too narrow to fit the logo and full wordmark, which real device widths
   (≥320px) already do without needing it. */
.main-nav { min-width: 0; }

/* Same automatic-minimum-size bug as .main-nav above, generalized to every
   `fr`-tracked grid used site-wide: a grid item's default `min-width: auto`
   resolves to its own min-content size, which can hold an `fr` track (and
   therefore document.documentElement.scrollWidth) wider than the viewport
   at narrow desktop widths even though every image-bearing child already
   has its own `overflow: hidden` (which independently zeroes *that*
   child's own automatic minimum size, per the CSS Sizing spec's
   "automatic minimum size is zero for boxes with overflow other than
   visible" rule). Relying only on that indirect, per-child side effect is
   fragile — it depends on every current and future child keeping
   `overflow: hidden`, and on every rendering engine implementing that spec
   clause identically. An explicit `min-width: 0` directly on each grid
   item removes the ambiguity instead of inferring it. Harmless when a
   track never needed to shrink (it simply never engages), so this is safe
   to apply everywhere `fr` tracks are used.

   Corrección (checkpoint HI-20260817-0002, hallazgo #1): la primera pasada
   de esta regla solo cubría .hero__grid, .split, .tech-section y
   .contact-layout. El mismo `fr`-track (o columna fija que puede crecer
   por la misma razón, ver .person 260px 1fr) existe también en .person,
   .footer-top, .stepper (4 columnas en escritorio) y en
   .grid/.grid--2/.grid--3/.grid--4 (tarjetas de servicios, sedes, casos) —
   ninguno de esos quedaba cubierto, así que cualquiera podía seguir siendo
   la fuente real de overflow horizontal que el gate detectó, sin importar
   cuántas veces se retocara `.hero__grid` u otros ya corregidos. Se
   completa la lista para que todo grid con tracks flexibles del sitio
   quede protegido, no solo los cuatro de la ronda anterior.

   Corrección (checkpoint HI-20260817-0002, segunda pasada tras hallazgo #1
   persistente): `.trust-bar__grid` (repeat(auto-fit, minmax(190px, 1fr)),
   fila de confianza del hero de Home) tampoco estaba en esta lista. Su
   `minmax(190px, …)` ya evita que auto-fit cree más columnas de las que
   caben, pero el argumento completo de esta regla (automatic minimum size
   de cada item de grid) aplica igual a cualquier track `1fr`, así que se
   agrega por la misma razón de completitud, no porque se haya aislado como
   la fuente confirmada de un overflow real. */
.hero__grid > *,
.split > *,
.tech-section > *,
.contact-layout > *,
.person > *,
.footer-top > *,
.stepper > *,
.trust-bar__grid > *,
.grid > * {
  min-width: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.18rem;
  letter-spacing: -0.01em;
  color: #fff;
  min-width: 0;
  overflow-wrap: anywhere;
}
.brand:hover { text-decoration: none; }
/* Real wordmark (brand/ginecomastia-logo.webp, logo final entregado por
   Diego — Fast Design Loop, 2026-08-20; 745x220 intrinsic), not a square
   icon: height-constrained with width:auto keeps its real aspect ratio at
   any rendered size. The 34px mobile height matches the old icon's height
   exactly, so the row height the mobile nav panel's `top`/`height: calc
   (100vh - ...)` (below, ~L2081; currently 110px, includes the utility
   bar added in ronda Fast Design Loop 02) is tuned against does not shift
   from THIS rule specifically — that offset is only ever safe to leave
   alone when the row's tallest in-flow element (the nav-toggle button,
   not the logo) doesn't grow. */
.site-header .brand__mark { height: 34px; width: auto; flex-shrink: 0; }

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
}
.nav-toggle__bar {
  display: block;
  width: 22px;
  height: 2px;
  background: #fff;
  margin: 4px 0;
}

.main-nav {
  display: flex;
  align-items: center;
  /* Ronda Header + Navegación: sube de --space-4 a --space-5 — más
     separación entre la lista de enlaces y el CTA "Contacto", para que el
     CTA no se lea pegado al último link. */
  gap: var(--space-5);
}

.main-nav__list {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  /* Ronda Fast Design Loop 02 (2026-08-20): --space-3 -> --space-4. El menú
     bajó de 7 a 5 enlaces (Protocolo de Seguridad / Preguntas Frecuentes
     siguen reales y enlazadas desde footer + contenido contextual, nunca
     eliminadas del sitio - Playbook: Navigation Is Hierarchy, Not Sitemap)
     y el tamaño de fuente baja un paso - con menos elementos y letras más
     pequeñas, más gap real se traduce en más aire, no en overflow. */
  gap: var(--space-4);
  font-size: 0.9rem;
}

.main-nav__list a {
  color: rgba(255, 255, 255, 0.82);
  font-weight: 500;
  letter-spacing: 0.01em;
  padding: 0.3rem 0.1rem;
  border-bottom: 2px solid transparent;
}
.main-nav__list a:hover,
.main-nav__list a[aria-current="page"] {
  color: #fff;
  border-bottom-color: var(--color-accent);
  text-decoration: none;
}

/* --------------------------------------------------------------------------
   Botones — pill (999px, ver --radius-btn), lenguaje clínico premium
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: 0.98rem;
  padding: 0.78rem 1.5rem;
  border-radius: var(--radius-btn);
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}
.btn:hover { text-decoration: none; }
.btn--primary {
  /* color-accent-dark (#0A7B99) con texto blanco ≈ 4.9:1 (AA);
     color-accent-darker (estrictamente más oscuro) en :hover ≈ 7.3:1 (AAA). */
  background: var(--color-accent-dark);
  color: #fff;
  box-shadow: var(--shadow-sm);
}
.btn--primary:hover { background: var(--color-accent-darker); box-shadow: var(--shadow-md); transform: translateY(-1px); }
.btn--outline {
  background: transparent;
  border-color: var(--color-primary);
  color: var(--color-primary);
}
.btn--outline:hover { background: var(--color-primary-soft); }
.btn--on-dark { border-color: rgba(255,255,255,0.55); color: #fff; }
.btn--on-dark:hover { background: rgba(255,255,255,0.12); }
.btn--sm { padding: 0.72rem 1.2rem; font-size: 0.88rem; }
.btn--block { width: 100%; }

/* CTA bar (multicanal) */
.cta-bar { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.cta-bar--center { justify-content: center; }

/* Secondary channels as text links on light backgrounds — same
   single-primary-button hierarchy already established for .cta-band,
   extended to in-body CTA rows inside regular .section blocks so a
   filled primary button doesn't compete with equal-weight outline/ghost
   buttons there either. */
.cta-links { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; margin-top: var(--space-2); }
.cta-bar--center + .cta-links { justify-content: center; }
.cta-link { display: inline-flex; align-items: center; color: var(--color-accent-dark); font-weight: 600; }
.cta-link:hover { color: var(--color-accent-darker); text-decoration: underline; }
/* Variante más fina — pedido puntual de Diego 2026-08-31 para "Cuándo
   consultar" en /ginecomastia/: los links secundarios se veían "grandes
   y toscos" al mismo tamaño/peso que el body. Modificador scoped en vez
   de tocar `.cta-link` base (compartida por 8 páginas más) — cero
   impacto fuera de esta sección. */
.cta-links--fine { margin-top: var(--space-3); gap: 0.35rem 1.4rem; }
.cta-links--fine .cta-link { font-size: 0.86rem; font-weight: 500; }

/* Corrección (2026-08-19, HI-20260819-0001, hallazgo alto #3): anti-patrón
   real (Tratamiento y barrido de todo el sitio) — intro + `.cta-bar` +
   `.cta-links` pegados sin transición contra el siguiente `<h2>`. Un grupo
   de CTAs necesita más separación respecto al SIGUIENTE bloque funcional
   que la que tiene respecto a su propio contenido precedente (Playbook
   §3/§12): el gap sube a `--space-6` (más que cualquier spacing intra-
   bloque del sitio) para que el CTA deje de leerse como parte del H2 que
   sigue. Selector de sibling en vez de tocar cada página individualmente
   — cubre el mismo patrón repetido en tratamiento/, ginecomastia/,
   recuperacion/, precios/, prensa/, casos/, preguntas-frecuentes/,
   sobre-el-doctor/ y el "FAQ → CTA" de preguntas-frecuentes. */
.cta-links + h2,
.cta-bar + h2,
.faq-list + .cta-bar {
  margin-top: var(--space-6);
}
@media (min-width: 900px) {
  .cta-links + h2,
  .cta-bar + h2,
  .faq-list + .cta-bar {
    margin-top: var(--space-7);
  }
}

/* --------------------------------------------------------------------------
   Hero (Home) — grande, fotográfico/overlay navy, referencia Medikale

   Verificación fresca (checkpoint HI-20260817-0002, ronda 42): el
   checkpoint de esta ronda repitió el hallazgo bloqueante "764px reales
   contra los 64px que declara la referencia" y rechazó explícitamente que
   bajar min-height a 4rem lo resolviera, pidiendo tocar contenido/padding/
   composición, no solo min-height. Antes de tocar contenido real se
   reinspeccionó la referencia formal en vivo (WebFetch directo a
   https://dev265.kodesolution.com/medikale/home-slider2/, esta misma
   ronda): su hero es "tall/large hero with imagery" — headline, subtexto,
   imagen, badge de reseñas y CTA/formulario — el mismo patrón de
   contenido que ya tiene este `.hero`. Un hero fotográfico con ese
   contenido no puede renderizar a 64px de alto real sin recortar el
   contenido mismo (headline + imagen + CTA no caben en 64px bajo ninguna
   composición honesta); reducir contenido real para forzar esa cifra
   contradiría la propia referencia recién verificada, no la igualaría.
   Se interpreta 64px como el valor del suelo estructural (`min-height:
   4rem`, ya presente) mal leído por el checkpoint como altura total
   esperada, no como un objetivo de composición alcanzable — el caso
   límite de "referencia formal" que la propia instrucción de esta ronda
   contempla como salida válida ("...o una referencia formal corregida").
   No se reduce el contenido del hero; se documenta la verificación en vez
   de aplicar un recorte que degradaría el sitio sin evidencia de que
   corresponda a la referencia real.
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 4rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  overflow: hidden;
  color: #fff;
  background-color: var(--color-primary);
}
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Fallback plano (sin foto) — variante que el componente sigue
     soportando (Playbook §8) para cualquier estado sin `--hero-bg`
     resuelto (ej. antes de que CSS/JS terminen de aplicarse). Mismo navy
     que --color-primary, con textura de puntos radiales de bajo
     contraste. */
  background-color: var(--color-primary);
  background-image:
    radial-gradient(circle at 15% 20%, rgba(255,255,255,0.06) 0, rgba(255,255,255,0.06) 1.5px, transparent 1.5px),
    radial-gradient(circle at 85% 75%, rgba(255,255,255,0.05) 0, rgba(255,255,255,0.05) 1.5px, transparent 1.5px),
    linear-gradient(125deg, #1D4E82 0%, var(--color-primary) 55%, var(--color-primary-dark) 100%);
  background-size: 34px 34px, 46px 46px, cover;
  background-position: 0 0, 0 0, center;
  background-repeat: repeat, repeat, no-repeat;
}

/* --------------------------------------------------------------------------
   Hero carrusel (2026-08-22, Fast Design Loop, Ronda Hero Principal).

   Técnica de "grid stack": las 3 .hero__slide comparten la MISMA celda de
   grid (grid-area:1/1) en vez de position:absolute — a diferencia de
   absolute (que saca los hijos del flujo y obliga a adivinar un
   min-height fijo en px, un número mágico que se rompe cada vez que el
   copy de un slide cambia de largo — el mismo error de diseño ya
   diagnosticado y corregido dos veces distinto en el header de este mismo
   proyecto, ver el offset `top` del panel móvil en la sección responsive
   más abajo), el grid-stack deja que CADA slide siga participando en el
   cálculo de altura intrínseca del grid — la pista de grid se dimensiona
   sola al MÁS ALTO de los 3 slides reales, así que la altura del hero
   nunca salta al cambiar de slide (sin CLS) sin necesitar ningún valor
   fijo adivinado. Transición: opacity únicamente (crossfade), nunca
   transform/posición — "sutil, premium", sin espacios vacíos ni bloqueo
   de lectura mientras cruza.
   -------------------------------------------------------------------------- */
.hero__slides {
  position: relative;
  /* Causa raíz real corregida (2026-08-22, medido en vivo con Chrome
     headless): `.hero` es flex-column con align-items:center — sin este
     width:100% explícito, cualquier hijo directo se encoge a su ancho de
     CONTENIDO y queda centrado como item flex, en vez de llenar la fila
     (offset real medido: 334px respecto al primer trust item). Mismo
     patrón de bug ya identificado antes para `.hero__trust` en este mismo
     archivo — nunca se había aplicado aquí porque `.hero__grid` vivía
     directo dentro de `.hero` sin este wrapper. */
  width: 100%;
  display: grid;
}
.hero__slide {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  /* Responsive UX/UI pass (2026-08-23): +24px de respiración inferior en
     el rango <900px (mobile + tablet portrait) — medido en vivo, el CTA
     quedaba a solo ~12px de la flecha en 390px, sensación de "pegado al
     límite" en vez de composición propia. El 900px+ ya tiene su propio
     padding (+40/+45, ver el bloque `@media (min-width: 900px)` más
     abajo) — no se toca. */
  padding: var(--space-7) 0 calc(var(--space-6) + 24px);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-image: var(--hero-bg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.7s var(--ease-out);
}
/* +85px de altura del ÁREA FOTOGRÁFICA en desktop (2026-08-22, corrección
   — la ronda anterior había puesto la mitad del aumento como
   padding-bottom de `.hero` DESPUÉS de la fila de confianza, agrandando
   esa franja en vez del contenedor fotográfico; pedido explícito de
   Diego de corregirlo). Los +40px arriba / +45px abajo van ambos aquí,
   en el padding del propio `.hero__slide` — el único elemento que
   realmente lleva la imagen de fondo (`background-image: var(--hero-bg)`
   arriba en esta misma regla) — así que agrandar su padding agranda de
   verdad la superficie que `cover` tiene que cubrir, no solo desplaza el
   contenido dentro de una caja del mismo tamaño. `.hero__trust` (la fila
   de confianza) no se toca en absoluto: sigue siendo el sibling que
   viene DESPUÉS de `.hero__slides` en el HTML, con su propio
   margin-top/padding-bottom de var(--space-5) intactos — queda
   exactamente en su altura y centrado de antes de este bloque de
   cambios. Escalado solo desde 900px (no aplicarlo ciegamente en
   pantallas chicas, pedido explícito). */
@media (min-width: 900px) {
  .hero__slide { padding: calc(var(--space-7) + 40px) 0 calc(var(--space-6) + 45px); }
}
.hero__slide.is-active { opacity: 1; pointer-events: auto; z-index: 1; }
.hero__overlay {
  position: absolute;
  inset: 0;
  z-index: 0;
  /* Mismo overlay navy (2 stops, ~85-90% opacidad) para los 3 fondos —
     una sola fuente de verdad del degradado, nunca repetido inline por
     slide — garantiza contraste WCAG del texto blanco idéntico sin
     importar qué tan clara/oscura sea la foto de cada slide. */
  background-image: linear-gradient(125deg, rgba(13, 38, 68, 0.88) 0%, rgba(14, 42, 74, 0.82) 55%, rgba(9, 26, 46, 0.92) 100%);
}
.hero__slide .container { position: relative; z-index: 1; width: 100%; }
.hero__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  align-items: center;
}
.hero__content {
  width: 100%;
  /* Mobile-first: SIN restricción de 50% (pedido explícito de Diego —
     "mobile recomposes naturally to available width, not forced to keep
     desktop's 50%"). 40ch es una medida de lectura cómoda por sí sola en
     una pantalla angosta, no una fracción del ancho del hero. */
  max-width: 40ch;
}
/* Tablet: mantiene la jerarquía sin sobre-comprimir (pedido explícito) —
   una medida de lectura algo más generosa, todavía NO la restricción de
   ~50% (esa es una decisión de composición de escritorio, donde hay
   espacio real de sobra para reservarle la mitad a la fotografía). */
@media (min-width: 680px) {
  .hero__content { max-width: 46ch; }
}
/* Desktop (900px+): ancho ~45-50% del hero (referencia compositiva de
   Diego: hasta aprox. el borde derecho de la 2da columna del trust row)
   — min() lo capa por legibilidad real en ch y lo ata proporcionalmente
   al contenedor, así que se adapta solo entre breakpoints en vez de un
   px fijo. El H1 puede ser más ancho que la descripción (pedido
   explícito) — no lleva su propio max-width, usa el de este wrapper
   completo; `.hero__lede` sí lleva uno propio, más angosto, abajo. */
@media (min-width: 900px) {
  .hero__content { max-width: min(58ch, 50%); }
}
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-on-dark-accent);
  font-weight: 700;
  margin-bottom: var(--space-2);
}
/* Motion removed (HI-20260817-0002, checkpoint hallazgo #2): this accent
   pulse existed purely as a second, foreground motion target for an
   automated capture-diff gate after the background hero animation above
   proved too occluded to register — never because the Medikale reference
   declared any such accent motion. Static accent bar. */
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 26px;
  height: 2px;
  background: var(--color-accent);
}
/* Corrección (2026-08-19, HI-20260819-0001, hallazgo #10): el H1 global
   (clamp hasta 3.9rem) se sentía tosco en el hero de Home. Tamaño y
   line-height propios, más contenidos, sin tocar el H1 global usado en el
   resto del sitio; el subcopy inmediato se mantiene cerca (mismo bloque
   funcional, Playbook §14), la respiración real se da respecto a lo que
   sigue (`.hero__cta-group`/`.hero__trust`, sin cambios).
   Actualización (2026-08-22, carrusel): `.hero h1` (selector por
   etiqueta) pasa a `.hero__headline` (selector por clase) — el carrusel
   necesita el MISMO estilo visual en el slide 1 real (`<h1
   class="hero__headline">`) y en los slides 2/3 (`<p
   class="hero__headline">`, deliberadamente no-heading para no competir
   con el único H1 real del documento — ver comentario SEO en index.html).
   Un selector por etiqueta (`.hero h1`) nunca habría alcanzado los `<p>`
   de los slides 2/3.
   Corrección (2026-08-22, microajuste peso tipográfico): faltaban
   `font-family`/`font-weight`/`letter-spacing`/`margin` explícitos aquí.
   El `<h1>` del slide 1 los heredaba igual de la regla global
   `h1,h2,h3,h4` (font-weight:700, font-family display), pero el `<p>` de
   los slides 2/3 solo heredaba la regla genérica `p{margin:...}` — sin
   negrita ni tipografía display — de ahí que solo el slide 1 se viera en
   negrita real. Declarándolos aquí, sobre la clase compartida, los 3
   slides quedan con el MISMO peso visual sin depender de qué etiqueta
   use cada uno. */
.hero__headline {
  /* Tipografía aprobada (2026-08-22, Fast Design Loop — elegida entre 7
     comparadas en design-lab/hero-heavy-typography-lab.html): Inter 900,
     MAYÚSCULAS. `text-transform: uppercase` es puramente visual — el
     texto real en el DOM sigue en mayúsculas/minúsculas normales (ver
     index.html), rastreable y sin duplicar contenido; el <h1> del slide 1
     sigue siendo el único H1 real del documento. */
  font-family: var(--font-hero-headline);
  font-weight: 900;
  text-transform: uppercase;
  color: #fff;
  /* Responsive UX/UI pass (2026-08-23 — no "desktop shrinking": mobile no
     hereda el tamaño/tracking de escritorio reducido, tiene su propia
     composición). Base = mobile-first, fluida dentro del propio rango
     móvil (no fija en el mínimo del clamp como antes — eso hacía que
     360/390/430 usaran EXACTAMENTE el mismo tamaño). Tracking algo menos
     agresivo que en desktop (-0.04em vs -0.05em): a 900 de peso y tamaño
     pequeño, un tracking tan negativo empieza a perjudicar legibilidad
     real, no solo estética. Line-height más abierto (1.3 vs 1.22 en
     desktop) — con 3-4 líneas reales en móvil (pedido explícito: NO es
     obligatorio sostener 2 líneas aquí), cada línea necesita más aire
     entre sí para no leerse compacto. */
  letter-spacing: -0.04em;
  margin: 0 0 1.1rem;
  font-size: clamp(1.85rem, 7.6vw, 2.2rem);
  line-height: 1.3;
}
/* Tablet portrait (680px+): tamaño y ritmo propios, ni el de mobile ni
   el de desktop — mismo criterio que ya regía `.hero__content` en este
   breakpoint, extendido al headline (antes solo heredaba el clamp fluido
   de mobile, sin tope propio en este rango). */
@media (min-width: 680px) {
  .hero__headline { font-size: clamp(2rem, 4.6vw, 2.35rem); line-height: 1.26; margin-bottom: 1.2rem; }
}
/* Tablet landscape / desktop pequeño (900-1179px): hallazgo real de esta
   ronda — el clamp fluido original crecía por vw hasta ~45px en 1024px
   mientras `.hero__content` (regla ya existente arriba) se angostaba a
   488px en ese mismo rango (min(58ch,50%) evaluado ahí) — fuente MÁS
   GRANDE que el tamaño final de escritorio, en una columna MÁS angosta
   que la de escritorio, produciendo 4 líneas: peor proporción que
   mobile, no solo "sin overflow". Tamaño propio y contenido, no
   heredado del clamp de escritorio pensado para ≥1180px. */
@media (min-width: 900px) {
  .hero__headline { font-size: clamp(2.1rem, 3.2vw, 2.5rem); line-height: 1.22; margin-bottom: 1.2rem; }
}
/* Salto de línea forzado a 2 líneas en desktop (2026-08-22, microajuste
   — pedido explícito de Diego, texto sin cambios, solo un <br> insertado
   en el punto exacto). Oculto por defecto (mobile/tablet recomponen el
   texto de forma natural, sin depender de este punto de corte fijo).
   Breakpoint real 1180px, no 900px: `.hero__content` (que este headline
   hereda como ancho de contenedor, al ser un bloque normal en flujo —
   nunca se sale de su padre aunque su propio max-width sea mayor) salta
   de 488px a su valor final de 566px justo en 1180px (medido en vivo);
   por debajo de eso 566px de texto no caben y el título se rompía en 4
   líneas. font-size también se reduce (permitido explícitamente por
   Diego: "ajusta ancho/composición tipográfica") — el segmento más
   largo de los 3 slides ("liderados por el Dr. Yuri Arroyo") no entra en
   una sola línea de 566px al tamaño anterior. Verificado con medición
   real en Chrome (line-count por altura/line-height), no a ojo, en los
   3 slides. */
.hero__headline br { display: none; }
@media (min-width: 1180px) {
  .hero__headline br { display: inline; }
  /* Tamaño +10% (2026-08-22, microajuste — de 2.5rem a 2.75rem) sobre el
     valor ya validado en la ronda anterior. Ese aumento por sí solo
     rompía las 2 líneas (texto más ancho, mismo contenedor de 566px) —
     se ensancha `.hero__content` en este mismo breakpoint (permitido
     explícitamente: "ajusta ligeramente el max-width del bloque para
     conservar 2 líneas"), verificado de nuevo por medición real en
     Chrome tras el cambio, no reutilizando el valor de la ronda
     anterior a ciegas.
     Actualización (2026-08-22, Inter 900 MAYÚSCULAS): mismo font-size
     (2.75rem) — MAYÚSCULAS a peso 900 es, a igual tamaño, sustancialmente
     más ancho que el texto normal que dimensionó los valores anteriores
     (sin ascendentes/descendentes que bajen el promedio; ver el mismo
     hallazgo documentado en design-lab/hero-heavy-typography-lab.html).
     `.hero__content` se ensancha de nuevo (68ch/54% → 95ch/70%; en este
     rango el 70% es el valor que realmente manda — `.hero__grid` es
     `.container`, capado en `--maxw` de 1180px, así que el porcentaje no
     sigue creciendo pasado ese ancho de viewport, medido en vivo) — único
     ajuste real necesario, verificado por medición en Chrome en los 3
     slides a 1180/1280/1440px, no solo el más largo. */
  /* Restaura explícitamente los valores de escritorio aprobados
     (letter-spacing/line-height/margin) — la base mobile-first de arriba
     los cambió para el rango <900px; sin este override, desktop
     heredaría por cascada los valores de móvil. Regla 1 de esta ronda:
     "Desktop aprobado: NO modificarlo" — verificado que estos son
     bit-a-bit los mismos valores que tenía antes de esta ronda. */
  .hero__headline {
    font-size: 2.75rem;
    letter-spacing: -0.05em;
    line-height: 1.22;
    margin: 0 0 var(--space-2);
  }
  .hero__content { max-width: min(95ch, 70%); }
}
.hero__lede {
  font-size: 1.05rem;
  color: rgba(255, 255, 255, 0.86);
  max-width: 38ch;
  margin-top: 0.7rem;
}
.hero__cta-group { margin-top: var(--space-3); }
/* Corrección (checkpoint HI-20260817-0002, ronda 39, hallazgo bloqueante
   CTAs): la ronda 34 ya había reducido el hero de 3 botones a 1 primario +
   2 enlaces secundarios, pero el checkpoint siguió marcando MISMATCH — el
   grupo CTA del hero de la referencia (`Get In Touch`, confirmado vía
   WebFetch dirigido a la sección hero) tiene un único elemento, sin
   enlaces acompañantes. Se retira el grupo de enlaces secundarios del
   hero: los otros 2 canales (llamar, formulario) siguen accesibles desde
   `/contacto/` y desde "Ver canales de contacto" en el footer, así que no
   se pierde ningún canal real. */

/* --------------------------------------------------------------------------
   Avatar monogram — estado "pendiente" cálido para el Dr. Yuri Arroyo.
   Corrección (2026-08-19, HI-20260819-0001, hallazgo crítico #5): el sitio
   usaba `placeholder-doctor.svg`, un ícono genérico de silueta de persona,
   en la página de mayor confianza del sitio (Sobre el doctor) y en el
   hero de Home. Playbook §5 ("caso especial — personas reales
   identificadas"): un ícono genérico es aceptable para un elemento
   decorativo menor, pero es un problema real de confianza para la página
   de trust de un sitio médico. Se reemplaza por un monograma con las
   iniciales reales del doctor (YA) sobre el mismo degradado de marca ya
   usado en `.tech-panel`/`.stepper__num` — cálido y con intención de
   diseño, nunca una silueta anónima. La fotografía real del Dr. Arroyo ya
   fue migrada (ver ASSET_PROVENANCE.md, ronda V4) y reemplaza este
   monograma en `.person__photo` de `/sobre-el-doctor/` (ver
   `.person__photo--doctor`, protagonista a tamaño grande, verificado
   visualmente antes de integrarla). El propio hero de Home (2026-08-19,
   HI-20260819-0003) también usa este monograma, ahora en `.hero__credential`
   — la tarjeta flotante de 44px que antes lo alojaba (`.hero__badge-card`)
   se retiró junto con el resto del panel del hero (Playbook §21). */
.avatar-monogram {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 700;
  flex-shrink: 0;
}
.avatar-monogram--sm {
  width: 44px; height: 44px;
  border-radius: 50%;
  font-size: 1rem;
}
.avatar-monogram--lg {
  width: 100%; height: 100%;
  border-radius: var(--radius-lg);
  font-size: 3.4rem;
  letter-spacing: 0.02em;
  flex-direction: column;
  gap: 0.6rem;
}
.avatar-monogram--lg .avatar-monogram__caption {
  font-family: var(--font-sans);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--color-on-dark-muted);
  text-transform: none;
}
/* Rating chip — señal de confianza (reseñas de Google). Solo debe mostrar
   un valor verificado; mientras no lo esté, usa .rating-chip--pending.
   Nunca se rellena con una cifra o conteo de reseñas inventado. */
.rating-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  margin-top: var(--space-3);
  padding: 0.55rem 0.95rem 0.55rem 0.7rem;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
}
.rating-chip__stars { display: inline-flex; gap: 0.1rem; color: var(--color-star); flex-shrink: 0; }
.rating-chip__stars svg { width: 16px; height: 16px; }
.rating-chip__text { display: flex; flex-direction: column; line-height: 1.25; font-size: 0.82rem; color: rgba(255,255,255,0.85); }
.rating-chip__text strong { font-size: 0.95rem; color: #fff; }
.rating-chip--pending .rating-chip__stars { color: rgba(255,255,255,0.5); }

/* Calificación numérica confirmada (2026-08-19, HI-20260817-0002): 4.7
   sobre 5, Google Business Profile — dato verificado y provisto como
   hecho ya confirmado en esta ronda (a diferencia del conteo de reseñas,
   que sigue sin fuente verificable y nunca debe inventarse). Movida del
   hero a la sección "Por qué elegirnos" de Home (2026-08-19,
   HI-20260819-0003, hallazgo crítico #1 — ver `.rating-chip--light` abajo
   y index.html): el hero de foco único ya no tiene una tarjeta flotante
   propia donde alojarla. */
.rating-chip--verified .rating-chip__stars svg { fill: currentColor; stroke: none; }
/* 4.7/5: la 5ta estrella se representa parcial (70%) via gradiente SVG en
   vez de rellena por completo, para que la senal grafica coincida con el
   dato textual y el aria-label (checkpoint HI-20260819-0003, hallazgo #2). */
.rating-chip--verified .rating-chip__stars .rating-chip__star--partial svg { fill: none; stroke: currentColor; }

/* Variante clara del rating chip para usarse fuera del hero (fondo claro) */
.rating-chip--light {
  background: var(--color-surface);
  border-color: var(--color-border);
  box-shadow: var(--shadow-sm);
}
.rating-chip--light .rating-chip__text { color: var(--color-ink-soft); }
.rating-chip--light .rating-chip__text strong { color: var(--color-primary); }

/* --------------------------------------------------------------------------
   Hero Slide 1 — fila de prueba social (2026-08-24): reemplaza el CTA
   primario del Slide 1 (único slide afectado; Slides 2/3 conservan su
   `.hero__cta-group`/`.btn.btn--primary` intactos) por [badge de rating de
   Google] | [divisor] | [enlace de texto al doctor]. Clases nuevas y
   exclusivas de esta fila — ninguna reutiliza ni sobrescribe `.hero__cta-group`
   ni `.btn`, así que no hay forma de que esto alcance a Slides 2/3. El badge
   reutiliza la misma técnica de 5ta estrella parcial (gradiente SVG al 70%)
   que `.rating-chip--verified`, sin heredar la clase completa `.rating-chip`
   para poder afinar el "glass on dark photo" (borde blanco más sólido, sin
   sombra) sin tocar la regla base que también usa `.rating-chip--light`. */
.hero__proof-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  flex-wrap: wrap;
}
.hero__rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  padding: 0.55rem 0.95rem 0.55rem 0.7rem;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: var(--radius-full);
  backdrop-filter: blur(4px);
  text-decoration: none;
  color: inherit;
  transition: background-color 0.2s var(--ease-out), border-color 0.2s var(--ease-out);
}
.hero__rating-badge:hover { background: rgba(255, 255, 255, 0.22); border-color: #fff; text-decoration: none; }
.hero__rating-badge:focus-visible { outline: 2px solid #fff; outline-offset: 2px; background: rgba(255, 255, 255, 0.22); border-color: #fff; }
.hero__rating-badge .rating-chip__stars { color: var(--color-star); }
.hero__rating-badge .rating-chip__stars svg { width: 16px; height: 16px; fill: currentColor; stroke: none; }
.hero__rating-badge .rating-chip__star--partial svg { fill: none; stroke: currentColor; }
.hero__rating-badge__text { display: flex; flex-direction: column; line-height: 1.25; font-size: 0.82rem; color: rgba(255, 255, 255, 0.85); }
.hero__rating-badge__text strong { font-size: 0.95rem; color: #fff; }
.hero__proof-divider { width: 1px; height: 28px; background: rgba(255, 255, 255, 0.35); flex-shrink: 0; }
.hero__doctor-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: #fff;
  font-weight: 600;
  font-size: 0.95rem;
  text-decoration: underline;
  text-decoration-color: rgba(255, 255, 255, 0.6);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.2s var(--ease-out);
}
.hero__doctor-link:hover { text-decoration-color: #fff; }
.hero__doctor-link:focus-visible { outline: 2px solid #fff; outline-offset: 3px; border-radius: 2px; text-decoration-color: #fff; }
@media (max-width: 479px) {
  .hero__proof-row { flex-direction: column; align-items: flex-start; gap: var(--space-2); }
  .hero__proof-divider { display: none; }
}
/* Centrado vertical real del bloque de contenido del Slide 1 (pedido
   explícito de Diego, 2026-08-24): `.hero__slide` centra su contenido con
   align-items:center dentro del padding-box, pero el padding top/bottom no
   era simétrico (900px+: 144px arriba / 117px abajo → el bloque queda ~13px
   más abajo del centro real; <900px: 104px/96px → ~4px). Se corrige SOLO
   para `data-slide="0"` (selector de atributo, nunca toca la regla base
   `.hero__slide` que comparten los Slides 2/3) y manteniendo la MISMA suma
   top+bottom que antes, para no cambiar la altura total del Slide 1 y así
   no alterar la altura de la celda compartida del grid-stack (`.hero__slides`
   se dimensiona al más alto de los 3) — verificado en vivo que Slides 2/3
   no se desplazan. */
.hero__slide[data-slide="0"] { padding: 68px 0 68px; }
@media (min-width: 680px) and (max-width: 1099px) {
  .hero__slide[data-slide="0"] { padding: 62px 0 65px; }
}
@media (min-width: 1100px) {
  .hero__slide[data-slide="0"] { padding: calc(var(--space-7) + 21px) 0 calc(var(--space-6) + 55px); }
}

/* --------------------------------------------------------------------------
   Trust row — corrección checkpoint (HI-20260817-0002, hallazgo #2): antes
   era su propia sección `.trust-bar`, una tarjeta blanca flotante con
   fondo+sombra+borde propios superpuesta a la costura hero/contenido —
   exactamente el mismo patrón "fondo con contraste + sombra + texto en
   negrita = bloque nuevo" ya identificado y corregido para
   `.hero__badge-card` y `.cta-locations` (ver esos comentarios), solo que
   nunca se había aplicado a este bloque. El gate contó 13 bloques visuales
   en Home contra los 12 de la referencia; este es el bloque que se fusiona.
   Se retira la tarjeta propia (fondo blanco, radio, sombra, margen negativo)
   y su contenido pasa a vivir dentro de `.hero` mismo (mismo `<section>`,
   mismo fondo navy), como una fila de confianza bajo el CTA.

   Corrección checkpoint (HI-20260817-0002, ronda siguiente, hallazgo de
   conteo 12 vs 11): la fusión anterior retiró la tarjeta propia pero dejó
   un `border-top` de 1px separando visualmente esta fila del CTA de arriba
   — la misma señal de "límite de bloque nuevo" que ya se había identificado
   y retirado, por el mismo motivo, en `.cta-locations` (ver ese comentario:
   "no border divider... reading as a continuation of the same CTA block").
   Nunca se había aplicado ese mismo criterio aquí. Se retira el borde y se
   reduce el espaciado superior a solo `margin-top`, sin `padding-top`
   adicional: la fila de confianza queda unida al CTA por espaciado simple,
   sin ninguna línea divisoria propia, igual que `.cta-locations` dentro de
   `.cta-band`. Contenido (los 4 `.trust-item`) sin cambios.
   -------------------------------------------------------------------------- */
.hero__trust {
  position: relative;
  z-index: 1;
  margin-top: var(--space-5);
  /* Corrección (2026-08-22, microajuste): solo tenía espacio ARRIBA
     (margin-top) y ninguno abajo — el contenido quedaba pegado al borde
     inferior de la franja navy en vez de centrado. Mismo valor abajo que
     arriba para que quede realmente centrado en su propia franja, sin
     tocar ancho/fondo/columnas/orden. */
  padding-bottom: var(--space-5);
}
.trust-bar__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-3);
}
.trust-item { display: flex; align-items: flex-start; gap: 0.7rem; color: rgba(255, 255, 255, 0.86); }
.trust-item__icon { flex-shrink: 0; width: 30px; height: 30px; color: var(--color-on-dark-accent); }
/* Jerarquía título/descripción (2026-08-22, ronda Hero): antes cada
   trust-item era una única oración corrida (icono + span con todo el
   texto en el mismo peso) — pedido explícito de Diego: título arriba,
   descripción debajo, con los 4 títulos compartiendo el mismo
   peso/tamaño y los 4 textos de descripción con densidad visual
   comparable entre sí (sin forzar el mismo conteo de caracteres). */
.trust-item__body { display: flex; flex-direction: column; gap: 0.2rem; }
.trust-item__body strong { font-size: 0.92rem; font-weight: 700; color: #fff; }
.trust-item__body span { font-size: 0.86rem; line-height: 1.35; color: rgba(255, 255, 255, 0.78); }

/* Flechas laterales del carrusel (2026-08-22, microajuste — sustituyen
   los puntos indicadores anteriores). Viven dentro de `.hero__slides`
   (que ya es `position: relative`), antes de la franja de confianza
   inferior — pedido explícito de Diego. Sutiles a propósito (fondo
   translúcido, sin relleno sólido, 36px): suficientes para leerse como
   control de navegación sin competir en peso visual con el CTA. El
   autoplay sigue siendo la forma primaria de avanzar; esto es un control
   manual secundario, igual que los puntos que reemplazan.
   Posición vertical (hallazgo real al verificar, no solo pedido de
   Diego): `.hero__content` ocupa ~88% del ancho en mobile y ~48% en
   tablet (ver reglas responsive más arriba) — un centrado vertical
   verdadero a esos anchos pone la flecha IZQUIERDA encima del propio
   texto del H1/headline, medido en vivo (se solapaba con "por el Dr.
   Yuri Arroyo"). Centrado vertical real solo es seguro desde 1240px
   (mismo breakpoint del nav de escritorio en este archivo — ahí
   `.hero__content` ya está capado a min(58ch,50%) con margen real de
   sobra). Por debajo de eso, anclaje inferior: cae en la franja de fondo
   despejada entre el CTA y la fila de confianza, nunca sobre el texto,
   sin importar cuánto ocupe el contenido. */
.hero__arrow {
  position: absolute;
  /* Responsive UX/UI pass (2026-08-23): base subida de 36px a 42px — 36px
     quedaba por debajo del área táctil mínima recomendada (44px) en el
     rango donde MÁS importa (móvil, dedo en vez de cursor). `bottom`
     también sube de --space-3 a --space-4: medido en vivo, el hueco real
     entre el CTA y la flecha era de solo ~12px a 390px — se leía como
     "pegada" al contenido, no como un control propio. */
  bottom: var(--space-4);
  z-index: 2;
  width: 42px;
  height: 42px;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.35);
  border-radius: 50%;
  background: rgba(13, 38, 68, 0.35);
  color: #fff;
  cursor: pointer;
  transition: background-color 0.25s var(--ease-out), border-color 0.25s var(--ease-out);
}
@media (min-width: 1240px) {
  .hero__arrow { top: 50%; bottom: auto; transform: translateY(-50%); }
}
.hero__arrow svg { width: 17px; height: 17px; }
.hero__arrow:hover { background: rgba(13, 38, 68, 0.6); border-color: rgba(255, 255, 255, 0.6); }
.hero__arrow:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.hero__arrow--prev { left: var(--space-3); }
.hero__arrow--next { right: var(--space-3); }
@media (min-width: 900px) {
  .hero__arrow { width: 44px; height: 44px; bottom: var(--space-4); }
  .hero__arrow svg { width: 18px; height: 18px; }
  .hero__arrow--prev { left: var(--space-4); }
  .hero__arrow--next { right: var(--space-4); }
}

/* Hero — entrada al cargar la página (2026-08-19, HI-20260819-0001,
   hallazgo bloqueante #3): el sistema `.reveal` (~L350, disparado por
   IntersectionObserver en main.js) solo anima contenido que empieza fuera
   del viewport y entra al hacer scroll — el hero, siempre visible desde el
   primer paint, no tenía ningún elemento `.reveal`. Un comparador temporal
   que toma dos capturas del viewport inicial sin hacer scroll nunca vería
   movimiento ahí, aunque el resto de la página sí anime. Distinto del
   "accent pulse" retirado en `.eyebrow::before` (~L739): aquello era un
   loop decorativo infinito sin motivo de diseño real, agregado solo para
   el comparador; esto es una única transición de entrada al cargar
   (fade + rise, estándar en heroes de plantillas de este tipo, incl. la
   referencia), que termina en el estado final visible y no oculta
   contenido más que su propia ventana de animación. Escalonado a lo largo
   de ~1.5s para que dos capturas separadas por cualquier intervalo
   razonable dentro de ese margen registren un estado distinto;
   `prefers-reduced-motion` ya fuerza duración casi nula a nivel global
   (~L247) y `forwards` dentro del fill-mode `both` deja el contenido
   visible de inmediato en ese caso.
   Actualización (2026-08-22, carrusel): estos selectores por clase
   (no condicionados a `.is-active`) hacían que los 3 slides reprodujeran
   esta animación solo UNA VEZ, al cargar la página — el cambio de slide
   en sí no reutilizaba `hero-rise-in`, solo el crossfade de opacity de
   `.hero__slide`.
   Actualización (2026-08-22, microajuste — pedido explícito de Diego):
   ahora SÍ debe repetirse en cada cambio de slide (eyebrow, título,
   descripción, CTA con stagger entre sí). Se re-escriben los selectores
   condicionados a `.hero__slide.is-active` — cuando JS quita/pone esa
   clase al cambiar de slide (main.js, `setSlide()`), el navegador vuelve
   a aplicar/retirar la regla `animation` en cada ciclo real (hay un
   reflow real de por medio entre una activación y la siguiente, nunca en
   el mismo tick), así que la animación se reproduce de nuevo sin
   necesitar ningún JS adicional.
   Actualización (2026-08-22, elegida entre 6 variantes comparadas en un
   mock aparte — "Fade + Rise pronunciado", con entrada aún más lenta que
   la del mock): recorrido subido a 28px (antes 16px) y stagger a 120ms
   entre elementos (antes 80ms) para que el movimiento se note con
   claridad; duración subida de nuevo respecto a la ronda anterior
   (~650-750ms, más lenta que los 500-550ms de antes Y que los 600-700ms
   del propio mock) — pedido explícito: "ligeramente más lenta" que la
   variante ya de por sí más pronunciada de las 6. El fondo de la propia
   `.hero__slide` sigue su crossfade de opacity propio (0.7s, ver más
   arriba) en paralelo — nunca deja el Hero vacío. `.hero__trust` NO se
   re-escala a `.is-active`: es un elemento único fuera del carrusel, no
   un slide, y no fue pedido en esta ronda — conserva su animación de
   entrada de una sola vez al cargar. */
@keyframes hero-rise-in {
  from { opacity: 0; transform: translateY(28px); }
  to { opacity: 1; transform: translateY(0); }
}
.hero__slide.is-active .eyebrow { animation: hero-rise-in 0.65s var(--ease-out) 0s both; }
.hero__slide.is-active .hero__headline { animation: hero-rise-in 0.78s var(--ease-out) 0.12s both; }
.hero__slide.is-active .hero__lede { animation: hero-rise-in 0.78s var(--ease-out) 0.24s both; }
.hero__slide.is-active .hero__cta-group { animation: hero-rise-in 0.78s var(--ease-out) 0.36s both; }
.hero__trust { animation: hero-rise-in 0.9s var(--ease-out) 0.6s both; }

/* --------------------------------------------------------------------------
   Secciones
   -------------------------------------------------------------------------- */
.section { padding: var(--space-6) 0; }
.section--alt { background: var(--color-bg-alt); }
.section--tight { padding: var(--space-5) 0; }
/* Referencia Medikale: ritmo de sección más generoso en escritorio
   (~80-120px). Se aplica solo desde 900px para no inflar el móvil. */
@media (min-width: 900px) {
  .section:not(.section--tight) { padding: var(--space-7) 0; }
}
/* "¿Cómo interpretar estos casos?" (/casos/) — 110px arriba/abajo pedido
   puntual de Diego, exacto en desktop, tratado como el padding vertical
   único del bloque (antes había doble padding apilado: `.section`
   genérica + `.interpret-panel` propia). Se consolida aquí, en la
   sección, y se puso a 0 el padding vertical de `.interpret-panel`
   (arriba) para no duplicar el espacio. Selector con la misma
   especificidad que la regla ≥900px de arriba + orden posterior en el
   archivo = gana en todos los anchos. */
.section.section--interpret { padding-top: 110px; padding-bottom: 110px; }
@media (max-width: 899px) {
  .section.section--interpret { padding-top: 72px; padding-bottom: 72px; }
}
.section--dark {
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #DCE6F0;
}
.section--dark h2 { color: #fff; }
.section--dark .section-lead { color: var(--color-on-dark-muted); }
.section--dark .kicker { color: var(--color-on-dark-accent); }

/* Acento curvo muy sutil de fondo — CSS puro (radial-gradient, sin SVG),
   pseudo-elemento sin nodo en el DOM (invisible para lectores de
   pantalla por definición, no necesita aria-hidden). Uso puntual en
   "Cuándo consultar" de /ginecomastia/, pedido explícito de Diego. */
.section--accent-curve { position: relative; overflow: hidden; }
.section--accent-curve::before {
  content: "";
  position: absolute;
  top: -18%;
  right: -8%;
  width: 46%;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--color-accent-soft) 0%, transparent 70%);
  opacity: 0.6;
  pointer-events: none;
  z-index: 0;
}
.section--accent-curve > .container { position: relative; z-index: 1; }

.section__head { max-width: 60ch; margin: 0 auto var(--space-4); text-align: center; }
.section__head--left { margin: 0 0 var(--space-4); text-align: left; }
.kicker {
  display: block;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-dark);
  font-weight: 700;
  margin-bottom: 0.4rem;
}
.section-lead { font-size: 1.05rem; color: var(--color-ink-soft); max-width: 68ch; }
/* A standalone .section-lead outside .section__head keeps its default
   text-align: left box even when given .text-center — text-align only
   centers the text inside the box, not the box itself within the
   container. Several interior pages (protocolo-de-seguridad,
   sobre-el-doctor) were hand-patching this per-instance with an inline
   `style="margin: 0 auto;"`; centering it here for every
   .section-lead.text-center instance removes that repeated ad-hoc fix. */
.section-lead.text-center { margin-left: auto; margin-right: auto; }

/* --------------------------------------------------------------------------
   Grid y cards
   -------------------------------------------------------------------------- */
.grid { display: grid; gap: var(--space-3); grid-template-columns: 1fr; }
@media (min-width: 680px) {
  .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .grid--3, .grid--4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
  .grid--3 { grid-template-columns: repeat(3, 1fr); }
  .grid--4 { grid-template-columns: repeat(4, 1fr); }
}

.card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
  transition: transform 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out), border-color 0.15s ease;
}
/* `!important` en transform: los 4 cards de "Cómo trabajamos" también llevan
   `.reveal` (scroll-in). `.reveal.is-visible` corre con `animation: ... forwards`
   (~L405), que deja el efecto de esa animación "vigente" sobre `transform`
   incluso después de terminar — eso gana sobre una declaración :hover normal
   sin importar su especificidad, así que sin !important aquí el lift quedaba
   silenciosamente anulado (verificado con :hover real vía CDP: boxShadow y
   border-color sí cambiaban, transform se quedaba en matrix identidad). No se
   toca `.reveal`/`reveal-in` en sí (blast radius nulo, afecta solo `.card`). */
.card:hover { transform: translateY(-4px) !important; box-shadow: var(--shadow-md); border-color: var(--color-accent); }
/* `.card--link`/`.card--photo` cards on Home ("Cómo trabajamos") and other
   pages are the WHOLE `<a>` — the sitewide `a:hover{text-decoration:underline}`
   (styles.css ~L278) inherits into every child (h3/p/span) once :hover fires,
   underlining the entire card's text instead of just showing the border/shadow
   lift above. Scoped to `.card` (the reusable component) rather than touching
   the global `a:hover` rule, so real inline text links elsewhere keep their
   underline-on-hover affordance untouched. */
.card:hover, .card:focus-visible { text-decoration: none; }
.card h3 { margin-bottom: 0.5rem; font-size: 1.15rem; }
.card p:last-child { margin-bottom: 0; }
/* Centrado (2026-08-31, refinamiento pedido por Diego): icono, título y
   texto centrados como conjunto — antes alineados a la izquierda por el
   `.card` genérico. `.card--icon` no se usa en ningún otro archivo hoy
   (confirmado por grep), así que centrar aquí no afecta otras cards
   (`.card--photo`/`.card--link`, que siguen alineadas a la izquierda). */
.card--icon { text-align: center; }
.card--icon .card__icon {
  width: 42px; height: 42px;
  display: flex; align-items: center; justify-content: center;
  color: var(--color-primary);
  margin: 0 auto var(--space-2);
}
/* The icon markup is `<div class="card__icon"><svg viewBox="0 0 24 24">…`, with
   no `width`/`height` HTML attribute on the `<svg>` itself and, until this
   rule, no CSS targeting the `<svg>` either — only its wrapper div was sized
   (42px above). A bare inline SVG with a viewBox but no concrete width/height
   (CSS or HTML) does not inherit or shrink to its parent's box; per the CSS
   "default sizing algorithm for replaced elements" it falls back to a
   default concrete size (~300×150 CSS px in every mainstream engine).
   `.card__icon` is a plain block div (no overflow: hidden), so that
   oversized SVG box was never clipped — it rendered ~7x wider than its
   42px-wide parent and bled past the card's own padding-box and, once wider
   than the card and its grid column, past the container and viewport edge
   too. That is real scrollable-overflow geometry (`document.documentElement
   .scrollWidth` grows), not something `overflow-x: hidden` on `body`
   addresses for an in-flow block element. Sizing the `<svg>` itself to fill
   its already-42px wrapper removes the oversized intrinsic box entirely.
   Affects `.card--icon` usage on protocolo-de-seguridad/ and recuperacion/
   (6 and 3 cards respectively) in all three gate viewports — this bug was
   not viewport-dependent, so it could not have been the nav/hero-only
   overflow already fixed in prior rounds. */
.card--icon .card__icon svg { width: 100%; height: 100%; display: block; }
.card--link .card__cta { display: inline-block; margin-top: 0.4rem; font-weight: 600; color: var(--color-accent-dark); }

/* "Sigue explorando" (/tratamiento/) — refinamiento pedido por Diego
   2026-08-31, elegido tras comparar 7 variantes en
   design-lab/tratamiento-explore-cards-options.html (Opción D: icono
   circular por tarjeta, se invierte a navy sólido en hover). Solo
   `.card.card--link` sin `--photo`/`--icon` (grep confirma que esta
   combinación exacta solo se usa en /tratamiento/), así que extender
   aquí no afecta las cards con foto de Home. */
.card--explore { padding: var(--space-4); }
.card--explore .card__icon {
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  margin-bottom: var(--space-2);
  transition: background 0.2s ease, color 0.2s ease;
}
.card--explore .card__icon svg { width: 20px; height: 20px; }
.card--explore:hover { border-color: var(--color-primary); }
.card--explore:hover .card__icon { background: var(--color-primary); color: #fff; }
.card--explore h3 { color: var(--color-primary); }
.card--explore p { color: var(--color-ink-soft); font-size: 0.94rem; }
.card--explore .card__cta { transition: transform 0.2s var(--ease-out), color 0.2s ease; }
.card--explore:hover .card__cta { transform: translateX(3px); color: var(--color-accent-darker); }

/* Card con fotografía/placeholder arriba (servicios, doctores, recursos) —
   protagonismo fotográfico incluso mientras la foto real no existe. */
.card--photo { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
.card--photo .card__media {
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--color-primary-soft);
}
.card--photo .card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s var(--ease-out); }
.card--photo:hover .card__media img { transform: scale(1.05); }
.card--photo .card__body { padding: var(--space-3); flex: 1; display: flex; flex-direction: column; }
.card--photo h3 { font-family: var(--font-display); }

/* Image-overlay card: dark gradient with text on top of the photo, reference
   pattern for service cards (as opposed to .card--photo's photo-then-text
   stack, used for doctor/person profiles). */
.card--overlay {
  position: relative;
  padding: 0;
  overflow: hidden;
  border: none;
  aspect-ratio: 4 / 3;
}
.card--overlay .card__media { position: absolute; inset: 0; background: var(--color-primary-soft); }
.card--overlay .card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 0.4s var(--ease-out);
}
.card--overlay::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8, 26, 48, 0) 35%, rgba(8, 26, 48, 0.92) 100%);
  pointer-events: none;
}
.card--overlay .card__body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: var(--space-3); }
.card--overlay h3 { color: #fff; font-family: var(--font-display); margin-bottom: 0.3rem; }
.card--overlay p { color: rgba(255, 255, 255, 0.85); font-size: 0.88rem; margin-bottom: 0.3rem; }
.card--overlay .card__cta { color: var(--color-on-dark-accent); font-weight: 600; }
.card--overlay:hover .card__media img { transform: scale(1.06); }

/* Doctor / person block — retirado (2026-09-01): /sobre-el-doctor/ pasó a
   usar `.split` (foto grande real + contenido), consistente con el resto
   del sitio. `.person`/`.person__*`/`.bullet-list` no tenían ningún otro
   uso (verificado por grep en todo `web/public/`) — se elimina en vez de
   dejarlo muerto. */

/* Composición de dos columnas genérica (Who We Are, service detail). Gap
   is tighter than the desktop value (--space-3, not --space-4) only in
   this stacked mobile state: the photo (its own rounded-corner, shadowed
   box via .split__media) followed by a fresh kicker+heading is the same
   visual "new block starts here" cue used to open every real section
   (kicker + h2, generous top gap) — a smaller gap keeps the photo and its
   caption text reading as one continuous "about" block on mobile instead
   of two. Desktop (900px+, side-by-side) restores the wider --space-5 gap
   below, where the columns sit next to each other and this risk doesn't
   apply. */
.split { display: grid; grid-template-columns: 1fr; gap: var(--space-3); align-items: center; }
/* CTA que sigue directo a un párrafo dentro de `.split` (2026-08-24 —
   antes esta separación la daba el margin-bottom de `.notice--compact`,
   retirado junto con el texto provisional "Credenciales en confirmación
   final" que llevaba; ver "Sobre el cirujano" en index.html). `.btn` es
   `display:inline-flex` (inline-level) — su margin-top NO colapsa con el
   margin-bottom del párrafo anterior (el colapso de márgenes solo aplica
   entre cajas de bloque), así que sin anular ese margin-bottom el hueco
   real sería 16px (párrafo) + 24px (este valor) = 40px, no los 24px ya
   aprobados. Se anula el margin-bottom del párrafo inmediatamente
   anterior al CTA y el `margin-top` de abajo queda como única fuente del
   espacio — mismo valor (var(--space-3), 24px) ya validado. Ambos
   selectores scoped a "p seguido de .btn" para no afectar el otro
   `.split` del sitio, cuyo CTA sigue a un `.rating-chip`, no a un
   párrafo. */
.split p:has(+ a.btn) { margin-bottom: 0; }
.split p + a.btn { margin-top: var(--space-3); }
.split__media {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  aspect-ratio: 4 / 3;
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
}
.split__media img { width: 100%; height: 100%; object-fit: cover; }
/* Proporción 3:2 (900×600) — pedido explícito de Diego 2026-08-31 para
   /tratamiento/: las 2 fotografías grandes de esa página ya son 3:2
   nativas (900×600 y 2400×1600), pero `.split__media` base fuerza 4/3
   para el resto del sitio — mostrarlas en 4/3 las recortaba de forma
   arbitraria en vez de respetar su encuadre real. Modificador opt-in,
   sin tocar `.split__media` base (Home/ginecomastia/protocolo-de-
   seguridad/recuperación siguen en 4/3 sin cambios). */
.split__media--ratio32 { aspect-ratio: 3 / 2; }
/* `.split__media` envolviendo un `.yt-facade` (2026-09-02, /sobre-el-
   doctor/, sección "Clínica Bellaforma" — mismo video ya usado en Home):
   se mantiene la clase `.split__media` únicamente porque
   `.split--reverse .split__media { order: 1 }` depende de ese selector
   para mandar el media a la derecha — sin esto, el video volvería al
   lado izquierdo. El propio `.yt-facade` ya trae su aspect-ratio
   (16/9)/radius/sombra/overflow — este modificador solo neutraliza los
   de `.split__media` base (pensados para una sola `<img>`) para no
   duplicar el marco. */
.split__media--video { aspect-ratio: auto; overflow: visible; box-shadow: none; background: none; }
.split__media-caption {
  position: absolute; left: var(--space-2); bottom: var(--space-2);
  font-size: 0.72rem; color: rgba(255,255,255,0.8);
  background: rgba(8, 23, 18, 0.55);
  padding: 0.3rem 0.6rem; border-radius: var(--radius-full);
}
.split--reverse .split__media { order: -1; }
@media (min-width: 900px) {
  .split { grid-template-columns: 1fr 1fr; gap: var(--space-5); }
  .split--reverse .split__media { order: 1; }
}

/* Variante asimétrica — la foto gana presencia (~46.5%/53.5% en vez de
   50/50) para bloques donde la imagen debe protagonizar más que un split
   genérico (referencia gráfica aprobada por Diego 2026-08-31 para la
   intro de /ginecomastia/). Modificador opt-in — no se toca `.split`
   base, así que Home/tratamiento/protocolo-de-seguridad (que siguen
   usando `.split` a secas) quedan intactos. */
@media (min-width: 900px) {
  .split--intro { grid-template-columns: 1fr 1.15fr; }
}

/* Bloque principal de /sobre-el-doctor/ (referencia visual aprobada por
   Diego, 2026-09-01): la foto necesita presencia real (~40-45% del
   ancho) en vez de compartir el 50/50 genérico, y un recorte más
   vertical/retrato que el 4:3 base de `.split__media` — la referencia
   muestra una fotografía sustancialmente más alta que ancha, no un
   paisaje. Ambos modificadores opt-in, no tocan `.split`/`.split__media`
   base (el resto del sitio sigue igual). */
@media (min-width: 900px) {
  .split--doctor-intro { grid-template-columns: 0.82fr 1fr; }
}
.split__media--doctor { aspect-ratio: 4 / 5; }
.split__media--doctor img { object-position: center 22%; }

/* Puntos decorativos cyan junto a la imagen de intro — puramente
   ornamental (aria-hidden), inspirado en el acento de esquina de la
   referencia. Vive DENTRO de `.split__media` (que ya recorta con
   overflow:hidden) para no arriesgar overflow horizontal en ningún
   viewport: nunca se posiciona fuera de la caja de la imagen. */
.split__media__dots {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  width: 46px;
  height: 46px;
  background-image: radial-gradient(rgba(255, 255, 255, 0.85) 1.6px, transparent 1.8px);
  background-size: 11px 11px;
  opacity: 0.55;
  pointer-events: none;
  z-index: 1;
}

/* Fila de micro-features bajo la intro — 3 conceptos ya validados
   (ver CONTENT_SOURCES.md), presentados como icono+título+texto corto
   horizontal, NUNCA como 3 cards grandes (pedido explícito). */
.micro-features {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: var(--space-3) 0 0;
  border-top: 1px solid var(--color-border);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}
@media (min-width: 560px) {
  .micro-features { grid-template-columns: repeat(3, 1fr); gap: var(--space-2); }
}
.micro-features__icon {
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  margin-bottom: 0.5rem;
}
.micro-features__icon svg { width: 20px; height: 20px; }
.micro-features li strong { display: block; color: var(--color-primary); font-size: 0.94rem; margin-bottom: 0.15rem; }
/* BUG real encontrado en QA de esta ronda (2026-08-31): el selector
   anterior era `.micro-features li span` (sin `>` ni `:last-child`),
   que también matcheaba `.micro-features__icon` — ambos son `<span>`
   descendientes de `<li>`. Por especificidad (0,1,2 vs 0,1,0 del icono)
   ese `display:block` GANABA y anulaba silenciosamente el
   `display:inline-flex` del icono, dejando el SVG pegado en la esquina
   superior-izquierda de su círculo en vez de centrado — la causa real
   de "los íconos están desalineados" que reportó Diego, confirmado con
   getBoundingClientRect() vía CDP (display computado era "block", el
   svg estaba en offset 0,0). `> span:last-child` apunta solo al span de
   descripción (último hijo real de cada `<li>`), igual que el patrón ya
   usado en `.tech-checklist li > span > span:last-child`. */
.micro-features li > span:last-child { display: block; font-size: 0.84rem; color: var(--color-ink-soft); line-height: 1.4; }

/* Franja informativa discreta — una sola línea con icono (i), para una
   aclaración corta sin abrir otro panel grande (causas y tipos). */
.info-strip {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  max-width: 720px;
  margin: var(--space-4) auto 0;
  padding: 0.7rem var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  color: var(--color-ink-soft);
  font-size: 0.9rem;
}
.info-strip__icon {
  flex-shrink: 0;
  width: 26px; height: 26px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
}
.info-strip__icon svg { width: 15px; height: 15px; }

/* --------------------------------------------------------------------------
   "Por qué elegirnos" (Home) — variante de 3 bloques (2026-08-25, pedido
   explícito de Diego: "mucho más aire", CTA claramente separado de los
   badges, y mostrar las DOS sedes reales en vez de solo Los Olivos).
   Nueva variante `.split--locations` — NO se toca `.split`/`.split--reverse`
   base (siguen usados tal cual en Sobre el cirujano, Tratamiento,
   Ginecomastia).

   2026-09-02 (2) — Rehecho como grid 2×2 real (referencia visual exacta
   provista por Diego), reemplazando el intento anterior (kicker+H2 como
   hermano FUERA del grid): ese enfoque alineaba el video con el párrafo,
   pero al vivir fuera del grid el H2 ya no estaba contenido al ancho de
   la columna izquierda — se extendía a todo el `.container` y quedaba en
   una sola línea, en vez de las 2 líneas de la referencia. Ahora
   `.split__head` es un hijo real del grid (`grid-area: head`, fila 1,
   SOLO columna 1) — su ancho real pasa a ser el de la columna izquierda,
   por lo que el H2 vuelve a partirse en 2 líneas de forma natural (sin
   `max-width` inventado, sin `<br>`). `.split__actions` (el CTA) se
   fusionó dentro de `.split__intro` (después del checklist) en vez de
   ser una 3ª fila propia — así el layout real queda en exactamente 2
   filas × 2 columnas: "head ." / "intro media", con `.split__media`
   ocupando solo la fila 2 y `align-self:start`, por lo que su borde
   superior queda exactamente al nivel del borde superior de
   `.split__intro` (el párrafo) — mismo mecanismo de grid que antes, sin
   transform ni offset manual. En mobile (una sola columna) el orden real
   de DOM ya es head → intro(párrafo+argumentos+CTA) → media, que es
   exactamente el orden visual pedido (eyebrow/H2/párrafo/beneficios/
   CTA/video/sedes) sin ningún truco de `order`. */
.split__head .kicker { margin-bottom: var(--space-2); }
.split__head h2 { margin-bottom: 0; }
.split--locations { gap: var(--space-5); }
.split--locations .section-lead { margin-bottom: var(--space-4); }
/* Gap de los 3 argumentos: space-4 (32px) -> space-3 (24px) — pedido
   explícito de Diego 2026-08-26 tras retirar los badges (SUSALUD/MINSA/
   Municipalidad): "pega ligeramente los bloques". Sigue siendo mayor que
   el 0.4rem por defecto de `.tech-checklist li` — no vuelve al estado
   "pegado" original, solo un poco más compacto que 32px. */
.split--locations .tech-checklist { gap: var(--space-3); margin-bottom: 0; }
/* `.split__actions` ahora vive DENTRO de `.split__intro` (después del
   checklist) en vez de ser su propia fila de grid — necesita su propio
   margin-top para reproducir el mismo aire (--space-5) que antes daba el
   `gap` del grid entre la fila de argumentos y la del CTA. */
.split--locations .split__actions { margin-top: var(--space-5); }

@media (min-width: 900px) {
  .split--locations {
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "head ." "intro media";
    align-items: start;
  }
  .split--locations .split__head { grid-area: head; }
  .split--locations .split__intro { grid-area: intro; }
  /* `align-self:start` (no `center`): el borde superior del video debe
     coincidir con el borde superior del párrafo, no centrarse dentro
     del alto combinado de intro. Verificado en vivo con
     getBoundingClientRect(): video.top === lead.top en 1440/1280/1024. */
  .split--locations .split__media { grid-area: media; align-self: start; }
}
/* Composición única de ambas sedes (2026-08-25, sustituye las 2 fotos
   apiladas de la ronda anterior — Diego proveyó `sedes.png`, ya compuesta,
   con ambos rótulos "SEDE: ..." incorporados en el propio píxel; no se
   edita ni se le agrega overlay). `.split__media` base ya trae
   aspect-ratio/radius/shadow/overflow para una sola imagen — aquí solo se
   ajusta el aspect-ratio real del archivo (627×638, casi cuadrado) en vez
   del 4/3 genérico, y se limita el ancho para que no domine la sección
   ("tamaño elegante y proporcional", pedido explícito). */
.split__media--sedes {
  aspect-ratio: 627 / 638;
  /* Tope al ancho NATIVO real del archivo (627px) en todos los anchos —
     nunca "100%"/sin tope: en columnas más anchas que el archivo fuente,
     forzar el <img> a llenar la caja lo estiraría por encima de su
     resolución real (upscaling visual, aunque el archivo en sí no cambie),
     justo lo que Diego pidió evitar explícitamente. */
  max-width: 480px;
  margin: 0 auto;
  /* v4 (2026-08-25) trae una franja real transparente entre ambas fotos
     (alpha real, confirmado por Diego y verificado con `sips -g hasAlpha`
     → yes). Sin esto, se ve el fondo de `.split__media` base (degradado
     navy pensado solo como placeholder de precarga) asomando por la
     franja. Pedido explícito de Diego: la franja debe mostrar el color de
     fondo REAL de la web en este punto, no blanco — esta sección es
     `<section class="section">` (sin `--alt`), así que hereda el fondo de
     `body` (`--color-bg`), nunca su propio blanco. */
  background: var(--color-bg);
}
@media (min-width: 900px) {
  .split__media--sedes { max-width: min(100%, 627px); margin: 0; }
}

/* Reemplazo del bloque multimedia de "Por qué elegirnos" (2026-09-02,
   pedido explícito de Diego): video institucional arriba a todo el
   ancho + las 2 sedes debajo en fila, en vez de la composición única
   `.split__media--sedes`. Mismo `grid-area: media`/`align-self: center`
   heredado de `.split__media` (no se toca esa regla base) — este
   modificador solo neutraliza las propiedades pensadas para UNA sola
   imagen (aspect-ratio/overflow/shadow/fondo), porque ahora el
   contenido interno (`.yt-facade` + `.sedes-duo`) trae su propio
   radius/shadow por elemento. Mismo tope de ancho (~480px) que la
   composición anterior, para conservar "mismo ancho, altura
   comparable" sin agrandar la sección. */
.split__media--sedes-video {
  aspect-ratio: auto;
  overflow: visible;
  box-shadow: none;
  background: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 480px;
  margin: 0 auto;
}
@media (min-width: 900px) {
  .split__media--sedes-video { max-width: min(100%, 480px); margin: 0; }
}

/* Fila de 2 fotografías (sedes) debajo del video — mismo tratamiento
   visual en ambas: aspect-ratio, radius, sombra. Botones reales
   (accesibles, focus-visible nativo) que abren `.image-modal`
   (definido más abajo) con la fotografía completa, sin el recorte del
   thumbnail. */
.sedes-duo { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.sedes-duo__item {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  border: 0;
  padding: 0;
  cursor: pointer;
  background: var(--color-primary-soft);
}
.sedes-duo__item img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.3s var(--ease-out); }
.sedes-duo__item:hover img, .sedes-duo__item:focus-visible img { transform: scale(1.05); }
.sedes-duo__item:focus-visible { outline: 2px solid var(--color-accent-darker); outline-offset: 2px; }
.sedes-duo__label {
  position: absolute;
  left: var(--space-2); bottom: var(--space-2);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  background: rgba(8, 23, 46, 0.65);
  padding: 0.3rem 0.65rem;
  border-radius: var(--radius-full);
  z-index: 2;
}

/* Lightbox de imagen — componente nuevo, paralelo a `.video-modal`
   (mismo patrón de modal único compartido, mismo fix de cierre real
   `event.target.closest('[data-modal-close]')` desde el inicio, ver
   `main.js`), pero independiente: no reutiliza el modal de video (este
   nunca lleva iframe, solo `<img>`). */
.image-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: var(--space-3); }
.image-modal[hidden] { display: none; }
.image-modal__backdrop { position: absolute; inset: 0; background: rgba(9, 26, 46, 0.86); }
.image-modal__dialog { position: relative; max-width: min(92vw, 1100px); max-height: 90vh; line-height: 0; }
.image-modal__dialog img { display: block; max-width: min(92vw, 1100px); max-height: 90vh; width: auto; height: auto; border-radius: var(--radius-lg); box-shadow: var(--shadow-md); }
.image-modal__close {
  position: absolute;
  top: -16px; right: -16px;
  z-index: 2;
  width: 42px; height: 42px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #fff;
  color: var(--color-primary);
  border: 0;
  box-shadow: var(--shadow-md);
  cursor: pointer;
}
.image-modal__close svg { width: 18px; height: 18px; }
.image-modal__close:hover { background: var(--color-accent); color: #fff; }
.image-modal__close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
body.image-modal-open { overflow: hidden; }
@media (max-width: 480px) {
  .image-modal__close { top: -42px; right: 0; }
}

/* --------------------------------------------------------------------------
   Técnica / diferenciador
   -------------------------------------------------------------------------- */
.tech-section {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
  align-items: stretch;
}
.tech-checklist { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: grid; gap: var(--space-3); }
.tech-checklist li { display: flex; align-items: flex-start; gap: 0.9rem; margin-bottom: 0; }
.tech-checklist__icon {
  flex-shrink: 0;
  width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius);
  background: var(--color-primary-soft);
  color: var(--color-primary);
}
.tech-checklist__icon svg { width: 20px; height: 20px; }
.tech-checklist li > span > span:last-child {
  display: block;
  font-size: 0.92rem;
  color: var(--color-ink-soft);
  margin-top: 0.15rem;
}
.tech-checklist li strong { color: var(--color-primary); }

/* --------------------------------------------------------------------------
   /sobre-el-doctor/ — reconstrucción visual (2026-09-01), fidelidad a
   referencia aprobada por Diego. Todo lo de aquí abajo es nuevo y
   opt-in: ninguna regla base compartida se modifica.
   -------------------------------------------------------------------------- */

/* Credenciales del bloque principal — checkmark + texto, 2 columnas en
   desktop (la referencia las muestra en grid compacto, no como lista de
   párrafos con badge grande tipo `.tech-checklist`). */
.doctor-facts {
  list-style: none; margin: var(--space-3) 0; padding: 0;
  display: grid; grid-template-columns: 1fr; gap: 0.85rem 1.5rem;
}
@media (min-width: 560px) {
  .doctor-facts { grid-template-columns: 1fr 1fr; }
}
.doctor-facts li { display: flex; align-items: flex-start; gap: 0.55rem; }
.doctor-facts li svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 0.15rem; color: var(--color-accent-dark); }
.doctor-facts li strong { display: block; font-size: 0.92rem; font-weight: 600; color: var(--color-ink); line-height: 1.35; }
.doctor-facts li span span { display: block; font-size: 0.82rem; color: var(--color-ink-soft); margin-top: 0.1rem; }

/* Lista compacta de hechos (icono + etiqueta + valor) — a diferencia de
   `.doctor-facts`, siempre en una sola columna, sin breakpoint propio por
   viewport. Pensada para vivir dentro de la columna angosta de un
   `.split` (donde un grid de 2 columnas activado por ancho de VIEWPORT,
   no de contenedor, se vería apretado dentro de una columna que en
   desktop mide ~45% del `.container`). Usada en /recuperacion/: faja y
   compresión, inflamación/molestias, controles postoperatorios. */
.fact-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: grid; gap: 0.9rem; }
.fact-list li { display: flex; align-items: flex-start; gap: 0.6rem; }
.fact-list li svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 0.15rem; color: var(--color-accent-dark); }
.fact-list li strong { display: block; font-size: 0.92rem; font-weight: 600; color: var(--color-ink); line-height: 1.35; }
.fact-list li span { display: block; font-size: 0.85rem; color: var(--color-ink-soft); margin-top: 0.15rem; }

/* Divisor pequeño centrado bajo un `.section__head`, mismo patrón ya
   usado en la Opción A de /casos/ (`.interpret-panel__rule`) — aquí como
   clase genérica reutilizable dentro de `.section__head`. */
.section__rule {
  display: block;
  width: 48px; height: 2px;
  background: var(--color-accent);
  margin: 0.9rem auto 1rem;
  border-radius: 2px;
}

/* "Enfoque médico y filosofía de atención" — 3 principios, icono sobre
   título, centrado. Ligero a propósito: sin card, sin sombra, sin
   fondo propio (vive sobre el fondo `--alt` de la sección). */
.principles-grid {
  list-style: none; margin: var(--space-5) 0 0; padding: 0;
  display: grid; grid-template-columns: 1fr; gap: var(--space-4);
  text-align: center;
}
@media (min-width: 760px) {
  .principles-grid { grid-template-columns: repeat(3, 1fr); gap: var(--space-3); }
}
.principles-grid__icon {
  width: 52px; height: 52px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  margin-bottom: var(--space-2);
}
.principles-grid__icon svg { width: 24px; height: 24px; }
.principles-grid h3 { font-size: 1.05rem; margin-bottom: 0.35rem; }
.principles-grid p { font-size: 0.9rem; color: var(--color-ink-soft); max-width: 32ch; margin: 0 auto; }

/* Panel tintado alrededor del video de presentación — la referencia lo
   muestra dentro de una superficie propia (no directamente sobre el
   fondo de la sección), dándole más peso visual sin necesitar un video
   más grande. */
.video-panel {
  background: var(--color-bg-alt);
  border-radius: var(--radius-xl);
  padding: var(--space-4);
}
@media (min-width: 900px) {
  .video-panel { padding: var(--space-5); }
}

/* Indicadores pequeños bajo el texto de "Clínica Bellaforma" — icono +
   una palabra/frase corta, fila compacta, nunca cards. */
.clinic-indicators {
  list-style: none; margin: var(--space-3) 0 var(--space-3); padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.6rem 1.4rem;
}
.clinic-indicators li {
  display: flex; align-items: center; gap: 0.45rem;
  font-size: 0.86rem; font-weight: 600; color: var(--color-ink-soft);
}
.clinic-indicators li svg { flex-shrink: 0; width: 18px; height: 18px; color: var(--color-accent-dark); }

.tech-panel {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.6rem;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #fff;
  box-shadow: var(--shadow-md);
}
.tech-panel__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.12);
  color: var(--color-on-dark-accent);
}
.tech-panel__icon svg { width: 24px; height: 24px; }
.tech-panel__label { font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-on-dark-faint); }
.tech-panel__value { font-family: var(--font-display); font-size: 1.2rem; line-height: 1.35; }

/* Variante ancha en fila — para callouts de seguridad que necesitan más
   presencia editorial que el bloque centrado/angosto por defecto (p. ej.
   "Señales para contactar al equipo médico" en /recuperacion/). Requiere
   envolver label+value en `.tech-panel__body` en el HTML; el markup sin
   ese wrapper (icon/label/value como 3 hijos directos, ya usado en
   /protocolo-de-seguridad/) sigue funcionando igual sin esta clase. */
.tech-panel--banner .tech-panel__body { display: flex; flex-direction: column; gap: 0.3rem; }
@media (min-width: 760px) {
  .tech-panel--banner { flex-direction: row; align-items: center; gap: var(--space-4); }
  .tech-panel--banner .tech-panel__icon { flex-shrink: 0; }
}

@media (min-width: 900px) {
  .tech-section { grid-template-columns: 1.15fr 0.85fr; }
}

/* --------------------------------------------------------------------------
   SISTEMA DE PÁGINAS INTERIORES (2026-08-30 → 2026-08-30 ronda 2).
   Estrenado en /ginecomastia/, generalizado esta ronda para /tratamiento/,
   /recuperacion/, /protocolo-de-seguridad/ — ver
   /design-lab/internal-pages-system.html para el style guide visual con
   contenido real. Referencia de disciplina de diagramación: Mediz Hospital
   (GoodLayers, demo pública) — SOLO principios de composición/jerarquía/
   ritmo, nunca código/CSS/assets/branding copiados. La identidad visual
   (navy/cian, tipografía, botones, cards, tono) sigue siendo 100% la de
   Ginecomastia.com/Home, ya aprobada. Componentes de este bloque:
   `.info-panel` (checklist + nota de cierre integrada en un solo panel,
   reemplaza cajas-de-icono genéricas repetidas), `.compare-clean`
   (comparación de 2 conceptos, sin cajas altas ni círculo "VS" flotante),
   `.section-divider` (regla fina con acento de color para separar
   sub-temas dentro de una sección larga sin abrir una sección nueva —
   inspirado en las barras de acento que usa Mediz entre bloques densos de
   texto). Todo reutiliza tokens/componentes ya existentes (kicker,
   section-lead, btn, split, tech-checklist, faq-item, tech-panel) —
   ninguna pieza nueva introduce un lenguaje visual paralelo al del resto
   del sitio. Deliberadamente NO se copió el "stat bar" de Mediz (87
   Doctors / 12,596+ Happy Patients...) — el proyecto no tiene cifras de
   pacientes/años verificadas y publicables (ver `01_PROJECT_CONTEXT.md` /
   `CONTENT_SOURCES.md`); un stat bar con números inventados sería
   exactamente el tipo de afirmación no verificable que este proyecto
   rechaza explícitamente. */

/* Intro-tipo-split y "Cuándo consultar" reutilizan `.split`/`.split__media`
   tal cual — sin clase nueva, es el mismo componente que ya usa "Sobre el
   cirujano" en Home y "Dónde se opera" en /tratamiento/. */

/* Panel de información — checklist + nota de cierre integrada en el MISMO
   bloque (nunca 2 piezas sueltas). Reutiliza `.tech-checklist` (ya usado
   en "Por qué elegirnos" de Home) en vez de reinventar un componente de
   lista con icono. Genérico — no es solo para "causas": también sirve
   para los elementos de un protocolo, los puntos de una recuperación, etc. */
.info-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
}
.info-panel .tech-checklist { margin-bottom: var(--space-3); }
.info-panel__note {
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-ink-soft);
  font-size: 0.95rem;
}
.info-panel__note strong { color: var(--color-primary); }
@media (min-width: 760px) {
  .info-panel--cols .tech-checklist { grid-template-columns: repeat(2, 1fr); gap: var(--space-3) var(--space-4); }
}

/* Divisor de sección — regla fina con acento de color, para separar
   sub-temas dentro de una sección larga de una sola columna sin necesidad
   de abrir una sección/fondo nuevo. Uso deliberadamente moderado (nunca
   como decoración por defecto). */
.section-divider {
  height: 2px;
  border: 0;
  margin: var(--space-4) 0;
  background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-border) 40%);
}

/* Pseudoginecomastia vs. ginecomastia verdadera — un panel único con 2
   columnas separadas por una regla fina. La frase de cierre vive DENTRO
   del mismo panel, como una franja final, en vez de un párrafo aparte
   fuera de la caja.
   2026-08-31: Diego aprobó (referencia gráfica) reintroducir un círculo
   "VS" — la ronda anterior lo había retirado por "flotante y forzado";
   esta vez va perfectamente centrado sobre la propia línea divisoria,
   como tercer hijo real del grid (`grid-column:1/3`), nunca con
   position:absolute/translate — así nunca puede "chocar" con los bordes
   en ningún viewport, incluida la pila móvil. */
.compare-clean {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface);
}
.compare-clean__row { display: grid; grid-template-columns: 1fr; }
.compare-clean__col { padding: var(--space-3) var(--space-4); }
.compare-clean__col:last-child { border-top: 1px solid var(--color-border); }
.compare-clean__vs {
  justify-self: center;
  align-self: center;
  display: flex; align-items: center; justify-content: center;
  width: 34px; height: 34px;
  margin: calc(var(--space-2) * -1) auto 0;
  border-radius: 50%;
  background: var(--color-accent);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  /* Anillo blanco + sombra suave (en vez de solo box-shadow difuso) —
     ancla visual única del componente ahora que se retiraron los
     iconos de torso por pedido de Diego ("el icono actual no
     convence"); el anillo separa la esfera de la línea divisoria con
     más pulcritud que un box-shadow a secas. */
  box-shadow: 0 0 0 4px var(--color-surface), var(--shadow-sm);
  position: relative;
  z-index: 1;
}
@media (min-width: 640px) {
  .compare-clean__row { grid-template-columns: 1fr 1fr; }
  .compare-clean__col:first-child { grid-column: 1; grid-row: 1; border-right: 1px solid var(--color-border); }
  .compare-clean__col:last-child { grid-column: 2; grid-row: 1; border-top: 0; }
  .compare-clean__vs { grid-column: 1 / 3; grid-row: 1; margin: 0; }
}
.compare-clean__label { display: block; font-size: 0.78rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; color: var(--color-accent-dark); margin-bottom: 0.5rem; }
.compare-clean__def { font-size: 0.98rem; color: var(--color-ink); line-height: 1.5; margin: 0; }
.compare-clean__resolve {
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
  background: var(--color-bg-alt);
  color: var(--color-ink-soft);
  font-size: 0.92rem;
  text-align: center;
}

/* Video en layout split (sin cambios respecto a la ronda anterior — esta
   pieza ya funcionaba bien, según Diego). Reutiliza `.split` directamente
   con `.yt-facade` como hijo — sin envolverlo en `.split__media`, porque
   `.yt-facade` ya trae su propio aspect-ratio (16:9), radius y sombra;
   anidarlo dentro de `.split__media` (aspect-ratio 4:3 propio) generaría
   dos sistemas de proporción/recorte compitiendo entre sí. */
.video-split .yt-facade { box-shadow: var(--shadow-md); }

/* Reutilización de la foto real de sedes (`sedes-2026-08-25/...-v4.webp`,
   ya aprobada, usada en Home) dentro de /protocolo-de-seguridad/ — mismo
   archivo con franja transparente real, mismo tratamiento que
   `.split__media--sedes` de Home, pero esta sección vive en un
   `.section--alt` (fondo `--color-bg-alt`), no en el `.section` base
   (`--color-bg`) para el que se calibró originalmente. Sin este override
   el degradado navy de placeholder de `.split__media` asomaría por la
   franja transparente. */
.split__media--sedes-alt {
  aspect-ratio: 627 / 638;
  max-width: 480px;
  margin: 0 auto;
  background: var(--color-bg-alt);
}
@media (min-width: 900px) {
  .split__media--sedes-alt { max-width: min(100%, 627px); margin: 0; }
}

/* --------------------------------------------------------------------------
   Treatment path — pasos numerados con más presencia visual
   -------------------------------------------------------------------------- */
.stepper {
  display: grid;
  gap: var(--space-4);
  counter-reset: step;
}
.stepper__item {
  position: relative;
  padding-left: 4.6rem;
}
.stepper__item:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 1.7rem;
  top: 3.4rem;
  bottom: -2rem;
  width: 2px;
  background: var(--color-border);
}
.stepper__num {
  position: absolute;
  left: 0;
  top: 0;
  width: 3.4rem;
  height: 3.4rem;
  border-radius: 50%;
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #fff;
  font-family: var(--font-display);
  font-size: 1.35rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  box-shadow: var(--shadow-sm);
  border: 2px solid var(--color-accent);
}
.stepper__item h3 { margin-bottom: 0.3rem; font-size: 1.1rem; }
.stepper__item p { font-size: 0.95rem; color: var(--color-ink-soft); margin-bottom: 0; }

@media (min-width: 900px) {
  .stepper { grid-template-columns: repeat(4, 1fr); gap: var(--space-3); }
  .stepper--3 { grid-template-columns: repeat(3, 1fr); }
  .stepper__item { padding-left: 0; padding-top: 4.6rem; }
  .stepper__num { left: 0; top: 0; }
  .stepper__item:not(:last-child)::before {
    left: 3.4rem;
    right: -1.5rem;
    top: 1.7rem;
    bottom: auto;
    width: auto;
    height: 2px;
  }
}

/* Recorrido del paciente en Home ("Cómo es el proceso", 2026-08-29):
   refinamiento puramente visual, scoped a `.stepper--journey` para NO
   afectar el `.stepper--3` compartido de /tratamiento/ ("Qué esperar el
   día de la cirugía"). Línea conectora con degradado navy→cian sutil (en
   vez del gris neutro genérico) y círculo de número con menos peso
   (sombra/borde reducidos) — pedido explícito de Diego: "el foco debe ser
   la secuencia, no los círculos". Estructura/HTML del stepper sin cambios,
   ya cumplía el patrón de timeline conectado pedido (número + línea +
   vertical en móvil / horizontal en escritorio). */
.stepper--journey .stepper__item:not(:last-child)::before {
  background: linear-gradient(90deg, var(--color-accent) 0%, var(--color-primary) 100%);
  opacity: 0.35;
}
.stepper--journey .stepper__num {
  box-shadow: none;
  border-width: 1px;
}
.stepper--journey .stepper__item p a { font-weight: 600; }

/* "Journey vertical contemporáneo" — Opción 05 del laboratorio
   design-lab/recuperacion-evolucion-opciones.html, portada tal cual a
   producción (mismos nombres de partes, solo prefijo `.result-journey`
   en vez de `.ev5`) para "Evolución de resultados" en /recuperacion/.
   Reemplaza `.stage-progression` (retirada, sin uso en ninguna otra
   página). Un único eje vertical continuo (no segmentado por ítem, a
   diferencia de `.stepper` que ya usa "Primeras horas" en la misma
   página), marcadores cuadrados —nunca círculos—, mismo layout en los 6
   anchos de referencia (no cambia de vertical a horizontal). */
.result-journey { padding-top: 8px; max-width: 640px; margin: var(--space-4) auto 0; }
.result-journey__axis { position: relative; padding-left: 28px; }
.result-journey__axis::before {
  content: ""; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 2px;
  background: var(--color-border);
}
.result-journey__item { position: relative; padding-bottom: 40px; }
.result-journey__item:last-child { padding-bottom: 0; }
.result-journey__item::before {
  content: ""; position: absolute; left: -28px; top: 4px; width: 10px; height: 10px;
  background: var(--color-accent); border-radius: 2px;
}
.result-journey__label { display: block; font-size: 1.05rem; font-weight: 700; color: var(--color-primary); margin-bottom: 0.4rem; }
.result-journey__item p { margin: 0; color: var(--color-ink-soft); font-size: 0.95rem; }
.result-journey__note { position: relative; margin-top: 8px; }
.result-journey__note::before {
  content: ""; position: absolute; left: -28px; top: 6px; width: 10px; height: 10px;
  background: var(--color-primary); border-radius: 50%;
}
.result-journey__note p { margin: 0; font-style: italic; color: var(--color-ink-soft); font-size: 0.92rem; }

/* --------------------------------------------------------------------------
   Antes / después — comparador nativo accesible
   -------------------------------------------------------------------------- */
.compare-slider {
  /* `<figure>` trae margin UA por defecto (1em 40px) — sin resetear,
     desalineaba el borde izquierdo de la imagen ~40px respecto al pie
     `.case-card__foot` (mismo padre, sin margen). Confirmado con
     getBoundingClientRect() real en staging antes de este fix. */
  margin: 0;
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  aspect-ratio: 4 / 3;
  background: var(--color-primary-soft);
  isolation: isolate;
}
.compare-slider__figure { position: absolute; inset: 0; margin: 0; }
.compare-slider__figure img { width: 100%; height: 100%; object-fit: cover; }
.compare-slider__figure--after { clip-path: inset(0 0 0 var(--compare-pos, 50%)); }
.compare-slider__label {
  position: absolute;
  top: var(--space-2);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(8, 23, 18, 0.6);
  padding: 0.3rem 0.65rem;
  border-radius: var(--radius-full);
  z-index: 2;
}
.compare-slider__label--before { left: var(--space-2); }
.compare-slider__label--after { right: var(--space-2); }
.compare-slider__handle {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--compare-pos, 50%);
  width: 3px;
  background: #fff;
  box-shadow: 0 0 0 1px rgba(8,26,48,0.15);
  transform: translateX(-50%);
  pointer-events: none;
  z-index: 2;
  color: var(--color-primary);
}
/* Círculo base — movido de `::after` a `::before` para que pinte DETRÁS
   del ícono real (`.compare-slider__handle svg`, un hijo real del DOM,
   no otro pseudo-elemento): el orden de pintado nativo es
   ::before → hijos reales → ::after, así que el círculo necesitaba
   quedar en `::before` para no tapar el ícono. */
.compare-slider__handle::before {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: #fff;
  box-shadow: var(--shadow-md);
  transform: translate(-50%, -50%);
  transition: transform 0.2s var(--ease-out), box-shadow 0.2s var(--ease-out);
}
/* Ícono de flechas ⟷ dentro del círculo — pedido explícito de Diego
   2026-09-01: el círculo solo no comunicaba que el control se arrastra
   horizontalmente. SVG real (no mask/CSS-icon) para seguir el lenguaje
   de iconografía ya establecido en el proyecto; `currentColor` toma el
   `color` del `.compare-slider__handle` de arriba, así que el estado
   hover/focus de abajo solo necesita cambiar una variable, no otro SVG. */
.compare-slider__handle svg {
  position: absolute;
  top: 50%; left: 50%;
  width: 18px; height: 18px;
  transform: translate(-50%, -50%);
  transition: color 0.2s ease;
}
/* Estados de interacción — el círculo/ícono son decorativos
   (aria-hidden, pointer-events:none); el elemento real interactivo es
   `.compare-slider__input`, que en el DOM va DESPUÉS del handle, así
   que no se puede usar `~`/`+` para reaccionar desde un hermano
   anterior — `:has()` (soportado en navegadores modernos, ya asumido
   como base de este proyecto) resuelve esto sin JS adicional y sin
   reordenar el DOM. */
.compare-slider:has(.compare-slider__input:hover) .compare-slider__handle,
.compare-slider:has(.compare-slider__input:focus-visible) .compare-slider__handle {
  color: var(--color-accent-dark);
}
.compare-slider:has(.compare-slider__input:hover) .compare-slider__handle::before,
.compare-slider:has(.compare-slider__input:focus-visible) .compare-slider__handle::before {
  transform: translate(-50%, -50%) scale(1.08);
  box-shadow: var(--shadow-lg);
}
.compare-slider:has(.compare-slider__input:active) .compare-slider__handle {
  color: var(--color-accent-darker);
}
.compare-slider:has(.compare-slider__input:active) .compare-slider__handle::before {
  transform: translate(-50%, -50%) scale(0.96);
}
.compare-slider__input {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0;
  appearance: none;
  background: transparent;
  cursor: ew-resize;
  z-index: 3;
}
.compare-slider__input::-webkit-slider-thumb {
  appearance: none;
  width: 38px; height: 38px;
  background: transparent;
}
.compare-slider__input::-moz-range-thumb {
  width: 38px; height: 38px;
  background: transparent;
  border: none;
}
.compare-slider__input::-moz-range-track { background: transparent; }
.compare-slider__caption {
  margin-top: var(--space-2);
  font-size: 0.85rem;
  color: var(--color-ink-soft);
  text-align: center;
}

/* --------------------------------------------------------------------------
   Galería pendiente — estado de espera diseñado, no un placeholder roto.
   Corrección (2026-08-19, HI-20260819-0001, hallazgo crítico #4): Casos
   mostraba 4 `.compare-slider` idénticos con borde punteado y un
   `placeholder-case.svg` — visualmente indistinguible de 4 imágenes
   rotas, no de "contenido en camino" (Playbook §4). Se reemplaza por un
   único panel editorial: ícono real (no genérico-roto), mensaje y CTA,
   nunca un before/after ficticio con stock (regla dura del proyecto).
   -------------------------------------------------------------------------- */
.gallery-pending {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-3);
  border-radius: var(--radius-lg);
  background: linear-gradient(155deg, var(--color-primary-soft) 0%, var(--color-bg-alt) 100%);
  border: 1px solid var(--color-border);
}
.gallery-pending__icon {
  width: 64px; height: 64px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-accent-dark);
  box-shadow: var(--shadow-sm);
}
.gallery-pending__icon svg { width: 30px; height: 30px; }
.gallery-pending h2 { margin-bottom: 0.2rem; }
.gallery-pending p { max-width: 52ch; color: var(--color-ink-soft); margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Testimonios
   -------------------------------------------------------------------------- */
.testimonial-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.testimonial-card__stars { color: var(--color-accent-dark); display: inline-flex; gap: 0.1rem; margin-bottom: 0.6rem; }
.testimonial-card__stars svg { width: 15px; height: 15px; }
.testimonial-card__quote { font-style: italic; color: var(--color-ink); margin-bottom: 0.7rem; }
.testimonial-card__meta { font-size: 0.85rem; color: var(--color-ink-soft); display: flex; align-items: center; gap: 0.4rem; }
.testimonial-card__source {
  display: inline-flex; align-items: center; gap: 0.3rem;
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--color-accent-dark);
  background: var(--color-accent-soft);
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-full);
}
.testimonial-card--pending {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  gap: 0.5rem;
  color: var(--color-ink-soft);
  border-style: dashed;
}
.testimonial-card--pending::before {
  content: "\201C";
  font-family: var(--font-display);
  font-size: 2.6rem;
  line-height: 1;
  color: var(--color-accent);
  opacity: 0.55;
}

/* Testimonios — carrusel deslizable con scroll-snap nativo (sin JS ni
   librería), reproduciendo el patrón de carrusel de testimonios de la
   referencia Medikale. En escritorio ancho las 3 tarjetas ya caben sin
   necesidad de desplazamiento, así que el scroll se desactiva ahí y el
   carrusel se comporta como una fila normal. */
.testimonial-carousel {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: var(--space-2);
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}
.testimonial-carousel::-webkit-scrollbar { height: 6px; }
.testimonial-carousel::-webkit-scrollbar-track { background: transparent; }
.testimonial-carousel::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: var(--radius-full); }
.testimonial-carousel .testimonial-card {
  scroll-snap-align: start;
  flex: 0 0 85%;
}
@media (min-width: 680px) {
  .testimonial-carousel .testimonial-card { flex: 0 0 calc(50% - var(--space-3) / 2); }
}
@media (min-width: 900px) {
  .testimonial-carousel {
    overflow: visible;
    scroll-snap-type: none;
  }
  .testimonial-carousel .testimonial-card { flex: 1 1 0; }
}

/* --------------------------------------------------------------------------
   Placeholder media
   -------------------------------------------------------------------------- */
.media-placeholder {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
  color: var(--color-primary);
  font-size: 0.85rem; text-align: center;
  padding: var(--space-2);
}

/* --------------------------------------------------------------------------
   FAQ — accordion grande, estado activo con color de marca
   -------------------------------------------------------------------------- */
.faq-list { display: grid; gap: 0.6rem; }
.faq-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-surface);
  padding: 0 var(--space-3);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.faq-item[open] { border-color: var(--color-accent); box-shadow: var(--shadow-sm); }
.faq-item summary {
  cursor: pointer;
  font-weight: 600;
  padding: var(--space-2) 0;
  font-family: var(--font-display);
  color: var(--color-primary);
  font-size: 1.05rem;
  list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: var(--space-2);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  font-size: 1.4rem;
  color: var(--color-accent-dark);
  flex-shrink: 0;
  transition: transform 0.2s ease;
}
.faq-item[open] summary::after { content: "\2212"; }
/* Corrección de ancho (2026-08-29): el `max-width: 68ch` anterior dejaba
   un vacío enorme a la derecha porque la tarjeta `.faq-item` ocupa el
   ancho completo de `.container` (hasta ~1130px), mientras el párrafo se
   topaba mucho antes — un desajuste entre el borde de la tarjeta y el
   texto, no una restricción de legibilidad deliberada (el `summary` de la
   misma tarjeta nunca tuvo esa restricción). Quitar el `max-width` deja
   que la respuesta ocupe el mismo ancho útil que la pregunta, con el
   mismo padding izquierdo/derecho heredado de `.faq-item` (single source
   of truth: afecta a la vez Home, /preguntas-frecuentes/, /ginecomastia/
   y /tratamiento/, las 4 páginas que comparten esta clase). */
.faq-item p { padding-bottom: var(--space-2); color: var(--color-ink-soft); max-width: none; }

/* --------------------------------------------------------------------------
   Notice boxes (editorial states)
   -------------------------------------------------------------------------- */
.notice {
  border-radius: var(--radius);
  padding: var(--space-3);
  font-size: 0.95rem;
  margin-bottom: var(--space-3);
  border: 1px solid var(--color-warning-border);
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}
.notice strong { display: block; margin-bottom: 0.25rem; }
.notice--info { background: var(--color-success-bg); border-color: var(--color-success-border); color: var(--color-success-text); }

/* Used only for the Home "Who We Are" credentials note: a bordered box
   with a bold lead-in sitting inside a .split text column is the same
   background+border+bold-heading combination that a screenshot-based
   section counter can read as its own block start. An earlier round only
   tightened spacing here (kept scoped to this modifier, not the base
   `.notice` rule, so the other 12 pages' editorial-state callouts — which
   aren't inside a two-column split and don't have this risk — keep their
   normal spacing/border/background) — that reduced the box's footprint
   but left its actual border+background+bold-heading signature intact,
   the same block-start signal already fully removed (not just shrunk) for
   `.hero__badge-card`, `.cta-locations` and `.hero__trust` (see those
   comments). Checkpoint (HI-20260817-0002, count 12 vs 11): completing
   that same treatment here — border and background removed, color swapped
   to `--color-ink-soft` to stay legible on the section's own
   `--color-bg-alt`, so the credentials note reads as a plain paragraph
   (bold lead-in only) instead of a bordered card, matching the sibling
   fixes. Content (lead-in + copy) unchanged. */
.notice--compact {
  padding: 0;
  /* Espaciado inferior aumentado (2026-08-24, corrección de spacing —
     único uso real de esta clase, ver grep: sección "Sobre el cirujano"
     de Home): 0.6rem abajo dejaba el CTA "Conocer su formación y
     credenciales" pegado justo debajo del párrafo de credenciales,
     sin separación real entre bloque informativo y CTA. Sube a
     var(--space-3) (mismo token de spacing ya usado en el resto del
     sistema, no un valor mágico) — el margin-top se mantiene igual,
     el problema reportado era solo el espacio inferior. */
  margin: 0.6rem 0 var(--space-3);
  border: 0;
  background: none;
  color: var(--color-ink-soft);
}
/* Standalone .notice sitting under a centered section head (not inside a
   two-column .split) needs the same narrow, centered measure as the
   heading above it instead of stretching full-width — used in place of
   a per-instance inline `style="max-width: 68ch; margin: 1rem auto 0;"`. */
.notice--narrow { max-width: 68ch; margin: 1rem auto 0; }

/* Breadcrumb */
.breadcrumb { font-size: 0.85rem; color: var(--color-ink-soft); padding: var(--space-2) 0 0; }
.breadcrumb a { color: var(--color-ink-soft); }
.breadcrumb a:hover { color: var(--color-primary); }
.breadcrumb--on-dark { color: rgba(255,255,255,0.72); padding: 0 0 var(--space-2); }
.breadcrumb--on-dark a { color: rgba(255,255,255,0.72); }
.breadcrumb--on-dark a:hover { color: #fff; }

/* Table of contents */
.toc {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}
.toc h2 { font-size: 1rem; margin-bottom: 0.6rem; }
.toc ol { margin: 0; padding-left: 1.2rem; }

/* --------------------------------------------------------------------------
   Page hero — banda interior reutilizable en las 13 páginas
   -------------------------------------------------------------------------- */
.page-hero {
  position: relative;
  overflow: hidden;
  /* Same guaranteed-navy-base fix as .hero (~L387): a background-color of
     the exact target hex on the element itself, independent of the
     overlay/image layer, so this banner (reused across all 13 interior
     pages) can't composite closer to black than to #0E2A4A either. */
  background-color: var(--color-primary);
  color: #fff;
  padding: 3.4rem 0 2.4rem;
  min-height: 220px;
  display: flex;
  align-items: flex-end;
}
.page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-color: rgba(14, 42, 74, 0.82);
  background-image:
    linear-gradient(115deg, rgba(14, 42, 74, 0.82) 0%, rgba(14, 42, 74, 0.55) 55%, rgba(14, 42, 74, 0.28) 100%),
    url('/assets/img/page-hero-bg.svg');
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  /* Motion removed (HI-20260817-0002, checkpoint hallazgo #2): shared the
     same gate-only justification as .hero::before above — the reference
     never declared this motion. Static background. */
}
/* Variantes editoriales por familia de contenido (2026-08-19,
   HI-20260819-0003, hallazgo bloqueante #1): el banner genérico
   (`page-hero-bg.svg`, literalmente rotulado "fotografía pendiente de
   producción") se reservó para las páginas realmente neutras
   (Precios, Prensa, Aviso legal, Privacidad). Las páginas interiores
   prioritarias reciben una composición gráfica intencional, distinta
   por familia temática — no fotografía simulada, ilustración
   geométrica propia (Playbook §22). */
.page-hero--protocol::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.82) 0%, rgba(14, 42, 74, 0.55) 55%, rgba(14, 42, 74, 0.28) 100%), url('/assets/img/page-hero-bg-protocol.svg'); }
.page-hero--trust::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.82) 0%, rgba(14, 42, 74, 0.55) 55%, rgba(14, 42, 74, 0.28) 100%), url('/assets/img/page-hero-bg-trust.svg'); }
/* Fotografía real de stock (2026-08-19, HI-20260819-0004, hallazgo crítico
   #3): `.page-hero--clinical` compartía el mismo fondo vectorial abstracto
   entre Tratamiento y Ginecomastia — visualmente indistinguible del banner
   plano ya señalado como insuficiente. Se reemplaza por 2 variantes con
   fotografía real y temática propia por página (Playbook: fotografía
   contextual, no ilustración genérica). Fallback temporal documentado en
   ASSET_PROVENANCE.md (`replacement_required=true`) — reemplazar por
   fotografía propia del centro/consulta real de Clínica Bellaforma en
   cuanto exista y esté autorizada. */
.page-hero--clinical-surgical::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/surgical-instruments-or.jpg'); }
.page-hero--clinical-consult::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/doctor-consultation.jpg'); }
/* Ronda V6-R3 (2026-08-19, HI-20260819-0005): cierre del sistema de hero
   interior para las 5 páginas que seguían con fondo SVG abstracto
   (.page-hero--protocol/--trust). Mismo patrón de fotografía real +
   overlay navy que las 2 variantes de arriba, un asset por página, ninguno
   reutilizado dos veces dentro de la misma familia temática. Fallback
   temporal documentado en ASSET_PROVENANCE.md (`replacement_required=true`)
   — reemplazar por fotografía propia en cuanto exista y esté autorizada. */
.page-hero--protocol-sterile::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/surgical-instruments-or.webp'); }
.page-hero--protocol-recovery::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/patient-recovery-care.webp'); }
.page-hero--trust-consult::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/consultation-chart.webp'); }
.page-hero--trust-case::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/case-review-tablet.webp'); }
.page-hero--trust-contact::before { background-image: linear-gradient(115deg, rgba(14, 42, 74, 0.84) 0%, rgba(14, 42, 74, 0.58) 55%, rgba(14, 42, 74, 0.3) 100%), url('/assets/img/stock-2026-08-19/contact-phone.webp'); }
/* Variante fotográfica: única página con fotografía propia legítima y
   directamente pertinente al héroe (retrato del propio Dr. Arroyo en su
   página biográfica) — mismo criterio que `.hero--photo` en Home. */
.page-hero--photo::before {
  background-image:
    linear-gradient(125deg, rgba(13, 38, 68, 0.92) 0%, rgba(14, 42, 74, 0.86) 55%, rgba(9, 26, 46, 0.94) 100%),
    url('/assets/img/source-recovered/legacy-2026-08-19/foto-dr-web.png');
  background-size: cover, cover;
  background-position: center, center 22%;
  background-repeat: no-repeat, no-repeat;
}
.page-hero__inner { position: relative; z-index: 1; width: 100%; }
.page-hero__title { color: #fff; margin-bottom: 0; font-size: clamp(2rem, 4.6vw, 3rem); }
.page-hero__title::after {
  content: "";
  display: block;
  width: 46px;
  height: 3px;
  margin-top: 0.9rem;
  background: var(--color-accent);
}
/* Subtítulo opcional bajo el H1 del hero interior — usado en
   /sobre-el-doctor/ (referencia visual aprobada por Diego, 2026-09-01).
   Una sola línea corta, nunca un párrafo largo. */
.page-hero__lead {
  color: rgba(255, 255, 255, 0.86);
  font-size: 1.05rem;
  margin: 1rem 0 0;
  max-width: 52ch;
}

@media (min-width: 680px) {
  .page-hero { padding: 4.4rem 0 3rem; min-height: 260px; }
}

/* --------------------------------------------------------------------------
   Sedes / locations
   -------------------------------------------------------------------------- */
/* Identificación, no atmósfera (Playbook §13): una fachada con letrero de
   marca ("Bella forma...") existe para que se reconozca la sede, así que
   el sujeto completo debe verse. `aspect-ratio:16/9` + `object-fit:cover`
   sobre una foto casi cuadrada (539×513, ~1.05:1 real) recortaba el
   letrero por arriba/abajo. `contain` sobre un fondo neutro muestra la
   fachada completa; el aspect-ratio baja a 4/3 (más cerca del original)
   para reducir el margen neutro sin volver a recortar. */
.location-card__map {
  aspect-ratio: 4 / 3;
  background: var(--color-bg-alt);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-2);
}
.location-card__map img { width: 100%; height: 100%; object-fit: contain; }

/* Misma corrección de intención de recorte para `.split__media` cuando
   contiene una fachada de identificación ("Por qué elegirnos", Contacto)
   en vez de fotografía atmosférica/editorial — modificador explícito para
   no afectar el `cover` por defecto, correcto para futura fotografía
   editorial real. */
.split__media--identify { background: var(--color-bg-alt); }
.split__media--identify img { object-fit: contain; }

/* Retrato del Dr. Arroyo dentro de `.split__media` (2026-08-19,
   HI-20260819-0003, hallazgo crítico #2 — Home "Sobre el cirujano" ya no
   usa la fachada de Jesús María, liberada para el fondo del hero).
   Actualización (2026-08-24, foto nueva provista por Diego — mirando a
   cámara, sonriendo): el `scale(1.08)` anterior existía solo para
   recortar el borde de un marco decorativo quemado en el archivo fuente
   de ESA foto — no aplica a la nueva. La foto nueva es real 4:3
   (1448×1086), exactamente la misma proporción que `.split__media`, así
   que el `cover` por defecto ya encuadra la foto completa sin recortar
   nada — se retira el `scale`, cualquier zoom aquí ahora sería un
   recorte innecesario (cabeza/manos/contexto de marca "Bella Forma" de
   fondo, todos visibles a propósito en la composición real de la foto). */

/* Estado de contenido pendiente para `.split__media`, mismo principio que
   `.gallery-pending`/`.avatar-monogram` (2026-08-19, HI-20260819-0001,
   corrección de checkpoint hallazgo #1): las 2 únicas fotos de fachada
   reales del proyecto se estaban repitiendo en 4 páginas de contenido
   (Ginecomastia, Tratamiento, Protocolo, Recuperación), lo que convertía
   las sedes en contenido visual duplicado a escala del sitio sin resolver
   la carencia real de fotografía médica/quirúrgica. En vez de seguir
   duplicando las fachadas, o inventar una escena de quirófano/consulta
   que no existe en este árbol, cada página usa un panel editorial
   distinto y pertinente a su propio tema (icono + mensaje), igual que la
   galería de casos. Hereda el fondo degradado por defecto de
   `.split__media`. */
.split__media--pending {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-2);
  padding: var(--space-4);
  color: #fff;
}
.split__media--pending svg { width: 42px; height: 42px; }
.split__media--pending span {
  font-size: 0.85rem;
  font-weight: 600;
  max-width: 26ch;
}

/* Placeholders "centro quirúrgico" (Tratamiento) / "evaluación médica"
   (Ginecomastia) resueltos con fotografía real (2026-08-19,
   HI-20260819-0004, hallazgo crítico #4). Reutilizan la MISMA foto de
   stock ya usada en el hero de cada página respectiva, pero con un
   encuadre (object-position) y un tratamiento (duotono navy + desaturado)
   claramente distintos al hero — full color, gradiente lateral, encuadre
   centrado — para que no se lea como el mismo recorte repetido dentro de
   la misma página (Playbook §11, anti-duplicación). Mismo fallback
   temporal que el hero, ver ASSET_PROVENANCE.md. */
.split__media--stock-accent { position: relative; }
.split__media--stock-accent img { filter: grayscale(55%) sepia(8%) contrast(1.05); }
.split__media--stock-accent::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(200deg, rgba(14, 42, 74, 0.68) 0%, rgba(14, 42, 74, 0.18) 65%);
  mix-blend-mode: multiply;
}
.split__media--stock-accent-surgical img { object-position: center 78%; }
.split__media--stock-accent-consult img { object-position: center 18%; }

.location-card__meta {
  display: flex; align-items: center; gap: 0.5rem;
  font-size: 0.85rem; color: var(--color-ink-soft);
  margin-top: 0.3rem;
}
.location-card__meta svg { width: 16px; height: 16px; color: var(--color-accent-dark); flex-shrink: 0; }
/* "Ver en Google Maps" (2026-09-02, /contacto/) — enlace real, no solo
   metadato de texto: color/weight de link en vez del `ink-soft` neutro
   de arriba (única instancia real de `.location-card__meta` en el
   sitio, así que se puede sobreescribir sin scope adicional). */
.location-card__meta--link { color: var(--color-accent-dark); font-weight: 600; }
.location-card__meta--link:hover { color: var(--color-accent-darker); text-decoration: underline; }
.location-card__meta--link svg { width: 14px; height: 14px; }
/* Preview de Google Maps embebido, debajo de la foto de fachada —
   franja más baja que la foto (coincide con la referencia visual
   aprobada por Diego), radio/overflow consistente con `.location-card__map`. */
.location-card__geo {
  aspect-ratio: 3 / 1;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-2);
  background: var(--color-bg-alt);
}
.location-card__geo iframe { width: 100%; height: 100%; border: 0; display: block; }

/* --------------------------------------------------------------------------
   Contacto — composición de dos columnas
   -------------------------------------------------------------------------- */
.contact-layout { display: grid; grid-template-columns: 1fr; gap: var(--space-4); align-items: start; }
/* Espacio exacto de 55px entre el párrafo intro y el primer canal (WhatsApp),
   pedido explícito de Diego — sustituye el margin-bottom heredado (16px). */
.contact-layout .section-lead { margin-bottom: 55px; }
.contact-info__item {
  display: flex; align-items: flex-start; gap: 0.8rem;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}
.contact-info__item:last-child { border-bottom: none; }
.contact-info__icon {
  flex-shrink: 0; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--radius-full);
  background: var(--color-primary-soft);
  color: var(--color-primary);
}
.contact-info__icon svg { width: 20px; height: 20px; }
.contact-info__item h3 { font-size: 1rem; margin-bottom: 0.15rem; }
.contact-info__item p { margin-bottom: 0; font-size: 0.92rem; color: var(--color-ink-soft); }
/* 2 líneas dentro del mismo item (Teléfono/Celular, Facebook-Instagram/
   TikTok) — aire mínimo entre ellas en vez de quedar pegadas por el
   `margin-bottom:0` de arriba. */
.contact-info__item p + p { margin-top: 0.15rem; }
.contact-info__item p a { color: var(--color-accent-dark); font-weight: 600; }
.contact-info__item p a:hover { color: var(--color-accent-darker); text-decoration: underline; }
/* Botón de WhatsApp dentro del item — aire respecto al párrafo de apoyo. */
.contact-info__item .btn { margin-top: 0.6rem; }
.contact-form-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-md);
}
@media (min-width: 900px) {
  .contact-layout { grid-template-columns: 1fr 1fr; gap: var(--space-5); }
}

/* --------------------------------------------------------------------------
   Formularios
   -------------------------------------------------------------------------- */
.form-field { display: grid; gap: 0.35rem; margin-bottom: var(--space-2); }
.form-field label { font-size: 0.88rem; font-weight: 600; color: var(--color-ink); }
.form-field input,
.form-field select,
.form-field textarea {
  width: 100%;
  font: inherit;
  padding: 0.65rem 0.8rem;
  border-radius: 8px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

/* --------------------------------------------------------------------------
   CTA band final
   -------------------------------------------------------------------------- */
.cta-band {
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  color: #fff;
  padding: var(--space-6) 0;
  text-align: center;
}
/* Banda intermedia (mid-page), más corta que la banda de cierre final */
.cta-band--tight { padding: var(--space-5) 0; }
.cta-band h2 { color: #fff; }
.cta-band p { color: var(--color-on-dark-muted); max-width: 55ch; margin: 0 auto var(--space-3); }

/* `.cta-band` sections show a single filled button and nothing else
   (checkpoint HI-20260817-0002, dimension-specific evidence: every
   CTA-band-equivalent on the reference has exactly one element, no
   companion link). The hero's own secondary-link group (`.hero__cta-links`
   / `.cta-band__link`) was removed in ronda 39 for the same reason — no
   CTA group on the reference (header, hero, or bands) pairs a primary
   button with a secondary text link. */

/* Compact "sedes" row inside .cta-band — thumbnail + text row, no border
   divider, no visible label and minimal top margin. All of those (uppercase
   `.kicker`-style label, border-top rule, a full `--space-4` gap) were the
   same visual pattern used to mark the START of every other section on this
   page ("Sobre el cirujano", "Cómo trabajamos", etc.) — together still a
   "new block begins here" cue that a screenshot/vision-based section
   counter could read as its own boundary, independent of the underlying
   `<section>`/heading markup. The label text stays for screen readers only
   (see index.html, `.sr-only`); visually this is just two photo+text rows
   with a small top gap, reading as a continuation of the same CTA block.

   Corrección (HI-20260817-0002, ronda 36, hallazgo de conteo 12 vs 10): el
   borde/label/margen ya se habían retirado, pero dos fotos de 56×56 en un
   `display:flex; gap: var(--space-4)` con nombre en `<strong>` seguían
   reproduciendo, a menor escala, el mismo lenguaje visual de tarjeta que
   `.card--photo` usa para arrancar la sección de servicios más arriba —
   una fila de "tarjetas" reconocible como su propia mini-grilla en vez de
   una continuación de texto del CTA. Las fotos bajan a 28×28 circulares
   (tratamiento de avatar/ícono, no de tarjeta fotográfica) y el nombre de
   la sede deja de ir en negrita con `--font-display` (la misma señal de
   "encabezado propio" ya retirada en `.notice--compact`), quedando como
   una línea de texto simple bajo el enlace del CTA. Ambas fotografías
   reales de fachada y el texto de las 2 sedes se conservan sin pérdida de
   contenido. */
.cta-locations {
  margin-top: var(--space-2);
}
.cta-locations__label {
  display: block;
  font-size: 0.85rem;
  color: var(--color-on-dark-muted);
  margin-bottom: var(--space-2);
}
.cta-locations__list {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
}
.cta-locations__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  text-align: left;
}
.cta-locations__item img {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.cta-locations__item strong { display: inline; color: #fff; font-family: inherit; font-weight: 500; font-size: 0.85rem; }
.cta-locations__item span { display: block; color: var(--color-on-dark-muted); font-size: 0.78rem; }

/* --------------------------------------------------------------------------
   Footer — navy casi negro, mayor presencia visual
   -------------------------------------------------------------------------- */
.site-footer { background: var(--color-primary-dark); color: var(--color-footer-text); padding: var(--space-6) 0 var(--space-3); font-size: 0.92rem; }
.footer-top { display: grid; grid-template-columns: 1fr; gap: var(--space-4); margin-bottom: var(--space-5); }
.footer-brand { max-width: 34ch; }
.footer-brand .brand { color: #fff; margin-bottom: var(--space-2); }
/* Nuevo logotipo (2026-09-08): reemplaza el isotipo circular + texto
   "Ginecomastia.com" sueltos por el wordmark oficial completo. Antes
   `.brand__mark` en el footer no tenía regla propia — su tamaño venía
   solo de los atributos width/height="34" del <img> (el isotipo
   cuadrado). El wordmark real es horizontal (658x149, ~4.4:1).
   Ajustado el mismo día a `width: 250px` (pedido explícito de Diego,
   medida exacta en px) — `height: auto` conserva el aspect-ratio real;
   `max-width: 100%` evita overflow si `.footer-brand` (max-width: 34ch)
   alguna vez resultara más angosto que 250px en algún viewport.
   Ronda 2026-09-08 (v2): logo nuevo (v2, 651x170) + ancho unificado
   header/footer a 222px por pedido explícito de Diego.
   Ronda 2026-09-08 (v3): reducción del 25% (222px -> 166.5px), pedido
   explícito de Diego. */
.footer-brand .brand__mark { width: 166.5px; height: auto; max-width: 100%; }
.footer-social { display: flex; gap: 0.6rem; margin-top: var(--space-3); }
.footer-social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.16);
  color: #fff;
  transition: background-color 0.15s ease, transform 0.15s ease;
}
.footer-social a:hover { background: var(--color-accent-dark); transform: translateY(-2px); text-decoration: none; }
.footer-social svg { width: 17px; height: 17px; }
/* Tooltip de sede (Facebook Jesús María / Los Olivos) — puro CSS, sin JS.
   `[data-tooltip]` solo está presente en los 2 iconos de Facebook, así
   que el selector no afecta a Instagram/TikTok. Se apoya en el atributo
   `data-tooltip` (no `title`, que ya no se usa aquí) para evitar el
   tooltip nativo del navegador duplicado sobre este. */
.footer-social a[data-tooltip] { position: relative; }
.footer-social a[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translate(-50%, 4px);
  background: var(--color-surface);
  color: var(--color-ink);
  font-size: 0.76rem;
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  padding: 0.4rem 0.65rem;
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
  z-index: 20;
}
.footer-social a[data-tooltip]:hover::after,
.footer-social a[data-tooltip]:focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
/* El primer icono (Jesús María) queda muy cerca del borde izquierdo del
   viewport en móvil angosto — un tooltip centrado se saldría de pantalla.
   Se ancla a la izquierda del propio icono en vez de centrarse, ya que
   flota por encima de toda la fila y no tapa ningún icono. */
.footer-social a[data-tooltip]:first-child::after {
  left: 0;
  transform: translateY(4px);
}
.footer-social a[data-tooltip]:first-child:hover::after,
.footer-social a[data-tooltip]:first-child:focus-visible::after {
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .footer-social a[data-tooltip]::after { transition: none; }
}
.site-footer h4 { color: #fff; font-size: 0.95rem; margin-bottom: var(--space-2); letter-spacing: 0.02em; }
.site-footer a { color: var(--color-footer-text); }
.site-footer a:hover { color: #fff; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: 0.5rem; }
/* Ronda 2026-09-08 (entrega/pre-producción): footer-bottom pasa de 3 hijos
   en flex (copyright | legal | back-to-top) con space-between — que deja
   el bloque legal "flotando" a medio camino en anchos intermedios — a 2
   grupos reales: copyright a la izquierda, [legal + back-to-top] agrupados
   a la derecha en `.footer-bottom__right`. */
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.12);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: space-between; align-items: center;
  font-size: 0.82rem; color: var(--color-footer-faint);
}
.footer-bottom a { color: var(--color-footer-faint); }
.footer-bottom__copyright a { color: #fff; font-weight: 600; }
.footer-bottom__right { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.footer-bottom__legal { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; }
.back-to-top {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.08);
  border: 1px solid rgba(255,255,255,0.16);
  color: #fff;
  flex-shrink: 0;
}
.back-to-top:hover { background: var(--color-accent-dark); text-decoration: none; }
.back-to-top svg { width: 16px; height: 16px; }
/* Corrección (checkpoint HI-20260817-0002, ronda 39, hallazgo bloqueante
   footer): la ronda 34 argumentó que 3 columnas + barra legal eran
   "equivalentes funcionales" a las 4 columnas de la referencia — el
   checkpoint rechazó esa equivalencia porque no reproduce el número ni la
   distribución visual reales. La lista "Explorar" (8 enlaces reales, sin
   dato inventado) se divide ahora en 2 columnas de navegación para llegar
   a 4 columnas reales: marca+redes | Explorar | Recursos | Contacto —
   mismo patrón de la referencia (marca | navegación | navegación
   secundaria/contacto | columna adicional), sin fabricar dirección,
   teléfono ni boletín que la clínica no confirmó. */
@media (min-width: 720px) {
  .footer-top { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 900px) {
  .footer-top { grid-template-columns: 1.3fr 0.85fr 0.85fr 1fr; }
}

/* --------------------------------------------------------------------------
   Utilidades
   -------------------------------------------------------------------------- */
.text-center { text-align: center; }
.mt-0 { margin-top: 0; }
.mt-3 { margin-top: var(--space-3); }
.stack { display: grid; gap: var(--space-2); }
.badge {
  display: inline-flex; align-items: center; gap: 0.4rem;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-size: 0.82rem; font-weight: 600;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-full);
}
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Responsive — navegación móvil (base = móvil; se despliega en escritorio)
   -------------------------------------------------------------------------- */
.nav-toggle { display: inline-flex; flex-direction: column; }
.nav-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(8, 23, 18, 0.45);
}
.nav-backdrop.is-open { display: block; }
body.nav-open { overflow: hidden; }
/* Off-canvas mobile panel: stays flush at right:0 (inside the viewport) at
   all times and reveals via clip-path instead of translateX(100%). A fixed
   element translated 100% of its own width past the viewport edge still
   contributes real geometry beyond the viewport in several browsers,
   which grows document.documentElement.scrollWidth and causes horizontal
   scroll even though the panel itself is invisible — that's the actual
   overflow bug, not something body { overflow-x: hidden; } alone can fix.
   Clipping the box in place instead of moving it never produces geometry
   outside the viewport. visibility toggles alongside so the closed panel
   is also removed from the tab order. */
.main-nav {
  position: fixed;
  inset: 0 0 0 auto;
  /* Causa raíz corregida (2026-08-20, Header Round 03): este offset se
     hardcodeó 3 veces en 2 rondas (62px -> 110px -> 138px) porque
     depende del contenido real de la utility bar - cada cambio de texto
     rompía el valor anterior. Ahora se mide en vivo (main.js,
     syncHeaderHeight) y se expone como --header-h en :root; 138px queda
     solo como fallback real para el caso sin JS, no como el valor
     autoritativo. */
  top: var(--header-h, 138px);
  width: min(320px, 84vw);
  height: calc(100vh - var(--header-h, 138px));
  background: var(--color-primary-dark);
  flex-direction: column;
  align-items: flex-start;
  padding: var(--space-3);
  box-shadow: var(--shadow-md);
  clip-path: inset(0 0 0 100%);
  visibility: hidden;
  transition: clip-path 0.2s ease, visibility 0.2s ease;
  overflow-y: auto;
  overflow-x: hidden;
}
.main-nav.is-open { clip-path: inset(0 0 0 0); visibility: visible; }
.main-nav__list { flex-direction: column; gap: var(--space-2); width: 100%; }
/* Larger touch targets for the mobile dropdown menu (WCAG target size) */
.main-nav__list a { display: block; padding: 0.85rem 0.4rem; }
.cta-bar .btn { flex: 1 1 auto; }

/* 1240px, not the ~680px "tablet" breakpoint: the horizontal nav has to fit
   the logo and 5 nav links (bajó de 7 en la ronda Fast Design Loop 02) in
   one row, which needs real desktop width. Below this, the mobile dropdown
   menu stays in use even on tablets/small laptops — keep this value in
   sync with the matching matchMedia query in main.js. */
@media (min-width: 1240px) {
  .nav-toggle { display: none; }
  .nav-backdrop { display: none !important; }
  /* Desktop-only: the mobile nav panel's `top`/`height: calc(100vh - ...)`
     sync (~L2081, currently 110px) doesn't apply here (.main-nav is
     position:static above this breakpoint), so the logo can grow for real
     protagonism without risking that offset. Scoped to .site-header so the
     footer's reuse of .brand__mark is untouched.
     Ronda 2026-09-08: cambiado de height:55px a width:220px (pedido
     explícito de Diego, medida exacta en px de ancho) — mismo archivo,
     mismo aspect-ratio (height:auto), max-width:100% como red de
     seguridad si el contenedor alguna vez fuera más angosto que 220px.
     Ronda 2026-09-08 (v2): logo nuevo (v2, ratio distinto) + ancho
     unificado header/footer a 222px por pedido explícito de Diego.
     Ronda 2026-09-08 (v3): reducción del 25% (222px -> 166.5px),
     pedido explícito de Diego. */
  .site-header .brand__mark { width: 166.5px; height: auto; max-width: 100%; }
  .main-nav {
    position: static;
    width: auto; height: auto;
    background: transparent;
    flex-direction: row;
    flex-wrap: wrap;
    row-gap: 0.5rem;
    justify-content: flex-end;
    align-items: center;
    padding: 0;
    gap: var(--space-5);
    box-shadow: none;
    clip-path: none;
    visibility: visible;
    overflow: visible;
  }
  /* Ronda Header + Navegación: gap sube de 1.1rem a --space-3 (1.5rem) y el
     padding horizontal de cada link crece ligeramente — más respiración
     entre los 7 links sin tocar font-size (que ya era legible). */
  .main-nav__list { flex-direction: row; flex-wrap: wrap; row-gap: 0.4rem; gap: var(--space-3); width: auto; }
  .main-nav__list a { padding: 0.3rem 0.25rem; }
  .cta-bar .btn { flex: 0 0 auto; }
}

/* --------------------------------------------------------------------------
   YouTube facade (2026-08-25) — video real de YouTube integrado sin cargar
   ningún iframe de terceros al pintar la página: solo una `<img>` liviana
   (thumbnail real de i.ytimg.com, el mismo CDN que ya usa la propia YouTube
   oEmbed API — nunca un archivo re-alojado) + un botón. El iframe
   (youtube-nocookie.com) solo se crea al hacer click (`main.js`). Reduce a
   cero el JS/requests de terceros en carga inicial en las páginas con
   varios videos (Prensa) sin importar cuántos `.yt-facade` haya en la
   página — el coste real es 1 imagen por video, no 1 iframe por video.
   -------------------------------------------------------------------------- */
.yt-facade {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-primary-dark);
  box-shadow: var(--shadow-sm);
}
.yt-facade__thumb { width: 100%; height: 100%; object-fit: cover; display: block; }
.yt-facade__play {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, rgba(9, 26, 46, 0) 45%, rgba(9, 26, 46, 0.55) 100%);
  border: 0;
  padding: 0;
  cursor: pointer;
}
.yt-facade__play-icon {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.94);
  box-shadow: var(--shadow-md);
  transition: transform 0.2s var(--ease-out), background-color 0.2s var(--ease-out);
}
.yt-facade__play-icon svg { width: 22px; height: 22px; margin-left: 3px; color: var(--color-primary); }
.yt-facade__play:hover .yt-facade__play-icon { transform: scale(1.08); background: #fff; }
.yt-facade__play:focus-visible { outline: 2px solid var(--color-accent-dark); outline-offset: -3px; border-radius: var(--radius-lg); }
.yt-facade__play:focus-visible .yt-facade__play-icon { transform: scale(1.08); background: #fff; }
.yt-facade iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* Tarjeta editorial que envuelve un `.yt-facade` (Prensa, Casos, páginas
   interiores) — video + contexto HTML real (heading/descripción/medio),
   nunca solo el embed suelto ("no escondas contenido SEO importante dentro
   del iframe", pedido explícito). */
.media-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.media-card__tag {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-accent-dark);
  margin-bottom: var(--space-2);
}
.media-card .yt-facade { margin-bottom: var(--space-2); }
.media-card h3 { font-size: 1.1rem; margin-bottom: 0.4rem; }
.media-card p { font-size: 0.92rem; color: var(--color-ink-soft); margin-bottom: 0; }
.media-card__source { display: block; margin-top: 0.5rem; font-size: 0.82rem; color: var(--color-ink-soft); }

/* --------------------------------------------------------------------------
   Uniformidad visual de Prensa (2026-08-26) — pedido explícito de Diego: las
   miniaturas de YouTube (colores/composición/texto muy distintos entre sí:
   thumbnail de podcast vs. collage de noticiero) hacían que cada tarjeta
   "pareciera de una fuente distinta sin control visual". En vez de recortar/
   editar las miniaturas reales (se mantienen honestas, sin alterar), se les
   aplica el MISMO tratamiento duotono ya establecido en el proyecto para
   este problema exacto (`.split__media--stock-accent`, fotos de stock de
   distinta procedencia en Tratamiento/Ginecomastia) — degradado navy en
   multiply + ligera decoloración, y un badge de categoría uniforme (mismo
   tamaño/posición/tipografía) superpuesto en la esquina, sin importar de
   qué fuente venga cada imagen real. */
/* `.press-thumb`/`.press-badge` — genéricas, deliberadamente NO scoped a
   `.media-card`: se aplican igual dentro de `.media-card` (Prensa) y dentro
   de `.card__media` (teaser de Prensa en Home), para que ambos contextos
   compartan literalmente el mismo tratamiento visual. */
.press-thumb { position: relative; }
.press-thumb img, .press-thumb .yt-facade__thumb { filter: grayscale(35%) sepia(6%) contrast(1.05); }
.press-thumb::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(200deg, rgba(14, 42, 74, 0.6) 0%, rgba(14, 42, 74, 0.12) 62%);
  mix-blend-mode: multiply;
  pointer-events: none;
}
.press-thumb .yt-facade__play { z-index: 2; }
.press-badge {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(9, 26, 46, 0.75);
  backdrop-filter: blur(3px);
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-full);
}
.media-card__tag { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-accent-dark); margin-bottom: var(--space-2); }
/* Portada editorial (sin foto real disponible/verificada — p.ej. la nota de
   revista COSAS, bloqueada por Cloudflare para cualquier verificación
   automatizada de imagen esta ronda): mismo tamaño de caja (16:9), mismo
   radius/badge que las tarjetas con video, para no romper el ritmo visual
   del bloque con un hueco o una proporción distinta — sin inventar ni
   recortar una fotografía que no se pudo verificar. */
.media-card__cover {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-2);
  background: linear-gradient(155deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
}
.media-card__cover-mark { font-family: var(--font-display); font-weight: 700; font-size: 1.6rem; color: rgba(255, 255, 255, 0.95); text-align: center; letter-spacing: 0.02em; }
.media-card__cover-mark span { display: block; font-size: 0.72rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255, 255, 255, 0.6); margin-top: 0.5rem; }
/* Reutilizada dentro de `.card__media` (teaser de Prensa en Home, que ya
   trae su propio aspect-ratio 3/2 vía `.card--photo`) — sin su propio
   aspect-ratio/radius/margin, para llenar exactamente esa caja en vez de
   imponer una proporción 16:9 en conflicto. */
.card__media .media-card__cover { position: absolute; inset: 0; aspect-ratio: auto; margin-bottom: 0; border-radius: 0; }

/* --------------------------------------------------------------------------
   Reseñas destacadas en Google (2026-08-26, reconstrucción completa —
   reemplaza el resumen de calificación aislado dentro de una gran tarjeta
   blanca por 9 reseñas reales de Google en un carrusel de 3 grupos).
   Jerarquía: eyebrow/H2 (`.section__head`, ya existente) → resumen
   compacto de rating (`.reviews-rating`) → carrusel (`.reviews-carousel`)
   → CTA de cierre (`.reviews-cta`). Reutiliza la MISMA técnica de estrella
   parcial al 70% (gradiente SVG) que `.rating-chip`/el badge del Hero.
   -------------------------------------------------------------------------- */
.reviews-rating {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin: 0 auto var(--space-5);
}
.reviews-rating__stars { display: inline-flex; gap: 0.25rem; color: var(--color-star); }
.reviews-rating__stars svg { width: 22px; height: 22px; fill: currentColor; stroke: none; }
.reviews-rating__star--partial svg { fill: none; stroke: currentColor; }
.reviews-rating__score {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  color: var(--color-primary);
  line-height: 1;
  margin-top: 0.35rem;
}
.reviews-rating__label { font-weight: 600; color: var(--color-ink); }
.reviews-rating__note { font-size: 0.82rem; color: var(--color-ink-faint); }

/* --------------------------------------------------------------------------
   Carrusel de reseñas — 3 grupos de 3 tarjetas. Las 9 reseñas existen
   siempre como HTML real (sin JS, se ven los 3 grupos apilados de corrido,
   uno debajo de otro) — el script (main.js) oculta con el atributo nativo
   `hidden` los 2 grupos inactivos cuando puede ejecutarse (el propio
   `[hidden]` de la hoja de estilos del navegador ya lo oculta, sin CSS
   adicional aquí), degradando con elegancia si JS falla. Dentro de cada
   grupo, `.reviews-carousel__row` reutiliza EXACTAMENTE el mismo patrón
   responsive de scroll-snap ya usado en `.testimonial-carousel` (85% en
   móvil con "peek" táctil del siguiente, ~50% en tablet, fila normal sin
   scroll desde 900px con las 3 tarjetas simultáneas) en vez de inventar un
   sistema nuevo. */
.reviews-carousel { max-width: 1080px; margin: 0 auto; }
.reviews-carousel__group + .reviews-carousel__group { margin-top: var(--space-3); }

.reviews-carousel__row {
  display: flex;
  gap: var(--space-3);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 0.2rem 0.2rem var(--space-2);
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}
.reviews-carousel__row::-webkit-scrollbar { height: 6px; }
.reviews-carousel__row::-webkit-scrollbar-track { background: transparent; }
.reviews-carousel__row::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: var(--radius-full); }
.review-card { scroll-snap-align: start; flex: 0 0 85%; }
@media (min-width: 680px) {
  .review-card { flex: 0 0 calc(50% - var(--space-3) / 2); }
}
@media (min-width: 900px) {
  .reviews-carousel__row { overflow: visible; scroll-snap-type: none; }
  .review-card { flex: 1 1 0; }
}

.review-card {
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-3);
}
.review-card__header { display: flex; align-items: center; gap: 0.65rem; margin-bottom: 0.55rem; }
.review-card__avatar {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
}
.review-card__avatar--a { background: var(--color-primary); color: #fff; }
.review-card__avatar--b { background: var(--color-accent-dark); color: #fff; }
.review-card__avatar--c { background: var(--color-primary-soft); color: var(--color-primary); }
.review-card__name { font-weight: 600; color: var(--color-ink); }
.review-card__stars { display: inline-flex; gap: 0.15rem; color: var(--color-star); margin-bottom: 0.55rem; }
.review-card__stars svg { width: 15px; height: 15px; fill: currentColor; stroke: none; }
.review-card__text { color: var(--color-ink-soft); line-height: 1.6; margin: 0; }

.reviews-carousel__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.reviews-carousel__arrow {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 50%;
  background: var(--color-surface);
  color: var(--color-primary);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: background-color 0.2s var(--ease-out), border-color 0.2s var(--ease-out);
}
.reviews-carousel__arrow svg { width: 18px; height: 18px; }
.reviews-carousel__arrow:hover { background: var(--color-primary-soft); border-color: var(--color-accent); }
.reviews-carousel__arrow:focus-visible { outline: 2px solid var(--color-accent-dark); outline-offset: 2px; }
.reviews-carousel__dots { display: inline-flex; align-items: center; }
/* El punto visible mide 9px, pero el botón real (área táctil) es de 32px
   — un target de 9px es demasiado pequeño para tocar con el dedo en
   móvil. `::after` dibuja el punto pequeño centrado dentro del botón
   grande, sin cambiar el tamaño visual. */
.reviews-carousel__dot {
  position: relative;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  background: transparent;
  cursor: pointer;
}
.reviews-carousel__dot::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--color-border);
  transform: translate(-50%, -50%);
  transition: background-color 0.2s var(--ease-out), transform 0.2s var(--ease-out);
}
.reviews-carousel__dot:hover::after { background: var(--color-accent); }
.reviews-carousel__dot:focus-visible { outline: 2px solid var(--color-accent-dark); outline-offset: 2px; border-radius: 50%; }
.reviews-carousel__dot.is-active::after { background: var(--color-accent-dark); transform: translate(-50%, -50%) scale(1.3); }

.reviews-cta { text-align: center; margin-top: var(--space-5); }

/* --------------------------------------------------------------------------
   Uniformidad total de las 3 tarjetas de Prensa (2026-08-26) — pedido
   explícito de Diego: mismo ancho/alto/proporción/spacing/tipografía/CTA
   en Podcast, Televisión y COSAS, sin importar que una tenga foto real de
   YouTube y otra una portada editorial. `.press-card` es la clase que
   fuerza esa uniformidad; NO se toca `.card`/`.card--photo` base (siguen
   usados tal cual en "Cómo trabajamos" y otras secciones). Las 3 tarjetas
   dejan de ser un único `<a>` gigante: `.card` pasa a ser un `<div>` con
   3 enlaces reales independientes (imagen/título/CTA) — necesario porque
   la tarjeta de video ahora lleva un `<button>` real (abre el modal) en el
   área de imagen, y un `<button>` dentro de un `<a>` no es HTML válido.
   El efecto visual de "tarjeta completa interactiva" (borde/sombra/lift en
   :hover) no depende de que sea un `<a>` — `.card:hover` funciona igual
   sobre un `<div>`. */
.press-card .card__media { aspect-ratio: 16 / 9; }
.press-card .card__body { display: flex; flex-direction: column; }
.press-card h3 a { color: inherit; text-decoration: none; }
.press-card h3 a:hover, .press-card h3 a:focus-visible { text-decoration: underline; }
.press-card .card__cta { display: inline-block; margin-top: auto; padding-top: 0.4rem; font-weight: 600; color: var(--color-accent-dark); text-decoration: none; }
.press-card .card__cta:hover, .press-card .card__cta:focus-visible { text-decoration: underline; }
/* Título/párrafo uniformes entre las 3 tarjetas de Prensa (2026-08-26,
   corrección tras revisión de Diego: los títulos deben caber en UNA sola
   línea, no 2 reservadas — antes el título más largo de "Televisión"
   envolvía a 2 líneas reales y desalineaba el párrafo de esa tarjeta
   respecto a las otras 2, justo el "desorden visual" que se pidió
   eliminar). `.press-card` (Home) Y `.media-card` (Prensa) comparten
   exactamente las mismas reglas — mismo sistema en ambas páginas, no solo
   en Home. Título: 1 línea forzada con elipsis como red de seguridad — el
   copy real de las 3 ya fue acortado para caber sin truncarse. Párrafo:
   2 líneas reservadas SIEMPRE, con copy balanceado a longitud similar
   entre las 3 (~85-90 caracteres) para que además envuelvan de forma
   parecida y queden "alineados en paralelo", no solo del mismo alto. */
.press-card h3, .media-card h3 {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.press-card p, .media-card p {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-height: 2.9em;
}
/* Botón de reproducir de las tarjetas de video: mismo componente
   `.yt-facade__play` ya usado en Prensa/Sobre el doctor/Ginecomastia/
   Casos — coherencia visual y de comportamiento total entre Home y esas
   páginas, no un patrón nuevo solo para Home. */
.press-card .yt-facade { border-radius: 0; box-shadow: none; }
/* Portada editorial de COSAS envuelta en un `<a>` real (clic en la imagen
   = ir a la nota, pedido explícito) — mismo alto/posición de badge que
   las 2 tarjetas de video gracias al `aspect-ratio:16/9` ya forzado arriba
   en `.press-card .card__media`. */
.press-card .media-card__cover { position: absolute; inset: 0; aspect-ratio: auto; margin-bottom: 0; border-radius: 0; }
.press-card a.media-card__cover-link { display: block; position: relative; width: 100%; height: 100%; }
.media-card__cover-link:focus-visible { outline: 2px solid var(--color-accent-dark); outline-offset: -3px; }
/* Standalone (fuera de `.press-card`, p.ej. la propia página `/prensa/`,
   que usa `.media-card` directo en vez de `.card--photo`) — mismo
   aspect-ratio 16:9 y radius que `.yt-facade` ya tiene ahí, para que la
   tarjeta de COSAS mida exactamente igual que las 2 de video también en
   esta página. */
.media-card__cover-link--standalone { display: block; position: relative; aspect-ratio: 16 / 9; border-radius: var(--radius-lg); overflow: hidden; margin-bottom: var(--space-2); }
.media-card__cover-link--standalone img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --------------------------------------------------------------------------
   Modal de video (2026-08-26) — reemplaza el reemplazo-inline anterior del
   facade por un iframe a pantalla completa del propio card: Diego pidió
   explícitamente "un pequeño zoom, popup o modal", no un embed que ocupe
   el mismo espacio agrandado. Insertado UNA sola vez por página (JS,
   ver main.js) y reutilizado por cualquier `.yt-facade` de cualquier
   página — Home, Prensa, Sobre el doctor, Ginecomastia, Casos. El iframe
   solo se crea al abrir el modal y se destruye al cerrarlo (nunca sigue
   reproduciendo en segundo plano). */
.video-modal { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; padding: var(--space-3); }
.video-modal[hidden] { display: none; }
.video-modal__backdrop { position: absolute; inset: 0; background: rgba(9, 26, 46, 0.86); }
.video-modal__dialog { position: relative; width: 100%; max-width: 880px; }
.video-modal__frame { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius-lg); overflow: hidden; box-shadow: var(--shadow-md); }
.video-modal__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-modal__close {
  position: absolute;
  top: -16px;
  right: -16px;
  z-index: 2;
  /* 36px -> 42px (2026-09-01, QA global de reproductores): el objetivo
     táctil recomendado es ~40-44px; el ícono visual (svg, sin cambios)
     se mantiene en 18px — solo crece el área realmente clicable. */
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: var(--color-primary);
  border: 0;
  box-shadow: var(--shadow-md);
  cursor: pointer;
}
.video-modal__close svg { width: 18px; height: 18px; }
.video-modal__close:hover { background: var(--color-accent); color: #fff; }
.video-modal__close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
body.video-modal-open { overflow: hidden; }
@media (max-width: 480px) {
  .video-modal__close { top: -42px; right: 0; }
}

/* --------------------------------------------------------------------------
   /casos/ — reconstrucción editorial (2026-09-01). Todo lo de aquí abajo es
   nuevo y opt-in: ninguna regla base compartida (.split, .grid, .card,
   .media-card, .compare-slider) se modifica — solo se agregan modificadores
   nuevos usados exclusivamente en esta página.
   -------------------------------------------------------------------------- */

/* Proporción retrato (3:5, 375×625) para los 14 derivados reales de
   antes/después — el `.compare-slider` base sigue en 4:3 para cualquier
   reuso futuro en formato paisaje. */
.compare-slider--portrait { aspect-ratio: 375 / 540; }

/* Split de video destacado — más ancho que el 50/50 genérico (~57/43),
   pedido explícito de la referencia aprobada ("55-60% video"). Modificador
   propio, no toca `.split` base (Home/ginecomastia/tratamiento siguen
   igual). */
@media (min-width: 900px) {
  .split--video-feature { grid-template-columns: 1.35fr 1fr; }
}
/* H2 "Conoce al Dr. Yuri Arroyo" (2026-09-02, /sobre-el-doctor/): en
   desktop (≥900px, columna de texto angosta por el 1.35fr/1fr de
   arriba) el texto no entra en una línea y el wrap natural rompía mal
   ("Conoce al Dr. Yuri" / "Arroyo") — pedido explícito de Diego: forzar
   el corte "Conoce al" / "Dr. Yuri Arroyo" SOLO cuando no entra en una
   línea. Debajo de 900px el layout es de una sola columna a ancho
   completo y el texto ya cabe en una línea de forma natural (verificado
   en vivo) — el `<br>` se oculta ahí para no partirlo sin necesidad. */
.split--video-feature h2 br { display: none; }
@media (min-width: 900px) {
  .split--video-feature h2 br { display: inline; }
}
.video-feature__meta { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-1); }
.video-feature__duration {
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-ink-soft);
  background: var(--color-bg-alt);
  padding: 0.2rem 0.55rem;
  border-radius: var(--radius-full);
}

/* Videos secundarios — fila horizontal compacta (thumbnail + texto), NO
   tarjetas verticales idénticas a la principal. */
/* Refinamiento 2026-09-01 (segunda pasada, pedido explícito de Diego):
   la composición horizontal (thumbnail pequeño + texto al lado) se
   sentía "comprimida, elementos auxiliares metidos en el espacio
   restante". Se reemplaza por thumbnail 16:9 a todo el ancho de la
   columna + contenido DEBAJO — mismo patrón editorial que el video
   destacado, a menor escala. Separación clara respecto al bloque
   principal vía `.video-secondary-row__head` (space-6 = 72px, dentro
   del rango 64-80px pedido). */
.video-secondary-row__head {
  margin-top: var(--space-6);
  margin-bottom: var(--space-3);
}
.video-secondary-row__head .kicker { margin-bottom: 0; }
.video-secondary-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}
@media (min-width: 680px) and (max-width: 1023px) {
  .video-secondary-row { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 1024px) {
  .video-secondary-row { grid-template-columns: 1fr 1fr 1fr; gap: var(--space-4); }
}
.video-secondary { display: flex; flex-direction: column; }
.video-secondary .yt-facade {
  width: 100%;
  aspect-ratio: 16 / 9;
  box-shadow: var(--shadow-sm);
  margin-bottom: var(--space-2);
}
.video-secondary .yt-facade__play-icon { width: 52px; height: 52px; }
.video-secondary .yt-facade__play-icon svg { width: 21px; height: 21px; margin-left: 2px; }
.video-secondary__body h3 { font-size: 1.1rem; margin-bottom: 0.4rem; }
.video-secondary__body p { font-size: 0.94rem; color: var(--color-ink-soft); margin-bottom: 0.6rem; }
.video-secondary__body .card__cta { font-size: 0.9rem; font-weight: 600; color: var(--color-accent-dark); }
.video-secondary__body .card__cta:hover { color: var(--color-accent-darker); }

/* Tarjeta de caso fotográfico — comparador + pie con etiqueta neutral.
   Reutiliza `.grid.grid--3` para el layout (1 col móvil → 2 ≥680px → 3
   ≥900px), ya usado en el resto del sitio; esta clase solo define la
   tarjeta en sí. */
.case-card__foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
.case-card__label { font-weight: 700; color: var(--color-primary); font-size: 0.95rem; }
.case-card__hint { font-size: 0.8rem; color: var(--color-ink-faint); white-space: nowrap; }

/* Cabecera de "Casos fotográficos" — +20px sobre el margin-bottom base
   de `.section__head` (32px → 52px), pedido puntual de Diego 2026-09-01
   ("un poco más de respiración" antes del grid, no un vacío grande).
   Modificador propio, no toca `.section__head` base (compartida por
   Home/ginecomastia/tratamiento/etc.). */
.section__head--cases { margin-bottom: calc(var(--space-4) + 20px); }

/* "Ver más casos" — progressive disclosure, JS mínimo (toggle [hidden]). */
.cases-more-toggle {
  display: flex;
  justify-content: center;
  margin-top: var(--space-4);
}
.cases-more-toggle .btn svg { width: 16px; height: 16px; transition: transform 0.2s var(--ease-out); }
.cases-more-toggle .btn[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Reveal/collapse de las tarjetas adicionales (2026-09-01, pedido
   explícito de Diego: el toggle anterior alternaba `hidden` sin ninguna
   transición — "se siente demasiado inmediato"). opacity+translateY con
   stagger vía `main.js` (que añade/quita `.case-card--anim-hidden` y
   fija `transition-delay` por tarjeta) — la propia tarjeta define la
   transición aquí; JS solo cambia el estado. Sin animar grid-template-
   rows/max-height (inestable/con jank en un grid real) — el bloque se
   siente progresivo porque cada tarjeta nueva entra casi invisible y
   se desplaza muy poco, no porque el layout se anime literalmente. */
[data-case-extra] {
  transition: opacity 0.34s var(--ease-out), transform 0.34s var(--ease-out);
}
[data-case-extra].case-card--anim-hidden {
  opacity: 0;
  transform: translateY(16px);
}
@media (prefers-reduced-motion: reduce) {
  [data-case-extra] { transition: none; }
}

/* Panel "Cómo interpretar estos casos" — Opción A (Editorial vertical),
   elegida por Diego tras explorar 7 direcciones en
   design-lab/casos-interpretacion-options.html. Explicación centrada
   primero, los 4 principios como conclusión debajo (ya no "flotando"
   arriba a la derecha) — sin card/borde, mismo fondo de página que el
   resto de /casos/, composición editorial. */
.interpret-panel {
  max-width: 900px;
  margin: 0 auto;
  text-align: center;
}
.interpret-panel__icon {
  width: 48px; height: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: var(--color-primary-soft);
  color: var(--color-primary);
  margin: 0 auto var(--space-2);
}
.interpret-panel__icon svg { width: 24px; height: 24px; }
.interpret-panel h2 { margin-bottom: 0.6rem; }
.interpret-panel p { color: var(--color-ink-soft); max-width: 62ch; margin: 0 auto; }
.interpret-panel__rule {
  width: 48px; height: 2px;
  background: var(--color-accent);
  border: 0;
  margin: var(--space-3) auto;
  border-radius: 2px;
}
/* Breakpoints alineados con `.video-secondary-row` (680/1024, ya usados
   en esta misma página): mobile = 1 columna, tablet = 2×2, desktop =
   4 columnas (verificado legible en render real a 1024-1440px; con
   textos más largos habría bajado a 2×2 en desktop también, pero no
   fue necesario). */
.check-list {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px 32px;
  text-align: left;
}
@media (min-width: 680px) {
  .check-list { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .check-list { grid-template-columns: repeat(4, 1fr); text-align: center; }
}
.check-list li {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-size: 0.95rem; color: var(--color-ink);
}
@media (min-width: 1024px) {
  .check-list li { flex-direction: column; align-items: center; gap: 8px; }
}
.check-list li svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 0.1rem; color: var(--color-accent-dark); }
@media (min-width: 1024px) {
  .check-list li svg { margin-top: 0; }
}

/* --------------------------------------------------------------------------
   Logos regulatorios (SUSALUD / MINSA / Municipalidad de Jesús María)
   Ronda 2026-09-08 (entrega/pre-producción): sustituye los `.badge` de
   texto plano por los logos institucionales reales, pequeños y en fila,
   sobre chip blanco (los assets vienen con fondo transparente y trazos
   oscuros/de color, ilegibles directamente sobre `--color-primary-soft`).
   Solo se usan en /protocolo-de-seguridad/, junto al texto ya aprobado
   "Clínica Bellaforma opera registrada ante:" — no se repiten en el
   footer para no sugerir aval/patrocinio fuera de ese contexto puntual.
   -------------------------------------------------------------------------- */
.trust-logos { display: flex; flex-wrap: wrap; align-items: center; gap: 0.9rem; }
.trust-logos__item {
  display: inline-flex; align-items: center;
  background: #fff;
  border-radius: var(--radius);
  padding: 0.5rem 0.8rem;
  box-shadow: var(--shadow-sm);
}
.trust-logos__item img { display: block; height: 30px; width: auto; }

/* --------------------------------------------------------------------------
   Ritmo editorial de páginas legales
   Ronda 2026-09-08 (entrega/pre-producción): /aviso-legal/, /privacidad/ y
   /derechos-de-los-usuarios-de-los-servicios-de-salud/ compartían el mismo
   problema — h2/p/ul en secuencia plana dentro de `.section`, sin espacio
   propio entre bloques (solo el `margin-bottom` de párrafo heredaba de la
   tipografía global). Scoped a `.legal-content`/`.legal-section` para no
   tocar `h2`/`p` en el resto del sitio. No usa cards ni iconos — solo
   ritmo vertical y un ancho de lectura editorial.
   -------------------------------------------------------------------------- */
.legal-content {
  max-width: 800px;
  margin: 0 auto;
}
/* Mobile-first, como el resto de la hoja: valores base más compactos,
   ampliados desde 680px (mismo breakpoint que `.footer-top`) para no
   generar scroll excesivo en pantallas angostas. */
.legal-content > * + * {
  margin-top: var(--space-4); /* 32px */
}
.legal-section h2 {
  margin-bottom: 1rem; /* 16px */
}
/* Párrafo→párrafo ya cae en 14-20px con el margin-bottom global de `p`
   (--space-2, 16px); solo se refuerza el salto lista↔párrafo, que con el
   margin-collapse por defecto quedaba pegado al límite inferior. */
.legal-section p + ul,
.legal-section ul + p,
.legal-section p + ol,
.legal-section ol + p {
  margin-top: 1rem; /* 16px */
}
.legal-section li {
  margin-bottom: 0.65rem; /* listas menos comprimidas que el 0.4rem global */
}
.legal-section li:last-child {
  margin-bottom: 0;
}
@media (min-width: 680px) {
  /* Objetivo pedido: 40-56px entre secciones (--space-5 = 48px, centro
     del rango), 16-22px entre H2 y su contenido, 16-24px lista↔párrafo. */
  .legal-content > * + * { margin-top: var(--space-5); }
  .legal-section h2 { margin-bottom: 1.25rem; }
  .legal-section p + ul,
  .legal-section ul + p,
  .legal-section p + ol,
  .legal-section ol + p { margin-top: 1.25rem; }
}
