/* Class & Villas -- portada editorial, Fase 2 (T297).
   Extiende tokens.css (no lo sustituye): ritmo de revista, jerarquía
   fotográfica y placeholders dignos y rotulados FICTICIO -- nunca cajas
   grises silenciosas. Ninguna imagen real: los "recuadros fotográficos"
   son degradados CSS puros con textura ligera, coherentes con la paleta
   mediterránea ya aprobada en tokens.css. */

.cv-editorial-section { padding: var(--cv-space-6) 0; }
.cv-editorial-section + .cv-editorial-section { border-top: 1px solid var(--cv-list-bg); }

.cv-kicker {
  display: inline-block; font-family: var(--cv-font-sub); font-weight: 800;
  font-size: 13px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--cv-terracotta-text); margin-bottom: var(--cv-space-2);
}

.cv-section-head { max-width: 720px; margin-bottom: var(--cv-space-4); }
/* :not(.cv-kicker) -- el kicker vive dentro de .cv-section-head en varias
   secciones; sin esta exclusión, esta regla (mayor especificidad que
   .cv-kicker por sí sola) le pisaba el color en todas menos en el hero. */
.cv-section-head p:not(.cv-kicker) { color: var(--cv-text); font-size: 17px; }

/* T309 Recuperación 6 -- franja editorial (Suscríbete + RRSS) de las
   páginas internas, ver components/_franja_editorial.html.twig. */
.cv-editorial-actions {
  display: flex; align-items: center; gap: var(--cv-space-3);
  flex-wrap: wrap; margin: 0 0 var(--cv-space-4);
}
.cv-social-inline { display: flex; gap: var(--cv-space-2); list-style: none; padding: 0; margin: 0; }
.cv-social-inline img { display: block; }

/* --- Placeholder fotográfico, siempre rotulado --- */
.cv-photo-placeholder {
  position: relative;
  display: flex; align-items: flex-end;
  border-radius: var(--cv-radius);
  overflow: hidden;
  background:
    linear-gradient(135deg, rgba(31,92,115,0.85), rgba(201,112,60,0.75)),
    repeating-linear-gradient(45deg, rgba(255,255,255,0.06) 0 2px, transparent 2px 12px);
  min-height: 220px;
}
.cv-photo-placeholder.cv-photo-tall { min-height: 340px; }
.cv-photo-placeholder.cv-photo-short { min-height: 160px; }
.cv-photo-badge {
  position: absolute; top: var(--cv-space-2); right: var(--cv-space-2);
  background: rgba(32,32,32,0.85); color: var(--cv-white);
  font-family: var(--cv-font-sub); font-weight: 800; font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 4px 10px; border-radius: 999px;
}
.cv-photo-caption {
  position: relative; z-index: 1;
  color: var(--cv-white); font-family: var(--cv-font-sub); font-weight: 700;
  padding: var(--cv-space-3); font-size: 15px;
}

/* --- T309 Recuperación 8 -- tarjetas editoriales SIN esquinas redondeadas,
   sin borde ni sombra de "tarjeta moderna" (hallazgo de José: propiedades,
   revista, artículos y recomendados se veían como cajas voluminosas
   redondeadas que no existen en el legacy real, donde la fotografía va a
   sangre y el texto va debajo, sin caja). Se anula aquí --cv-radius y el
   border/padding de .cv-card SOLO para estas 4 familias de tarjeta -- el
   resto de usos de .cv-card (CMS, agencias, distribución) no se tocan. */
.cv-propiedad-card,
.cv-revista-card,
.cv-news-card,
.cv-recomendado-card,
.cv-familia-empresa-card {
  border: 0; padding: 0; background: transparent; border-radius: 0;
}
.cv-propiedad-card .cv-photo-real,
.cv-propiedad-card .cv-photo-placeholder,
.cv-revista-card .cv-photo-real,
.cv-revista-card .cv-photo-placeholder,
.cv-news-card .cv-photo-real,
.cv-news-card .cv-photo-placeholder,
.cv-recomendado-card .cv-photo-real,
.cv-recomendado-card .cv-photo-placeholder,
.cv-familia-empresa-card .cv-photo-real,
.cv-familia-empresa-card .cv-photo-placeholder {
  border-radius: 0;
}
/* T309 Recuperación 9 -- Portal/Recomendados usan <h2> en el listado plano
   (única cabecera de nivel de tarjeta en esa vista) y <h3> dentro de las
   secciones de familia de Recomendados (que ya llevan su propio <h2> de
   familia); ambas comparten la misma tipografía fina de tarjeta. */
.cv-propiedad-card h2, .cv-propiedad-card h3,
.cv-revista-card h2,
.cv-news-card h2, .cv-news-card h3,
.cv-recomendado-card h2, .cv-recomendado-card h3,
.cv-familia-empresa-card h2, .cv-familia-empresa-card h3 {
  font-weight: 400; font-size: 19px; margin: var(--cv-space-2) 0 4px;
}
.cv-propiedad-card h2 a, .cv-propiedad-card h3 a,
.cv-news-card h2 a, .cv-news-card h3 a,
.cv-recomendado-card h2 a, .cv-recomendado-card h3 a,
.cv-familia-empresa-card h2 a, .cv-familia-empresa-card h3 a {
  color: var(--cv-secondary); text-decoration: none;
}
.cv-propiedad-card h2 a:hover, .cv-propiedad-card h3 a:hover,
.cv-news-card h2 a:hover, .cv-news-card h3 a:hover,
.cv-recomendado-card h2 a:hover, .cv-recomendado-card h3 a:hover,
.cv-familia-empresa-card h2 a:hover, .cv-familia-empresa-card h3 a:hover { color: var(--cv-primary); }
.cv-propiedad-precio { font-weight: 700; color: var(--cv-primary); font-size: 15px; margin: 2px 0 0; }

