/* San Juan Turismo — extensión de componentes sobre San Juan Gobierno DS */
/* Reutiliza tokens --gsj-* del sistema base. Única excepción: el bloque de contraste
   de acá abajo, que deriva un tono del naranja de marca para cumplir WCAG AA. */

/* ==========================================================================
   Polish a11y H1 — correcciones de contraste WCAG AA (2026-07-28)
   Origen: auditoría de las 23 pantallas (DEV-docs/Auditoria-H1-Pantallas-2026-07-28.md).
   Se corrige acá, en la capa turismo, y NO en el DS Gobierno padre (_ds/…/portal.css):
   forkear el base tendría un blast radius enorme y lo dejaría fuera de sync.
   Los valores de contraste anotados están calculados sobre sRGB (scratchpad/contraste_a11y.py).
   NOTA: en rigor esto es deuda del DS padre — conviene empujarlo aguas arriba.
   ========================================================================== */
:root {
  /* Heredado: rgba(60,60,67,.6) → 3.44:1 sobre blanco, 3.25:1 sobre primary-container. Falla AA.
     .78 → 5.63 / 5.24 / 5.15 y mantiene distancia visual con text-secondary (.85 → 6.93). */
  --gsj-color-text-muted: rgba(60, 60, 67, 0.78);

  /* El naranja de marca oklch(68.5% .19 51) = #f16e00 rinde 3.01:1: no sirve ni como texto
     sobre blanco ni como relleno con texto blanco. Se separan los tres usos:
       · decorativo (íconos, bordes, focus, swatches) → sigue --gsj-color-primary, intacto
       · naranja como TEXTO                          → --gsj-color-primary-text (#bd0000, 6.62:1, ya existía)
       · relleno que LLEVA texto blanco              → --gsj-color-primary-fill (nuevo) */
  --gsj-color-primary-fill: oklch(56% 0.19 51deg);        /* #c54500 — blanco encima: 4.96:1 */
  --gsj-color-primary-fill-hover: oklch(50% 0.19 45deg);  /* #b32a00 — blanco encima: 6.47:1 */
}

/* --- Rellenos que llevan texto blanco (el base los hardcodea, el token no interviene) --- */
.gsj-button--primary,
.gsj-badge--primary,
.gsj-hero {
  background-color: var(--gsj-color-primary-fill);
}
.gsj-button--primary:hover {
  background-color: var(--gsj-color-primary-fill-hover);
}
/* :active del base ya es oklch(49% .22 35) = #bd0000 (6.69:1) — no se toca. */

/* Texto secundario del hero: blanco al 75%/90% sobre el relleno nuevo todavía da 3.44/4.31.
   .95 lo lleva a 4.63:1 sin perder la jerarquía (que la dan tamaño, peso y caja alta). */
.gsj-hero__eyebrow,
.gsj-hero__subtitle {
  color: rgba(255, 255, 255, 0.95);
}

/* --- Naranja usado como TEXTO: 3.01:1 → 6.62:1 --- */
/* --secondary es el peor caso del kit (2.80:1 sobre su propio fill) y es el botón "Buscar"
   del header: 55 usos en 22 de las 23 pantallas. El fill no cambia, solo el texto. */
.gsj-button--secondary,
.gsj-button--secondary:hover,
.gsj-button--ghost,
.gsj-button--inverse,
.gsj-button--inverse:hover,
.gsj-badge--outline {
  color: var(--gsj-color-primary-text);
}
/* El borde del badge outline seguía naranja y quedaba desalineado del texto nuevo. */
.gsj-badge--outline {
  box-shadow: inset 0 0 0 1px currentColor;
}

/* --- Naranja como texto y como relleno, en componentes del DS base --- */
/* La barra del ítem activo acompaña al texto para que no queden dos naranjas distintos. */
.gsj-header__nav-link.is-active,
.gsj-section-header__label,
.gsj-section-header__link {
  color: var(--gsj-color-primary-text);
}
.gsj-header__nav-link.is-active::after {
  background-color: var(--gsj-color-primary-text);
}

/* Cabecera de sección en fila (2026-09-22): el DS base (`portal.css`) no tiene regla de móvil para
   `--row`, y el enlace de la derecha aplastaba el título a 146 px de ancho — medido en
   /experiencias con los agrupadores, y ya pasaba en «Llegar a San Juan» de Planificá. Debajo
   de 768 px se apila: rótulo, título, bajada y el enlace al final, alineados a la izquierda. */
@media (max-width: 767px) {
  .gsj-section-header--row {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }
}
/* El skip-link es el primer elemento que recibe foco en las 23 pantallas y era 3.01:1. */
.gsj-skip-link,
.gsj-pagination__link.is-active {
  background-color: var(--gsj-color-primary-fill);
}

/* ==========================================================================
   El DS base hardcodea los literales de color en CADA componente: los tokens
   están declarados pero casi no se usan (--gsj-color-text-muted aparece 1 sola
   vez en portal.css: su propia definición). Por eso redefinir el token no
   levantaba nada del base — hay que reapuntar las reglas una por una.
   Verificado midiendo contraste sobre el render real, no leyendo el CSS.
   ========================================================================== */

/* Escribían rgba(60,60,67,.6) a mano → 3.44:1. Ahora siguen el token (5.63:1). */
.gsj-field__message,
.gsj-breadcrumb__item:not(:last-child)::after,
.gsj-breadcrumb__link,
.gsj-section-nav__link,
.gsj-card__text,
.gsj-card__date,
.gsj-step-indicator__label,
.gsj-summary__key,
.gsj-table__caption,
.gsj-pagination__ellipsis,
.gsj-text-footnote,
.gsj-text-caption,
.gsj-text-muted,
.gsj-section-header__subtitle,
.gsj-hero__search-icon,
.gsj-accordion__icon {
  color: var(--gsj-color-text-muted);
}
/* .gsj-input:disabled queda como está a propósito: WCAG 1.4.3 exceptúa los
   controles deshabilitados, y aclararlo borraría la señal de "no disponible". */

/* ==========================================================================
   Escalón intermedio SIN destino en la ruta de navegación (2026-07-31, H2.7)

   Hueco real del componente: `gsj-breadcrumb` sólo tiene dos estados, enlace
   (`__link`) y página actual (`__current`), y el kit nunca dibujó un tercero
   porque sus 21 pantallas son maquetas donde todos los escalones intermedios
   llevan a algún lado. En un Joomla real no: un ítem de menú de tipo `heading`
   o `separator` es un rótulo que agrupa y no navega, así que el Pathway lo
   entrega SIN link (`SitePathway::__construct` le pone `$url = null`).

   No es hipotético: hoy mismo pasa en `/info/institucional` y `/info/descargas`,
   las dos colgando del heading «+ Info» de `menu-principal`, las dos servidas
   por Hydrogen. Con `__link` a secas quedaba un texto que se ilumina al pasarle
   el mouse y no responde al clic — una promesa falsa. Este modificador conserva
   el gris apagado y la altura (para que no salte el renglón) y le saca lo
   interactivo.
   ========================================================================== */
.gsj-breadcrumb__link--inerte,
.gsj-breadcrumb__link--inerte:hover {
  background-color: transparent;
  color: var(--gsj-color-text-muted);
  cursor: default;
}

/* Footer sobre #2D2D2D: los títulos de columna daban 4.07:1 y la línea legal 3.34:1.
   Se suben manteniendo la jerarquía por debajo de los links (.75 → 8.9:1). */
.gsj-footer__col-title,
.gsj-footer__contact-label {
  color: rgba(255, 255, 255, 0.62);
}
.gsj-footer__legal {
  color: rgba(255, 255, 255, 0.58);
}

/* ==========================================================================
   Footer institucional (2026-07-28)
   El DS Gobierno padre ya define el footer completo — identidad, tagline, contacto,
   redes y escudo — pero el kit solo había cableado la barra de navegación, y esa barra
   se había desincronizado en 8 variantes distintas entre sesiones. Acá NO se inventa
   markup nuevo: se usa el vocabulario que el base ya trae (.gsj-footer__meta,
   __meta-identity, __logo, __tagline, __contact*, __social-*, __escudo) y solo se
   ajusta lo que el arte real de San Juan necesita.
   ========================================================================== */

