/* =========================================================================
 * DISEÑO GLOBAL DEL TEMA — placeholder (slot de UX/UI).
 *
 * Asset GLOBAL del tema: vive en la RAÍZ del tema (`assets/`), no bajo
 * `elementor-widgets/`, porque estila TODO el sitio (header/footer + todos los
 * widgets). Lo REEMPLAZARÁ el CSS único que entregue el equipo de UX/UI. Se encola
 * site-wide desde functions.php (junto a style.css), no por widget.
 *
 * Lo FUNCIONAL de los widgets (ocultar estados con [hidden]) vive en
 * `elementor-widgets/assets/tv-widgets.css`, que NO se reemplaza. Ver
 * `elementor-widgets/CONVENTIONS.md` (modelo de dos vías).
 * ========================================================================= */

/* =========================================================================
 * Tipografía: Poppins (self-hosted) en TODO el sitio.
 * Elementor trae Roboto por defecto (variables --e-global-typography-* + CSS del
 * "kit") que gana al font del tema. Se fija Poppins en esas variables globales y,
 * con prioridad, en body + contenedor de widgets, para que el diseño use Poppins de
 * punta a punta (shell + widgets).
 * @font-face duplicado aquí (además de base.css) porque este archivo se encola con
 * ?ver= y se refresca en cada bump; el @import de base.css NO lleva versión y puede
 * quedar cacheado. Rutas relativas a assets/theme-ui.css → assets/frontend/fonts/.
 * ========================================================================= */
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 400; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-Regular.ttf") format("truetype"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 500; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-Medium.ttf") format("truetype"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 600; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-SemiBold.ttf") format("truetype"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 700; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-Bold.ttf") format("truetype"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 800; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-ExtraBold.ttf") format("truetype"); }
@font-face { font-family: "Poppins"; font-style: normal; font-weight: 900; font-display: swap; src: url("frontend/fonts/Poppins/Poppins-Black.ttf") format("truetype"); }
:root,
body {
	--e-global-typography-primary-font-family: "Poppins";
	--e-global-typography-secondary-font-family: "Poppins";
	--e-global-typography-text-font-family: "Poppins";
	--e-global-typography-accent-font-family: "Poppins";
}
body,
.elementor-widget-container { font-family: var(--tv-font) !important; }

.tv-widget {
	margin: 0 0 1rem;
}

/* --- Widgets de API: cabecera (título + bajada) arriba, botón al FINAL --- */
.tv-widget__header { margin: 0 0 1rem; }
.tv-widget__title { font-size: 1.4rem; margin: 0 0 .35rem; color: var(--tv-theme-color, #6eb92b); }
.tv-widget__bajada { margin: 0 0 .6rem; color: #666; }
.tv-widget__footer { margin: 1rem 0 0; }
.tv-widget__button {
	display: inline-block;
	padding: .5rem 1rem;
	background: var(--tv-theme-color, #6eb92b);
	color: #fff;
	border-radius: 6px;
	text-decoration: none;
}

/* Estados/notas de los widgets y áreas = TEXTO PLANO. Resetea el "pill" (borde +
   punto) que la entrega aplica a .tv-status (badge), para que nuestros mensajes no
   salgan como pastillas. Los badges del template se usan con clases propias
   (p. ej. .tv-brand-count). Colisión de la iteración previa, resuelta por reset. */
.tv-status {
	display: block;
	margin: 0 0 .5rem;
	padding: 0;
	border: 0;
	border-radius: 0;
	gap: 0;
	color: #666;
	font-size: .9rem;
}
.tv-status::before { content: none; }

.tv-status[data-state="error"] {
	color: #b00020;
}

/* --- Widget Sectores (diseño de ejemplo, se reemplaza con el de UX/UI) --- */

.tv-sectores__title {
	color: var(--tv-theme-color, #6eb92b);
	margin: 0 0 .5rem;
	font-size: 1.1rem;
}

.tv-sectores__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: .5rem;
}

.tv-sectores__item {
	background: var(--tv-theme-color, #6eb92b);
	color: #fff;
	border-radius: 999px;
	padding: .25rem .75rem;
	font-size: .9rem;
	line-height: 1.4;
	animation: tv-fade-in .25s ease both;
}

@keyframes tv-fade-in {
	from { opacity: 0; transform: translateY(4px); }
	to   { opacity: 1; transform: translateY(0); }
}

/* --- Noticias · listado/archivo (v3): header .tv-public-heading + filtro .tv-public-filters +
   .tv-news-list (grid de cards .tv-news-list-card). El HOME usa .tv-landing-news-card. --- */
/* Cabecera sticky (como comercios): sin el offset de header fijo de v3. */
.tv-noticias--archivo .tv-public-heading { padding-top: 16px; }
/* Grid del archivo: una columna que NO crece con textos largos (min-width:0 → truncan). */
.tv-news-list__page { grid-template-columns: minmax(0, 1fr); }
.tv-news-list__page > * { min-width: 0; }
/* Card = <a> (la entrega usa <button>): sin subrayado. */
a.tv-news-list-card { text-decoration: none; }
/* Imagen destacada real dentro del media de 50px (recorta); si no hay, va el placeholder gris+logo. */
.tv-news-list-card__image { width: 100%; height: 100%; object-fit: cover; }
/* Home: card de ANCHO FIJO (la entrega solo da min-width → con títulos largos el flex-basis:auto la
   estiraba) + título a 2 líneas (…) → todas las noticias del MISMO tamaño en el scroller. */
.tv-landing-news-card { width: 240px; }
.tv-landing-news-card__body strong { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
/* Home (v3): imagen destacada real dentro del media de .tv-landing-news-card (recuadro de 116px). Si
   el post no tiene imagen, va el PLACEHOLDER del sitio (.tv-img-ph). Apunta SOLO a la imagen real. */
.tv-landing-news-card__image { width: 100%; height: 116px; }
img.tv-landing-news-card__image { height: 116px !important; object-fit: cover; object-position: center; }

/* =========================================================================
 * Placeholder de imagen faltante (REUTILIZABLE): fondo gris claro + logo (blanco)
 * centrado. Se aplica en cualquier hueco de imagen sin foto (noticias, comercios,
 * beneficios, detalle…) vía tv_img_placeholder() o añadiendo .tv-img-ph al hueco.
 * NOTA: el logo es blanco; si sobre este gris queda muy tenue, subir el tono del
 * fondo (o usar el logo oscuro).
 * ========================================================================= */
.tv-img-ph { display: flex; align-items: center; justify-content: center; background: var(--tv-color-line, #e2e5ec); }
.tv-img-ph__logo { width: 55%; max-width: 130px; height: auto; object-fit: contain; opacity: .9; }

/* Paginación con el estilo del template (.tv-pagination / feedback.css): botones 34×34,
   activo verde. La LÓGICA es paginate_links() de WP (salida .page-numbers); aquí solo se
   estiliza esa salida. (.tv-noticias__more se retiró: «Últimas» usa .tv-button--compact.) */
.tv-noticias__pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0;
	padding-top: 14px;
}
.tv-noticias__pagination .page-numbers {
	min-width: 34px;
	height: 34px;
	padding: 0 8px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--tv-color-line, #e2e5ec);
	border-radius: var(--tv-radius-md, 10px);
	background: #fff;
	color: var(--tv-color-muted, #6b7a99);
	font-size: 13px;
	font-weight: 700;
	text-decoration: none;
}
.tv-noticias__pagination a.page-numbers:hover,
.tv-noticias__pagination a.page-numbers:focus { border-color: var(--tv-color-secondary, #3b8925); color: var(--tv-color-secondary, #3b8925); }
.tv-noticias__pagination .page-numbers.current {
	border-color: var(--tv-color-secondary, #3b8925);
	background: var(--tv-color-secondary, #3b8925);
	color: #fff;
}
.tv-noticias__pagination .page-numbers.dots { border-color: transparent; background: transparent; }

/* --- Single de noticia (v3 `news-detail`): back + media (imagen real) + artículo (meta/título/
   lead/cuerpo) + compartir. La entrega estila .tv-news-detail__* y .tv-news-share*; aquí: back <a>,
   cabecera sticky, imagen del media, tipografía extra del cuerpo (lo que la entrega no cubre) e
   icono X relleno. Deprecado el .tv-single__topbar/hero/eyebrow/title/lead del tag previo. --- */
/* Botones volver = <a> (la entrega asume <button>): sin subrayado. */
a.tv-icon-button,
a.tv-public-back { text-decoration: none; }
/* Cabecera sticky (nuestra), no fija: sin el offset de header fijo de v3. */
.tv-news-detail__top { padding-top: 12px; }
/* Media con imagen destacada real: llena los 178px y recorta CENTRADO (como las cards de noticias:
   alto fijo + object-position center; el !important vence el height:auto de WP/Elementor). */
.tv-news-detail__media { overflow: hidden; }
.tv-news-detail__image { width: 100%; height: 178px; }
img.tv-news-detail__image { height: 178px !important; object-fit: cover; object-position: center; }
/* Cuerpo (the_content): la entrega estila los <p>; aquí lo que NO cubre (encabezados, imágenes, enlaces). */
.tv-news-detail__body h2,
.tv-news-detail__body h3 { color: var(--tv-color-ink, #214654); margin: 1.2em 0 .4em; }
.tv-news-detail__body img { max-width: 100%; height: auto; border-radius: 12px; }
.tv-news-detail__body a { color: var(--tv-color-secondary, #3b8925); }
/* Icono X: la base .tv-icon es fill:none/stroke → el logo X va RELLENO (no el pajarito de Twitter). */
.tv-icon--filled { fill: currentColor; stroke: none; }
/* Los botones de compartir son <a> (la entrega usa <button>): sin subrayado ni color de enlace. */
.tv-news-share__item { text-decoration: none; color: inherit; }
/* Prev/next entre entradas: botones verdes (se conservan; NO la paginación numerada del template). */
.tv-single__nav { margin: 1.25rem 0 0; padding: 0 15px; }
.tv-single__nav .nav-links { display: flex; justify-content: space-between; gap: 1rem; }
/* Prev/next como botones verdes del sitio (misma línea que .tv-button). */
.tv-single__nav a {
	display: inline-flex;
	align-items: center;
	padding: .55rem .9rem;
	border-radius: var(--tv-radius-sm, 6px);
	background: var(--tv-color-secondary, #3b8925);
	color: #fff;
	font-size: .85rem;
	font-weight: 700;
	line-height: 1.3;
	text-decoration: none;
}
.tv-single__nav a:hover,
.tv-single__nav a:focus { background: #32751f; }

/* --- Comercios · listado «Todos los comercios» (v3): header .tv-public-heading (back + título +
   copy + búsqueda + filtros) + lista .tv-public-list (grid) con cards .tv-public-list-card, todo de
   la entrega. Reconciliaciones mínimas abajo. --- */
/* Icono Lucide de la categoría dentro del filtro (solo si la API trae `icono`); el gap del
   .tv-public-filters__item da la separación. Compartido con el listado de beneficios. */
.tv-cat__ico { display: inline-flex; width: 14px; height: 14px; }
.tv-cat__ico svg { width: 14px; height: 14px; }
/* Card: .tv-comercio / .tv-beneficio (li) = CONTENEDOR (hook de analítica/favoritos). La caja + el
   truncado del nombre/meta los da .tv-public-list-card (entrega). La lista es un <ul> con <li>
   (la entrega usa <button>) → quitamos marcador y margen por defecto del navegador. */
.tv-public-list { list-style: none; margin: 0; grid-template-columns: minmax(0, 1fr); }
/* Las cards no deben crecer con textos largos: el item del grid (li) tiene min-width:auto por
   defecto y no se encoge → la caja se pasaba del borde. minmax(0,1fr) + min-width:0 la limitan
   a la columna y el texto TRUNCA (…), conservando el margen lateral de .tv-public-list (15px). */
.tv-public-list > li { min-width: 0; }
.tv-comercio { list-style: none; margin: 0; }
a.tv-public-list-card { text-decoration: none; }
.tv-comercio .tv-fav-btn { margin-top: 6px; }
/* Logo v3: imagen real → caja neutra; ajuste del <img> dentro del tile de 50px. */
.tv-public-list-card__logo--img { background: #f2f4f8; }
.tv-public-list-card__logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Iconos/logos REDONDEADOS como el template: la entrega redondea los tiles (border-radius), pero con
   una IMAGEN real (logo/foto/placeholder) en vez de la letra del demo, la imagen se salía de las
   esquinas → se recorta al radio. Cubre listados de comercios/beneficios, single de comercio,
   comercios del single de beneficio, y el media de las noticias del archivo. */
.tv-public-list-card__logo,
.tv-news-list-card__media,
.tv-brand-detail__logo { overflow: hidden; }
/* Nuestra cabecera es STICKY (toma su espacio), no fija → el padding-top de 96px de v3 sobra. */
.tv-comercios .tv-public-heading { padding-top: 16px; }
/* Ancho completo: el contenedor del widget de Elementor no aporta padding. */
.elementor-widget-tv_comercios > .elementor-widget-container { padding: 0; }
.tv-comercios { margin: 0; }

/* Paginación (JS, botones .tv-page) con el estilo del template: 34×34, activo verde.
   La LÓGICA (renderPagination) no cambia; solo el estilo. Compartida con beneficios. */
.tv-comercios__pagination,
.tv-beneficios__pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 6px;
	margin: 14px 0 0;
}
.tv-page {
	min-width: 34px;
	height: 34px;
	padding: 0 8px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border: 1px solid var(--tv-color-line, #e2e5ec);
	border-radius: var(--tv-radius-md, 10px);
	background: #fff;
	color: var(--tv-color-muted, #6b7a99);
	font: inherit;
	font-size: 13px;
	font-weight: 700;
	line-height: 1;
	cursor: pointer;
}
.tv-page:hover:not(:disabled):not(.is-active) { border-color: var(--tv-color-secondary, #3b8925); color: var(--tv-color-secondary, #3b8925); }
.tv-page.is-active { border-color: var(--tv-color-secondary, #3b8925); background: var(--tv-color-secondary, #3b8925); color: #fff; }
.tv-page:disabled { opacity: .4; cursor: default; }
.tv-page--gap { border-color: transparent; background: transparent; }

/* --- Beneficios (placeholder; UX/UI lo reemplaza) --- */
.tv-beneficios__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 1rem;
}
.tv-beneficio { border: 1px solid #eee; border-radius: 8px; padding: .85rem; background: #fff; }
.tv-beneficio__valor {
	display: inline-block;
	margin: 0 0 .4rem;
	padding: .2rem .6rem;
	background: var(--tv-theme-color, #6eb92b);
	color: #fff;
	border-radius: 999px;
	font-weight: 700;
	font-size: .9rem;
}
.tv-beneficio__nombre { font-size: 1.05rem; margin: 0 0 .3rem; color: var(--tv-theme-color, #6eb92b); }
.tv-beneficio__desc { margin: 0; color: #555; font-size: .9rem; }
/* Comercio del beneficio: secundario (logo ~50px + nombre), bajo el descuento y el nombre. */
.tv-beneficio__comercio { display: flex; align-items: center; gap: .5rem; margin: .65rem 0 0; padding-top: .55rem; border-top: 1px solid #f0f0f0; }
.tv-beneficio__comercio-logo { width: 50px; height: 50px; object-fit: contain; border-radius: 6px; flex: 0 0 auto; }
.tv-beneficio__comercio-nombre { font-size: .82rem; color: #888; }

/* Cards clickeables (comercio / beneficio) → página de detalle */
.tv-comercio__link,
.tv-beneficio__link { display: block; color: inherit; text-decoration: none; }

/* --- Hero: el estilo lo pone la ENTREGA (assets/frontend/css/pages/public.css:
   .tv-hero / __title / __accent / __copy / __metric* / __actions). Aquí NO se
   redefine para no volver a pisarlo (M7 · Capa 2). --- */
/* Full-bleed: el contenedor del widget en Elementor no aporta padding/margin (así
   el verde llega a los bordes). El ancho/tope los da la sección: ponla en "ancho
   completo" con padding 0. */
.elementor-widget-tv_hero > .elementor-widget-container { padding: 0; margin: 0; }

/* --- Comercios · variante "Marcas aliadas" (v3): grid 4-col + cards blancas + heading con badge
   + CTA centrado, todo de la entrega (.tv-brand-strip* / .tv-landing-heading--with-badge /
   .tv-landing-module__action). Aquí solo el ajuste de los logos REALES: v3 usa iniciales de color;
   nosotros imagen real cuando exista (caja neutra), o caja de color con iniciales si no hay logo. */
.tv-brand-strip__logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* Caja del logo con imagen real: fondo neutro (v3 quitó el fondo por defecto de .tv-brand-strip__logo). */
.tv-brand-strip__logo--img { background: #f2f4f8; }

/* --- Beneficios · "Convenios destacados" (card vertical + scroller horizontal) --- */
/* --- Beneficios · "Convenios destacados" (v3): card .tv-landing-convention-card (logo comercio +
   nombre + categoría + tag descuento) en .tv-landing-scroller, todo de la entrega. Aquí solo el
   logo REAL: imagen real → caja neutra; sin logo → caja de color + iniciales (3 variantes v3). --- */
.tv-benefit-card__logo-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: inherit; }
.tv-landing-convention-card__logo img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: inherit; }
.tv-landing-convention-card__logo--img { background: #f2f4f8; }

/* --- Beneficios · variante "Listado — todos los beneficios" (v3): misma estructura que comercios
   (.tv-public-heading + búsqueda + filtros + .tv-public-list con cards .tv-public-list-card, todo de
   la entrega). El .tv-beneficio (li) es solo CONTENEDOR (hook de analítica CLIC_BENEFICIO) → se
   resetea el estilo viejo del placeholder (.tv-beneficio border/bg, que aún usa el detalle). --- */
.tv-beneficios--listado { margin: 0; }
.tv-beneficios--listado .tv-beneficio { border: 0; border-radius: 0; padding: 0; margin: 0; background: none; }
/* Cabecera sticky (como comercios): sin el offset de header fijo de v3. */
.tv-beneficios--listado .tv-public-heading { padding-top: 16px; }
/* Ancho completo SOLO en la variante listado (destacados conserva su layout). */
.elementor-widget-tv_beneficios > .elementor-widget-container:has(.tv-beneficios--listado) { padding: 0; }

/* Carrusel arrastrable con mouse (drag-to-scroll de theme-ui.js → initScrollers). */
.tv-scroller { cursor: grab; }
.tv-scroller.is-dragging { cursor: grabbing; user-select: none; }
.tv-scroller.is-dragging a { pointer-events: none; }

/* --- Comercio (single/detalle) v3 (.tv-brand-detail__*): la entrega estila header/galería/identidad/
   ubicación/contacto/beneficios. Aquí solo reconciliaciones data-driven: cabecera sticky, logo real,
   icono Lucide de categoría, mapa REAL embebido (en vez del ilustrativo), coordenadas, corazón de
   favorito, min-width de las cards y <a> sin subrayado (la entrega usa <button>). --- */
.elementor-widget-tv_comercio_detalle > .elementor-widget-container { padding: 0; }
.tv-brand-detail { margin: 0; }
/* Cabecera sticky (nuestra), no fija: sin el offset de header fijo de v3 (96px → 16px). */
.tv-brand-detail__header { padding-top: 16px; }
/* Logo real dentro del cuadro de identidad (v3 usa una letra); si no hay, van las iniciales. */
.tv-brand-detail__logo img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: inherit; }
/* Icono Lucide de la categoría (la API trae `icono`). */
.tv-brand-detail__category .tv-cs__cat-ico { display: inline-flex; width: 14px; height: 14px; }
.tv-brand-detail__category .tv-cs__cat-ico svg { width: 14px; height: 14px; }
/* Galería: la imagen real llena el recuadro. */
.tv-brand-gallery__image { width: 100%; height: 100%; object-fit: cover; }
/* Mapa REAL embebido (iframe) en la ubicación, en vez del mapa ilustrativo de v3. */
.tv-brand-map-embed { margin-top: 12px; border: 1px solid var(--tv-color-line, #e2e5ec); border-radius: 12px; overflow: hidden; }
.tv-brand-map-embed iframe { width: 100%; display: block; border: 0; }
.tv-brand-location__coords { margin: 8px 0 0; color: var(--tv-color-soft, #9aa3b5); font-size: 12px; }
/* Corazón de favorito: relleno rojo cuando ya es favorito. */
.tv-brand-detail__favorite.is-fav { color: #e5484d; }
.tv-brand-detail__favorite.is-fav svg { fill: currentColor; }
/* Los <a> de la entrega van sin subrayado (la entrega los diseñó como <button>). */
a.tv-brand-detail__back,
a.tv-brand-benefit,
a.tv-brand-contact__row,
a.tv-brand-location__route { text-decoration: none; }
/* Cards de beneficio: que un texto largo no estire la caja. */
.tv-brand-detail__benefits > * { min-width: 0; }

/* --- Beneficio (single) v3: cabecera .tv-public-heading (back + título) + hero del descuento
   (.tv-benefit-detail__discount) + descripción (.tv-benefit-detail__card) + «Disponible en» con
   cards .tv-public-list-card y «Válido hasta …» por comercio (asignacion.vigencia_hasta, dato de la
   API). El estilo viejo (hero verde + card genérica + validez editable) queda DEPRECADO. --- */
.elementor-widget-tv_beneficio_detalle > .elementor-widget-container { padding: 0; }
.tv-beneficio-detalle { margin: 0; }
/* Cabecera sticky (como los listados): sin el offset de header fijo de v3. */
.tv-beneficio-detalle .tv-public-heading { margin: 0; padding-top: 16px; }

/* «Disponible en»: título de sección (.tv-section__title) + lista vertical de cards
   .tv-public-list-card (mismo estilo que el listado; el truncado del nombre/meta lo da la entrega).
   min-width:0 evita que un nombre largo estire la caja. Cada card muestra «Válido hasta …» en el <em>. */
.tv-bs__comercios { margin: 0; }
.tv-bs__comercios-title { margin: 0 0 12px; }
.tv-bs__comercios-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; grid-template-columns: minmax(0, 1fr); }
.tv-bs__comercio { list-style: none; margin: 0; min-width: 0; }
/* Botón "añadir a favoritos" — compartido por las cards del listado de comercios y el single de
   comercio. Oculto por defecto; el JS lo revela solo si hay un vecino con sesión iniciada. */
.tv-fav-btn {
	display: block; width: 100%;
	padding: .6rem .85rem;
	border: 0; border-top: 1px solid #f0f0f0;
	background: #fafafa;
	color: var(--tv-theme-color, #6eb92b);
	font: inherit; font-weight: 600; font-size: .9rem;
	text-align: center; cursor: pointer;
}
.tv-fav-btn:hover { background: #f2f7ec; }
.tv-fav-btn:disabled { cursor: default; }
.tv-fav-btn.is-fav { color: #3d6b12; background: #eef6e3; }
/* El favorito del single de comercio es ahora el corazón .tv-brand-detail__favorite (arriba). */

/* --- Vecino · Ingresar (M7 · Auth): re-skin al visual v3 .tv-login (público centrado,
   fuera del marco de app). Mi Cuenta sigue provisional hasta su re-skin. --- */
.tv-vecino-auth,
.tv-comercio-auth { max-width: 460px; margin: 2rem auto; padding: 0; }
/* Campo de login sin modificador (email del comercio): mismo respiro inferior que --rut.
   Scoped a .tv-comercio-auth para no alterar el login del vecino; --password vuelve a 12px. */
.tv-comercio-auth .tv-login__field { margin-bottom: 18px; }
.tv-comercio-auth .tv-login__field--password { margin-bottom: 12px; }
/* v3 estila .tv-login__switch-button / __forgot / __register como <button>; aquí son <a>
   reales (navegación entre logins + modal de recuperación) → reset del subrayado UA. */
a.tv-login__switch-button,
a.tv-login__forgot,
.tv-login__register a { text-decoration: none; }
.tv-login__register a { color: var(--tv-color-primary, #f5ab44); font-weight: 700; }
/* Alerta de validación (initAuthValidation la muestra/oculta con [hidden]): el atributo
   debe ganar al display:flex de .tv-alert (feedback.css) → si no, se vería siempre. */
.tv-alert[hidden] { display: none; }
/* Línea de error de API/red (vecino.js escribe en #pwa-vecino-login-msg): sin texto no
   reserva espacio (evita el hueco del min-height cuando está vacía). */
.tv-msg:empty { display: none; }
.tv-status--ok { color: #2e7d32; }
.tv-corregir__motivo {
	margin: 0 0 1.25rem;
	padding: .75rem 1rem;
	background: #fff8e1;
	border-left: 3px solid #f0ad4e;
	border-radius: 6px;
	color: #664d03;
	font-size: .9rem;
}
.tv-vecino-links { margin: 1rem 0 0; font-size: .9rem; }
.tv-vecino-links a { color: var(--tv-theme-color, #6eb92b); }

.tv-mi-cuenta { max-width: 560px; margin: 1.5rem auto; padding: 0 1.25rem; }
.tv-mc__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.tv-mc__saludo { margin: 0; font-size: 1.4rem; color: var(--tv-theme-color, #6eb92b); }
.tv-mc__tarjeta { margin: 1.25rem 0; text-align: center; }
.tv-mc__tarjeta-img { max-width: 100%; height: auto; border-radius: 12px; box-shadow: 0 8px 24px rgba(0, 0, 0, .15); }
.tv-mc__nav {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: .75rem;
	margin: 1.5rem 0;
}
.tv-mc__nav-item {
	display: block;
	padding: 1rem;
	text-align: center;
	border: 1px solid #eee;
	border-radius: 10px;
	background: #fff;
	color: #555;
}
.tv-mc__nav-item.is-soon { opacity: .5; cursor: default; }
button.tv-mc__nav-item { width: 100%; font: inherit; cursor: pointer; }
.tv-mc__nav-item.is-active { border-color: var(--tv-theme-color, #6eb92b); color: var(--tv-theme-color, #6eb92b); font-weight: 600; }

/* --- Mi cuenta · paneles Favoritos / Transacciones (iter 3a; provisional, UX/UI reskinea en M7). --- */
.tv-mc__panels { margin: 1rem 0 2rem; }
.tv-mc__panel-title { font-size: 1.15rem; margin: 0 0 .75rem; }
.tv-status--err { color: #b32d2e; }
.tv-mc__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; }

.tv-fav { display: flex; align-items: center; gap: .8rem; padding: .7rem .8rem; border: 1px solid #eee; border-radius: 10px; }
.tv-fav__logo { width: 44px; height: 44px; object-fit: cover; border-radius: 8px; flex: 0 0 auto; }
.tv-fav__body { display: flex; flex-direction: column; gap: .1rem; min-width: 0; flex: 1 1 auto; }
.tv-fav__nombre { font-weight: 600; }
.tv-fav__cat, .tv-fav__dir { font-size: .85rem; color: #777; }
.tv-fav__del { flex: 0 0 auto; }

.tv-tx { display: flex; align-items: center; gap: .8rem; padding: .6rem .8rem; border: 1px solid #eee; border-radius: 10px; }
.tv-tx__fecha { flex: 0 0 auto; color: #777; font-size: .85rem; }
.tv-tx__comercio { flex: 1 1 auto; min-width: 0; font-weight: 600; }
.tv-tx__estado { flex: 0 0 auto; font-size: .78rem; padding: .15rem .5rem; border-radius: 999px; background: #eee; color: #555; }
.tv-tx__estado[data-estado="APROBADA"] { background: #e6f4d9; color: #3d6b12; }
.tv-tx__estado[data-estado="RECHAZADA"] { background: #fbe0e0; color: #b32d2e; }

.tv-mc__pager { display: flex; align-items: center; justify-content: center; gap: 1rem; margin-top: 1rem; }
.tv-mc__pager-info { font-size: .9rem; color: #555; }
.tv-mc__pager .tv-btn-link:disabled { opacity: .4; cursor: default; }

/* --- Mi cuenta · Datos (iter 3b; provisional, UX/UI reskinea en M7). --- */
.tv-mi-datos { max-width: 560px; }
.tv-md__back { display: inline-block; margin-bottom: .5rem; }
.tv-md__subtitulo { font-size: 1.1rem; margin: 1.5rem 0 .5rem; }
.tv-md__list { margin: 0; }
.tv-md__row { display: flex; justify-content: space-between; gap: 1rem; padding: .5rem 0; border-bottom: 1px solid #f0f0f0; }
.tv-md__row dt { margin: 0; color: #777; font-size: .9rem; }
.tv-md__row dd { margin: 0; font-weight: 600; text-align: right; word-break: break-word; }
.tv-md__nota { margin-top: 1.25rem; }

/* --- Mi cuenta · Solicitudes (iter 3c; provisional, UX/UI reskinea en M7). --- */
.tv-mis-solic { max-width: 560px; }
.tv-solic__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; }
.tv-solic { padding: .7rem .85rem; border: 1px solid #eee; border-radius: 10px; }
.tv-solic__row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }
.tv-solic__estado { font-size: .78rem; padding: .15rem .5rem; border-radius: 999px; background: #eee; color: #555; }
.tv-solic__estado[data-estado="APROBADA"] { background: #e6f4d9; color: #3d6b12; }
.tv-solic__estado[data-estado="RECHAZADA"] { background: #fbe0e0; color: #b32d2e; }
.tv-solic__estado[data-estado="OBSERVADA"] { background: #fdf0d5; color: #8a6d1c; }
.tv-solic__fecha { color: #777; font-size: .85rem; }
.tv-solic__motivo { margin: .5rem 0 0; font-size: .9rem; }
.tv-solic__obs-nota { margin: .4rem 0 0; font-size: .85rem; color: #8a6d1c; }
.tv-solic__reno { margin-top: 1.75rem; padding-top: 1.25rem; border-top: 1px solid #eee; }

/* --- Mi comercio (M6; provisional, UX/UI reskinea en M7). --- */
.tv-comercio-panel { max-width: 560px; margin: 2rem auto; padding: 0 1.25rem; }
.tv-mic__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.tv-mic__nombre { margin: 0; font-size: 1.3rem; color: var(--tv-theme-color, #6eb92b); }
.tv-mic__scan { margin: 1rem 0 1.5rem; }
.tv-mic__logo { max-width: 120px; height: auto; border-radius: 8px; margin-bottom: .75rem; }
.tv-mic__benef-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.tv-mic__benef-item { display: flex; align-items: baseline; gap: .6rem; padding: .5rem .7rem; border: 1px solid #eee; border-radius: 10px; }
.tv-mic__benef-valor { font-weight: 700; color: var(--tv-theme-color, #6eb92b); }
.tv-mic__benef-nombre { font-weight: 600; }
.tv-mic__benef-vig { margin-left: auto; color: #888; font-size: .8rem; }
.tv-mic__hist-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.tv-tx__benef { color: #555; font-size: .85rem; }

/* --- Perfil del comercio (mi-comercio rework; provisional, UX/UI reskinea en M7). --- */
.tv-mic__id { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
.tv-mic__estado { font-size: .72rem; padding: .12rem .5rem; border-radius: 999px; background: #fbe0e0; color: #b32d2e; }
.tv-mic__estado[data-estado="ACTIVO"] { background: #e6f4d9; color: #3d6b12; }
.tv-mic__kpis { display: flex; gap: 1rem; margin: 1.25rem 0; }
.tv-mic__kpi { flex: 1; text-align: center; padding: 1rem; border: 1px solid #eee; border-radius: 12px; background: #fafafa; }
.tv-mic__kpi-num { display: block; font-size: 1.8rem; font-weight: 800; line-height: 1; color: var(--tv-theme-color, #6eb92b); }
.tv-mic__kpi-lbl { display: block; margin-top: .3rem; font-size: .82rem; color: #555; }
.tv-mic__desc { color: #444; margin: 0 0 1rem; }
.tv-mic__galeria { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); gap: .5rem; }
.tv-mic__galeria-item img { width: 100%; height: 90px; object-fit: cover; border-radius: 8px; display: block; }

/* --- Mis beneficios (home del comercio) — re-skin v3 (.tv-commerce-*). La lógica de comercio.js
   NO cambia; aquí solo se MAPEAN sus señales al estilo de la entrega. --- */
/* Filtro activo: comercio.js alterna `is-active` (la entrega usa el modificador --active). */
.tv-commerce-filters__item.is-active { background: var(--tv-color-secondary, #3b8925); color: #fff; }
/* Card vencida: comercio.js marca `data-vencido` (la entrega usa --expired) → icono gris + su badge. */
.tv-commerce-benefit-card[data-vencido] .tv-commerce-benefit-card__icon { background: #f5f5f5; color: var(--tv-color-soft, #9aa3b5); }
.tv-commerce-benefit-card:not([data-vencido]) .tv-commerce-benefit-card__badge { display: none; }
/* Estado del detalle (comercio.js pone data-estado ACTIVO/VENCIDO/FUTURO): color por estado. */
.tv-commerce-benefit-detail__status[data-estado="VENCIDO"] { color: #b32d2e; }
.tv-commerce-benefit-detail__status[data-estado="FUTURO"] { color: #1e4fb3; }

/* --- Actividad (comercio) — re-skin v3 (.tv-commerce-activity*). La entrega ya estila la row y
   sus hijos (icono/`> div`/strong/span/time); aquí solo el filtro por día (que la entrega no
   tiene), el contenedor de filas (separado del label para el innerHTML de comercio.js) y el mapeo
   del estado del icono, que lleva ✓/✗ como TEXTO. La lógica de comercio.js no cambia. --- */
.tv-act__filtro { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; padding: 12px 15px 0; }
.tv-act__filtro input[type="date"] { padding: .4rem .6rem; border: 1px solid var(--tv-color-line, #e2e5ec); border-radius: 8px; font: inherit; }
.tv-commerce-activity__rows { display: grid; gap: 8px; }
/* Icono con ✓/✗ de texto: el default de la entrega ya es verde (APROBADA); RECHAZADA → rojo. */
.tv-commerce-activity-row__icon { font-size: 16px; font-weight: 800; }
.tv-commerce-activity-row__icon[data-estado="RECHAZADA"] { background: #feeceb; color: #dc3545; }

/* --- Escáner QR (comercio) — re-skin v3 (.tv-scan* con estados). comercio.js (initQr) conduce la
   máquina de estados por data-scan-state (idle → scanning → success | error) e intercambia el ícono
   del marco. La cámara REAL (#pwa-qr-reader = .tv-scan__camera) reemplaza al marco mockup solo
   mientras se escanea, y se deja GRANDE (como estaba). La lógica (POST/geoloc/parseo) NO cambia. --- */
#pwa-qr { display: flex; flex-direction: column; min-height: 100%; }
.tv-qr-screen .tv-scan { width: 100%; flex: 1 1 auto; }
/* Cámara: oculta salvo en «scanning»; grande (ancho completo, alto natural del lector) y redondeada.
   El marco mockup se oculta mientras se escanea (la cámara ocupa su lugar). */
.tv-scan__camera { display: none; width: 100%; max-width: 340px; margin: 0 auto 24px; border-radius: 16px; overflow: hidden; background: #000; }
.tv-scan[data-scan-state="scanning"] .tv-scan__camera { display: block; }
.tv-scan[data-scan-state="scanning"] .tv-scan__target { display: none; }
/* Badge del resultado: la entrega lo estila verde (Aprobada); el canje Rechazado → rojo. */
.tv-scan-result em[data-estado="RECHAZADA"] { background: #feeceb; color: #dc3545; }
/* El nombre del vecino (data-field=vecino) titula la tarjeta; el mensaje de la API va debajo, suave. */
.tv-scan-result [data-field="mensaje"] { color: var(--tv-color-soft, #6b7280); }

/* --- Registro del vecino (wizard 4 pasos) — re-skin v3 (.tv-register* / .tv-register-type /
   .tv-document-upload / .tv-success). Público centrado. La navegación por pasos + la validación
   NATIVA HTML5 las maneja registro.js; aquí va solo el glue visual. --- */
.tv-registro { max-width: 460px; margin: .75rem auto 2rem; padding: 0; }
/* Dos campos en una fila (ej. Número + Depto/casa): grid a 2 columnas, sin margen interno
   (el respiro vertical lo da la fila, no cada .tv-field). */
.tv-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 1rem 0; }
.tv-field-row .tv-field { margin: 0; }
/* Tipo de vecino activo (paso 1): el radio real va oculto (.tv-sr-only); espejamos el estado
   [aria-pressed="true"] de la entrega con :has(:checked) y damos foco de teclado con :focus-within. */
.tv-register-type:has(input:checked) { border-color: var(--tv-color-secondary, #3b8925); background: var(--tv-color-info-bg, #eef6ef); }
.tv-register-type:has(input:checked) .tv-register-type__radio { border-color: var(--tv-color-secondary, #3b8925); background: var(--tv-color-secondary, #3b8925); }
.tv-register-type:has(input:checked) .tv-register-type__radio svg { visibility: visible; }
.tv-register-type:focus-within { border-color: var(--tv-color-secondary, #3b8925); }
/* Barra de progreso: registro.js marca is-active/is-done (la entrega usa el modificador --active). */
.tv-register__progress-step.is-active,
.tv-register__progress-step.is-done { background: var(--tv-color-secondary, #3b8925); }
/* Navegación del wizard (prev/next/submit): fila de botones a ancho completo. */
.tv-reg__nav { display: flex; gap: 12px; margin-top: 16px; padding: 0 10px 4px; }
.tv-reg__nav .tv-button { flex: 1; }
.tv-btn--ghost { background: #fff; color: var(--tv-theme-color, #6eb92b); border: 1px solid var(--tv-theme-color, #6eb92b); }
/* Documento faltante (validación del paso 3): borde rojo en la card de subida. */
.tv-document-upload--error { border-style: solid; border-color: #dc2626; }
/* Las cards de subir documento (paso 3) quedaban pegadas: el contenedor es un <div> (no el
   <form> .tv-form de la entrega), así que su gap no aplicaba. Lo hacemos grid con separación. */
.tv-register__documents-form { display: grid; gap: 16px; }
/* Pantalla de éxito (paso 4): oculta la cabecera del wizard (volver + progreso), como el
   template — el éxito es una pantalla limpia. :has() detecta el paso 4 visible. */
.tv-register:has( .tv-reg__step[data-step="4"]:not([hidden]) ) .tv-register__header { display: none; }
/* Fuente del código de seguimiento (registro.js escribe aquí): siempre oculta; initRegistroTracking
   mueve su valor a la caja .tv-tracking. */
#pwa-reg-request-id { display: none; }

/* --- Home privado del vecino (resident-home): la entrega estila card-summary y accesos como
   <button>; aquí el resumen de tarjeta y algunos accesos son <a> reales → reset del subrayado.
   «Encuestas» va deshabilitada (vista diferida). --- */
a.tv-resident-card-summary,
a.tv-quick-actions__item,
a.tv-resident-card__back { text-decoration: none; }
.tv-quick-actions__item:disabled,
.tv-quick-actions__item[aria-disabled="true"] { opacity: .5; cursor: default; }
/* El badge estila TODO <span> hijo como punto de 6px (la entrega pone el texto como nodo suelto);
   nuestro texto va en un <span> extra para que vecino.js lo actualice → lo sacamos de ese punteo. */
.tv-resident-card-summary__badge > [data-tv-resident-badge] { width: auto; height: auto; border-radius: 0; background: none; }

/* --- Mi Tarjeta (resident-card) bajo swup (M8a): la imagen (portrait 9:16, la genera el backend) se
   carga async DESPUÉS del swap → al aparecer empujaba el layout (salto). Reservamos su área desde el
   inicio con aspect-ratio para que loading→imagen no salte, y la revelamos con un fundido corto.
   initResidentCard (la lógica) NO cambia. --- */
.tv-resident-card__media-wrap { aspect-ratio: 1080 / 1920; display: grid; place-items: center; }
.tv-resident-card__media { width: 100%; max-height: 100%; }
.tv-resident-card__media img { max-height: 100%; animation: tv-card-fade .3s ease-out; }
@keyframes tv-card-fade { from { opacity: 0; } to { opacity: 1; } }

/* --- Modal de permiso de notificaciones (resident-home): la entrega usaba un glifo en el cuadro de
   58px; con la campana SVG (.tv-icon = 20px) la agrandamos un poco para que llene el recuadro. --- */
.tv-modal__icon .tv-icon { width: 26px; height: 26px; }
/* En el home PÚBLICO no hay marco de app (.tv-app posicionado) → el modal usa position:fixed para
   cubrir el viewport. En el marco de app queda absolute (lo cubre .tv-app). --- */
.tv-modal--fixed { position: fixed; }

/* =========================================================================
 * M7 · Capa 1a — Header público restilizado (diseño de Frontend).
 * Clases propias del tema (no de la entrega) que consumen los tokens --tv-color-*
 * de tokens.css. El "marco de app" (.tv-app + drawer + bottom-nav) llega en 1b.
 * ========================================================================= */
.tv-site-header {
	position: sticky;
	top: 0;
	z-index: 200;
	background: var(--tv-color-surface, #fff);
	border-bottom: 1px solid var(--tv-color-line, #e2e5ec);
	box-shadow: var(--tv-shadow-sm, 0 2px 8px rgba(33, 70, 84, .06));
}
.tv-site-header__inner {
	max-width: 1120px;
	margin: 0 auto;
	padding: .6rem 1.25rem;
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: .6rem 1.25rem;
}
.tv-site-header__brand { display: inline-flex; align-items: center; margin-right: auto; }
.tv-site-header__logo { height: 44px; width: auto; display: block; }
.tv-site-nav { display: flex; flex-wrap: wrap; gap: .25rem 1rem; align-items: center; }
.tv-site-nav__link {
	color: var(--tv-color-copy, #4a5568);
	text-decoration: none;
	font-weight: 600;
	font-size: .95rem;
	padding: .35rem .25rem;
}
.tv-site-nav__link:hover,
.tv-site-nav__link:focus { color: var(--tv-color-primary, #f5ab44); }
.tv-site-header__auth { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem; }
.tv-site-header__link {
	color: var(--tv-color-ink, #214654);
	text-decoration: none;
	font-weight: 700;
	font-size: .95rem;
}
.tv-site-header__link:hover,
.tv-site-header__link:focus { color: var(--tv-color-primary, #f5ab44); }

/* Botón utilitario del shell (header/auth). Distinto de .tv-btn (forms) y de la
   entrega (.tv-button); pill con la paleta nueva. */
.tv-btn-ui {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: .5rem 1.05rem;
	border-radius: 999px;
	border: 1px solid transparent;
	font-weight: 700;
	font-size: .9rem;
	line-height: 1;
	text-decoration: none;
	cursor: pointer;
}
.tv-btn-ui--primary {
	background: var(--tv-color-primary, #f5ab44);
	color: #fff;
	border-color: var(--tv-color-primary, #f5ab44);
}
.tv-btn-ui--primary:hover,
.tv-btn-ui--primary:focus { background: var(--tv-color-primary-dark, #d98f2f); border-color: var(--tv-color-primary-dark, #d98f2f); }
.tv-btn-ui--ghost {
	background: transparent;
	color: var(--tv-color-ink, #214654);
	border-color: var(--tv-color-line, #e2e5ec);
}
.tv-btn-ui--ghost:hover,
.tv-btn-ui--ghost:focus { border-color: var(--tv-color-primary, #f5ab44); color: var(--tv-color-primary, #f5ab44); }

@media (max-width: 560px) {
	.tv-site-header__logo { height: 38px; }
	.tv-site-nav { width: 100%; order: 3; }
}

/* --- Menú lateral (hamburguesa + drawer) del diseño de Frontend --- */
/* El wrapper agrupa header + drawer bajo un solo [data-tv-app] para el JS, pero no
   debe generar caja: con display:contents el header sticky se referencia al body
   (si no, quedaría atrapado en un contenedor tan alto como el propio header). */
.tv-shell-chrome { display: contents; }
/* Botón hamburguesa en el header sólido (la entrega lo estila para su header
   transparente; aquí, sobre barra blanca, fijamos color/borde propios). */
.tv-site-header__menu {
	width: 44px;
	height: 44px;
	padding: 0;
	display: grid;
	place-items: center;
	border: 1px solid var(--tv-color-line, #e2e5ec);
	border-radius: 10px;
	background: var(--tv-color-surface, #fff);
	color: var(--tv-color-ink, #214654);
}
.tv-site-header__menu:hover,
.tv-site-header__menu:focus { border-color: var(--tv-color-primary, #f5ab44); color: var(--tv-color-primary, #f5ab44); }
.tv-site-header__menu .tv-icon { width: 24px; height: 24px; }

/* El drawer y su backdrop son OVERLAYS de viewport: la entrega los posiciona
   `absolute` dentro de su marco de app (.tv-app, height fija). Como el menú vive
   fuera de ese marco (público), se fijan a la ventana. Conservan transform/anchura
   y z-index (310/300) de la entrega. */
.tv-drawer { position: fixed; }
.tv-drawer-backdrop { position: fixed; }
/* El drawer cerrado vive FUERA del borde derecho (translateX(102%)). Sin el marco de app (.tv-app,
   overflow:hidden) que lo recorta en la entrega, eso genera scroll horizontal en las páginas
   públicas: al ensanchar la ventana la página se corre y el menú/cabecera «se pierden». Se recorta
   a nivel de viewport. `clip` (no `hidden`) NO crea contexto de scroll → no rompe el header sticky. */
html,
body { overflow-x: clip; }

/* Bloqueo del scroll del fondo mientras el drawer está abierto. */
html.tv-drawer-lock,
html.tv-drawer-lock body { overflow: hidden; }

/* Los enlaces del menú son <a> reales (la entrega los estilaba como <button>):
   resetea el subrayado y fija el color ink (si no, salen azules/subrayados por
   la regla UA a:link). Conserva el layout de fila 44px de .tv-drawer__link. */
.tv-drawer__link,
a.tv-drawer__link { color: var(--tv-color-ink, #214654); text-decoration: none; }
.tv-drawer__link:hover,
.tv-drawer__link:focus,
a.tv-drawer__link:hover,
a.tv-drawer__link:focus { background: var(--tv-color-canvas, #f7f8fa); color: var(--tv-color-ink, #214654); }

/* Botón "Ingresar" del pie: sobre el verde del footer, el .tv-button base (verde)
   sería invisible → variante fantasma (transparente + borde blanco). */
.tv-public-footer .tv-footer-btn--ghost {
	background: transparent;
	border-color: rgba(255, 255, 255, .4);
	color: #fff;
}
.tv-public-footer .tv-footer-btn--ghost:hover,
.tv-public-footer .tv-footer-btn--ghost:focus {
	background: rgba(255, 255, 255, .12);
	border-color: rgba(255, 255, 255, .7);
}

/* --- Header "overlay" en el home: transparente sobre el hero, sólido al scroll --- */
/* Gana a la posición sticky del .tv-site-header base por ir después en el archivo. */
.tv-site-header--overlay {
	position: fixed;
	left: 0;
	right: 0;
	top: 0;
	background: transparent;
	border-bottom-color: transparent;
	box-shadow: none;
	transition: background 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
}
.tv-site-header--overlay.tv-site-header--solid {
	background: var(--tv-color-surface, #fff);
	border-bottom-color: var(--tv-color-line, #e2e5ec);
	box-shadow: var(--tv-shadow-sm, 0 2px 8px rgba(33, 70, 84, .06));
}
/* Estado transparente: hamburguesa clara sobre el verde del hero. */
.tv-site-header--overlay:not(.tv-site-header--solid) .tv-site-header__menu {
	color: #fff;
	border-color: rgba(255, 255, 255, .55);
	background: rgba(255, 255, 255, .12);
}
/* (El wrapper del artículo pasó a .tv-article, SIN padding, para que el hero quede a
   sangre completa y a tope bajo el header overlay. El espaciado lo controla la
   sección de Elementor / el CSS del template. Ya no se necesita un reset aquí.) */

/* =========================================================================
 * M7 · Capa 1b — Marco de app (.tv-app 402px + bottom-nav) · SOLO áreas autenticadas.
 * El marco envuelve las cuentas (vecino/comercio); el público sigue full-width.
 * La entrega ya trae, vendorizado: shell.css → .tv-app / .tv-app__viewport / .tv-screen
 * y navigation.css → .tv-header / .tv-bottom-nav / .tv-drawer. Aquí va solo el GLUE de
 * integración a nuestro shell WordPress (centrado en desktop, header/nav en flujo, drawer
 * recortado por el marco, enlaces reales). La LÓGICA (datos/sesión) no se toca.
 * ========================================================================= */

/* Fondo tras el marco en desktop: el .tv-app (canvas + borde + sombra) "flota" como un
   dispositivo. En ≤760px el marco es full-screen (media query de shell.css) y este fondo
   queda oculto. */
body.tv-app-page { background: #e7ebf2; }

/* Centrado del marco en desktop. shell.css fija height: calc(100vh - 48px) → con 24px de
   margen arriba/abajo cuadra a 100vh. La media query de shell.css (≤760px) lo lleva a
   full-screen (borde/radio/sombra a 0, height 100vh); ahí anulamos también el margen. */
.tv-app { margin: 24px auto; }
@media (max-width: 760px) { .tv-app { margin: 0; } }

/* Header y bottom-nav EN FLUJO dentro del marco. La entrega los posiciona `absolute` para
   superponerse a un hero; nuestras vistas autenticadas no tienen hero, así que el marco se
   resuelve como columna flex (shell.css: .tv-app es flex column): header arriba (altura
   propia), viewport elástico en medio (scroll interno) y nav abajo. `position: relative`
   (no static) conserva el z-index para que el icono «destacado» del nav sobresalga por
   encima del viewport. Se acota con `> ` para no afectar headers/nav de las vistas. */
.tv-app > .tv-header { position: relative; flex: 0 0 auto; }
.tv-app > .tv-bottom-nav { position: relative; flex: 0 0 auto; z-index: 90; }

/* Marca del app-header (envuelve el logo; la entrega usa un <img> suelto). */
.tv-header__brand { display: inline-flex; align-items: center; text-decoration: none; }

/* Drawer y backdrop DENTRO del marco: se recortan por el overflow del .tv-app (la entrega
   los pone `absolute`). Reafirmamos `absolute` porque el shell público los fija al viewport
   (.tv-drawer{position:fixed} más arriba); dentro del marco vuelven a anclarse a .tv-app. */
.tv-app .tv-drawer { position: absolute; }
.tv-app .tv-drawer-backdrop { position: absolute; }

/* Bottom-nav: los ítems navegables son <a>/<button> reales → reset del subrayado UA y
   cursor; el ítem deshabilitado (Encuestas, aún sin destino) se atenúa. Color/estado
   activo los aporta navigation.css (soft → secondary). */
.tv-bottom-nav__item { text-decoration: none; cursor: pointer; }
.tv-bottom-nav__item:disabled,
.tv-bottom-nav__item[aria-disabled="true"] { opacity: .5; cursor: default; }