/* Foto completa clicable en Recomendados (familia + listado plano):
   José -- "el DOM real confirma que solo el título enlaza"; mismo patrón
   de bloque + zoom suave que .cv-propiedad-foto-link. */
.cv-recomendado-foto-link, .cv-familia-empresa-foto-link { display: block; overflow: hidden; }
.cv-recomendado-foto-link img, .cv-familia-empresa-foto-link img { transition: transform .4s ease; }
.cv-recomendado-foto-link:hover img, .cv-recomendado-foto-link:focus-visible img,
.cv-familia-empresa-foto-link:hover img, .cv-familia-empresa-foto-link:focus-visible img { transform: scale(1.06); }

/* T309 Recuperación 10 -- misma foto-clicable en Noticias (legacy real
   ".noticias-image" envuelve solo la foto, título en <a> aparte -- igual
   patrón que Recomendados/Portal). */
.cv-noticias-foto-link { display: block; overflow: hidden; }
.cv-noticias-foto-link img { transition: transform .4s ease; }
.cv-noticias-foto-link:hover img, .cv-noticias-foto-link:focus-visible img { transform: scale(1.06); }

/* T309 Recuperación 21 -- hallazgo de José: las tarjetas de Noticias
   (listado) y Recomendados (listado plano + sub-rejilla de familia) no
   tenían ningún marco de altura fija -- .cv-photo-real{height:100%} no
   resolvía a nada real porque ninguno de estos 3 envoltorios ("foto-link")
   fijaba su propia altura, así que la foto quedaba a su propia proporción
   intrínseca (solo con el suelo de min-height:160px de .cv-photo-short).
   Se reutiliza la MISMA proporción horizontal ya real y en uso en las
   tiles de familia de Recomendados (aspect-ratio:560/341, directorio.css)
   para que las 3 rejillas compartan el mismo marco uniforme, sin inventar
   una proporción nueva. La imagen y el placeholder se estiran dentro del
   marco (object-fit:cover ya viene de .cv-photo-real), nunca deforman ni
   alteran la altura de la tarjeta. */
.cv-recomendado-foto-link, .cv-familia-empresa-foto-link, .cv-noticias-foto-link {
  aspect-ratio: 560 / 341;
}
.cv-recomendado-foto-link .cv-photo-real, .cv-recomendado-foto-link .cv-photo-placeholder,
.cv-familia-empresa-foto-link .cv-photo-real, .cv-familia-empresa-foto-link .cv-photo-placeholder,
.cv-noticias-foto-link .cv-photo-real, .cv-noticias-foto-link .cv-photo-placeholder {
  width: 100%; height: 100%; min-height: 0;
}

/* --- Hero editorial ---
   T309 Recuperación 6 -- reconstruido a partir de "hero dividido" (grid de
   2 columnas texto/imagen) a gran fotografía a ancho completo con el
   bloque de texto debajo, reproduciendo la jerarquía real del legacy
   (slider de portada a ancho completo, "PROPIEDADES DESTACADAS"
   inmediatamente después -- ese segundo bloque ya vive en
   home/index.html.twig, sin cambios aquí).
   T309 Recuperación 6C -- `.cv-hero` es ahora SOLO la fotografía panorámica
   (sin bloque de texto debajo): el kicker/título/lead/CTA que antes vivían
   aquí (`.cv-hero-texto`/`.cv-hero-title`/`.cv-hero-lead`) se trasladaron a
   components/revista_intro.html.twig, tras "Propiedades destacadas" -- ver
   ese componente, que reutiliza `.cv-section-head` sin necesitar reglas
   propias. */
/* T309 Recuperación 8 -- hallazgo de José: la fotografía se veía con
   márgenes blancos laterales porque `.cv-hero` vive dentro de
   `<main class="cv-container">` (base.html.twig), que aplica
   `padding: 0 var(--cv-space-3)` a TODO su contenido, hero incluido. La
   técnica de "full-bleed" clásica (ancho 100vw + márgenes negativos que
   compensan exactamente el centrado del contenedor) saca la fotografía de
   ese padding sin reestructurar base.html.twig -- que es compartido por
   todas las plantillas, no solo la portada. */
/* T309 Recuperación 11B -- rechazo visual de José ("todo igual, no
   equivalente"): la reconstrucción anterior (R8) era una pista deslizante
   propia con puntos indicadores -- ninguno de los dos existe en el legacy
   real. Reescrito 1:1 sobre las clases y reglas legacy reales, extraídas
   de legacy_home_fresh.html línea 276 y css/style.css?v=20260717j líneas
   8854-8973: ".banner-outerblock" > ".banner-wrap" (ancho completo real,
   fuera de .cv-container igual que antes) con altura por vh según el
   mismo desglose de breakpoints legacy, cross-fade de opacidad (data-
   cycle-fx=fade legacy) en vez de pista deslizante, SIN puntos, y
   controles ".navwrap"/"prevControl"/"nextControl" con los iconos SVG
   reales del legacy a media altura. */