/* ===== Header: franja muerta de 1024 a ~1064px =====
   El base enciende `.gsj-header__nav` a partir de 1024px, pero nuestra nav en español tiene
   6 ítems ("Planificá tu viaje" es el largo) y entre logo + nav + botón Buscar necesita
   ~1064px: entre 1024 y 1064 el header desbordaba y aparecía scroll horizontal en 21 de las
   23 pantallas (incluye el viewport clásico de 1024×768). Se comprime el padding de los
   links solo en esa banda —60px de aire, alcanza— en vez de subir el breakpoint, que
   dejaría el hamburguesa (todavía sin cablear fuera de index) como única nav. */
@media (min-width: 1024px) and (max-width: 1099px) {
  .gsj-header__nav-link {
    padding: 0 7px;
  }
}

/* El footer funciona como mapa del sitio (patrón verificado en tourismthailand.org): espeja
   el menú real en vez de una selección corta. Son 5 grupos —Destinos, Experiencias, Servicios,
   Planificá y Institucional—, uno más que las 4 columnas del base. A 768px entran de a 3
   (5 ítems = 3+2, más parejo que 4+1) y recién a 1024 se abren las 5. */
@media (min-width: 768px) {
  .gsj-footer__nav-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (min-width: 1024px) {
  .gsj-footer__nav-grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

/* El base arma la identidad como isologo 36×36 + wordmark en texto. Se usa en su lugar el
   LOCKUP oficial 2025, que ya trae el wordmark dibujado: positivo en el header, negativo
   en el footer. El texto del nombre deja de existir como nodo — el nombre viaja en el `alt`. */
.gsj-header__logo-img--lockup {
  width: auto;
  height: 36px;
}
.gsj-footer__logo--lockup {
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.gsj-footer__logo-img--lockup {
  width: auto;
  height: 44px;
}

/* El __meta del base es "identidad | contacto | escudo". Los sitios de interés pasan a
   columna propia en vez de colgar del bloque de contacto: son navegación, no datos de
   atención. Queda "identidad | contacto | sitios | escudo". */
@media (min-width: 768px) {
  .gsj-footer__meta {
    grid-template-columns: 1fr 1fr 1fr auto;
  }
}

/* Escudo institucional: logo de Gobierno de San Juan 2025 (`GSJ_logo_H_negativo.webp`),
   negativo → SOLO fondo oscuro, no reutilizarlo sobre claro.
   Descartados a propósito: `gobierno_de_san_juan.svg` y `ministerio_de_turismo_y_cultura.svg`
   (junio 2019) — son la marca anterior, y el segundo además nombra un organismo que ya no
   corresponde (hoy es Secretaría de Turismo). Siguen en `images/logos/` del Joomla, no en el DS. */
.gsj-footer__escudo {
  flex-direction: column;
  gap: 16px;
}
.gsj-footer__escudo img {
  height: 40px;
}
.gsj-footer__escudo a {
  display: inline-flex;
  border-radius: 4px;
}
@media (min-width: 768px) {
  /* mismo breakpoint en el que el base pasa __meta a "1fr 1fr auto" */
  .gsj-footer__escudo {
    align-items: flex-end;
  }
}

/* Los "Sitios de interés" reusan __links dentro de un __contact-block. */
.gsj-footer__contact .gsj-footer__links {
  gap: 2px;
}

/* --- Escala tipográfica del footer ---
   El base mezcla cuatro tamaños (12 · 13 · 15px), así que los enlaces se leían más
   grandes que los datos de contacto de al lado y la línea legal quedaba diminuta.
   Se normaliza a DOS tamaños, subiendo el piso en vez de bajar el techo (prioridad:
   legibilidad — nadie se beneficia de un pie a 12px):
     · 15px → todo el contenido (enlaces, líneas de contacto, bajada)
     · 13px → solo las etiquetas en caja alta y la línea legal, que siguen siendo
              jerarquía secundaria pero dejan de ser texto diminuto. */
.gsj-footer__contact-line,
.gsj-footer__tagline,
.gsj-footer__link {
  font-size: 0.9375rem;
}
.gsj-footer__col-title,
.gsj-footer__contact-label,
.gsj-footer__legal,
.gsj-footer__bottom-link {
  font-size: 0.8125rem;
}
/* Los enlaces del pie legal son enlaces: mismo tratamiento de color que el resto,
   y heredan el tamaño de su línea para no romperla. */
.gsj-footer__bottom-link {
  color: rgba(255, 255, 255, 0.75);
}
.gsj-footer__legal .gsj-footer__link {
  font-size: inherit;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* El hover del social del base pinta el naranja de marca sin oscurecer detrás del ícono
   blanco (3.01:1). Se alinea con el resto de los rellenos. */
.gsj-footer__social-link:hover {
  background-color: var(--gsj-color-primary-fill);
}

/* image-slot (placeholder de foto, tool de Claude Design) usa currentColor para su ícono/borde
   punteado/caption vacíos; sin este reset hereda el color de link del <a> contenedor (rojizo) y
   el placeholder "foto pendiente" se lee como un error en vez de neutral. */
image-slot { color: var(--gsj-color-text-muted); }

/* `line-height` explícito en el chip, por el `button{line-height:inherit}` de nucleus que el
   kit no carga. ⚠️ Hoy NO se ve —`.gsj-chip` clava `height:32px` y centra con flex, así que la
   caja da 143×32 idéntica en los dos lados— pero es el mismo defecto latente que en
   `.gsj-facets__clear` SÍ se manifestó (14px contra 21px) porque ese no tiene alto fijo.
   Se declara para cerrar la familia: un componente que no declara una propiedad queda a
   merced de lo que traiga el sitio, y el día que alguien saque el `height` esto reaparece.
   Alcance: `.gsj-chip` la usan 6 pantallas del kit (agenda, búsqueda, servicios, atractivos). */
.gsj-chip { line-height: normal; }

/* ---- DestinoCard: card fotográfica de atractivo/destino ---- */

/* 🔴 EL SCRIM NO ES NEGRO: es el marrón oscuro de la paleta.
   `--gsj-color-on-primary-container` = `oklch(22% .06 51deg)` = rgb(47 16 0), o sea el tono
   oscuro de la rampa de marca (el mismo que ya usa el texto del subtipo activo en el rail).
   Va en CANALES sueltos porque un degradé necesita alfa y el token es un color sólido; así
   el literal vive en UN solo lugar en vez de repetirse en las tres paradas del degradé.
   Por qué no negro: el placeholder de las tarjetas sin foto es `--gsj-color-bg-muted`
   —`oklch(96% .01 70deg)`, un crema CÁLIDO— y un scrim gris choca contra eso; el marrón
   integra. Sobre foto lee como sombra natural en vez de como una caja negra.
   ⚠️ Si cambia `--gsj-color-on-primary-container`, hay que recalcular estos canales: el
   navegador no puede sacar los componentes de un token oklch para meterlos en un rgba(). */
:root { --gsj-scrim-card: 47 16 0; }

.gsj-card--destino {
  position: relative;
  border-radius: var(--gsj-radius-card);
  overflow: hidden;
  aspect-ratio: 4/5;
  display: block;
  text-decoration: none;
  background-color: var(--gsj-color-bg-muted);
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
  transition: transform var(--gsj-duration-base) var(--gsj-ease-standard), box-shadow var(--gsj-duration-base) var(--gsj-ease-standard);
}
.gsj-card--destino:hover {
  transform: translateY(-8px);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.18), 0 8px 10px -6px rgba(0, 0, 0, 0.12);
  text-decoration: none;
}
.gsj-card--destino__media {
  position: absolute;
  inset: 0;
}
.gsj-card--destino__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms var(--gsj-ease-standard);
}
.gsj-card--destino:hover .gsj-card--destino__media img {
  transform: scale(1.06);
}
/* 🔴 ESTE DEGRADÉ YA NO ES EL QUE HACE LEGIBLE EL TEXTO — ver `__overlay` acá abajo.
   Quedó como apoyo suave en el borde inferior (asienta la tarjeta y le da peso al pie);
   el contraste lo garantiza el scrim atado al bloque de texto. */
.gsj-card--destino__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgb(var(--gsj-scrim-card) / 0.28) 0%, rgb(var(--gsj-scrim-card) / 0) 45%);
}
.gsj-card--destino__badge {
  position: absolute;
  top: var(--gsj-space-4);
  left: var(--gsj-space-4);
  z-index: 1;
}
/* 🔴 CUANDO HAY BADGE DE 360°, EL DE SUBTIPO TIENE QUE CEDER. Los dos están clavados en
   esquinas opuestas (`__badge` arriba-izquierda, el de 360° arriba-derecha con su inline),
   y a 390px la tarjeta mide 138-163px: no entran los dos. Medido: **3 tarjetas en
   /atractivos y 2 en /destinos/valle-de-tulum**, con «Espacios Culturales» pisado 43px.
   ⚠️ NO es un defecto del directorio — la ficha de Valle de Tulum ya lo traía desde H2.5.2
   y nadie lo había mirado a 390. Por eso la regla vive acá, en la capa de la card, y
   corrige las dos páginas de una vez.
   `:has()` en vez de una clase nueva en el markup: así no hay que tocar `SjturCards` ni
   sumar una clase que el kit no tiene. Donde `:has()` no exista, queda el comportamiento
   de hoy — que es el que había, no uno peor. */