.cv-hero {
  width: 100vw; margin-left: calc(50% - 50vw); margin-right: calc(50% - 50vw);
  height: auto; position: relative; z-index: 1;
}
.cv-hero-wrap {
  width: 100%; background: transparent; overflow: hidden; position: relative;
  height: 44vh;
}
@media screen and (min-width: 64em) and (max-width: 90em) { .cv-hero-wrap { height: 61vh; } }
@media screen and (min-width: 90.0625em) and (max-width: 119.9375em) { .cv-hero-wrap { height: 66vh; } }
@media screen and (min-width: 120em) { .cv-hero-wrap { height: 71vh; } }
@media only screen and (max-width: 767px) { .cv-hero-wrap { height: 286px; } }
.cv-hero-track { margin: 0; padding: 0; list-style: none; height: 100%; position: relative; }
.cv-hero-slide {
  width: 100%; height: 100%; position: absolute; inset: 0; margin-bottom: 0;
  opacity: 0; transition: opacity 1s ease; z-index: 1;
}
.cv-hero-slide.is-active { opacity: 1; z-index: 2; }
@media (prefers-reduced-motion: reduce) { .cv-hero-slide { transition: none; } }
@media only screen and (max-width: 767px) { .cv-hero-slide { height: 286px !important; } }
.cv-hero-slide picture { display: block; width: 100%; height: 100%; }
.cv-hero-photo {
  height: 100%; width: 100%; object-fit: cover; object-position: center center;
  max-width: none; display: block; border-radius: 0;
}
.navwrap {
  width: 100%; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  z-index: 9999; padding: 0 34px; display: flex; justify-content: space-between;
}
@media only screen and (max-width: 767px) { .navwrap { padding: 0 20px; } }
.navwrap span { cursor: pointer; background: none; border: 0; padding: 0; display: block; }
@media only screen and (max-width: 767px) { .navwrap span img { width: 15px; } }

/* --- Revista destacada ---
   T309 Recuperación 8 -- fusión de lo que eran dos bloques consecutivos
   (revista_intro.html.twig + revista_destacada.html.twig: kicker/título/
   lead/CTA "Leer la edición" duplicados, portada y numeración repetidas)
   en un único componente con una sola jerarquía (ver
   components/revista_destacada.html.twig). Sin caja/borde/sombra --
   fotografía a sangre en su columna, texto en la otra, igual que el resto
   de secciones editoriales tras T309R8. */
.cv-revista-destacada { display: grid; gap: var(--cv-space-4); }
/* T309 Recuperación 25 -- José: la portada renderizaba 340x479.7px
   dentro de una columna de ~591px (grid 1fr/1fr a partes iguales),
   alineada a la izquierda -- gran hueco en blanco antes de que empezara
   el texto. La columna de la portada pasa a minmax(0,340px) (el ancho
   real que la caja ya usaba vía max-width) en vez de 1fr, así deja de
   sobrar columna que crea el hueco, y align-items:center centra
   verticalmente el bloque de texto respecto a la portada (antes
   align-items:start, el texto arrancaba arriba mientras la portada es
   bastante más alta) -- todo el bloque pasa a leerse como una única
   composición, no dos elementos flotando por separado. */
@media (min-width: 768px) {
  .cv-revista-destacada { grid-template-columns: minmax(0, 340px) 1fr; align-items: center; justify-items: stretch; }
}
/* T309 Recuperación 18 -- hallazgo de José: el bloque se veía horizontal
   y la portada blanca se perdía sobre el fondo blanco. Misma proporción
   vertical real de portada de revista que ya usa el archivo
   (.cv-revista-card, ver aspect-ratio 1920/2709 más abajo) más un borde
   fino que la delimita sin caja pesada ni color de relleno. */
.cv-revista-destacada .cv-photo-real,
.cv-revista-destacada .cv-photo-placeholder {
  border-radius: 0;
  /* T309 Recuperación 25 -- proporción real verificada con `file`/
     getimagesize() sobre el archivo físico servido como
     imagen_principal_sha256 (variante 'l', la preferida por
     MediaResolver::ORDEN_VARIANTE_GRANDE = ['l','g','m','s']):
     840x1134px exactos (ratio 0.7407). El valor previo (1920/2709 =
     0.7089) no correspondía a ningún archivo real de los efectivamente
     servidos y dejaba una caja más alta que la portada real -- con
     object-fit:contain eso producía una banda vacía visible (fondo
     --cv-list-bg, #ededed) dentro de la propia caja, la "franja gris"
     reportada por José. */
  aspect-ratio: 840 / 1134; height: auto; min-height: 0; width: 100%;
  max-width: 340px; margin: 0 auto; border: 1px solid var(--cv-list-bg);
  /* Blanco, nunca crema/gris (regla sitewide de esta ronda -- ver
     retirada de --cv-sand en tokens.css/directorio.css). Con la
     proporción ya corregida arriba la banda no debería verse en la
     práctica; se deja como respaldo honesto solo por si un futuro
     archivo real no coincidiera exactamente con 840x1134. */
  background: var(--cv-white);
}
/* .cv-photo-contain (más abajo en este fichero) fija
   background:var(--cv-list-bg) con !important -- esta regla más
   específica (3 clases) es la única forma de que el blanco gane aquí sin
   tocar el fondo gris que sigue sirviendo de respaldo honesto en el
   resto de usos de .cv-photo-contain (revista listado/ficha, fuera de
   alcance de esta ronda). */
.cv-revista-destacada .cv-photo-real.cv-photo-contain { background: var(--cv-white) !important; }
.cv-revista-destacada-body { }
.cv-revista-numero { font-size: 13px; font-weight: 700; color: var(--cv-sea-blue); text-transform: uppercase; letter-spacing: 0.04em; }

/* --- Noticias / categorías --- */
.cv-news-card h3 { font-size: 20px; margin-top: var(--cv-space-2); }
.cv-news-category { font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em; color: var(--cv-terracotta-text); }

.cv-categorias { display: flex; flex-wrap: wrap; gap: var(--cv-space-2); list-style: none; padding: 0; margin: 0; }
.cv-categorias a {
  display: inline-block; padding: 10px 18px; min-height: 44px; line-height: 24px;
  border-radius: 999px; border: 1px solid var(--cv-sea-blue);
  color: var(--cv-sea-blue); text-decoration: none; font-weight: 700; font-size: 14px;
}

/* --- Recomendados --- */
.cv-recomendado-card { display: flex; flex-direction: column; gap: var(--cv-space-2); }
/* min-height:140px del placeholder retirado en T309R25 -- superado por el
   aspect-ratio uniforme de .cv-recomendado-card .cv-photo-placeholder
   más abajo en este fichero (junto a .cv-photo-real). */

/* --- Bandas CTA (Distribución / B2B / Newsletter) ---
   T309 Recuperación 8 -- franja editorial sobria (hallazgo de José: las 3
   cajas de cierre -- Encuéntranos cerca de ti / ¿Eres profesional del
   sector? / La revista en tu buzón -- eran cajas muy coloridas y
   redondeadas sin equivalente en el legacy). Sustituidas por una franja
   monocroma: fondo blanco/marfil, separador de línea fina entre franjas
   consecutivas (heredado de .cv-editorial-section + .cv-list-bg), sin
   gradientes ni esquinas redondeadas grandes, tipografía fina, CTA
   discreta (variante secundaria en vez de botón sólido de color). Las 3
   franjas comparten el mismo tratamiento sobrio -- solo cambia el fondo
   entre blanco y marfil muy claro para separarlas visualmente sin apelar
   al color saturado. */
.cv-cta-band {
  padding: var(--cv-space-5) var(--cv-space-4);
  display: grid; gap: var(--cv-space-3);
  border-top: 1px solid var(--cv-list-bg); border-bottom: 1px solid var(--cv-list-bg);
}
@media (min-width: 768px) and (max-width: 1023px) { .cv-cta-band { grid-template-columns: 1fr auto; align-items: center; } }

/* T309 Recuperación 18 -- hallazgo de José: las 3 franjas finales
   (Encuéntranos cerca de ti / ¿Eres profesional del sector? / La revista
   en tu buzón) deben quedar en una única línea horizontal de 3 columnas
   en escritorio, apilándose solo en tablet/móvil. Desde 1024px cada
   franja pasa a ser una columna angosta (título+CTA apilados, no lado a
   lado) con bordes que dibujan una sola franja continua en vez de 3
   líneas horizontales independientes. */
@media (min-width: 1024px) {
  .cv-cta-row { display: grid; grid-template-columns: repeat(3, 1fr); align-items: stretch; }
  /* T309 Recuperación 25 -- José: las 3 columnas ya compartían el mismo
     ancho, pero no la misma altura ni el mismo punto de anclaje del
     botón -- cada `<section class="cv-editorial-section">` (el hijo
     directo real de .cv-cta-row, ver home/index.html.twig) solo era tan
     alto como su propio contenido, así que la franja con el texto más
     largo (p.ej. "La revista en tu buzón") quedaba más alta y su botón
     más abajo que las otras dos, sin alinear. height:100% + flex en la
     sección y en .cv-cta-band (flex-direction:column,
     justify-content:space-between) hace que el bloque título+texto se
     quede arriba y el botón se ancle siempre al borde inferior de la
     columna, igual en las 3, con independencia de cuánto texto tenga
     cada una. */
  .cv-cta-row .cv-editorial-section { height: 100%; display: flex; }
  .cv-cta-row .cv-cta-band {
    flex: 1 1 auto; display: flex; flex-direction: column; justify-content: space-between;
    border-top: 1px solid var(--cv-list-bg); border-bottom: 1px solid var(--cv-list-bg);
    border-left: 1px solid var(--cv-list-bg);
  }
  .cv-cta-row .cv-cta-band:last-child { border-right: 1px solid var(--cv-list-bg); }
  .cv-cta-row .cv-cta-band .cv-btn-primary { align-self: flex-start; }
}
.cv-cta-band h2 { font-weight: 400; font-size: clamp(22px, 3vw, 30px); }
.cv-cta-band-sea, .cv-cta-band-terracota { background: var(--cv-white); }
/* T309 Recuperación 22 -- hallazgo de José: la franja "¿Eres profesional
   del sector?" quedaba con fondo crema (--cv-sand, rgb(244,237,225)),
   la única de las 3 franjas finales que no era blanca. La portada solo
   debe usar blanco/negro en cajas y CTA, con líneas grises finas como
   separador (ya presentes vía border en modo fila de tokens.css) -- se
   iguala al resto, sin fondo de color. */
.cv-cta-band-sand { background: var(--cv-white); }
.cv-cta-band .cv-btn-primary {
  justify-self: start; background: transparent; color: var(--cv-secondary);
  border: 1px solid var(--cv-secondary); font-weight: 400;
}
.cv-cta-band .cv-btn-primary:hover, .cv-cta-band .cv-btn-primary:focus-visible {
  background: var(--cv-secondary); color: var(--cv-white);
}

/* T309 Recuperación 22 -- hallazgo de José: "Leer la edición" (revista
   destacada) y "Ver todos" (Recomendados) usaban cv-btn-secondary, fondo
   crema (--cv-sand). Botón sobrio rectangular blanco/negro para CTA
   secundarias de portada, mismo criterio que el botón outline de las 3
   franjas finales -- reutilizable, sin depender de --cv-sand. */
.cv-btn-outline {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--cv-space-2);
  min-height: 48px; padding: 0 var(--cv-space-4);
  background: var(--cv-white); color: var(--cv-secondary);
  border: 1px solid var(--cv-secondary); border-radius: 0;
  font-weight: 700; font-size: 15px; font-family: var(--cv-font-main); text-decoration: none;
}
.cv-btn-outline:hover, .cv-btn-outline:focus-visible { background: var(--cv-secondary); color: var(--cv-white); }

/* --- Propiedades (escaparate profesional) ---
   T309 Recuperación 8 -- convertido en carrusel horizontal real (hallazgo
   de José: el legacy usa un slider de propiedades, no una rejilla
   estática). Estructura genérica .cv-carousel/.cv-carousel-track,
   reutilizada también en el nuevo bloque de accesos editoriales; el
   desplazamiento nativo por scroll-snap funciona sin JS (swipe táctil
   incluido), y assets/js/portada.js añade los botones prev/next y el
   soporte de teclado con flechas sobre el propio carrusel. */