.gsj-card--destino:has(.gsj-badge--primary) .gsj-card--destino__badge {
  max-width: calc(100% - 88px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 🔴 EL SCRIM DE LECTURA VIVE ACÁ Y NO EN LA TARJETA, Y LA DIFERENCIA SE MIDIÓ.
   El degradé de `__media::after` cubría la tarjeta ENTERA y se desvanecía justo donde
   arranca la primera línea del título. Medido sobre las 57 del directorio con póster
   BLANCO —el peor caso, y el que importa porque todas las tarjetas van a llevar foto y
   una foto puede ser de nieve—: **título 1,24:1 a 390px con 57 de 57 reprobando** y la
   ubicación 2,81:1 con 35 reprobando. AA pide 3:1 para el título (texto grande) y 4,5:1
   para la ubicación. Se veía a simple vista: media grilla con el título ilegible.

   Se probaron cinco candidatos midiendo cada uno (`scratchpad/probar_scrim.js`). Los que
   oscurecen la tarjeta entera cumplen, pero dejan la foto en 170/255 — y en un sitio de
   turismo eso se paga en CADA tarjeta. Atar el scrim al bloque de texto —que mide lo que
   mide el texto— da lo mismo en contraste y deja la foto en 246/255, prácticamente
   intacta. Resultado: **título 4,76:1 a 1280 y 4,32:1 a 390 · ubicación 7,72 y 6,84 · 0 reprobadas.**
   Se aflojó a propósito desde un primer intento que daba 16,5:1 —el DOBLE de lo que pide AA—
   y se veía como un bloque negro: el criterio es margen, no récord. Queda ~45% por encima
   del umbral, que es lo que cubre un título de 4 líneas donde no hay clamp.

   Los 36px de arriba no son aire decorativo: son la zona donde el degradé se desvanece.
   Sin ellos la primera línea del título cae dentro del fundido y el número baja al filo —
   sin margen para un título de 4 líneas en la home o en una ficha, donde
   NO hay clamp. Se paga con 20px de alto en una caja que ya era la más alta de la card.

   Alcance: esto cambia TODA card de destino/atractivo del sitio y de 8 pantallas del kit.
   Es a propósito y estaba anotado en el plan como pendiente de decisión. Como el kit carga
   este mismo archivo, los dos lados se mueven juntos. */
.gsj-card--destino__overlay {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  padding: 36px var(--gsj-space-6) var(--gsj-space-6);
  background: linear-gradient(to top, rgb(var(--gsj-scrim-card) / 0.72) 0%, rgb(var(--gsj-scrim-card) / 0.66) 78%, rgb(var(--gsj-scrim-card) / 0) 100%);
  color: var(--gsj-color-text-on-dark);
}
.gsj-card--destino__title {
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-title-3);
  font-weight: 700;
  line-height: var(--gsj-line-height-snug);
  color: var(--gsj-color-text-on-dark);
  margin: 0 0 4px;
}
/* 🔴 EL TÍTULO SE CLAMPEA A 3 LÍNEAS, Y ES CONDICIÓN DEL SCRIM, NO ESTÉTICA.
   El scrim de `__overlay` cubre una altura acotada; un título que se va a 4 o 5 líneas trepa
   por encima del fundido y se queda sin fondo. Medido a 390px con el scrim ya puesto y ANTES
   de esta regla: `/destinos/valle-de-tulum` daba **1,11:1 en 6 textos**, o sea texto blanco
   sobre foto clara. En el directorio no pasaba porque ahí ya había clamp — y eso enmascaró
   el problema hasta que se midieron las fichas.
   Segunda razón, independiente: sin clamp el título se monta sobre el `__badge`, que está
   clavado en `top:16px`. Medido: 13 de 57 tarjetas a 390px, con el overlay saliéndose hasta
   88px FUERA de la tarjeta.
   ⚠️ El kit no puede revelar ninguna de las dos: sus mocks tienen títulos de una o dos
   líneas. El catálogo real tiene «Casa de Laprida y Ex Cabildo. Bien Integrante del
   Patrimonio Cultural.», 70 caracteres.
   El texto completo sigue en el DOM: se recorta a la vista, no al lector de pantalla. */
.gsj-card--destino__title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}

.gsj-card--destino__location {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-subheadline);
  /* 🔴 Era `rgba(255,255,255,0.85)` y NO llegaba a AA sobre el degradé de la card.
     Medido con `scratchpad/contraste_card_destino.js`, que sustituye la foto por un póster
     BLANCO (peor caso, porque la foto la cambia el cliente) y muestrea el píxel más claro
     detrás del texto: **2,86:1 en el destacado del hub y 4,37:1 en la grilla a 1280**,
     contra los 4,5:1 que pide AA para texto normal. No es un defecto del hub: falla igual
     en la grilla, en la home y en cualquier card de destino/atractivo. El título convive
     bien porque es «texto grande» (umbral 3:1) y va en blanco puro; esta línea se quedó
     con un 15% de transparencia que no aporta nada y cuesta el criterio. */
  color: var(--gsj-color-text-on-dark);
  margin: 0;
}

/* 🔴 CLAMP A 3 LÍNEAS, PERO SÓLO EN EL USO DE «BAJADA», Y LA DISTINCIÓN NO ES UN CAPRICHO.
   `__location` tiene DOS usos con el mismo nombre: en la card de Atractivo es una ubicación
   («Valle de Tulum») precedida de un pin SVG, y en la de Destino es la BAJADA del artículo,
   una frase entera. A 390px esa bajada se iba a 5 líneas y el overlay medía 237px dentro de
   una tarjeta de 201px: **36px de texto fuera de la tarjeta**, sin scrim debajo. Ahí caía el
   peor contraste de todo el sitio (3,05:1 en «Valle de Iglesia»).
   ⚠️ El kit tiene el MISMO defecto —se le sale 11px— así que no alcanzaba con copiarlo.

   🔴 Y el primer intento de arreglarlo ROMPIÓ el otro uso: al poner el clamp en la clase, el
   `display:-webkit-box` pisó el `display:flex` que alinea el pin con el texto, y las
   ubicaciones de `/destinos/valle-de-tulum` cayeron de 4,71:1 a 4,36:1 — 11 textos por
   debajo de AA. Lo delató medir DESPUÉS de tocar, no antes. Por eso el clamp va con
   `:not(:has(svg))`: la variante con pin conserva su flex intacto. */
.gsj-card--destino__location:not(:has(svg)) {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  overflow: hidden;
}
.gsj-card--destino__location svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

/* ---- Gallery: tira de fotos horizontal ---- */
.gsj-gallery {
  position: relative;
}
.gsj-gallery__track {
  display: flex;
  gap: var(--gsj-space-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scroll-behavior: smooth;
  scrollbar-width: none;
  padding-block: var(--gsj-space-2);
}
.gsj-gallery__track::-webkit-scrollbar {
  display: none;
}
.gsj-gallery__item {
  flex-shrink: 0;
  scroll-snap-align: start;
  border-radius: var(--gsj-radius-lg);
  overflow: hidden;
  width: min(78vw, 380px);
  aspect-ratio: 4/3;
  cursor: pointer;
}
/* El enlace envuelve la foto y tiene que llenar la celda: sin esto el área clickeable
   quedaba en la caja de línea del <a> y no en la foto. */
.gsj-gallery__link {
  display: block;
  width: 100%;
  height: 100%;
}
.gsj-gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms var(--gsj-ease-standard);
}
.gsj-gallery__item:hover img {
  transform: scale(1.05);
}

/* ---- Gallery bento: foto líder 2×2 + miniaturas + celda "+N fotos" ---- */
.gsj-gallery--bento {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 150px;
  gap: 10px;
}
.gsj-gallery--bento .gsj-gallery__item {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}
.gsj-gallery--bento .gsj-gallery__item:first-child {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
/* La celda «+N fotos» del bento MUESTRA LA FOTO SIGUIENTE de fondo (pedido del usuario,
   2026-07-30). Era un rectángulo gris plano; ahora enseña la primera foto que el bento no
   llegó a mostrar —la misma a la que el enlace ya apuntaba—, así que la promesa del «+N»
   se ve. El texto queda encima de un scrim.

   Cómo se apilan las capas, que es lo único delicado acá: `isolation: isolate` crea el
   contexto de apilamiento del propio enlace, y la foto y el scrim van con `z-index: -1`.
   Un `z-index` negativo se pinta por DEBAJO del contenido pero por ENCIMA del fondo del
   elemento, así que el texto queda arriba sin necesidad de subirlo y el
   `background-color` sigue sirviendo de respaldo si la foto no carga. El scrim va después
   del `img` en el DOM, y con el mismo `z-index` eso alcanza para que lo tape. */
.gsj-gallery--bento__more {
  /* En el kit es un <a> (inline, sin margen); el sitio también lo emite como <a>, pero el
     margen propio se declara igual: si algún día vuelve a ser <p>, el
     `p { margin: 1.5rem 0 }` de nucleus le mete 24px arriba y descuelga la celda de la
     grilla. Margen propio, siempre, aunque sea 0. */
  margin: 0;
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--gsj-radius-lg);
  background-color: var(--gsj-color-bg-muted);
  color: var(--gsj-color-text-on-dark);
  font-family: var(--gsj-font-heading);
  /* 600 y no 500: sobre una foto, medio punto de peso es lo que separa «se lee» de «casi
     se lee», y no cambia la altura de la caja. */
  font-weight: 600;
  font-size: 0.9375rem;
  text-decoration: none;
}
/* `image-slot` es el placeholder del kit; `img` es lo que emite el sitio. */
.gsj-gallery--bento__more > img,
.gsj-gallery--bento__more > image-slot {
  position: absolute;
  inset: 0;
  z-index: -1;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--gsj-duration-base) var(--gsj-ease-standard);
}
.gsj-gallery--bento__more::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* 🔴 El 0.62 NO es a ojo: es el mínimo que pasa AA (4,5:1 para texto normal de 15px)
     sobre un póster BLANCO PURO, que es el peor caso porque la foto la elige el cliente.
     Medido con `scratchpad/contraste_bento_more.js`. Con 0.55 daba 3,95:1 y no llegaba. */
  background-color: rgba(22, 13, 4, 0.62);
}
/* 🔴 EL HOVER MUEVE LA FOTO, NO EL SCRIM. El primer intento aclaraba el scrim a 0.45 para
   dejar ver mejor la imagen, y eso tiraba el contraste a **2,96:1**: los criterios de WCAG
   valen en TODOS los estados, no sólo en reposo, así que un hover que aclara el fondo
   detrás de un texto blanco rompe la conformidad justo cuando el usuario está mirando.
   Un zoom sutil da la misma señal de "esto es clicable" sin tocar la legibilidad. */
.gsj-gallery--bento__more:hover > img,
.gsj-gallery--bento__more:hover > image-slot {
  transform: scale(1.05);
}
@media (prefers-reduced-motion: reduce) {
  .gsj-gallery--bento__more > img,
  .gsj-gallery--bento__more > image-slot {
    transition: none;
  }
  .gsj-gallery--bento__more:hover > img,
  .gsj-gallery--bento__more:hover > image-slot {
    transform: none;
  }
}
@media (max-width: 640px) {
  .gsj-gallery--bento {
    grid-template-columns: 1fr 1fr;
    grid-auto-rows: 120px;
  }
  .gsj-gallery--bento .gsj-gallery__item:first-child {
    grid-column: 1 / -1;
    grid-row: auto;
    aspect-ratio: 16 / 10;
  }
}

/* ---- ItineraryCard: itinerario de varios días ---- */
.gsj-itinerary-card {
  display: flex;
  flex-direction: column;
  background-color: var(--gsj-color-surface);
  border: 1px solid var(--gsj-color-border);
  border-radius: var(--gsj-radius-card);
  overflow: hidden;
  transition: box-shadow var(--gsj-duration-base) var(--gsj-ease-standard), transform var(--gsj-duration-base) var(--gsj-ease-standard), border-color var(--gsj-duration-fast) var(--gsj-ease-standard);
}
.gsj-itinerary-card:hover {
  transform: translateY(-6px);
  border-color: var(--gsj-color-primary);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
}
.gsj-itinerary-card__media {
  aspect-ratio: 16/9;
  position: relative;
}
.gsj-itinerary-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.gsj-itinerary-card__days {
  position: absolute;
  top: var(--gsj-space-4);
  left: var(--gsj-space-4);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background-color: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-caption-1);
  font-weight: 600;
  padding: 6px 12px;
  border-radius: var(--gsj-radius-full);
  backdrop-filter: blur(4px);
}
.gsj-itinerary-card__days svg {
  width: 14px;
  height: 14px;
}
.gsj-itinerary-card__body {
  padding: var(--gsj-space-6);
  display: flex;
  flex-direction: column;
  gap: var(--gsj-space-3);
}
.gsj-itinerary-card__difficulty {
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-caption-1);
  font-weight: 600;
  color: var(--gsj-color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.gsj-itinerary-card__steps {
  list-style: none;
  padding: 0;
  margin: 4px 0 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.gsj-itinerary-card__steps li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-size: var(--gsj-font-size-subheadline);
  color: var(--gsj-color-text-secondary);
  line-height: var(--gsj-line-height-snug);
}
.gsj-itinerary-card__step-num {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: var(--gsj-radius-full);
  background-color: var(--gsj-color-primary-container);
  color: var(--gsj-color-on-primary-container);
  font-family: var(--gsj-font-heading);
  font-size: 0.6875rem;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* ---- EventCard: agenda / calendario turístico ----
   🔴 `color` Y `font-family` DECLARADOS, y no son redundantes. La EventCard es un `<a>`, y
   ni ella ni sus partes declaraban ninguna de las dos: las heredaban. Mientras la card vivió
   sólo en `/agenda` no se notó, porque ahí el contexto del sitio y el del kit coinciden. Al
   llevarla a la home aparecieron **7 divergencias de una sola causa**, y con los colores
   CRUZADOS: el kit heredaba el color de texto (`oklch(.192 0 0)`) y el sitio el de enlace
   (`oklch(.49 .22 35)`), así que `__media` y `__row` daban uno y `__date-day` el otro.
   Lo mismo con la tipografía: `Ubuntu` en el kit contra `Lora, Georgia, serif` en el sitio.
   Es el mismo patrón que ya obligó a declarar `line-height` en `gsj-prestador-card__title`:
   un componente que no declara una propiedad queda a merced de dónde lo pongan. */
.gsj-event-card {
  display: flex;
  gap: var(--gsj-space-5);
  align-items: flex-start;
  padding: var(--gsj-space-5) 0;
  border-bottom: 0.5px solid var(--gsj-color-border);
  color: var(--gsj-color-text);
  font-family: var(--gsj-font-heading);
  line-height: 1.3;
  text-decoration: none;
}
.gsj-event-card:last-child {
  border-bottom: none;
}
.gsj-event-card__date {
  flex-shrink: 0;
  width: 64px;
  height: 64px;
  border-radius: var(--gsj-radius-md);
  background-color: var(--gsj-color-primary-container);
  color: var(--gsj-color-on-primary-container);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-family: var(--gsj-font-heading);
  line-height: 1;
}
.gsj-event-card__date-day {
  font-size: 1.375rem;
  font-weight: 700;
}
.gsj-event-card__date-month {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-top: 4px;
}
.gsj-event-card__body {
  flex: 1;
  min-width: 0;
}
.gsj-event-card__title {
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-headline);
  font-weight: 700;
  color: var(--gsj-color-text);
  /* margin-top explicito: el nivel del heading es configurable (headingLevel), y h3/h4
     traen margenes por defecto distintos del navegador. Sin esto, cambiar el nivel
     por accesibilidad movia la card. (Bloque 2 a11y, 2026-07-28) */
  margin-top: 0;
  margin-bottom: 4px;
}
.gsj-event-card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 16px;
  font-size: var(--gsj-font-size-footnote);
  color: var(--gsj-color-text-muted);
}
.gsj-event-card__meta span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.gsj-event-card__meta svg {
  width: 14px;
  height: 14px;
}