.cv-propiedad-card .cv-photo-placeholder { min-height: 190px; }
.cv-propiedad-card .cv-photo-real,
.cv-propiedad-card .cv-photo-placeholder { aspect-ratio: 4 / 3; height: auto; min-height: 0; }
.cv-propiedad-location { font-size: 13px; color: var(--cv-sea-blue); font-weight: 700; margin-top: var(--cv-space-1); }

.cv-carousel { position: relative; }
.cv-carousel-track {
  display: flex; gap: var(--cv-space-4); overflow-x: auto; scroll-snap-type: x proximity;
  margin: 0; padding: 0 2px var(--cv-space-2); list-style: none;
  scrollbar-width: thin;
}
.cv-carousel-item { flex: 0 0 240px; scroll-snap-align: start; }
@media (min-width: 600px) { .cv-carousel-item { flex-basis: 300px; } }
.cv-carousel-arrow {
  position: absolute; top: 38%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 999px; border: 1px solid var(--cv-list-bg);
  background: var(--cv-white); cursor: pointer; z-index: 2; font-size: 18px; color: var(--cv-secondary);
}
.cv-carousel-arrow:hover, .cv-carousel-arrow:focus-visible { background: var(--cv-list-bg); }
.cv-carousel-prev { left: -6px; }
.cv-carousel-next { right: -6px; }
@media (min-width: 768px) { .cv-carousel-prev { left: -18px; } .cv-carousel-next { right: -18px; } }

/* --- Accesos editoriales (La Revista / Portal Inmobiliario / Recomendados) ---
   T309 Recuperación 8 -- franja de 3 accesos con fotografía a sangre,
   degradado inferior y etiqueta blanca (hallazgo de José: bloque ausente
   por completo en la portada privada, presente en el legacy real justo
   tras "Propiedades destacadas"). Sin esquinas redondeadas, foto real de
   la edición vigente / de una propiedad publicada / de un recomendado
   publicado -- nunca imagen inventada. */
/* T309 Recuperación 33 -- hallazgo de José (captura real a 1280): las 3
   cajas (La Revista/Portal Inmobiliario/Recomendados) medían el mismo
   rectángulo total (~386,7 x 570,1 px) pero NO la misma altura de foto --
   La Revista reservaba una franja de título de ~60px en flujo normal
   (flex column, imagen + etiqueta como 2 bloques separados), mientras
   Portal/Recomendados superponían la etiqueta EN DEGRADADO sobre una foto
   a sangre de altura completa (position:absolute + gradiente). Se unifica
   la estructura: las 3 tarjetas son ahora flex column con la misma franja
   de etiqueta en flujo normal (nunca superpuesta), y la foto ocupa el
   resto del alto disponible -- object-fit:cover para Portal/Recomendados
   (fotografías reales, se recortan sin deformar), object-fit:contain solo
   para La Revista (portada real 1920x2709, ver variante más abajo: "no
   deformar/no recortar la portada" ya validado en R22, se conserva tal
   cual). align-items:stretch iguala el alto total de las 3 celdas. */
.cv-accesos { display: grid; gap: var(--cv-space-4); grid-template-columns: 1fr; align-items: stretch; }
@media (min-width: 768px) { .cv-accesos { grid-template-columns: repeat(3, 1fr); } }
.cv-acceso-card {
  position: relative; display: flex; flex-direction: column; overflow: hidden;
  color: var(--cv-secondary); text-decoration: none; border: 1px solid var(--cv-list-bg);
  background: var(--cv-white);
}
.cv-acceso-card img {
  display: block; flex: 1 1 auto; min-height: 220px; width: 100%; height: auto;
  object-fit: cover;
}
/* Respaldo honesto: si el acceso no tiene foto real verificada (caso hoy
   inexistente mientras las 3 secciones tengan medio), se muestra el mismo
   placeholder editorial que el resto del sitio en vez de desaparecer la
   caja -- las 3 cajas existen siempre, nunca se fabrica una imagen. */
.cv-acceso-card .cv-photo-placeholder { flex: 1 1 auto; min-height: 220px; margin: 0; }
.cv-acceso-label {
  display: block; flex: 0 0 auto; color: var(--cv-secondary); background: var(--cv-white);
  border-top: 1px solid var(--cv-list-bg);
  padding: var(--cv-space-3); font-family: var(--cv-font-sub); font-weight: 700;
  font-size: 18px; text-transform: uppercase; letter-spacing: 0.03em;
}
.cv-acceso-label::after { content: " →"; }

/* T309 Recuperación 22 -- "La Revista" es la única de las 3 con imagen
   vertical (portada real, 1920x2709). Con object-fit:cover a sangre (como
   las otras 2) se recortaba el logo/la portada por los lados. Se conserva
   la variante que muestra la portada COMPLETA (object-fit:contain) sobre
   fondo blanco -- nunca fondo crema. T309 Recuperación 33: ya no necesita
   overrides de posicionamiento (position/flex/border-top) porque ahora
   coinciden con la base de .cv-acceso-card/.cv-acceso-label; solo el
   ajuste de imagen (contain + padding) sigue siendo distinto. */
.cv-acceso-card-revista img {
  min-height: 0; object-fit: contain; background: var(--cv-white); padding: var(--cv-space-3);
}
.cv-acceso-card-revista .cv-photo-placeholder { min-height: 0; }

/* --- Newsletter ---
   T309 Recuperación 8 -- la franja de newsletter comparte ahora el mismo
   tratamiento sobrio que el resto de .cv-cta-band (ver arriba); estas
   reglas ya no fuerzan fondo oscuro. Las reglas de formulario se
   conservan inertes por si el bloque recupera un formulario inline en el
   futuro -- el componente actual (components/newsletter.html.twig) solo
   enlaza a la página real de alta. */