/* ---- EventCard --media: la variante de los BLOQUES de «próximos eventos» ----
   Dónde va y dónde no. En `/agenda` la EventCard es una FILA DE LISTA: densa, sin foto,
   separada por una línea, porque ahí hay decenas y lo que se escanea es la fecha. En los
   bloques de home / destino / experiencia / ficha hay dos o tres, sirven para DESCUBRIR y
   la foto es lo que las hace mirar. Son las dos escuelas que ya usa el resto del sitio, y
   por eso esto es un modificador y no un cambio del componente.

   🔴 EL MODIFICADOR NO REDEFINE NADA DE `__date`, `__body`, `__title` NI `__meta`. Todo lo
   de adentro queda intacto, así que `/agenda` —que no lleva el modificador— no se puede
   mover. Lo único que cambia es la caja: de fila a tarjeta.

   El chrome sale de `gsj-promo-card`, que es la card vertical con media 16:9 que el DS ya
   tiene; no se inventa un tratamiento nuevo para esto.

   ⚠️ El markup con media mete un `__row` alrededor de fecha+cuerpo. La variante sin media
   NO lo lleva, y de ahí que acá se declare `display:block` en vez de reordenar el flex: el
   `__row` es quien vuelve a poner la fecha al lado del cuerpo. */
.gsj-event-card--media {
  display: block;
  padding: 0;
  border-bottom: none;
  background-color: var(--gsj-color-surface);
  border: 1px solid var(--gsj-color-border);
  border-radius: var(--gsj-radius-card);
  overflow: hidden;
  transition: box-shadow var(--gsj-duration-base) var(--gsj-ease-standard),
              transform var(--gsj-duration-base) var(--gsj-ease-standard),
              border-color var(--gsj-duration-fast) var(--gsj-ease-standard);
}
/* `:last-child` del componente base quita el borde inferior; acá el borde es la caja
   entera y no un separador, así que hay que reponerlo o la última card queda sin marco. */
.gsj-event-card--media:last-child {
  border: 1px solid var(--gsj-color-border);
}
.gsj-event-card--media:hover {
  border-color: var(--gsj-color-primary);
  box-shadow: 0 20px 25px -5px rgba(0, 0, 0, .1), 0 8px 10px -6px rgba(0, 0, 0, .1);
  transform: translateY(-6px);
  text-decoration: none;
}
.gsj-event-card__media {
  display: block;
  aspect-ratio: 16 / 9;
  background-color: var(--gsj-color-bg-muted);
  position: relative;
}
.gsj-event-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: absolute;
  inset: 0;
}

/* ---- `--afiche`: la variante que NO recorta ---------------------------------------------
   🔴 EXISTE PORQUE UN AFICHE NO SOBREVIVE EL RECORTE 16/9. `cover` está bien para la foto de
   un lugar —sobra cielo o sobra piso— y mutila un afiche, donde lo que se pierde es el
   TÍTULO. Medido sobre los 10 afiches que publica la Agenda Cultural del Ministerio para los
   eventos de la agenda: **7 tienen relación menor que 1.2**, porque nacen cuadrados o 4:5
   para el feed de Instagram.
   La condición la decide `SjturCards::esAfiche()` midiendo el ARCHIVO, no la carpeta ni el
   nombre: el mismo bloque mezcla afiches de eventos con fotos de fichas.
   🔸 `contain` a secas dejaría dos franjas muertas a los costados, así que el fondo es una
   copia del propio afiche desenfocada. Va como un segundo `<img>` y no como un
   `background-image` inline: la ficha de prestador se limpió de 12 estilos inline el
   2026-08-03 y reponerlos acá sería devolver el problema por la ventana. */
.gsj-event-card__media--afiche img {
  object-fit: contain;
}

.gsj-event-card__media-fondo {
  object-fit: cover;
  /* ⚠️ El `scale` no es cosmético: `blur()` samplea fuera del borde de la imagen y sin
     agrandarla primero deja un halo translúcido en los cuatro lados. */
  filter: blur(18px) saturate(1.35);
  transform: scale(1.15);
  z-index: 0;
}

/* El afiche legible siempre por encima de su propio fondo. */
.gsj-event-card__media--afiche .gsj-event-card__media-fondo ~ img {
  z-index: 1;
}
.gsj-event-card__row {
  display: flex;
  gap: var(--gsj-space-5);
  align-items: flex-start;
  padding: var(--gsj-space-5);
}

/* ---- gsj-grid-3: la grilla de 3 que el kit usa y el DS de turismo no declaraba ----
   🔴 EXISTE PORQUE KIT Y SITIO LA RESOLVÍAN DISTINTO. Ninguna de las tres hojas de turismo
   la definía: el kit la heredaba del DS Gobierno padre —que la deja con columnas AUTO, o sea
   del ancho del contenido: medido 265 / 653 / 265 px, con la tarjeta del texto más largo
   comiéndose la mitad de la banda— y el sitio la tomaba de otro lado con columnas iguales
   (394 / 394 / 394). Declararla acá, en la hoja que cargan LOS DOS, los alinea de una vez.
   Los valores son los de `ds-bundle/_ds_bundle.css`, que es la intención escrita del DS:
   una columna en mobile, dos desde 768 y tres desde 1024.
   🔴 `minmax(0, 1fr)` Y NO `1fr` A SECAS, y ésa es la parte que importa: `1fr` significa
   `minmax(auto, 1fr)`, así que una tarjeta con un texto largo —en el mock, «Lun a sáb · 10,
   11:30, 16 y 17:30 h»— empuja su columna y se come la banda: medido 265 / 653 / 265 px en
   el kit contra 394 / 394 / 394 en el sitio, que tiene textos más cortos. O sea que el ancho
   de las tarjetas dependía de cuál texto tocara, no del diseño. Con el `minmax(0, …)` el
   contenido largo envuelve en vez de estirar, y las tres columnas miden igual en los dos
   lados sea cual sea el evento. */
.gsj-grid-3 {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gsj-space-6);
}
@media (min-width: 768px) {
  .gsj-grid-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .gsj-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---- Info práctica: bloque icono + dato (clima, temporada, cómo llegar) ---- */
.gsj-info-block {
  display: flex;
  gap: var(--gsj-space-4);
  align-items: flex-start;
}
.gsj-info-block__icon {
  flex-shrink: 0;
  width: 48px;
  height: 48px;
  border-radius: var(--gsj-radius-md);
  background-color: rgba(255, 130, 0, 0.1);
  color: var(--gsj-color-primary);
  display: flex;
  align-items: center;
  justify-content: center;
}
.gsj-info-block__icon svg {
  width: 24px;
  height: 24px;
}
.gsj-info-block__label {
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-footnote);
  font-weight: 600;
  color: var(--gsj-color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin: 0 0 4px;
}
.gsj-info-block__value {
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-headline);
  font-weight: 600;
  color: var(--gsj-color-text);
  margin: 0;
}

/* ---- Map pin inline (usado en cards y breadcrumbs de ubicación) ---- */
.gsj-map-pin {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--gsj-font-heading);
  font-size: var(--gsj-font-size-subheadline);
  color: var(--gsj-color-text-secondary);
}
.gsj-map-pin svg {
  width: 16px;
  height: 16px;
  color: var(--gsj-color-primary);
  flex-shrink: 0;
}

/* ---- Mapa placeholder card (mapa interactivo de atractivos) ---- */
.gsj-map-card {
  position: relative;
  border-radius: var(--gsj-radius-card);
  overflow: hidden;
  border: 1px solid var(--gsj-color-border);
  background: var(--gsj-color-bg-subtle);
}
.gsj-map-card__canvas {
  aspect-ratio: 16/10;
  width: 100%;
  position: relative;
}