.cv-newsletter form { display: flex; flex-wrap: wrap; gap: var(--cv-space-2); margin-top: var(--cv-space-3); }
.cv-newsletter .cv-field { flex: 1 1 240px; margin-bottom: 0; }
.cv-newsletter-disclaimer { font-size: 12px; opacity: 0.85; margin-top: var(--cv-space-2); }

/* --- Vista previa CMS (borrador/publicado) --- */
.cv-preview-banner {
  padding: var(--cv-space-2) var(--cv-space-3);
  font-weight: 800; text-align: center; font-family: var(--cv-font-sub);
  letter-spacing: 0.04em; text-transform: uppercase; font-size: 13px;
}
.cv-preview-banner-draft { background: var(--cv-amber); color: var(--cv-secondary); }
.cv-preview-banner-published { background: var(--cv-success); color: var(--cv-white); }

/* --- T306 Recuperación 24: imagen real de medios verificados (mismas
   dimensiones/recorte que el placeholder que sustituye, para que listado
   y ficha no salten al mezclar entidades con y sin foto real). --- */
.cv-photo-real {
  display: block; width: 100%; height: 100%;
  min-height: inherit; object-fit: cover;
  border-radius: var(--cv-radius);
}
.cv-photo-real.cv-photo-tall { min-height: 340px; }
.cv-photo-real.cv-photo-short { min-height: 160px; }
.cv-propiedad-card .cv-photo-real,
.cv-punto-card .cv-photo-real { min-height: 150px; }

/* T309 Recuperación 25 -- José: medición real en la portada mostró que el
   marco de foto de .cv-recomendado-card NO era uniforme entre tarjetas
   pese a que la caja exterior sí lo era -- Vicens Ash/Euro Jávea
   386.12x385.56 (casi cuadrado, ratio 1.0016) pero Giuliano - Villas solo
   386.12x289.58 (más bajo), porque solo min-height:150px (sin tope
   superior) dejaba que cada imagen resolviera a su propia proporción
   intrínseca. Se fija un aspect-ratio único (redondeado desde la medida
   real 386.12/385.56 -- un cuadrado casi exacto) + object-fit:cover para
   que TODAS las fotos llenen la misma caja sin deformarse, con
   width:100% en vez de min-height, que es lo que dejaba el hueco. Regla
   puesta en portada.css (no en el home-page-scoped) porque
   .cv-recomendado-card se reutiliza tal cual en recomendados/listado.html.twig
   y recomendados/categoria.html.twig (vía .cv-recomendado-foto-link, que
   ya fuerza 100%/100% sobre esta misma imagen) -- un único cambio aquí
   basta para que las 3 vistas (portada + listado + categoría) compartan
   el mismo marco cuadrado. Sin border-radius, sin crema (regla sitewide
   de esta ronda). */
.cv-recomendado-card .cv-photo-real,
.cv-recomendado-card .cv-photo-placeholder {
  width: 100%; aspect-ratio: 386 / 385.56; height: auto; min-height: 0; object-fit: cover;
}
/* T309 Recuperación 25 -- decisión de object-position para los 4 clientes
   con foto real recuperada en T309R23 (Euro Jávea, Giuliano - Villas,
   Structural surveys, La Fábrica), medida real via `file` sobre el
   archivo físico servido (variante 'l'):
     Euro Jávea         1600x1066 (ratio 1.501)
     Giuliano - Villas   1600x1200 (ratio 1.333)
     Structural surveys   640x480  (ratio 1.333)
     La Fábrica          1600x1067 (ratio 1.499)
   Las 4 son horizontales moderadas (máx. 1.50 vs el marco casi-cuadrado
   de arriba, ratio ≈1.0) -- un recorte object-position:center (el valor
   por defecto, no se sobreescribe) retira una franja lateral de solo
   ~20-25% de cada lado sin cortar previsiblemente sujeto alguno; ninguna
   es panorámica extrema (ratio >2) ni tiene metadato/nombre de archivo
   que indique un sujeto marcadamente descentrado. Por eso no se fija un
   object-position específico para ninguna de las 4 -- el centrado por
   defecto ya es la elección justificada, no una omisión. */

/* T309 Recuperación 6 / 6B -- portadas de revista NUNCA recortadas
   (hallazgo #5: cubiertas 346/345/344 se veían recortadas
   horizontalmente, el logotipo de portada quedaba fuera de encuadre).
   object-fit:contain + fondo neutro en vez de cover: se ve el arte
   completo, con bandas si la relación de aspecto real no coincide con la
   caja, nunca un recorte. T309R6B añade un selector de especificidad
   real (0,3,1: img.cv-photo-real.cv-photo-contain) además de la clase
   sola -- la auditoría de navegador real de Codex midió computed
   object-fit:cover pese a la clase estar presente en el HTML, y esta
   regla más específica es la defensa adicional exigida (junto con el
   cache-busting de este fichero, ver <link> en las plantillas). */
.cv-photo-contain,
img.cv-photo-real.cv-photo-contain {
  object-fit: contain !important;
  object-position: center top;
  background: var(--cv-list-bg);
}

/* T309 Recuperación 8 -- causa raíz real de las cajas negras bajo las
   portadas del archivo de revistas (hallazgo de José, confirmado con
   navegador real): el contenedor `.cv-photo-short` fija un min-height de
   160px en una imagen ancha (100% del ancho de tarjeta) mientras la
   portada real mide 1920x2709 (ratio ~0.709, muy vertical) -- el
   letterboxing resultante de `object-fit:contain` es enorme y el fondo
   (antes --cv-secondary, casi negro) lo hace ilegible como "caja negra".
   Fijar aspect-ratio a la proporción real de portada elimina el
   letterboxing casi por completo (todas las portadas verificadas
   comparten esa proporción, ver getimagesize() en runtime) y sustituye
   min-height/short por una caja ajustada; el fondo claro (--cv-list-bg,
   ver arriba) solo actuaría como red de seguridad residual si alguna
   portada individual difiere ligeramente. */
.cv-revista-card .cv-photo-real.cv-photo-contain,
.cv-revista-card .cv-photo-placeholder.cv-photo-missing {
  aspect-ratio: 1920 / 2709; height: auto; min-height: 0; width: 100%;
}

/* ==========================================================================
   T309 Recuperación 11 -- rechazo visual de José: Propiedades destacadas y
   Últimos artículos reconstruidos con las clases y valores REALES del
   legacy (extraídos de css/style.css?v=20260717j, fetch fresco), no una
   reinterpretación editorial genérica. Se usan los mismos nombres de clase
   que el legacy (grid-title/right-blk/subscribe-btn/grid-single/grid-image/
   grid-content/destacadas-marquee/destacadas-track/button-container/
   btn-primary/article-block/more-link) para que la trazabilidad frente a
   la especificación legacy sea directa. Estas reglas solo se cargan en
   portada.css (portada + su preview CMS), sin efecto en el resto del sitio.
   ========================================================================== */

/* --- Título de sección + Suscríbete/RRSS en la misma fila (legacy
   ".grid-title"/".right-blk", ver css/style.css líneas ~11229-11457) --- */
.grid-title {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: flex-end; width: 100%; margin: 0 0 28px;
}
.grid-title h4 {
  color: #202020; text-transform: uppercase; font-weight: 800;
  font-size: 2rem; line-height: 1; margin: 0; padding: 0.25em 0 0;
}
.grid-title .cv-editorial-actions { margin: 0; }
@media only screen and (max-width: 767px) {
  .grid-title { margin: 0 0 20px; }
  .grid-title h4 { font-size: 1.5rem; padding-bottom: 12px; }
}

/* --- Cinta continua "Propiedades destacadas" (legacy ".destacadas-
   marquee"/".destacadas-track", css/style.css líneas ~11628-11671): auto-
   scroll infinito SIN flechas, pausa al pasar el ratón, lista duplicada
   una vez para que translateX(-50%) cierre el bucle sin corte visible. --- */
.destacadas-marquee {
  width: 100%; overflow: hidden; position: relative;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 3%, #000 97%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 3%, #000 97%, transparent);
}
.destacadas-marquee .destacadas-track {
  display: flex; width: max-content; margin: 0; padding: 0; list-style: none;
  animation: destacadas-scroll 45s linear infinite;
}
.destacadas-marquee:hover .destacadas-track { animation-play-state: paused; }
.destacadas-marquee .destacadas-track .grid-single {
  float: none; flex: 0 0 auto; width: 280px; margin: 0 12px;
}
@media only screen and (max-width: 767px) {
  .destacadas-marquee .destacadas-track .grid-single { width: 220px; }
}
@keyframes destacadas-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .destacadas-marquee .destacadas-track { animation: none; }
}

/* T309 Recuperación 22 -- hallazgo de José: el nombre de población encima
   del precio, en Propiedades Destacadas, se veía a 13px (sin regla propia,
   heredaba el tamaño por defecto del navegador para <h6>). Debe verse
   aproximadamente al doble en escritorio (26px), con una escala más
   contenida en móvil. Escopeado a .destacadas-track (solo la portada) para
   no afectar al listado real de propiedades, que reutiliza la misma
   marca .grid-single/.grid-content/h6 con su propio criterio ya validado. */
.destacadas-track .grid-content h6 { font-size: 18px; }
@media (min-width: 1024px) {
  .destacadas-track .grid-content h6 { font-size: 26px; }
}

/* --- Tarjeta genérica ".grid-single"/".grid-image"/".grid-content" (legacy
   css/style.css líneas ~11569-11730): foto 240px object-fit:cover, texto
   centrado debajo, sin caja/borde/sombra, hover zoom 1.2 sobre la foto. --- */