/* 🔴 EL EMBEBIDO LLENA LA CAJA, Y LA REGLA VIVE EN EL COMPONENTE — 15.ª aparición
   del bug de la propiedad no declarada, y la primera que ROMPE algo visible.
   `__canvas` fija la caja con `aspect-ratio`, pero el `<iframe>` de adentro no
   hereda nada: sin esto cae a su tamaño intrínseco por defecto (**300×150**) y
   queda un mapita en el ángulo de una tarjeta de 718×449. Medido en la ficha de
   Prestador el 2026-08-03, y lo reportó el usuario mirando la página.
   Estaba resuelto DOS VECES a mano y en ningún lado reusable: `default_atractivo.php`
   con la clase de página `.atr-mapa` y `default_prestador.php` con un `style=""`
   inline en el propio `<iframe>` — que al pasar la ficha a cero inline se fue, y con
   él el tamaño del mapa. Puesto acá lo hereda cualquier consumidor del componente y
   no hay que acordarse. (`.atr-mapa` queda como estaba: es inofensiva y declara lo
   mismo.) */
.gsj-map-card__canvas iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.gsj-map-card__marker {
  position: absolute;
  transform: translate(-50%, -100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
}
.gsj-map-card__marker-dot {
  width: 32px;
  height: 32px;
  border-radius: 50% 50% 50% 0;
  transform: rotate(-45deg);
  background-color: var(--gsj-color-primary);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform var(--gsj-duration-fast) var(--gsj-ease-standard);
}
.gsj-map-card__marker:hover .gsj-map-card__marker-dot {
  transform: rotate(-45deg) scale(1.15);
}
.gsj-map-card__marker-dot svg {
  transform: rotate(45deg);
  width: 16px;
  height: 16px;
  color: #fff;
}
.gsj-map-card__marker-label {
  margin-top: 6px;
  background: rgba(0, 0, 0, 0.72);
  color: #fff;
  font-family: var(--gsj-font-heading);
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: var(--gsj-radius-full);
  white-space: nowrap;
}

/* ---- Season / weather chip ---- */
.gsj-season-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--gsj-space-2);
}

/* ===== Menú mobile (drawer) — patrón de sitio. Lo abre .gsj-header__menu-toggle; comportamiento en gsj-nav-drawer.js ===== */
.gsj-nav-drawer[hidden] {
  display: none;
}

.gsj-nav-drawer {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: #fff;
  display: flex;
  flex-direction: column;
}

.gsj-nav-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border-bottom: 1px solid var(--gsj-color-border);
}

.gsj-nav-drawer__title {
  font-family: var(--gsj-font-heading);
  font-weight: 700;
  font-size: 17px;
  color: var(--gsj-color-text);
}

.gsj-nav-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: var(--gsj-radius-full);
  background: var(--gsj-color-bg-subtle);
  color: var(--gsj-color-text);
  cursor: pointer;
}

.gsj-nav-drawer__close:focus-visible {
  outline: 3px solid var(--gsj-color-primary);
  outline-offset: 2px;
}

.gsj-nav-drawer__search {
  padding: 16px;
}

.gsj-nav-drawer__nav {
  display: flex;
  flex-direction: column;
}

.gsj-nav-drawer__nav a {
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 0 20px;
  border-bottom: 1px solid var(--gsj-color-border);
  font-family: var(--gsj-font-heading);
  font-size: 17px;
  color: var(--gsj-color-text);
  text-decoration: none;
}

.gsj-nav-drawer__nav a:hover {
  background: var(--gsj-color-bg-subtle);
}

.gsj-nav-drawer__foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  background: var(--gsj-color-bg-subtle);
}

.gsj-nav-drawer__foot a {
  font-size: 14px;
  color: var(--gsj-color-text-secondary);
}

@media (min-width: 1024px) {
  .gsj-nav-drawer {
    display: none;
  }
}

/* ============================================================
   Utilidades de accesibilidad (Bloque 2 a11y, 2026-07-28)
   ============================================================ */

/* Texto solo para lectores de pantalla. Da nombre accesible real a controles
   cuyo contenido visible es puramente gráfico (glifos de estrellas, iconos,
   imágenes de tapa), sin cambiar nada de lo que se ve.
   Estaba definida suelta en el <style> de index.html; se promueve acá porque
   el Bloque 2 la necesita en varias pantallas. */
.u-sr-only {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------------
   Dots del carrusel: blanco táctil que se pisaba a sí mismo
   (Bloque 2 a11y, 2026-07-28)

   El DS base agranda el área clickable de cada dot con
   `.gsj-carousel__dot::before {inset:-18px}` = 44x44px. Pero los deja
   separados 8px, así que con 8px de ancho los centros quedan a 16px:
   cada expansor de 44px TAPA POR COMPLETO a sus vecinos de la izquierda
   y, siendo hermanos posicionados, gana el último del DOM.

   Efecto medido con elementFromPoint sobre el render (scratchpad/dots_hit3.js):
   tocar el dot 0 activaba el 1, el 1 activaba el 2, el 2 el 3. Solo el
   último dot respondía a su propia posición. No lo veía ni la auditoría
   ni el conteo de tamaños, porque el área es correcta: lo que fallaba
   era a quién le llegaba el clic.

   Arreglo: separarlos 16px y acotar el expansor en horizontal a la mitad
   de la separación (-8px). Queda un blanco de 24x44px por dot, sin solape
   y cumpliendo el mínimo de 24px de WCAG 2.2 SC 2.5.8.
   ------------------------------------------------------------------ */
.gsj-carousel__dots {
  gap: 16px;
}

.gsj-carousel__dot::before {
  inset: -18px -8px;
}

/* ==========================================================================
   Escala de títulos de página (2026-07-28)

   Lo detectó el usuario navegando: "el título Agenda de San Juan queda más chico
   que los titulares de otras secciones". Medido sobre el render, el kit tenía
   NUEVE tamaños de H1 distintos a 1280px (24 · 32 · 34 · 36 · 38 · 44,8 · 48 ·
   54,4 · 57,6), casi todos por `style="font-size:…"` inline puesto en la sesión
   en que se canonizó cada pantalla. Eso no es jerarquía, es acumulación.

   El estándar ya existía y venía del DS base: `.gsj-section-header__title` =
   clamp(2rem, 4vw, 3rem) → 48px @1280 / 32px @390, que usaban 17 de 27 pantallas.
   La regla es entonces NO OVERRIDEAR, y declarar las dos únicas excepciones
   legítimas como familias con nombre:

     1. Título de página en flujo  → 48/32, el default del base. Sin clase extra.
     2. Título sobre hero          → art dirigido por pantalla (Home, Destino,
        Experiencia, Atractivo). NO se unifica: cada uno está compuesto contra su
        propia imagen. Queda fuera de esta escala a propósito.
     3. Título que es NOMBRE PROPIO → `--nombre`, más chico y con `text-wrap:balance`.

   Por qué la 3 existe: en las fichas el H1 no es un rótulo corto que escribimos
   nosotros, es un dato del catálogo. Medido en la base: 826 títulos publicados,
   media 19 caracteres pero **máximo 82** y 37 por encima de 40 ("FG Mountain
   Guide | Guía de escalada en roca | Trekking Precordillera y Cordillera").
   A 48px eso ocupa media pantalla; y en `prestador` el H1 sólo dispone de 246px
   de ancho a 390px. Un tamaño menor acá no es inconsistencia: es la respuesta a
   un contenido de largo variable que no controlamos.
   ========================================================================== */

/* Nombre propio de un registro (ficha de prestador, de evento, de promo). */
.gsj-page-title--nombre {
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);   /* 40px @1280 · 28px @390 */
  line-height: var(--gsj-line-height-tight);
  text-wrap: balance;
  /* 🔴 `margin: 0` DECLARADO, no heredado — 14.ª aparición del bug que el kit no
     puede revelar. Las tres pantallas del kit que usan esta clase le ponen el
     margen INLINE (`style="margin:0"`), así que el kit se ve bien y la clase
     nunca tuvo que declararlo. En el sitio, en cambio, Gantry sirve nucleus con
     `h1..h6 { margin: .75rem 0 1.5rem }`, y al pasar la ficha de Prestador a cero
     estilos inline el título se comió 12px arriba y 24px abajo que el diseño no
     tiene. El contenedor de cada ficha ya separa con su propio `gap`. */
  margin: 0;
}