.grid-single { list-style: none; }
.grid-single .grid-image {
  position: relative; height: 240px; overflow: hidden; background: #F5F5F3;
}
.grid-single .grid-image a,
.grid-single .grid-image img { display: block; width: 100%; height: 100%; }
.grid-single .grid-image .cv-photo-real,
.grid-single .grid-image img.cv-photo-real {
  height: 100%; object-fit: cover; border-radius: 0;
  transition: transform 400ms ease-in-out;
}
.grid-single .grid-image .cv-photo-placeholder {
  position: absolute; inset: 0; width: 100%; height: 100%; min-height: 0;
}
.grid-single:hover .grid-image .cv-photo-real { transform: scale(1.2); }
.grid-single .grid-content { width: 100%; padding: 20px 20px 15px; text-align: center; }
.grid-single .grid-content h6 { color: #202020; font-family: "Lato"; font-weight: bold; padding-bottom: 7px; margin: 0; }
.grid-single .grid-content h6 a { color: #202020; text-decoration: none; }
.grid-single .grid-content .new-price { font-family: "Lato"; font-weight: 300; }

/* --- Botón sólido negro "Ver todas las propiedades" (legacy ".btn-
   primary" envuelto en ".button-container", css/style.css líneas
   ~8058-8087 y ~11731-11736). --- */
.button-container { width: 100%; text-align: center; margin-top: var(--cv-space-4); }
.btn-primary {
  display: inline-block; height: 48px; line-height: 44px; max-width: 260px;
  font-family: "Lato"; font-size: 14px; font-weight: 600; color: #fff;
  background: #000; border: 2px solid #000; padding: 0 22px;
  text-transform: uppercase; text-decoration: none; border-radius: 2px;
  transition: all 0.3s ease-in-out;
}
.btn-primary:hover, .btn-primary:focus { color: #fff; background: #CB1A1A; border-color: #CB1A1A; }

/* --- "Últimos artículos" (legacy ".article-block", css/style.css líneas
   ~11843-11919): fondo gris muy claro, h4 + "Ver más artículos" en la
   misma fila, 4 columnas, foto 210px, categoría/fecha/extracto reales. --- */
.article-block { width: 100%; padding: 40px 0 30px; background: #f8f8f8; }
.article-block::after { content: ""; display: table; clear: both; }
.article-block h4 { float: left; font-size: 24px; font-weight: 600; margin: 0 0 30px; }
.article-block .more-link {
  float: right; color: #333; text-decoration: none; position: relative; padding-bottom: 2px;
}
.article-block .more-link:after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: #E4E3E3; }
.article-block .article-slider {
  clear: both; display: grid; grid-template-columns: 1fr; gap: var(--cv-space-4);
}
@media (min-width: 600px) { .article-block .article-slider { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .article-block .article-slider { grid-template-columns: repeat(4, 1fr); } }
.article-block .cv-news-card .grid-image { height: 210px; margin: 0 0 15px; }
.article-block .cv-news-card .grid-image .cv-photo-real { border-radius: 0; }
.article-block .cv-news-card span { color: #999; font-size: 13px; text-transform: uppercase; display: block; margin: 0 0 8px; }
.article-block .cv-news-card span.date { text-transform: none; color: #999; margin: 8px 0 0; }
/* T309 Recuperación 25 -- José: el titular (antes un h6 sin clase propia)
   heredaba el tamaño UA por defecto de h6 (0.67em del body, ≈10.72px con
   line-height≈13.94px), MENOR que el extracto de abajo (14px/21px) --
   jerarquía invertida. Ground truth legacy (css/style.css?v=20260717j):
   titular 18px/22px, extracto 16px/25.6px. Se fijan en px absolutos (no
   clamp/vw) para que el titular NUNCA quede por debajo del extracto en
   ningún ancho de viewport, móvil incluido. Peso 400 -- misma convención
   "fino y elegante" que .cv-prop-titulo-fino (directorio.css): tamaño
   claramente mayor sin caer en el bold macizo que tenía antes. */
.article-block .cv-news-card .cv-news-headline {
  color: #222; font-weight: 400; font-size: 18px; line-height: 22px; margin: 0 0 4px;
}
.article-block .cv-news-card .cv-news-headline a { color: #222; text-decoration: none; }
.article-block .cv-news-card .cv-news-headline a:hover,
.article-block .cv-news-card .cv-news-headline a:focus-visible { color: #CB1A1A; }
/* Subtítulo real (campo 'subtitulo' -- NoticiaFixtureRepository), nivel
   intermedio: más pequeño que el titular y tipográficamente distinto del
   extracto (cursiva + tono más apagado) aunque el tamaño sea parecido,
   para que se lean como dos piezas de texto distintas, no una sola caja
   de párrafo. Se omite por completo en el DOM cuando la pieza no tiene
   subtítulo real (ver plantilla) -- nunca texto fabricado. */
.article-block .cv-news-card .cv-news-subtitle {
  color: #555; font-style: italic; font-weight: 400; font-size: 15px; line-height: 1.4; margin: 0 0 8px;
}

/* T309 Recuperación 29 -- hallazgo de José: sección "Feed Instagram"
   restaurada en portada (faltaba por completo frente al legacy real, ver
   components/instagram_feed.html.twig). Cabecera con título + CTA en la
   misma fila desde tablet (mismo patrón "más-link discreto" ya usado en
   .article-block .more-link, sin pastilla ni fondo crema -- convención
   sitewide de esta ronda). Rejilla plana: sin borde/fondo por celda, solo
   la fotografía; marco cuadrado uniforme (mismo criterio que
   .cv-recomendado-card, T309 Recuperación 25) vía aspect-ratio 1/1 +
   object-fit:cover, para que las 8 miniaturas reales (proporciones
   nativas distintas: 320x320, 320x400, 320x214, 320x178...) llenen la
   misma caja sin deformarse y sin salto de layout (width/height fijos en
   el <img>, ver plantilla). */
.cv-instagram-section .cv-section-head {
  display: flex; flex-direction: column; gap: var(--cv-space-2);
  max-width: none;
}
@media (min-width: 768px) {
  .cv-instagram-section .cv-section-head {
    flex-direction: row; align-items: baseline; justify-content: space-between;
  }
}
.cv-more-link {
  display: inline-flex; align-items: center; gap: var(--cv-space-1);
  color: var(--cv-secondary); font-weight: 600; font-size: 15px; text-decoration: none;
  white-space: nowrap;
}
.cv-more-link img { display: block; }
.cv-more-link:hover, .cv-more-link:focus-visible { color: var(--cv-primary); }
/* T309 Recuperación 33 -- hallazgo de José: las 8 miniaturas cabían en
   una sola fila de 8 columnas desde 1024px (137x137px), demasiado
   pequeñas. Se pasa a 4 columnas fijas desde tablet (768px) en adelante
   -- dos filas de 4 en vez de una fila de 8 -- para que cada foto sea
   sensiblemente más grande sin romper la rejilla cuadrada ni el criterio
   de object-fit:cover ya validado. Móvil se mantiene en 2 columnas. */
.cv-instagram-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--cv-space-2);
  grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 768px) { .cv-instagram-grid { grid-template-columns: repeat(4, 1fr); gap: var(--cv-space-3); } }
.cv-instagram-grid li { margin: 0; padding: 0; }
.cv-instagram-grid a { display: block; overflow: hidden; }
.cv-instagram-photo {
  display: block; width: 100%; height: auto; aspect-ratio: 1 / 1;
  object-fit: cover; border-radius: 0; transition: transform .4s ease;
}
.cv-instagram-grid a:hover .cv-instagram-photo,
.cv-instagram-grid a:focus-visible .cv-instagram-photo { transform: scale(1.06); }
.article-block .cv-news-card .cv-news-summary { color: #666; font-size: 16px; line-height: 25.6px; margin: 0; }