/* ==========================================================================
   Estrellas de categoría hotelera — contraste (2026-07-28, cierra el Bloque 2)

   El widget encodeaba la categoría SOLO en el color de un glifo:
     .gsj-stars        → --gsj-color-accent = #ffc100 → 1.63:1 sobre blanco
     .gsj-stars--empty → --gsj-color-text-placeholder → 1.73:1
   A 1.63:1 las estrellas son prácticamente invisibles con baja visión o con la
   pantalla al sol. La accesibilidad semántica ya estaba resuelta (label real en
   `u-sr-only` + glifos `aria-hidden`), pero eso no ayuda a quien MIRA la pantalla.

   Dos cambios, ninguno toca el token de marca:

   1. Token propio `--gsj-color-stars`, derivado del ámbar de marca conservando su
      tono exacto (85.5deg) y bajando la claridad hasta el punto más alto que
      alcanza 3:1 — el mínimo de WCAG 1.4.11 para un objeto gráfico portador de
      información. Medido resolviendo oklch a sRGB sobre el render
      (scratchpad/estrellas_color.js): 66% → #bb8900 → 3.14:1. Un paso más claro
      (68%) da 2.91:1 y no llega. Es el mismo patrón que ya se usó con el naranja:
      no se cambia el color de marca, se deriva uno para el uso que lo necesita.

   2. La estrella vacía pasa a ser un CONTORNO (☆) en vez de una estrella llena en
      gris claro. Así la diferencia entre llena y vacía deja de depender del
      contraste entre dos colores pálidos y pasa a ser de FORMA: se lee igual en
      escala de grises, con daltonismo o con el brillo bajo. El gris del contorno
      puede ser suave justamente porque ya no carga la información solo.
   ========================================================================== */
:root {
  --gsj-color-stars: oklch(66% 0.15 85.5deg);   /* #bb8900 — 3.14:1 sobre blanco */
}

/* Las REGLAS de .gsj-stars viven en turismo-components.css, que es donde el
   selector se define y que se importa DESPUES de este archivo: puestas acá
   perdían por orden de cascada aunque la especificidad fuera igual. */

/* ==========================================================================
   Grilla de página (2026-07-28)

   Lo detectó el usuario navegando: "el ancho del main o contenido de las páginas
   es distinto entre sí… no quiero ver esos saltos al navegar, o que el breadcrumb
   no esté en la misma posición". Medido sobre el render a 1280px, el contenido
   arrancaba en TRECE bordes izquierdos distintos y el breadcrumb en SIETE.

   La causa es la misma que la de los títulos: el DS base ya definía la grilla
   —`.gsj-container` = max-width 1280px + padding 24px, o sea contenido de 1232px
   arrancando en x=24— pero nueve pantallas se declararon su propio contenedor en
   la sesión en que se canonizaron: 1120px (contacto, destino, experiencia,
   folletos, institucional, oficinas), 1200px (destinos, experiencias) y 1140px
   (atractivo). Ninguno de esos números respondía a una decisión: eran defaults
   distintos de sesiones distintas.

   Ahora todos los `*-main` de página heredan la misma medida vía `.gsj-page-main`.
   Se conserva el `padding-bottom` propio de cada una (es aire de cierre, no grilla).

   Excepción declarada: `.gsj-prosa` — columna de lectura angosta (~68ch) para
   contenido informativo largo. Ahí la medida corta NO es inconsistencia, es
   legibilidad: una línea de 1232px es imposible de leer de corrido. Pero el
   breadcrumb y el resto del cromo de navegación quedan sobre la grilla común,
   así lo único angosto es el texto y se lee como decisión, no como error.
   ========================================================================== */
.gsj-page-main {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: 24px;
}

/* Columna de lectura para prosa larga (artículos de Contenido Informativo). */
.gsj-prosa {
  max-width: 820px;
  margin-inline: auto;
}

/* Direcciones de correo largas: `relacionesinstitucionalesmtyc@gmail.com` (39 caracteres,
   sin espacios) no tiene dónde cortar y desbordaba el viewport a 360px en institucional.
   Es un dato del organismo que se repite, así que la regla va al DS y no a la pantalla.
   `anywhere` (y no `break-word`) para que además NO ensanche la caja al calcular min-content. */
a[href^="mailto:"] {
  overflow-wrap: anywhere;
}

/* ==========================================================================
   Carrusel: la sombra del hover se cortaba (2026-07-28)

   `.gsj-carousel__track` del DS base tiene `overflow-x:auto`, y la spec obliga a que
   el otro eje compute a `auto` también — un contenedor de scroll NO puede tener un eje
   `visible`. O sea que **recorta en vertical aunque nadie se lo haya pedido**.

   Con `padding-block: 8px` y una card cuyo hover hace `translateY(-8px)` +
   `box-shadow: 0 20px 25px -5px`, la sombra necesita 40−8 = **32px** por debajo y solo
   tenía 8: se cortaba en seco al pasar el mouse. Lo mismo a los lados (la sombra se
   extiende 20px y el borde izquierdo no tenía padding).

   Se agranda el padding lo que la sombra necesita y se compensa con un margen negativo
   exactamente igual, así **el track gana aire sin que nada cambie de posición**: las
   cards siguen alineadas a la grilla de página. Verificado con diff de estilos computados.
   ========================================================================== */
.gsj-carousel__track {
  padding-block: 8px 40px;
  margin-block-end: -32px;
  padding-inline: 24px 40px;
  margin-inline: -24px;
  /* El track tiene `scroll-snap-type`, y el snap NO conoce el padding nuevo: en el riel
     que desborda arrastraba la primera card hasta el borde (scrollLeft quedaba en 24) y
     la dejaba otra vez sin aire para su sombra. `scroll-padding` se lo informa. */
  scroll-padding-inline-start: 24px;
}

/* ────────────────────────────────────────────────────────────────────────────
   🔴 MÁRGENES PROPIOS: UNA CLASE DE BUG QUE EL KIT NO PUEDE REVELAR (H2.5.2.1)
   Gantry sirve `nucleus.css` + `bootstrap5.css`, que traen `p { margin: 1.5rem 0 }`.
   Las pantallas del kit son HTML suelto y cargan SÓLO el bundle del DS, así que
   ahí esa regla NO EXISTE. Consecuencia: todo componente `gsj-*` que use <p> o un
   heading y dé por sentado que el margen es 0 se ve BIEN EN EL KIT e INFLADO EN EL
   SITIO. Se destapó midiendo `gsj-info-block`, que salía **98px de alto contra los
   54px del kit** — 44px de aire muerto, y se leía como un bloque desarmado.

   Aparecieron SEIS, y **dos son del footer**, o sea que el bug viajaba en todas las
   páginas del template nuevo desde H2.2 sin que nadie lo viera.

   REGLA QUE SALE DE ESTO: un elemento con clase `gsj-*` declara SUS PROPIOS
   márgenes, siempre, aunque sean 0. No se puede confiar en el margen ambiente.
   NO se resuelve con un reset tipo `p[class*="gsj-"]{margin:0}`: eso tiene más
   especificidad que las reglas de los propios componentes y les pisaría los
   `margin-bottom` intencionales.
   Guardia de regresión: `scratchpad/margenes_gsj.js`, que compara el margen
   computado de cada componente contra su par del kit.
   ──────────────────────────────────────────────────────────────────────────── */

.gsj-footer__tagline,
.gsj-footer__contact-label {
  margin: 0;
}

/* 🔴 SÉPTIMA APARICIÓN DEL BUG DE MÁRGENES PROPIOS, y la de mayor alcance: el título
   del `gsj-section-header` vive en el DS padre (`portal.css`) y no declara margen, así
   que el `h2 { margin: … }` de nucleus le mete **12px arriba y 24px abajo** y descuelga
   el encabezado de su sección. No es de la ficha de Atractivo: viaja en TODA sección que
   use `gsj-section-header`, en todo el sitio. Se corrige acá porque ésta es la capa de
   overrides del DS padre. */
.gsj-section-header__title {
  margin: 0;
}


/* 🔴 OCTAVA Y NOVENA APARICIÓN DEL BUG DE MÁRGENES PROPIOS, las dos en `gsj-card` y las
   dos encontradas midiendo el hub de Experiencias (H2.6). El DS padre declara el margen de
   ABAJO de las dos y se olvida del de ARRIBA:
     `.gsj-card__title` → `margin-bottom: 8px`  y nada arriba → nucleus le mete **12px**
     `.gsj-card__text`  → `margin-bottom: 20px` y nada arriba → `p{margin:1.5rem 0}` → **24px**
   Efecto: el cuerpo de CADA tarjeta del sitio queda 36px más alto que en el kit, y como
   `gsj-card` es la card genérica, viaja por los 16 listados. Declarar sólo el margen que
   uno "usa" es justo la trampa que la regla previene: se ve bien en el kit —que no carga
   nucleus— y mal en el sitio. Se conservan los `margin-bottom` del DS.

   ⚠️ Y ojo con el corolario: la de arriba (`gsj-section-header__title`) se resolvió con
   `margin: 0` porque ese título no lleva margen abajo. Acá `margin: 0` BORRARÍA la
   separación entre el título y el texto de la tarjeta. Por eso van sólo los de arriba. */
.gsj-card__title,
.gsj-card__text {
  margin-top: 0;
}

/* 🔴 DÉCIMA APARICIÓN DEL BUG DE MÁRGENES PROPIOS, medida al implementar el hero
   compacto de `/planifica-tu-viaje` (H2.7): 12px arriba y 24px abajo contra los 0 del
   kit. `.gsj-hero__title` del DS padre declara familia, tamaño, peso, color,
   interlineado, tracking, ancho máximo y `text-wrap` — todo menos el margen, y el
   `h1,h2,…{margin:…}` de nucleus se lo pone. El subtítulo va con él por el
   `p{margin:1.5rem 0}`, que es el mismo defecto de `.gsj-card__text`.
   No es de una página: viaja en TODO hero del sitio, y los tres del kit que todavía no
   se implementaron (error-404, error-500, sección en preparación) lo traen igual.
   Se conservan los márgenes ABAJO que el hero necesita para separar sus tres líneas,
   como en `.gsj-card__title`: acá `margin: 0` pegotearía eyebrow, título y bajada. */
.gsj-hero__title,
.gsj-hero__eyebrow,
.gsj-hero__subtitle {
  margin-top: 0;
}

.gsj-hero__title {
  margin-bottom: 0;
}

/* El aire interno de la tarjeta genérica. Estaba como `style="padding:28px"` INLINE en
   `SjturCards::tarjetaSimple()`, o sea imposible de ajustar por contexto sin tocar PHP —
   y el contexto importa: el kit usa 22px en el hub de Experiencias, 28 en otras 12
   pantallas, 24 en 6 y 32 en 3. El default queda en 28 (idéntico a lo que emitía el
   inline, así que las pantallas ya medidas no se mueven) y el hub lo baja a 22 desde
   `turismo-pages.css`. El DS padre trae 40px, que no lo usa ninguna pantalla del kit. */
.gsj-card__body {
  padding: 28px;
}


/* 🔴 LA TIRA NO SE PODÍA SCROLLEAR CON MOUSE: `.gsj-gallery__track` esconde su barra
   (`scrollbar-width: none` + `::-webkit-scrollbar{display:none}`), así que con touch se
   arrastra pero en escritorio no había NINGUNA forma de ver las fotos que quedaban fuera.
   La variante de tira es la de 1 a 4 fotos —127 de las 297 galerías, casi la mitad— y a
   ancho de escritorio **entran todas**: ahí el scroller no tiene razón de existir.
   ⇒ De 720px para arriba la tira se vuelve grilla y no desborda. Abajo sigue siendo
   scroller táctil, que es donde el patrón sirve. */
@media (min-width: 720px) {
  .gsj-gallery__track {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    overflow-x: visible;
  }
  .gsj-gallery__track > .gsj-gallery__item {
    width: auto;
  }
  /* 2026-09-24 ([U]: «cuando hay una o 2 fotos se ve gigante»): con `auto-fit` una sola foto llenaba el
     hueco entero (1232×924) y dos, la mitad cada una. Con 1-2 fotos se usan las MISMAS columnas que
     tendría una tira de 3 (400px a 1280; 2 columnas de 328 entre 720 y 799) y quedan a la izquierda.
     El selector: el primer hijo es también uno de los dos últimos ⇒ la tira tiene 1 o 2 fotos.
     `SjturGaleria::sizesTira()` calcula igual (trata 1-2 como 3). Sin `:has()` queda como antes. */
  .gsj-gallery__track:has(> .gsj-gallery__item:first-child:nth-last-child(-n + 2)) {
    grid-template-columns: repeat(auto-fill, minmax(max(240px, calc((100% - 32px) / 3)), 1fr));
  }
}


/* 🔴 OTRA APARICIÓN DEL BUG DE MÁRGENES PROPIOS (la del DIRECTORIO, H2.7): `.gsj-checkbox`
   declara tamaño, borde y `margin-top: 2px` en el DS padre — y NADA en los otros tres
   lados, así que el estilado de `input` del sitio le mete **6px de margen derecho**
   (medido contra el kit: el contador de la faceta corría 6px). Como `gsj-facets__option`
   ya separa con `gap: 10px`, el margen ambiente se suma al gap y descoloca todas las
   casillas del rail — en el directorio, en la búsqueda y en el sheet, que comparten el
   componente. El `margin-top: 2px` del DS se conserva: sólo se declaran los lados que el
   DS se olvidó de declarar.
   ⚠️ CON `input.` ADELANTE, y no es estilo: el culpable es `nucleus.css` con
   `input[type="checkbox"] { margin-right: .375rem }` — especificidad (0,1,1). Un
   `.gsj-checkbox` pelado (0,1,0) pierde contra eso aunque cargue después (medido: la
   regla servida y el margen intacto). */
input.gsj-checkbox,
input.gsj-radio {
  margin-right: 0;
  margin-bottom: 0;
  margin-left: 0;
}


/* 🔴 14.ª APARICIÓN DEL BUG QUE EL KIT NO PUEDE REVELAR — ahora los CAMPOS DE FORMULARIO.
   Salió al implementar `/contacto`, que es el único formulario de datos del sitio (los
   otros cuatro `.gsj-input` son cajas de búsqueda `type="search"` y están a salvo, ver
   abajo). Son DOS hojas que el kit no carga y que le ganan al DS:

     · `hydrogen-joomla_28.css:185` — la capa que Gantry compila para vestir el markup de
       Joomla — `textarea, input[type="text"], input[type="email"], … {
       background-color:#fff; border:1px solid #ddd; box-shadow: inset 0 1px 1px rgba(0,0,0,.075) }`
     · `nucleus.css:1291` — `input[type="text"], input[type="email"], … { padding:.375rem }`

   Las dos valen (0,1,1) y `.gsj-input` vale (0,1,0): **el DS pierde aunque su hoja cargue
   después**, que es la trampa de siempre. Medido en `/contacto` antes de esta guardia, los
   tres inputs salían con 6px de padding en lugar de `0 16px`, **fondo blanco en lugar del
   beige del DS**, borde de 1px #ddd en vez del hairline de .5px y una sombra interior que
   el diseño no tiene.

   EL `textarea` SÓLO SE LLEVA LA SOMBRA, y por eso va aparte: para él las dos reglas son
   `textarea` a secas (0,0,1), así que `.gsj-textarea` (0,1,0) gana el padding, el borde y
   el fondo. Lo único que le queda es el `box-shadow`, que el DS no declara y por lo tanto
   nadie disputa.

   POR QUÉ LAS CAJAS DE BÚSQUEDA NO NECESITAN NADA: las cuatro (`sjturheader`,
   `sjturhero`, y las dos de `com_finder`) viven bajo `.gsj-hero__search-form .gsj-input`,
   que vale (0,2,0) y le gana a todo esto. Verificado midiendo `/`, `/servicios/alojamiento`
   y el drawer de `/contacto` antes y después: no se movió ni un píxel.

   Guardia con `input.` adelante para empatar en (0,1,1) y ganar por orden de hoja. Los
   estados siguen mandando: `.gsj-input:hover` y `.gsj-input:focus` valen (0,2,0). */
input.gsj-input {
  padding: 0 16px;
  background-color: var(--gsj-color-bg-muted);
  border: .5px solid var(--gsj-color-border);
  box-shadow: none;
}

textarea.gsj-textarea {
  box-shadow: none;
}

/* Y la 15.ª, del mismo formulario: `label { margin-bottom: .375rem }` (`nucleus.css:1274`).
   `.gsj-checkbox-label` declara display, gap, tipografía, alto mínimo y padding — todo
   menos sus márgenes—, así que el label de «Enviarme una copia» arrastraba 6px de más
   contra el botón de envío. `.gsj-label` no lo sufre: ése sí declara `margin-bottom`. */
label.gsj-checkbox-label,
label.gsj-radio-label {
  margin-bottom: 0;
}
