/*
|--------------------------------------------------------------------------
| AMOREA — FICHA INDIVIDUAL DE PRODUCTO (single-product)
|--------------------------------------------------------------------------
|
| Sólo lo que Blocksy/WooCommerce no traen ya resuelto: la galería
| (zoom/lightbox/miniaturas/swipe), el layout base de imagen+resumen y
| el AJAX del formulario de compra son 100% nativos, no se tocan acá.
| Este archivo separa visualmente `.entry-summary` en dos columnas
| (`.product-central-info` / `.product-buy-card`, ver
| inc/woocommerce/single-product-hooks.php) y estiliza los bloques
| propios de Amorea (badge, atributos, tarjeta de compra, "Completa tu
| regalo", beneficios). El carrusel/tarjetas de "Completa tu regalo"
| reutilizan `.cart-forgot*` de cart.css (encolado también acá, ver
| theme-assets.php) — no se repiten esas reglas.
|
*/

/* ==========================================================================
   `#main-container` (nativo de Blocksy, envuelve TODO el <main> del
   sitio) trae `overflow: clip` fijo en AMBOS ejes (main.scss,
   comentario propio de Blocksy: "to remove later, check
   caniuse.com"). Un `position: sticky` (que ancla verticalmente,
   `top`) DENTRO de un ancestro con overflow-Y distinto de `visible`
   deja de funcionar — comprobado en vivo: .product-buy-card-sticky-wrap
   nunca se quedaba fijo al hacer scroll por este candado.

   PERO el candado en el eje X sí es necesario acá: el panel
   desplegable del carrito del header (`.ct-cart-content`, nativo de
   Blocksy) vive siempre renderizado fuera del viewport a la derecha y
   depende de este mismo `overflow-x: clip` para no generar scroll
   horizontal real — comprobado en vivo: al anular el overflow
   completo aparecía una barra de scroll horizontal real en cuanto el
   carrito tenía algún producto (ese panel se vuelve más alto/ancho
   con contenido real).

   Por eso se libera SÓLO el eje Y (el que necesita el sticky) y se
   mantiene el eje X clippeado (el que necesita el panel del header) —
   sólo en single-product, nunca sitewide.
   ========================================================================== */

body.single-product #main-container {
	overflow-x: clip;
	overflow-y: visible;
}

/* ==========================================================================
   Galería — miniatura seleccionada claramente distinguible. El nativo
   de Blocksy es un borde gris casi invisible (.ct-media-container:after
   con border-color: rgba(0,0,0,0.2) en default-gallery.scss) y ninguna
   otra señal — reportado como "casi imperceptible". Se refuerza con
   el marco rosa de marca (ya usado en toda la ficha) MÁS opacidad
   reducida en la miniatura seleccionada (las demás quedan a color
   completo) — a pedido del cliente, al revés del patrón más común de
   atenuar las NO seleccionadas. La clase `.active` la gestiona ahora
   single-product.js (ver setupThumbsSelection()) en vez de depender
   del módulo JS perezoso de Blocksy.
   ========================================================================== */

/*
 * `body.single-product` de más para pesar más que la regla nativa de
 * Blocksy (`div.woocommerce-product-gallery li.active { opacity: 1 }`,
 * default-gallery.scss) — sin este refuerzo, la nativa gana y el
 * opacity de acá nunca se aplicaba (comprobado en vivo).
 */
body.single-product .flexy-pills li.active {
	opacity: 0.55;
	transition: opacity 0.2s ease;
}

.flexy-pills li.active .ct-media-container:after {
	border-color: var(--color-primary);
	border-width: 2px;
}

/*
 * Flechas izquierda/derecha de la imagen principal (.flexy-arrow-prev/
 * next, nativas de Blocksy) — su color de fondo en hover usa la
 * variable `--flexy-nav-background-hover-color`, que sin definir cae
 * al azul de Blocksy (`--theme-palette-color-1`, flexy.scss). Se
 * define esa misma variable con el rosa de marca, sin tocar el
 * archivo nativo — mismo mecanismo de override que ya usa el resto de
 * la ficha para el azul de Blocksy (tarjeta de compra, relacionados).
 * El foco por teclado no tenía ningún estilo propio (dependía del
 * outline por defecto del navegador) — se agrega uno con el mismo
 * rosa, visible y consistente con hover.
 */
.woocommerce-product-gallery {
	--flexy-nav-background-hover-color: var(--color-primary);
	--flexy-nav-arrow-hover-color: #fff;
}

.woocommerce-product-gallery .flexy-arrow-prev:focus-visible,
.woocommerce-product-gallery .flexy-arrow-next:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

/*
 * Encontrado al activar el lightbox (a pedido del cliente): el botón
 * 🔍 nativo de Blocksy (`.woocommerce-product-gallery__trigger`) se
 * posiciona arriba a la derecha de la imagen — EXACTAMENTE donde ya
 * está el corazón de favoritos (`.product-single-wishlist`, mismo
 * `top`/`right`) — con más z-index, tapaba el corazón por completo e
 * impedía hacerle click. Se reubica acá el botón del lightbox abajo a
 * la derecha (único lugar libre de la imagen) — nunca se toca la
 * posición del corazón, que es parte del diseño ya aprobado. Selector
 * con prefijo (0,2,0) a propósito: single-product.css carga ANTES que
 * woocommerce.min.css de Blocksy (donde vive la regla nativa,
 * `.woocommerce-product-gallery__trigger` sola, 0,1,0) — sin ganar por
 * especificidad, la nativa reemplazaría esto pese a cargar después.
 */
.woocommerce-product-gallery .woocommerce-product-gallery__trigger {
	top: auto;
	bottom: 20px;
	right: 20px;
}

/*
 * Reportado por el cliente tras activar el lightbox: el botón 🔍 se
 * pone azul (no rosado) al hacer foco/hover. Blocksy define ese botón
 * con sus propias variables CSS (`--lightbox-button-*`,
 * inc/dynamic-styles/global/woocommerce/single-product-gallery.php,
 * selector `.woocommerce-product-gallery__trigger`) que, sin valor
 * propio en el Customizer (este sitio nunca las configuró), caen al
 * azul nativo de Blocksy (`--theme-palette-color-1`, popup-box.scss)
 * en hover. El foco por teclado tampoco tenía outline propio (mismo
 * caso que las flechas y las miniaturas más arriba) — el azul que se
 * veía ahí era el outline por defecto del navegador. Mismo mecanismo
 * de override que ya usa el resto de la ficha para este azul.
 */
.woocommerce-product-gallery__trigger {
	--lightbox-button-icon-hover-color: #fff;
	--lightbox-button-hover-background-color: var(--color-primary);
}

.woocommerce-product-gallery__trigger:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

/*
 * Foco por teclado en cada miniatura — encontrado en la auditoría
 * técnica de la galería: antes ningún <li> era alcanzable por teclado
 * (setupThumbsSelection() en single-product.js les agrega ahora
 * role/tabindex reales, patrón "roving tabindex"), así que tampoco
 * había ningún estilo de foco para ellas. Mismo rosa/outline que ya
 * usan las flechas arriba, mismo criterio de "visible sólo navegando
 * con teclado" — nunca aparece con clicks de mouse.
 */
.flexy-pills li:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

/*
 * Scroll de la columna de miniaturas: nativo real del navegador
 * (`overflow-y: auto`), sin transform ni JS de terceros. La selección
 * de una miniatura reacomoda la columna sola (scrollTo calculado en
 * setupThumbsSelection(), single-product.js) — no hace falta ninguna
 * flecha/pestaña adicional para desplazarse.
 */
@media (min-width: 768px) {
	.thumbs-left .flexy-pills {
		overflow-y: auto;
		scroll-behavior: smooth;
		scrollbar-width: none;
	}

	.thumbs-left .flexy-pills::-webkit-scrollbar {
		display: none;
	}
}

/* ==========================================================================
   Badge "Más vendido" sobre la galería
   ========================================================================== */

.product-single-badge {
	position: absolute;
	top: 20px;
	left: 20px;
	z-index: 2;
	display: inline-flex;
	align-items: center;
	background: var(--color-primary);
	color: #fff;
	font-family: var(--font-body);
	font-size: var(--text-xs);
	font-weight: 700;
	letter-spacing: 0.03em;
	text-transform: uppercase;
	padding: 6px 14px;
	border-radius: var(--radius-sm);
}

/*
 * En escritorio con miniaturas verticales (`.thumbs-left`, ver arriba
 * `theme_mod_gallery_style`), Blocksy reserva la franja izquierda de
 * `.woocommerce-product-gallery` para las miniaturas (posición
 * absolute) y empuja la imagen principal a la derecha con
 * `margin-inline-start: calc(--thumbs-width + --thumbs-spacing)`
 * (default-gallery.scss). El badge, anclado al wrapper de 3 columnas,
 * quedaba en `left: 20px` de ESE wrapper — que coincide con el borde
 * izquierdo de las MINIATURAS, no de la imagen (se veía flotando
 * entre ambas). Se le suma el mismo offset que usa Blocksy para la
 * imagen, sólo desde el breakpoint en que `.thumbs-left` aplica
 * (`media-breakpoint-up(md)` nativo, 768px).
 */
@media (min-width: 768px) {
	.thumbs-left .product-single-badge {
		left: calc(var(--thumbs-width, 100px) + var(--thumbs-spacing, 15px) + 20px);
	}
}

/* ==========================================================================
   Badge "AGOTADO" sobre la galería — `.out-of-stock-badge` (nativo de
   Blocksy, `blocksy_get_woo_out_of_stock_badge()`, texto traducido
   "SOLD OUT" → "AGOTADO" vía gettext, ver
   inc/woocommerce/single-product-hooks.php). Nunca se le había escrito
   CSS propio (dependía 100% del default de Blocksy).

   BUG REAL medido en el navegador (`getBoundingClientRect()` +
   `getComputedStyle()`, no adivinado) — CAUSA REAL, no la de
   `.product-single-badge` de arriba (esa es una condición distinta,
   `.thumbs-left`, que en este catálogo no llega a activarse en ningún
   producto probado): la causa real es la regla de acá abajo,
   `.product:not(.thumbs-left) .ct-product-gallery-container
   {padding-inline-start:115px}` (≥950px) — reserva ese espacio a
   propósito para que un producto de UNA sola foto mida lo mismo que uno
   con miniaturas (ver su comentario completo, arriba). La imagen
   (`width:100%` normal, dentro del flujo) SÍ respeta ese padding y se
   angosta en consecuencia — pero `.out-of-stock-badge` es
   `position:absolute` (nativo de Blocksy) con `left` medido desde el
   BORDE del padding, no desde donde empieza el contenido: quedaba dentro
   de esos 115px reservados, nunca sobre la foto (medido: badge en
   x=71-151, imagen recién en x=166, a 1024px de viewport). Mismo
   selector/condición EXACTOS que la regla causante — así se auto-anula
   en cuanto el catálogo real tenga miniaturas en todos los productos
   (comentario de esa regla), sin volver a tocar esto. */
@media (min-width: 950px) {
	.product:not(.thumbs-left) .out-of-stock-badge {
		left: calc(115px + 20px);
	}
}

/*
 * La cinta nativa "SALE" (azul, en inglés) nunca formó parte del
 * diseño pedido — el único badge de la ficha es "Más vendido" (dato
 * real por tag). El precio de oferta real sigue mostrándose tachado
 * en la tarjeta de compra (`.product-buy-card > .price`), eso no se
 * toca: sólo se oculta la cinta sobre la galería, sólo en
 * single-product.
 */
.woocommerce-product-gallery .onsale {
	display: none;
}

/* ==========================================================================
   Breadcrumb nativo (`.ct-breadcrumbs` de Blocksy, dentro de
   `.hero-section.is-width-constrained` — ver docblock del filtro
   "Home"→"Inicio" en single-product-hooks.php: nunca se reconstruye a
   mano). Dos problemas reales medidos en vivo a 1440px:
   1. Quedaba 48px más adentro que la galería/tarjetas de abajo (120px
      vs 72px de borde izquierdo) porque `.hero-section` usa el
      `max-width: 1200px` genérico de Blocksy (`--theme-block-max-width`)
      en vez de los 1400px que usa el resto de la ficha — se iguala al
      mismo max-width, el `width: var(--theme-container-width)` nativo
      (ya el mismo `min(100%, 90vw)` que usa la galería) no se toca.
   2. 80px de padding-top (`.ct-container-full`, nativo/genérico de
      Blocksy) + 40px de margin-bottom (`.hero-section`) sumaban 120px
      de aire entre el header y la galería — se reducen a la escala de
      espaciado propia del sitio.
   Sólo en single-product: `.ct-container-full` es una clase genérica
   de Blocksy que envuelve contenido de ancho completo en otras
   plantillas también, no se toca sin el scope de `body.single-product`.
   ========================================================================== */

body.single-product .ct-container-full {
	padding-top: var(--space-6);
}

body.single-product .hero-section.is-width-constrained {
	max-width: 1400px;
	margin-bottom: var(--space-5);
}

/* ==========================================================================
   Ancho consistente de toda la ficha (breadcrumb, 3 columnas, "Completa
   tu regalo", beneficios) — alineado al MISMO borde derecho que el
   header. Comprobado en vivo (link "Contacto" del menú principal cae
   en 1368px a 1440px de viewport): el contenedor del header NO usa la
   variable genérica de ancho de contenido (`--theme-normal-container-max-width`,
   1200px) — tiene su propia fórmula fluida propia
   (`width: min(100%, 90vw); max-width: 1400px`, confirmado inspeccionando
   el `.ct-container` real del header, que mide 1296px a 1440px de
   viewport, no 1200px). Se replica esa misma fórmula acá para que el
   borde derecho quede exactamente alineado al del header en cualquier
   ancho — sólo en single-product, Home/Shop/Carrito/Checkout no
   cargan este archivo.
   ========================================================================== */

body.single-product .container {
	width: min(100%, 90vw);
	max-width: 1400px;
}

/* ==========================================================================
   Tres columnas reales (galería | información | tarjeta de compra)
   ========================================================================== */

/*
 * `.product-entry-wrapper` (Blocksy, envuelve galería + .summary) ya es
 * `display: flex` nativo con SU propia proporción galería/resumen — no
 * alcanza para 3 columnas reales porque `.summary` es un solo hijo. En
 * vez de anidar un segundo flex/grid DENTRO de `.summary` (lo que
 * intentamos antes: como `.summary` sólo tiene el ~48% del ancho total,
 * `.product-central-info` + `.product-buy-card` nunca entraban lado a
 * lado y la tarjeta terminaba cayendo debajo), `.summary` pasa a
 * `display: contents` — desaparece como caja, sus dos hijos
 * (`.product-central-info`, `.product-buy-card`) quedan como
 * participantes DIRECTOS del grid de `.product-entry-wrapper`, al mismo
 * nivel que la galería. Es el mecanismo que sugirió el cliente
 * (`display: contents`) para no depender de overrides de template ni
 * de posiciones absolutas/anchos fijos.
 */
body.single-product .product-entry-wrapper {
	width: min(100%, 90vw);
	max-width: 1400px;
	margin-inline: auto;
	display: grid;
	/*
	 * 38.43% / 34.57% / 27% en vez de 42% / 31% / 27%: la columna de
	 * galería se achica exactamente al ancho real que ya ocupa la
	 * imagen (ver comentario en `.ct-product-gallery-container` más
	 * abajo — la reducción de 6 a 5 miniaturas vive ACÁ ahora, no como
	 * un max-width interno) y esos puntos que antes quedaban como aire
	 * libre dentro de la columna 1 pasan a la columna 2. La suma de
	 * columna 1 + columna 2 no cambia (42+31 = 38.43+34.57 = 73%), así
	 * que la tarjeta de compra (columna 3) no se mueve ni un píxel —
	 * sólo se angosta la columna de la galería y se ensancha la de
	 * información central para que la separación entre la imagen y el
	 * título vuelva a ser el mismo `column-gap` que hay en todos los
	 * demás huecos del layout, en vez de un hueco extra sin usar.
	 *
	 * `fr` (no `%`) a propósito — reportado en vivo con captura real:
	 * "Tu carrito" (columna 3) terminaba pegado al borde de la ventana,
	 * sin margen ni borde derecho visible, específicamente entre
	 * ~950px y ~1400px de ancho real. Causa real, confirmada midiendo
	 * `getBoundingClientRect()` de cada columna: con columnas en `%`
	 * que YA suman 100% del ancho del grid, el `column-gap` se agrega
	 * ENCIMA de esas columnas (no se resta de ellas) — el contenido
	 * real del grid terminaba siendo ~48px más ancho que el propio
	 * `.product-entry-wrapper` (2 huecos × 24px de `column-gap`), y esa
	 * columna 3 quedaba corrida esos mismos 48px hacia la derecha,
	 * saliéndose del wrapper. `fr` reparte el espacio disponible en la
	 * MISMA proporción (38.43:34.57:27, sin cambiar ninguna relación ya
	 * documentada arriba) pero el propio Grid resta el `column-gap`
	 * ANTES de repartir — sin ese excedente, ninguna columna vuelve a
	 * salirse del wrapper, en ningún ancho.
	 */
	grid-template-columns: 38.43fr 34.57fr 27fr;
	column-gap: var(--space-6);
	align-items: start;
}

/*
 * Reportado en vivo con captura real (~950-1400px de ancho real): ni
 * 2 columnas de características ni achicar más el texto alcanzaban a
 * bajar el bloque de información por debajo del alto real de la
 * imagen — la imagen se angosta (y por lo tanto se achica, tiene
 * `aspect-ratio` fijo) mucho más rápido que lo que el texto puede
 * seguir achicándose sin volverse ilegible. Se le da más ancho a la
 * columna de la galería EN ESTE RANGO PUNTUAL (no en todos los anchos:
 * a partir de 1400px la imagen ya mide de sobra sin esto) — quitándole
 * ese mismo ancho a la columna de información (no a la de "Tu
 * carrito", columna 3, que no cambia acá tampoco): la SUMA columna 1 +
 * columna 2 sigue siendo 73% (mismo criterio que ya se documentó
 * arriba para no reabrir el hueco que esa relación resolvía), sólo
 * cambia el reparto interno entre ambas.
 *
 * `fr` acá también (mismo motivo documentado en la regla base de
 * arriba) — el bug de "Tu carrito" saliéndose del wrapper por el
 * `column-gap` no restado se reproducía CON MÁS fuerza justo en este
 * rango puntual.
 */
@media (min-width: 950px) and (max-width: 1400px) {
	body.single-product .product-entry-wrapper {
		grid-template-columns: 49fr 24fr 27fr;
	}

	/*
	 * Reportado en vivo, mismo caso: con la columna de información ya
	 * angosta acá (27%, arriba), el título real (varía por producto, acá
	 * "Oso marron amoroso prueba") partía a 3 líneas — comprobado en
	 * vivo, 109px de alto — y ESO, no ya las características, era lo
	 * último que dejaba a esta columna un poco más alta que la imagen.
	 * El tamaño de acá ya es fluido (`clamp`, ver la regla base de
	 * `.product_title`) pero su piso mínimo (1.5rem) seguía siendo
	 * grande para esta columna angosta en concreto. Se baja sólo el piso
	 * mínimo EN ESTE RANGO puntual — el título sigue siendo el mismo
	 * elemento, mismo lugar, sólo un poco más chico acá para que quepa
	 * en 2 líneas en vez de 3.
	 *
	 * `body.single-product` de prefijo a propósito — comprobado en vivo
	 * que sin él esta regla NO cambiaba nada: la base incondicional de
	 * `.product_title` (misma especificidad, 2 clases) vive más abajo en
	 * este mismo archivo, así que en el empate ganaba ella por venir
	 * después, sin importar que esta regla estuviera dentro de una media
	 * query más específica en intención. El prefijo agrega especificidad
	 * real (3 clases), gana siempre, sin depender del orden.
	 */
	body.single-product .product-central-info .product_title {
		font-size: clamp(0.95rem, 0.6rem + 1.4vw, 2.25rem);
	}
}

/*
 * Columna 3 (tarjeta de compra + mini-carrito) — CONTAINER QUERY, no
 * media query (2026-10-03). Las reglas de acá abajo dependen de cuánto
 * mide ESTA columna, no del viewport de la página — con media queries
 * fijas (950-1400px y, más abajo, 950-1099px) quedaba un hueco real sin
 * cubrir entre ambas: a 1100-1199px de viewport la columna medía
 * 254-262px (angosta de verdad) pero esos anchos NO entraban en el
 * rango "angosto" (950-1099px) ni tenían match en éste — el nombre del
 * producto del mini-carrito desbordaba y tapaba el botón de eliminar,
 * reportado en vivo, confirmado con DevTools Protocol (remove button
 * fuera del área visible exactamente en ese rango, en CUALQUIER
 * producto, nombre largo o corto — no depende del contenido).
 *
 * `container-type: inline-size` se declara en `.product-buy-card-
 * sticky-wrap` (ver más abajo en este archivo) — el ancho real medido
 * (no adivinado) va de ~218px (viewport 950px) a ~365px (viewport
 * ≥1600px, tope real del contenedor general) cuando el grid de 3
 * columnas está activo; por debajo de 950px el layout se apila a una
 * columna y esta columna vuelve a medir el ancho casi completo de la
 * página — un @container con `max-width` simplemente no aplica ahí,
 * sin necesitar ningún guard de viewport aparte (reemplaza el antiguo
 * `if (window.innerWidth < 950) return;` que tenía single-product.js
 * para un cálculo ya eliminado, ver setupCompleteGiftOverlap() más
 * arriba en ese archivo).
 */
@container amorea-buy-column (max-width: 370px) {
	/*
	 * BUG REAL encontrado 2026-10-03 (reportado en vivo por la usuaria,
	 * "Agregar al carrito" se veía pegado a la izquierda del botón en
	 * vez de centrado, en TODOS los anchos): esta columna (sticky,
	 * 3 columnas) nunca mide más de ~365px de ancho real ni siquiera en
	 * escritorio completo (ver comentario de arriba) — este
	 * `@container(max-width:370px)` en la práctica SIEMPRE está activo,
	 * no es sólo para pantallas angostas. La ronda anterior puso acá
	 * `justify-content:flex-start` para un caso real de recorte
	 * asimétrico con `overflow:hidden` — verificado en vivo con el
	 * contenido/padding ACTUALES (320-1920px de viewport): el texto
	 * nunca llega a desbordar su caja (94-102px de texto contra
	 * 110-374px de botón disponible), así que ese recorte ya no puede
	 * ocurrir y la alineación a la izquierda no tiene ninguna función —
	 * sólo descentraba el botón sin necesidad. Se quita
	 * `justify-content:flex-start` (vuelve a `center`, la base nativa
	 * de `.button`). Si en el futuro el texto del botón cambia a algo
	 * mucho más largo, volver a medir antes de reintroducir
	 * `flex-start` — no asumir que ya no hace falta para siempre.
	 */
	body.single-product .product-buy-card .single_add_to_cart_button,
	body.single-product .product-buy-card .ct-product-add-to-cart .button {
		padding-inline: var(--space-1);
		font-size: 11px;
		height: 36px;
		min-height: 36px;
	}

	body.single-product .product-buy-card__actions .ct-product-add-to-cart form.cart {
		height: 36px;
	}

	body.single-product .product-buy-card__actions .ct-product-add-to-cart form.cart .ct-cart-actions {
		height: 36px;
	}

	body.single-product .product-buy-card__view-cart {
		padding-inline: var(--space-1);
		font-size: 11px;
		height: 36px;
		min-height: 36px;
	}

	/*
	 * Reportado en vivo: con las 2 filas de arriba ya más bajas (cantidad
	 * 32px, botones 36px), el aire que separaba "Ver carrito" de "Envíos
	 * rápidos" (pensado para el alto de 44px de siempre) quedaba
	 * desproporcionado — se achica sólo acá.
	 */
	body.single-product .product-buy-card__footer {
		margin-top: var(--space-3);
		padding-top: var(--space-3);
	}

	/*
	 * Reportado en vivo: pese al ellipsis ya arreglado (ver
	 * `.product-buy-card__btn-label` más abajo en el archivo), el pedido
	 * real es que "Agregar al carrito" se lea COMPLETO, nunca cortado —
	 * acá la tarjeta de compra es la columna 3 de un grid de 3 columnas,
	 * así que "Agregar al carrito" y "Ver carrito" compiten por muy poco
	 * ancho. Se libera espacio real en vez de seguir bajando el tamaño
	 * de letra (ya estaba en 11px, más chico deja de ser legible): menos
	 * padding horizontal en la propia tarjeta (`--space-3` en vez de
	 * `--space-6`, sólo acá) + menos gap entre los dos botones +
	 * columnas YA NO iguales (`3fr 2fr` en vez de `1fr 1fr`) —
	 * "Agregar al carrito" (19 caracteres) necesita bastante más ancho
	 * real que "Ver carrito" (11), repartir el espacio a partes iguales
	 * entre ambos era lo que sobraba de un lado y faltaba del otro.
	 */
	body.single-product .product-buy-card {
		padding-inline: var(--space-3);
	}

	body.single-product .product-buy-card__actions {
		grid-template-columns: 3fr 2fr;
		gap: var(--space-1) var(--space-2);
	}

	/*
	 * Reportado en vivo, "media pantalla": dos problemas puntuales de
	 * este rango.
	 * 1. El selector de cantidad real (130px fijo, mismo valor en
	 *    cualquier ancho) se ve desproporcionadamente grande acá porque
	 *    la tarjeta entera es más angosta que en escritorio completo —
	 *    se achica sólo acá.
	 * 2. Cuando el selector de cantidad es real, "Ver carrito" pasa a su
	 *    propia fila (ver `:has()` más abajo en el archivo) — pero el
	 *    `gap` de arriba (`var(--space-1)`, 4px) se pensó para el layout
	 *    ANTERIOR de 2 columnas siempre lado a lado, no para esta fila
	 *    nueva abajo — 4px se veía "pegado". Se sube sólo el espacio
	 *    ENTRE FILAS (`row-gap`) a `--space-3` acá, sin tocar el `--
	 *    space-2` que ya separa bien cantidad de botón en la misma fila.
	 */
	body.single-product .product-buy-card__actions:has(.quantity:not(.hidden)) {
		row-gap: var(--space-3);
	}

	/*
	 * `body.single-product` de prefijo, y repitiendo la cadena completa
	 * hasta `.quantity` (mismo motivo ya documentado varias veces en este
	 * archivo): la regla incondicional que fija 130px
	 * (`.product-buy-card__actions .ct-product-add-to-cart form.cart
	 * .ct-cart-actions .quantity`, más abajo) encadena 5 clases — un
	 * primer intento acá con menos clases (`.product-buy-card__actions
	 * .quantity`) perdía ese empate de especificidad sin importar que
	 * viviera dentro de esta media query.
	 */
	body.single-product .product-buy-card__actions .ct-product-add-to-cart form.cart .ct-cart-actions .quantity {
		width: 75px;
	}

	/*
	 * Reportado en vivo: con un número de 2 dígitos (10+), el "+"/"-"
	 * tapaban el número — causa real: Blocksy calcula el tamaño de esos
	 * botones Y el padding-inline del propio input a partir de
	 * `--quantity-height` (`tamaño del botón = altura/1.8`, `padding del
	 * input = altura/1.8 + 9%` — ver woocommerce.min.css nativo), y esa
	 * variable nunca se había redefinido acá (queda en su default,
	 * 55px) — a los 75px de ancho que ya dejamos arriba, ese padding le
	 * comía casi toda la caja (33px de cada lado, sólo ~8px libres para
	 * el número). Bajar `--quantity-height` acá encoge AMBAS cosas a la
	 * vez (mismo origen real, un solo número que arreglar) — el "+"/"-"
	 * quedan más chicos pero el número (incluso de 2 dígitos) por fin
	 * tiene espacio real para verse completo.
	 */
	body.single-product .product-buy-card__actions .ct-product-add-to-cart form.cart .ct-cart-actions .quantity {
		--quantity-height: 36px;
	}
}

.entry-summary.entry-summary-items {
	display: contents;
}

/*
 * `.woocommerce-product-gallery` no se estiraba sola al ancho de su
 * columna del grid (comprobado en vivo: quedaba en ~260px de un
 * espacio de ~520px) — Blocksy trae su propio ancho dinámico para esa
 * caja pensado para su layout flex original. Se fuerza acá al 100% de
 * la columna; el <img> real ya es responsive nativo
 * (`max-width:100%; height:auto` + srcset), así que sólo faltaba que
 * su contenedor ocupara el espacio real disponible.
 */
.woocommerce-product-gallery {
	grid-column: 1;
	width: 100% !important;
}

.woocommerce-product-gallery .ct-product-gallery-container,
.woocommerce-product-gallery .ct-media-container,
.woocommerce-product-gallery img {
	width: 100%;
}

/*
 * A pedido del cliente: reducir de 6 a 5 las miniaturas que se ven
 * completas sin scroll, achicando la imagen principal en proporción
 * (todo lo demás de la ficha queda igual).
 *
 * Cada miniatura mide 115px de alto siempre (100px imagen cuadrada +
 * 15px de separación — `--thumbs-width`/`--thumbs-spacing` nativos de
 * Blocksy, default-gallery.scss, no configurables desde acá) y la
 * columna de miniaturas siempre iguala su alto al de la imagen
 * principal (`.flexy-pills { height: 100% }` nativo). La imagen
 * principal no tiene alto propio — sale de su ancho real × el
 * `aspect-ratio` que Blocksy calcula por imagen (inline, varía según
 * las fotos reales de cada producto) — así que la única forma de
 * bajarle el alto sin pelear contra ese aspect-ratio dinámico es
 * angostar el ancho: mismo aspect-ratio, menos ancho, por lo tanto
 * menos alto, en la misma proporción para cualquier producto.
 *
 * Ese angostado vive ahora en `grid-template-columns` de
 * `.product-entry-wrapper` (38.43% en vez de 42% para la columna de
 * galería — ver comentario ahí), NO acá como un `max-width` interno.
 * Motivo: un `max-width` interno deja aire libre sin usar dentro de
 * la columna 1 del grid (entre el borde derecho de la imagen y el
 * borde de la columna) — eso fue lo que causaba que el título se
 * viera "separado" de la imagen, con un hueco más grande que el
 * `column-gap` normal del resto del layout. Angostando la columna del
 * grid directamente, la imagen sigue midiendo lo mismo (91.5% de
 * 42% ≈ 100% de 38.43%, mismo resultado en píxeles) pero ya no queda
 * aire suelto: el contenedor de la galería y la imagen vuelven a
 * medir exactamente lo mismo.
 */
.woocommerce-product-gallery .ct-product-gallery-container {
	max-width: 100%;
}

/*
 * Estandarización pedida por el cliente: un producto con UNA sola foto
 * (sin miniaturas) mostraba la imagen principal más grande que uno con
 * varias fotos, porque Blocksy sólo angosta la imagen principal
 * (`.thumbs-left .flexy{margin-inline-start:115px}`, nativo) cuando
 * SÍ hay miniaturas que le hacen lugar a la izquierda — mismo ancho de
 * columna de galería, pero la imagen ocupa distinto ancho según cuántas
 * fotos tenga el producto, y con el mismo `aspect-ratio` fijo (2:3, ver
 * memoria del proyecto), distinto ancho es también distinto alto.
 *
 * CORRECCIÓN sobre el primer intento (`.woocommerce-product-gallery
 * .flexy`): no hacía nada — comprobado leyendo el HTML real. Con UNA
 * sola foto, Blocksy NUNCA imprime el carrusel `.flexy` (no hay nada
 * que deslizar): el marcado es sólo
 * `.ct-product-gallery-container > .ct-media-container`, directo, sin
 * ningún `.flexy` de por medio — el selector apuntaba a un elemento que
 * ese caso ni siquiera tiene en el DOM. `.thumbs-left` (la señal real
 * de "tiene más de una foto") tampoco vive en la galería: Blocksy la
 * agrega al `<div class="product ...">` de más afuera, dos niveles por
 * encima.
 *
 * Se reserva el mismo espacio (115px = 100px de miniatura + 15px de
 * separación, valor nativo fijo de Blocksy, no responsive) como
 * `padding-inline-start` del CONTENEDOR (no `margin` en un `.flexy`
 * que puede no existir) — `padding` en vez de `margin` a propósito:
 * `.ct-media-container`/`img` ya tienen `width:100%` fijado más arriba
 * en este archivo, así que un `margin` se sumaría por fuera de ese
 * 100% y desbordaría; el `padding` en cambio angosta el propio
 * contenedor, y el `width:100%` de adentro ya calcula sobre ese
 * espacio más chico, sin desbordar. `.product:not(.thumbs-left)` sólo
 * cuando NO hay miniaturas reales — con miniaturas, Blocksy ya angosta
 * la imagen por su cuenta (nativo, sin tocar) y sumarle este padding
 * duplicaría el espacio.
 *
 * Cuando en producción cada producto tenga varias fotos reales (el
 * caso real, este es sólo un estado de catálogo incompleto), ningún
 * producto va a tener `:not(.thumbs-left)` y esta regla deja de
 * aplicarse sola, sin que haga falta tocarla de nuevo. Sólo en el
 * rango de escritorio donde las miniaturas van verticales a la
 * izquierda (≥950px — por debajo, `.thumbs-left` ya se neutraliza a
 * horizontal, ver el bloque de esa media query más abajo, y ahí la
 * imagen principal SIEMPRE va a ancho completo, con o sin miniaturas,
 * no hay nada que igualar).
 */
@media (min-width: 950px) {
	.product:not(.thumbs-left) .woocommerce-product-gallery .ct-product-gallery-container {
		padding-inline-start: 115px;
	}
}

.product-central-info {
	grid-column: 2;
	min-width: 0;
}

.product-central-info .product_title {
	margin-bottom: 0;
	/*
	 * Blocksy/Amorea usan --font-display (Cormorant Garamond, serif) en
	 * los h1 por defecto en todo el sitio — acá se pide explícitamente
	 * sans-serif (como el resto de la interfaz de la ficha), sólo para
	 * este título, sin tocar la convención de h1 del resto del sitio.
	 */
	font-family: var(--font-body);
	font-weight: 800;
	font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.25rem);
	line-height: 1.15;
	color: var(--color-text);
}

/*
 * Corazón de favoritos ENCIMA de la galería (esquina superior
 * derecha), no junto al título. `right: 20px` puro no alcanza porque
 * `.product-entry-wrapper` es el grid de LAS TRES columnas — "a la
 * derecha del wrapper" cae cerca de la tarjeta de compra, no de la
 * imagen (comprobado en vivo). Probé anclarlo por `grid-column`/
 * `grid-row` (el área de un grid item absolute debería ser su propio
 * contenedor de bloque según la spec) pero Chromium no lo resolvió
 * así en la práctica — así que assets/js/single-product.js reubica
 * este nodo DENTRO de `.woocommerce-product-gallery` (que ya es
 * `position: relative` nativo) apenas carga la página; acá sólo queda
 * el posicionamiento final, ya relativo al contenedor real de la
 * imagen.
 */
.product-single-wishlist {
	position: absolute;
	top: 20px;
	/*
	 * `right: 20px` puro: `.woocommerce-product-gallery` ya mide
	 * exactamente lo mismo que la imagen real (ver comentario en
	 * `.ct-product-gallery-container` — la reducción de 6 a 5
	 * miniaturas angosta la columna del grid completa, ya no deja
	 * aire libre interno), así que ya no hace falta compensar ningún
	 * espacio suelto para que el corazón caiga sobre la foto.
	 */
	right: 20px;
	z-index: 2;
}

.product-single-wishlist .product-card-toolbar__wishlist {
	background: var(--color-surface);
	box-shadow: var(--shadow-sm);
}

/*
 * Reportado en vivo: al pasar el mouse (y por lo tanto también al
 * hacer click para marcar/desmarcar, que siempre ocurre con el cursor
 * encima) el corazón "se pone en blanco" y se vuelve invisible. Causa
 * real: la regla de arriba (`background: var(--color-surface)`, 2
 * clases) empata en especificidad con `.btn-icon:hover` de buttons.css
 * (también 2 selectores) — y esta hoja (`single-product.css`) carga
 * DESPUÉS de `buttons.css` en el `<head>` real, así que en el empate
 * gana esta regla y el círculo se queda blanco incluso en hover. El
 * ícono sí cambia a blanco en hover (`.btn-icon:hover{color:...}`, sin
 * ninguna regla que lo contradiga) — corazón blanco sobre círculo
 * blanco, invisible, sea que se esté marcando o desmarcando. Se
 * restaura el fondo rosado en hover sólo acá, con más especificidad
 * real (2 clases + pseudo-clase) para ganar sin depender del orden de
 * carga.
 */
.product-single-wishlist .product-card-toolbar__wishlist:hover {
	background: var(--color-primary);
}

/*
 * Fondo rosado extremadamente suave — mismo tono ya usado para esto
 * exacto en el banner de categoría (rgba(203,37,90,0.05), ver
 * archive.css `.shop-banner`), no un color nuevo aproximado. Acento
 * rosado discreto en el borde izquierdo, nunca compite con el nombre/
 * estrellas/características/precio (peso visual bajo, sin sombra ni
 * borde alrededor completo).
 */
.product-central-info .woocommerce-product-details__short-description {
	color: var(--color-text);
	font-size: var(--text-sm);
	font-weight: 400;
	line-height: 1.5;
	background: rgba(203, 37, 90, 0.05);
	border-left: 2px solid var(--color-accent);
	border-radius: var(--radius-sm);
	padding: var(--space-2) var(--space-3);
	margin-block: var(--space-3);
}

/*
 * `display: inline-block` explícito — el reset base de SVG de Blocksy
 * (selector `svg{...}` sin scope) puede afectar el flujo por defecto;
 * esto fuerza que el corazón quede SIEMPRE en línea, justo antes del
 * texto (nunca arriba, en su propia línea), sin importar el contenido
 * real de la descripción corta.
 */
.product-short-description__heart {
	display: inline-block;
	vertical-align: -2px;
	color: var(--color-primary);
	fill: var(--color-primary);
	flex: 0 0 auto;
	margin-right: 6px;
}

/*
 * Estrellas propias de la ficha — reemplazan el rating nativo de
 * Blocksy (que no imprime nada con promedio 0) y el enlace de texto
 * "Sé el primero en opinar"/"Sin opiniones": ahí sólo van las 5
 * estrellas, el texto vive únicamente en el nombre accesible
 * (aria-label del propio enlace). Relleno proporcional real vía
 * `clip-path` sobre una segunda estrella idéntica superpuesta —
 * admite cualquier fracción (ej. 3.7 de 5), no sólo enteros/mitades.
 *
 * Color, tamaño y forma calcados a propósito del widget NATIVO de
 * WooCommerce (`.star-rating`, ver woocommerce.css del plugin) — pedido
 * explícito de la usuaria tras ver ambos lado a lado: relleno
 * `#ffba00` (el mismo `--wc-orange` real que define WooCommerce en su
 * propia hoja de estilos, nunca un tono aproximado), vacío `#cfc8d8`
 * (el mismo gris real que usa `.star-rating::before` para las estrellas
 * sin rellenar), 14px (equivalente a `.star-rating` nativo,
 * `font-size:.857em` sobre texto base), estrella sólida de 5 puntas
 * sin trazo (nunca el contorno redondeado que tenía antes). El
 * comportamiento (siempre 5, relleno proporcional real, nunca oculta
 * con promedio 0, accesible, enlaza a Reseñas) es el mismo de siempre
 * — sólo cambia el estilo visual.
 */
.product-single-rating-link {
	display: inline-block;
	margin-block: var(--space-2);
	text-decoration: none;
	border-radius: var(--radius-sm);
}

.product-single-rating-link:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 3px;
}

.product-single-rating-stars {
	display: inline-flex;
	align-items: center;
	gap: 1px;
}

.product-single-star {
	position: relative;
	display: inline-flex;
	width: 14px;
	height: 14px;
}

.product-single-star__outline,
.product-single-star__fill {
	position: absolute;
	inset: 0;
	stroke: none;
}

.product-single-star__outline {
	fill: #cfc8d8;
}

.product-single-star__fill {
	fill: #ffba00;
	clip-path: inset(0 calc(100% - var(--amorea-star-fill, 0%)) 0 0);
}

.product-single-rating-count {
	margin-left: 4px;
	color: var(--color-text-muted);
	font-size: var(--text-sm);
}

/*
 * Pestaña "Reseñas" — quita el azul nativo de la línea inferior de la
 * pestaña activa. CAUSA REAL (confirmada leyendo el CSS real de
 * Blocksy, woocommerce.min.css): la línea es un `::before` nativo de
 * WooCommerce/Blocksy con especificidad muy alta
 * (`.woocommerce div.product div.woocommerce-tabs ul.tabs li.active:before`),
 * `background: var(--tab-background, var(--theme-palette-color-1))` —
 * un azul/color de tema, nunca el rosado Amorea. En vez de pelear esa
 * especificidad con más reglas, se sobreescriben las variables que esa
 * regla YA lee, declaradas en `.woocommerce-tabs` (ancestro real tanto
 * de `ul.tabs` como del panel) — Blocksy sigue pintando SU MISMO
 * `::before` (nunca se duplica ni se agrega un segundo subrayado), sólo
 * que ahora con el color real de Amorea. Mismo mecanismo para el color
 * del texto activo (`--theme-link-active-color`).
 */
.woocommerce-tabs {
	--tab-background: var(--color-primary);
	--theme-link-active-color: var(--color-primary);
	--theme-link-hover-color: var(--color-primary);
}

/*
 * Reportado en vivo con captura real: con "Descripción" e "Información
 * adicional" ya quitadas (arriba, `woocommerce_product_tabs`), a
 * "Reseñas" le queda como ÚNICA pestaña — WooCommerce igual imprime el
 * `<ul class="tabs">` con ese único ítem ("Reseñas (10)"), pero como
 * nunca lo marca `.active` (no hay entre qué elegir), ninguna de las
 * variables de arriba llega a aplicarle color: queda con el azul y el
 * subrayado nativos del navegador, sin ninguna función real (no hay
 * una segunda pestaña a la que cambiar). El contenido real (el panel
 * con "Opiniones de clientes" ya rediseñado) no depende de este `<ul>`
 * para mostrarse — sigue vivo siempre, se oculta sólo el selector de
 * pestañas, nunca el panel ni su lógica de reseñas/AJAX.
 *
 * CORRECCIÓN (van dos intentos): el primero (`.woocommerce-tabs .tabs`,
 * 2 clases) no alcanzaba — la regla nativa real,
 * `.woocommerce div.product div.woocommerce-tabs ul.tabs`, junta 4
 * clases + 3 tipos, mucho más específica. El SEGUNDO intento agregó esa
 * misma cadena (`.woocommerce div.product ...`) pero SEGUÍA sin
 * aplicarse — comprobado en vivo con `document.styleSheets`: en el DOM
 * real de esta ficha, `.woocommerce` NO es un `<div>` envolvente
 * separado, es una clase más del propio `<body>`
 * (`<body class="... woocommerce woocommerce-page ...">`) — pedirle a
 * `.woocommerce` que fuera DESCENDIENTE de `body.single-product` (dos
 * nodos distintos) nunca podía matchear si es la MISMA etiqueta. Se
 * corrige uniendo ambas clases sobre el propio `body` (selector
 * compuesto, sin espacio) en vez de tratarlas como ancestro/
 * descendiente — con eso sí gana por especificidad real, sin depender
 * de qué hoja cargue después.
 */
body.single-product.woocommerce div.product div.woocommerce-tabs ul.tabs {
	display: none;
}

/*
 * Reportado en vivo con captura real: mucho espacio vacío entre
 * "Completa tu regalo" y "Opiniones de clientes". Causa real: dos
 * `padding-top` nativos de Blocksy pensados para separar la BARRA DE
 * PESTAÑAS (ya oculta arriba) del contenido de arriba —
 * `.product > .woocommerce-tabs{padding-top:60px}` (el wrapper) +
 * `...woocommerce-Tabs-panel.panel{padding-top:40px}` (el panel) — acá
 * ya no hay ninguna barra de pestañas que separar, así que ese aire ya
 * no cumple ningún propósito visual, sólo deja hueco.
 *
 * CERO (no un espaciado propio acá) a propósito — pedido explícito del
 * cliente: el espacio entre CADA sección de la ficha debe ser el
 * mismo, y el valor de referencia elegido es el que YA existe entre el
 * panel del carrusel de "Completa tu regalo" y su aviso de dedicatoria
 * (16px, `--space-4`, `margin-top` de
 * `.product-complete-gift__dedication-notice`). Esa sección ya aporta
 * ese mismo `--space-4` como su propio `padding-bottom` (ver
 * `.product-complete-gift` más abajo) — sumarle algo acá duplicaría el
 * espacio, tal como pasaba antes. Misma cadena de ancestros reales que
 * la regla de arriba (empata/gana la especificidad nativa sin depender
 * del orden de carga).
 */
body.single-product.woocommerce div.product div.woocommerce-tabs {
	padding-top: 0;
}

body.single-product.woocommerce div.product div.woocommerce-tabs div.woocommerce-Tabs-panel.panel {
	padding-top: 0;
}

/* ==========================================================================
   Opiniones de clientes (#opiniones) — dos estados aprobados
   ========================================================================== */

/*
 * Fondo BLANCO en ambos estados — el rosado es sólo acento (borde),
 * nunca el fondo dominante del bloque; corrige el rectángulo rosado
 * grande de la ronda anterior. Sin reseñas: tarjeta COMPACTA, ancho
 * contenido (no ocupa todo el ancho de la columna) — nunca una caja
 * enorme innecesaria. Con reseñas: ancho completo (necesita espacio
 * real para el listado), mismo borde e identidad de marca.
 */
.product-reviews {
	background: var(--color-surface);
	border: 1px solid var(--color-accent);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-sm);
	padding: var(--space-4);
	margin-block: var(--space-4);
}

/*
 * Reportado en vivo: con el formulario completo adentro (calificación +
 * reseña + botón, no sólo el mensaje corto de "aún no tiene reseñas"),
 * 420px dejaba a "Sé la primera en opinar sobre..." partido en 2
 * líneas — se sube a 480px, suficiente para el título real de este
 * producto en una sola línea sin agrandar la tarjeta a lo ancho de
 * toda la columna.
 */
/*
 * Reportado en vivo: sin `margin-inline:auto`, esta tarjeta angosta
 * (480px) quedaba pegada al borde izquierdo del área de contenido en
 * vez de centrada en la pantalla — se nota sobre todo en escritorio
 * ancho, donde el área disponible es mucho más ancha que la tarjeta.
 * Aplica igual a "Opiniones de clientes" sin reseñas y a "Agrega tu
 * reseña" (reutiliza esta misma clase, ver single-product-reviews.php).
 */
.product-reviews.is-empty {
	max-width: 480px;
	margin-inline: auto;
	padding: var(--space-4);
}

.product-reviews__title {
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-weight: 700;
	color: var(--color-text);
	margin: 0 0 var(--space-2);
}

/*
 * Reportado en vivo comparando contra el mockup: el corazón del título
 * se veía RELLENO (sólido) en vez de VACÍO (sólo contorno) — causa
 * real: esta regla fijaba `fill` con más prioridad que el atributo
 * `fill="none"` ya puesto en el propio SVG (ver single-product-
 * reviews.php) — un `fill` en hoja de estilos externa siempre le gana a
 * un atributo de presentación del propio SVG, sin importar el valor de
 * este último. `fill: none` acá (no se quita la propiedad, se fija el
 * valor correcto) deja que el contorno (`stroke`, ya rosado vía
 * `color`) sea lo único visible, corazón hueco como el resto de la
 * ficha (`.product-single-wishlist`, mismo corazón hueco).
 */
.product-reviews__title-heart {
	display: inline-block;
	vertical-align: -2px;
	color: var(--color-primary);
	fill: none;
}

/*
 * Réplica del mockup aprobado por la clienta: DOS destellos (no uno)
 * cerca del corazón del título — vía `::before`/`::after` en vez de
 * SVGs a mano. El wrapper (`<span>`, ver woocommerce/single-product-
 * reviews.php) es lo que hace falta para que el `position:absolute` de
 * los destellos tenga como referencia sólo al corazón (16x16px) y no a
 * todo el `<h2>`. Gap más chico al texto (`margin-right`, antes 6px) —
 * reportado en vivo que el título se veía corrido de más hacia la
 * derecha frente al mockup.
 */
.product-reviews__title-icon {
	position: relative;
	display: inline-block;
	margin-right: 10px;
}

.product-reviews__title-icon::after {
	content: "✦";
	position: absolute;
	top: -6px;
	right: -8px;
	font-size: 10px;
	color: var(--color-primary);
}

.product-reviews__title-icon::before {
	content: "✦";
	position: absolute;
	bottom: -2px;
	left: -7px;
	font-size: 6px;
	color: var(--color-primary);
}

/*
 * Dos columnas — resumen (promedio/estrellas/total/"Escribir una
 * reseña") a la izquierda, listado real a la derecha, separadas por
 * una línea vertical suave. Fidelidad a la referencia aprobada.
 */
.product-reviews__body {
	display: grid;
	grid-template-columns: minmax(150px, 200px) 1fr;
	gap: var(--space-6);
}

.product-reviews__summary-col {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-1);
	padding-right: var(--space-5);
	border-right: 1px solid var(--color-border);
}

.reviews-average-number {
	font-family: var(--font-body);
	font-size: 2.5rem;
	font-weight: 700;
	line-height: 1;
	color: var(--color-text);
	margin: 0;
}

.reviews-stars {
	display: inline-flex;
	align-items: center;
	gap: 2px;
}

.reviews-star {
	fill: var(--color-primary);
	stroke: var(--color-primary);
}

.reviews-star.is-empty {
	fill: none;
	stroke: var(--color-primary);
	opacity: 0.5;
}

.reviews-count {
	color: var(--color-text-muted);
	font-size: var(--text-sm);
	margin: 0 0 var(--space-2);
}

.reviews-write-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 38px;
	padding: 0 var(--space-3);
	border: 2px solid var(--color-primary);
	border-radius: var(--radius-pill);
	color: var(--color-primary);
	font-size: var(--text-sm);
	font-weight: 700;
	text-decoration: none;
	white-space: nowrap;
	transition: background var(--transition-base), color var(--transition-base);
}

.reviews-write-link:hover,
.reviews-write-link:focus-visible {
	background: var(--color-primary);
	color: #fff;
}

.product-reviews__list-col {
	min-width: 0;
}

/* Estado vacío — visualmente secundario, sin ningún bloque destacado. */
.product-reviews__empty-title {
	color: var(--color-text);
	font-size: var(--text-xs);
	font-weight: 600;
	margin: 0 0 var(--space-1);
}

.product-reviews__empty-text {
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	margin: 0 0 var(--space-2);
}

.product-reviews__gate {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	margin-top: var(--space-3);
}

.product-reviews__gate a {
	color: var(--color-primary);
	font-weight: 600;
	text-decoration: underline;
}

/* Lista de reseñas reales — sin la línea azul nativa de WordPress. */
.product-reviews .commentlist {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
}

.product-reviews .commentlist .comment_container {
	display: flex;
	gap: var(--space-3);
	padding-bottom: var(--space-4);
	border-bottom: 1px solid var(--color-border);
}

/*
 * Reportado en vivo con captura real: la fecha de cada reseña ("hace
 * X días"/fecha real) no quedaba en una misma línea vertical — unas
 * más a la izquierda, otras más a la derecha. Causa real: `.comment-
 * text` (nativo de WooCommerce, envuelve estrellas+meta+el texto de la
 * reseña) es el SEGUNDO hijo flex de `.comment_container` (arriba)
 * pero nunca tuvo `flex-grow` propio — con el valor por defecto
 * (`flex:0 1 auto`), un hijo flex sin ancho propio se dimensiona por el
 * ancho MÁXIMO que pediría su contenido sin envolver ("max-content"),
 * y ese máximo lo termina fijando el párrafo de la reseña — una reseña
 * corta ("Muy suave y tierno...") pedía un `.comment-text` angosto,
 * una más larga ("Excelente atención durante...") uno más ancho, así
 * que cada reseña terminaba con su PROPIO ancho de fila, y `.meta`
 * (`justify-content:space-between`, ver más abajo) empujaba la fecha
 * al borde derecho de ESE ancho variable en vez de al borde real de la
 * columna. `flex:1 1 auto` (crece para llenar el espacio real
 * disponible, ya no se limita a su contenido) + `min-width:0` (para
 * que el párrafo de la reseña pueda seguir envolviendo texto en vez de
 * forzar el ancho mínimo automático de un hijo flex) iguala el ancho
 * de fila de TODAS las reseñas al mismo, real, disponible — la fecha
 * cae siempre en el mismo borde derecho, en línea.
 */
.product-reviews .commentlist .comment_container .comment-text {
	flex: 1 1 auto;
	min-width: 0;
}

.product-reviews .commentlist li:last-child .comment_container {
	border-bottom: none;
	padding-bottom: 0;
}

/* Avatar con iniciales reales — nunca una foto externa/Gravatar. */
.reviews-avatar {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border-radius: 50%;
	background: var(--color-accent);
	color: var(--color-primary-hover);
	font-size: var(--text-sm);
	font-weight: 700;
	flex: 0 0 auto;
}

/* Estrellas + nombre/insignia/fecha en una sola fila (ver reviews-ajax.php, wrapper real sin duplicar hooks nativos). */
.reviews-item-header {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-1) var(--space-3);
	margin-bottom: var(--space-1);
}

.product-reviews .star-rating {
	color: var(--color-primary);
	margin: 0;
}

.product-reviews .meta {
	display: flex;
	flex: 1 1 auto;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-2);
	margin: 0;
}

.reviews-item-meta-left {
	display: inline-flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-2);
}

.product-reviews .woocommerce-review__author {
	color: var(--color-text);
	font-weight: 700;
}

.product-reviews .woocommerce-review__verified {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	color: var(--color-primary);
	font-style: normal;
	font-size: var(--text-xs);
	font-weight: 600;
}

.product-reviews .woocommerce-review__published-date {
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	white-space: nowrap;
}

.reviews-status {
	min-height: 1.2em;
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	margin: var(--space-2) 0;
}

.reviews-controls {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3);
	margin-top: var(--space-3);
}

.reviews-toggle {
	color: var(--color-primary);
	font-weight: 600;
	font-size: var(--text-sm);
	text-decoration: underline;
	cursor: pointer;
}

.reviews-pagination {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2);
}

.reviews-pagination__link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 32px;
	height: 32px;
	padding: 0 var(--space-2);
	border-radius: var(--radius-sm);
	font-size: var(--text-sm);
	font-weight: 600;
	color: var(--color-text);
	text-decoration: none;
}

.reviews-pagination__link--current {
	background: var(--color-primary);
	color: #fff;
}

.reviews-pagination__link--disabled {
	color: var(--color-text-muted);
	opacity: 0.5;
}

a.reviews-pagination__link:hover,
a.reviews-pagination__link:focus-visible {
	background: var(--color-accent);
}

/*
 * Reportado en vivo comparando contra el mockup: no debía haber ninguna
 * raya entre "Solo los clientes..." y "Sé la primera en opinar..." —
 * causa real: este `border-top` (pensado en su momento sólo para
 * separar visualmente el formulario) quedaba exactamente ahí porque
 * `#review_form_wrapper` empieza justo después del texto vacío. Se
 * quita la línea; el espacio (`margin-top`) se conserva para que
 * siga habiendo aire entre ambos bloques, sólo sin el trazo.
 */
#review_form_wrapper {
	margin-top: var(--space-2);
}

/*
 * Réplica del mockup aprobado por la clienta: en vez de un título plano
 * con borde inferior, "Sé la primera en opinar..." pasa a vivir en una
 * franja rosada con acento a la izquierda — MISMO lenguaje visual que
 * ya usa la descripción corta de arriba de la ficha
 * (`.woocommerce-product-details__short-description`, mismo rosado al
 * 5% de opacidad + borde izquierdo con `--color-accent`), reutilizado
 * acá para que ambos bloques "destacados suaves" de la ficha se vean
 * consistentes entre sí. Reportado en vivo comparando contra el
 * mockup: la primera versión quedó demasiado gruesa (mucho padding) y
 * con letra grande — se achica a `--text-sm` + padding vertical mínimo,
 * franja delgada de verdad.
 */
#review_form_wrapper #reply-title {
	display: block;
	font-family: var(--font-body);
	font-size: var(--text-xs);
	font-weight: 600;
	color: var(--color-text);
	background: rgba(203, 37, 90, 0.05);
	border-left: 3px solid var(--color-accent);
	border-radius: var(--radius-sm);
	padding: var(--space-1) var(--space-3);
	margin-bottom: var(--space-2);
}

/*
 * Reportado en vivo comparando contra el mockup: las estrellas debían
 * ir DEBAJO de "Tu calificación", en su propia fila — causa real:
 * Blocksy fija `.comment-form-rating{display:flex;align-items:center}`
 * (fila, nativo, woocommerce.min.css) — con eso, el label y `p.stars`
 * quedan lado a lado en vez de uno debajo del otro. Se pisa a columna
 * acá, sólo en este formulario.
 */
/*
 * Reportado en vivo comparando contra el mockup: sobraba espacio entre
 * las estrellas y "Tu reseña", y entre la caja de texto y "Tu opinión
 * ayudará..." (32px reales, medido con getBoundingClientRect) — causa
 * real, comprobada con `getComputedStyle(commentform)`: `#commentform`
 * (`.comment-form.has-labels-outside`) es un `display:grid` NATIVO de
 * Blocksy con `row-gap:24px` propio — un intento anterior de arreglar
 * esto poniendo `margin-bottom` en cada campo nunca alcanzaba porque el
 * `gap` de un grid se SUMA aparte de cualquier margen de sus hijos,
 * nunca lo reemplaza. Se pisa `row-gap` acá (no todo `gap`, para no
 * tocar el `column-gap` nativo por si algún campo llega a necesitar 2
 * columnas) — sin margen propio en los campos (alcanza con el gap del
 * grid para todo el espaciado vertical real de este formulario).
 */
#review_form_wrapper #commentform {
	row-gap: var(--space-2);
}

#review_form_wrapper .comment-form-rating,
#review_form_wrapper .comment-form-comment,
#review_form_wrapper .comment-form-field-textarea,
#review_form_wrapper .comment-form-author,
#review_form_wrapper .comment-form-email {
	margin: 0;
}

#review_form_wrapper .comment-form-rating {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
}

/*
 * Reportado en vivo con captura real, comparado contra el mockup de
 * referencia: "Tu calificación" salía en MAYÚSCULAS ("TU CALIFICACIÓN")
 * — causa real, comprobada leyendo el CSS nativo de Blocksy
 * (woocommerce.min.css): `.comment-form-rating label{text-transform:
 * uppercase}` (propio de Blocksy, nunca de este archivo). Esta regla de
 * acá nunca pisaba esa propiedad puntual (sólo tocaba tamaño/color/
 * peso), así que sin un valor propio para `text-transform`, ganaba la
 * de Blocksy — igual en minúsculas/mayúsculas normales que el resto de
 * los labels de este mismo formulario (ninguno de los otros la tenía,
 * por eso sólo "Tu calificación" se veía distinto).
 */
#review_form_wrapper .comment-form-rating label,
#review_form_wrapper .comment-form-comment label,
#review_form_wrapper .comment-form-field-textarea label,
#review_form_wrapper .comment-form-author label,
#review_form_wrapper .comment-form-email label {
	display: block;
	font-size: var(--text-xs);
	font-weight: 600;
	color: var(--color-text);
	text-transform: none;
	margin-bottom: var(--space-1);
}

#review_form_wrapper .required {
	color: var(--color-primary);
}

/*
 * Estrellas del widget nativo de calificación (`p.stars a`, ícono de la
 * fuente `WooCommerce` vía `::before`, ver woocommerce.css) en rosado
 * Amorea en vez del naranja nativo de Blocksy (`color`, de donde el
 * `::before` toma `currentColor`). Reportado en vivo comparando contra
 * el mockup: un primer intento (32px) quedó DEMASIADO grande — se pide
 * expresamente más chicas incluso que las del propio mockup de
 * referencia; 16px (apenas más que el tamaño nativo real de Blocksy,
 * 14.4px) más un gap chico entre cada una para que sigan siendo puntos
 * de click distinguibles sin dominar la fila.
 */
#review_form_wrapper .comment-form-rating p.stars {
	display: flex;
	gap: var(--space-1);
}

#review_form_wrapper .comment-form-rating p.stars a {
	color: var(--color-primary);
	font-size: 16px;
}

/*
 * Pedido explícito del cliente: esta misma tarjeta (calificación + caja
 * de reseña + botón) vive en TODOS los productos, siempre visible —
 * sólo cambia si está deshabilitada o no según si la cuenta logueada
 * compró ESTE producto (`$amorea_can_review`, ver single-product-
 * reviews.php — clase `is-disabled` en el propio `<form>` cuando no).
 * `textarea`/`select`/botón ya llevan el atributo nativo `disabled`
 * (gris/no-editable de fábrica en cualquier navegador, sin CSS propio
 * necesario) — acá sólo lo que el HTML no puede deshabilitar solo:
 * las estrellas son `<a>` (un link no tiene estado `disabled` nativo,
 * ver wishlist-hooks.php para el mismo caso ya resuelto con otro
 * elemento) y el botón necesita además `pointer-events:none` porque
 * `disabled` en un `<button>` ya bloquea el click nativo, pero no
 * mientas la tarjeta entera se ve "apagada" si no se le baja también
 * la opacidad acá.
 */
#commentform.is-disabled .comment-form-rating p.stars {
	pointer-events: none;
	opacity: 0.4;
}

#commentform.is-disabled .comment-form-rating label,
#commentform.is-disabled .comment-form-comment label,
#commentform.is-disabled .comment-form-field-textarea label {
	opacity: 0.6;
}

#commentform.is-disabled .comment-form-comment textarea,
#commentform.is-disabled .comment-form-field-textarea textarea {
	background: var(--color-border);
	cursor: not-allowed;
}

#commentform.is-disabled .form-submit button.submit,
#commentform.is-disabled .form-submit input[type="submit"] {
	background: var(--color-text-muted);
	opacity: 0.6;
	cursor: not-allowed;
	pointer-events: none;
}

.product-reviews__form-note--disabled {
	color: var(--color-text-muted);
}

.product-reviews__form-note--disabled a {
	color: var(--color-primary);
	font-weight: 600;
	text-decoration: underline;
}

/*
 * Nueva línea de ayuda bajo "Tu reseña" (pedido explícito, réplica del
 * mockup) — mismo corazón/rosado ya usado en el resto de la ficha
 * (`.product-short-description__heart`), sólo un poco más chico. Vive
 * en `comment_notes_after` (argumento nativo de `comment_form()`,
 * pensado justo para texto entre los campos y el botón) — ver
 * woocommerce/single-product-reviews.php.
 */
/*
 * Reportado en vivo comparando contra el mockup: quedaba demasiado
 * lejos de la caja de "Tu reseña" (el mismo `--space-4` de arriba Y de
 * abajo) — se achica sólo el de ARRIBA (pegado a la caja, como en el
 * mockup); el de abajo se mantiene para separarlo del botón "Enviar
 * reseña".
 */
.product-reviews__form-note {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	margin: 0;
}

.product-reviews__form-note-heart {
	flex: 0 0 auto;
	color: var(--color-primary);
}

/*
 * `.comment-form-field-textarea` (no `.comment-form-comment`, que es lo
 * que pedía el selector viejo) — comprobado en vivo leyendo el HTML
 * real: esta versión de WordPress envuelve el textarea de la reseña
 * con esa clase genérica (`comment-form-field-{tipo}`), ignorando el
 * `'comment_field'` HTML que arma single-product-reviews.php. Se deja
 * `.comment-form-comment` también, sin quitarlo (por si otra versión de
 * WordPress vuelve a la clase vieja) — igual causa real que ya se
 * documentó abajo para el botón "Enviar" (`<button>` en vez de
 * `<input type=submit>`): ningún ancho/borde/foco de acá se aplicaba.
 */
#review_form_wrapper .comment-form-rating select,
#review_form_wrapper .comment-form-comment textarea,
#review_form_wrapper .comment-form-field-textarea textarea,
#review_form_wrapper .comment-form-author input,
#review_form_wrapper .comment-form-email input {
	width: 100%;
	max-width: 420px;
	padding: var(--space-1) var(--space-2);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-sm);
	font-family: var(--font-body);
	font-size: var(--text-xs);
}

/*
 * Reportado en vivo comparando contra el mockup: la caja de "Tu
 * reseña" quedaba muy alta — a la mitad (`rows="3"` en vez de `rows="8"`
 * en single-product-reviews.php, más este `height` explícito que
 * ignora el alto real que el propio `rows` calcularía).
 */
#review_form_wrapper .comment-form-comment textarea,
#review_form_wrapper .comment-form-field-textarea textarea {
	height: 48px;
	border-color: var(--color-accent);
}

/*
 * Reportado en vivo con captura real: al hacer foco en "Tu reseña" el
 * recuadro se ponía AZUL — causa real: nunca había un `:focus` propio
 * acá, así que el foco caía al outline azul nativo del navegador (el
 * mismo caso ya resuelto varias veces en esta ficha para otros
 * elementos: flechas de galería, miniaturas, botón del lightbox). Se
 * pide explícitamente "un solo borde rosado Amorea" (no un borde rosado
 * más un outline azul aparte) — `outline:none` quita el azul nativo y
 * el único borde visible en foco pasa a ser este, ya rosado.
 */
#review_form_wrapper .comment-form-comment textarea:focus,
#review_form_wrapper .comment-form-field-textarea textarea:focus,
#review_form_wrapper .comment-form-author input:focus,
#review_form_wrapper .comment-form-email input:focus {
	outline: none;
	border-color: var(--color-primary);
}

/*
 * Reportado en vivo con captura real: el botón "Enviar" se veía azul
 * nativo pese a que la regla de abajo ya pedía rosado — causa real:
 * esta versión de WordPress genera el submit de `comment_form()` como
 * `<button class="submit">` (no como `<input type="submit">`, que es
 * lo que pedía el selector viejo) — nunca matcheaba, así que el botón
 * quedaba con el estilo nativo de Blocksy (azul) sin que esta regla
 * hiciera nada. Se agrega `button.submit` sin quitar `input[type=submit]`
 * (por si una versión distinta de WordPress vuelve a cambiarlo). Más
 * chico que antes (menos padding/font-size), a pedido del cliente.
 */
/*
 * Réplica del mockup aprobado: botón de ancho completo (antes chico y
 * en píldora — la clienta pasó una captura de referencia exacta a
 * seguir, que reemplaza ese pedido anterior de "más chico"), esquinas
 * redondeadas moderadas (`--radius-md`, no `--radius-pill`) y texto
 * "Enviar reseña" (ver `label_submit` en single-product-reviews.php).
 */
#review_form_wrapper .form-submit {
	margin: 0;
}

/*
 * Reportado en vivo con captura real: el botón se veía corrido a la
 * izquierda dentro de la tarjeta — causa real: `max-width:420px` en un
 * bloque de `width:100%` sin `margin-inline:auto` se alinea al INICIO
 * de su contenedor por defecto (la tarjeta mide ~448px de ancho útil,
 * más que esos 420px, así que sobraba espacio real a la derecha, nunca
 * repartido). Mismo motivo por el que el textarea/inputs de arriba NO
 * se ven torcidos: son más angostos que la tarjeta por muy poco, la
 * diferencia real se nota bastante menos que en un bloque sólido como
 * este botón.
 */
#review_form_wrapper .form-submit input[type="submit"],
#review_form_wrapper .form-submit button.submit {
	display: block;
	width: 100%;
	max-width: 420px;
	margin-inline: auto;
	background: var(--color-primary);
	color: #fff;
	border: none;
	border-radius: var(--radius-md);
	padding: var(--space-2) var(--space-4);
	font-size: var(--text-xs);
	font-weight: 700;
	cursor: pointer;
}

#review_form_wrapper .form-submit input[type="submit"]:hover,
#review_form_wrapper .form-submit button.submit:hover {
	background: var(--color-primary-hover);
}

#review_form_wrapper .form-submit input[type="submit"]:focus-visible,
#review_form_wrapper .form-submit button.submit:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

/*
 * ---- Características reales (tamaño/color/material/etc.) como pills
 * informativas ----
 *
 * SIN contenedor rosado dominante — fondo transparente, integrado con
 * el resto de la ficha (corrige el bloque rosado grande de la ronda
 * anterior). El rosado Amorea vive únicamente en el NOMBRE de cada
 * característica (arriba); el valor va en oscuro (abajo), como en el
 * diseño original. Cada característica sigue siendo su propio grupo
 * (nombre arriba, pills abajo) — eso no cambia, ni se ensancha fuera de
 * la columna central de información.
 *
 * SUPERA una decisión anterior de esta misma ronda ("una característica
 * debajo de otra, nunca tabla horizontal"): con las 7 características
 * reales completas (Tamaño/Color/Material/Edad recomendada/Relleno/
 * Textura/Lavable), la columna en una sola fila por característica
 * terminaba más alta que la imagen de la galería — reportado en vivo
 * por el cliente: eso empujaba "Completa tu regalo" más abajo (la fila
 * del grid de 3 columnas mide lo que mida la columna MÁS alta). Se
 * pasa a 2 columnas (`grid`, no una tabla real con bordes/celdas — cada
 * grupo se ve exactamente igual, sólo dos por fila en vez de uno) para
 * bajar esa altura casi a la mitad. Sólo en escritorio (≥950px, mismo
 * rango donde el grid de 3 columnas está activo y esta altura sí
 * afecta a "Completa tu regalo" — en `<950px` todo se apila en una sola
 * columna de todos modos, ahí seguir en una lista vertical es más
 * fácil de leer y no genera el problema real).
 */
.product-single-attributes {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	margin-block: var(--space-5);
}

/*
 * Reportado en vivo: con `display:grid` puro (sin `grid-auto-flow`), el
 * orden de llenado es por FILA — con sólo 2 características reales
 * (ej. "Oso Gigante Amoroso": Tamaño + Color), esas dos terminaban una
 * al lado de la otra (misma fila, columna 1 y columna 2) en vez de una
 * debajo de la otra. Pedido explícito del cliente: la columna 1 se
 * llena COMPLETA primero, recién después se pasa a la columna 2 — igual
 * en escritorio completo, no sólo en pantallas angostas. `grid-auto-
 * flow:column` cambia el orden de llenado a por COLUMNA; necesita un
 * número de filas explícito para saber cuándo "está llena" y saltar a
 * la columna 2 (si no, con filas implícitas, una sola columna
 * absorbería TODO sin usar nunca la columna 2). `4` = techo real
 * (`ceil(7/2)`, 7 características máximas posibles: Tamaño/Color/
 * Material/Edad/Relleno/Textura/Lavable, ver attributes.php) — con 7
 * completas, columna 1 se llena exacto (4) y columna 2 recibe las 3
 * restantes, igual que antes; con menos de 5, TODAS caen en columna 1
 * (columna 2 quedaría vacía, sin usarse) — comprobado en vivo que las
 * filas explícitas sin contenido en NINGUNA columna miden 0 (no dejan
 * hueco fantasma), así que esto no le agrega alto a productos con pocas
 * características.
 */
@media (min-width: 950px) {
	.product-single-attributes {
		display: grid;
		grid-auto-flow: column;
		grid-template-columns: repeat(2, 1fr);
		grid-template-rows: repeat(4, auto);
		column-gap: var(--space-5);
	}
}

/*
 * Reportado en vivo con captura real, ventana angosta (~950-1300px de
 * ancho real): 2 columnas SEGUÍAN sin alcanzar — en ese rango la
 * columna de la galería (%, se angosta con el viewport) ya mide poco
 * (comprobado: 377px de alto a 1058px de ancho real, contra 544px que
 * ya medían las características solas en 2 columnas, sin contar
 * título/reseñas/descripción corta arriba). El texto no se achica solo
 * al angostar la columna como sí lo hace una imagen con aspect-ratio
 * fijo — por eso 2 columnas alcanzaba en pantallas anchas (>1400px,
 * donde la imagen ya mide 560px+) pero no accá. Se compacta más fuerte
 * SÓLO en este rango puntual: 3 columnas en vez de 2 (menos filas) +
 * pills/gaps más chicos — no elimina el 100% del desborde en el caso
 * más extremo (limitación real del texto, no more se puede seguir
 * achicando sin volverse ilegible), pero lo reduce a un residual mucho
 * menor. Por encima de 1400px la imagen ya mide de sobra y no hace
 * falta esta compactación extra (se mantienen las 2 columnas, más
 * legibles).
 */
/*
 * `body.single-product` de prefijo en las 3 reglas de acá abajo que
 * pisan una base incondicional (`.product-single-attributes__group`,
 * `.product-attr-pill`, `.product-single-attributes__label`) — MISMA
 * causa real que ya se documentó arriba para el título y los botones:
 * esas bases viven MÁS ABAJO en este archivo con la misma
 * especificidad (una clase), así que sin este prefijo el empate lo
 * ganaba siempre la base por venir después, sin importar que esta
 * regla estuviera dentro de una media query más específica en
 * intención. Comprobado en vivo con `getComputedStyle()` real: las 3
 * seguían midiendo el valor base (pill en `6px 14px`/14px, no el
 * `3px 10px`/12px que se pedía acá) — la compactación de este bloque
 * nunca se había aplicado desde que se escribió.
 */
@media (min-width: 950px) and (max-width: 1400px) {
	.product-single-attributes {
		gap: var(--space-1) var(--space-2);
		margin-block: var(--space-1);
	}

	body.single-product .product-single-attributes__group {
		gap: 2px;
	}

	body.single-product .product-attr-pill {
		padding: 2px 4px;
		font-size: 9.5px;
	}

	body.single-product .product-single-attributes__label {
		font-size: 10px;
	}

	.product-central-info .woocommerce-product-details__short-description {
		font-size: var(--text-xs);
		padding: var(--space-1) var(--space-2);
		margin-block: var(--space-2);
	}

	.product-single-rating-link {
		margin-block: var(--space-1);
	}
}

/*
 * Modo móvil real (<950px, el mismo rango que sin esta regla cae en la
 * columna única de `.product-single-attributes` de más arriba) —
 * reportado en vivo con captura real: una sola columna dejaba mucho
 * espacio en blanco a la derecha de cada pill corta ("30 cm", "Marron",
 * etc.), que nunca llega a usar el ancho completo de su fila. Se pasa a
 * 2 columnas + letras más chicas, pensadas para leerse de cerca en un
 * teléfono sin ocupar tanto alto.
 *
 * `grid-auto-flow: row` (orden de lectura normal, izquierda a derecha,
 * de a 2) en vez del `column` + filas explícitas de escritorio (ver
 * `@media (min-width:950px)` más arriba, ahí sí a pedido puntual del
 * cliente): con un número IMPAR de características (7 como máximo:
 * Tamaño/Color/Material/Edad/Relleno/Textura/Lavable), cualquier grid
 * de 2 columnas deja UNA celda vacía — con `column` esa celda vacía
 * quedaba a media altura (columna derecha 3 filas, columna izquierda
 * 4, un hueco asimétrico abajo a la derecha, reportado como "sigue
 * viéndose descentrado"); con `row` el hueco cae siempre al FINAL
 * (última fila, un solo ítem a la izquierda) — mismo caso de siempre
 * al envolver texto, sin desbalancear el alto de las dos columnas.
 * `body.single-product` de prefijo en las 3 reglas de abajo por la
 * misma razón ya documentada arriba para el rango 950-1400: sin él, la
 * base incondicional que vive más abajo en este archivo (misma
 * especificidad, una clase) gana el empate por venir después en el
 * cascade.
 */
@media (max-width: 949px) {
	.product-single-attributes {
		display: grid;
		grid-template-columns: repeat(2, 1fr);
		column-gap: var(--space-3);
		row-gap: var(--space-2);
		margin-block: var(--space-3);
	}

	body.single-product .product-single-attributes__group {
		gap: 3px;
	}

	body.single-product .product-attr-pill {
		padding: 3px 10px;
		font-size: 12px;
	}

	body.single-product .product-single-attributes__label {
		font-size: 11px;
	}
}

.product-single-attributes__group {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	/*
	 * Reportado en vivo con captura real: sin esto, esta celda del grid
	 * de 2 columnas (ver `.product-single-attributes` más arriba) nunca
	 * se achicaba por debajo del ancho "natural" de su contenido (el
	 * mínimo automático de un ítem de grid, por spec) — con una pill
	 * como "Todas las edades" que necesita más ancho del que le toca a
	 * esta columna, esa pill (y toda la celda) se desbordaba hacia la
	 * DERECHA, literalmente encima de la tarjeta de precio (columna 3
	 * del grid principal) en vez de quedarse contenida acá. `min-width:
	 * 0` permite que la celda sí se achique hasta su ancho real
	 * asignado; el recorte con "…" de la pill (ver `.product-attr-pill`
	 * más abajo) se encarga de que el texto se vea prolijo ahí adentro
	 * en vez de desbordar sin control.
	 */
	min-width: 0;
}

.product-single-attributes__label {
	font-weight: 600;
	font-size: var(--text-xs);
	color: var(--color-primary);
}

.product-single-attributes__pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	list-style: none;
	margin: 0;
	padding: 0;
	min-width: 0;
}

.product-attr-pill {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-pill);
	padding: 6px 14px;
	font-size: var(--text-sm);
	font-weight: 600;
	color: var(--color-text);
	background: var(--color-surface);
	/*
	 * Reportado en vivo con captura real (dos veces): la primera versión
	 * de este ajuste sólo tenía `white-space:nowrap` — evitaba que un
	 * valor largo ("Todas las edades") partiera su texto a 2 líneas,
	 * pero sin nada que CONTENGA ese ancho, la pill se desbordaba sin
	 * control hacia la derecha, encima de la tarjeta de precio (columna
	 * 3 del grid principal) — mucho peor que 2 líneas. `max-width:100%`
	 * (contenido por el `min-width:0` del grupo/lista de arriba) +
	 * `overflow:hidden` + `text-overflow:ellipsis`: ahora, si el valor
	 * real no entra en el ancho real de su columna, se recorta con "…"
	 * DENTRO de la propia pill — nunca se desborda hacia afuera, nunca
	 * tapa nada de al lado.
	 */
	white-space: nowrap;
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
}

.product-attr-pill__swatch {
	width: 16px;
	height: 16px;
	border-radius: 50%;
	border: 1px solid var(--color-border);
	background-color: var(--gray-200);
	flex: 0 0 auto;
}

.product-single-attributes__text {
	color: var(--color-text);
	font-size: var(--text-sm);
	margin: 0;
}

/* ==========================================================================
   Tarjeta de compra (precio, stock, cantidad, agregar al carrito)
   ========================================================================== */

/*
 * Envuelve la tarjeta de compra Y el mini-carrito juntos: el `position:
 * sticky` vive acá, en el wrapper, no en cada uno por separado. Con
 * cada uno sticky de forma independiente, al hacer scroll la tarjeta
 * de compra (ancla arriba) terminaba superpuesta ENCIMA del
 * mini-carrito (que seguía el scroll normal debajo) — comprobado en
 * vivo, reportado por el cliente. Como una sola unidad sticky, bajan
 * y se detienen juntos, nunca uno sobre el otro.
 *
 * `container-type: inline-size` (2026-10-03): declara esta columna
 * como contenedor de Container Queries — todo lo que dentro de ella
 * necesita achicarse cuando el espacio real se angosta (botones,
 * selector de cantidad, filas del mini-carrito) responde a SU PROPIO
 * ancho real (`@container amorea-buy-column (max-width: ...)`, ver
 * esas reglas más abajo en este archivo), no al viewport de la
 * página — así no quedan huecos sin cubrir entre rangos de ancho
 * probados a mano. `container-name` explícito (no `container: ... /
 * inline-size` ni un nombre genérico) para que las reglas que lo usan
 * sean legibles sobre cuál contenedor en concreto están consultando.
 */
.product-buy-card-sticky-wrap {
	grid-column: 3;
	/*
	 * BUG REAL encontrado 2026-10-03 (reportado en vivo, persistía
	 * después de acotar el ancho de "Completa tu regalo" a columnas
	 * 1-2): una FILA de grid es una franja horizontal completa — aunque
	 * esa sección ya sólo ocupa columnas 1-2 en la fila 2, la fila 1
	 * (la que decide cuándo empieza la fila 2) seguía midiendo lo que
	 * pidiera la columna MÁS ALTA de las 3, columna 3 incluida. Con el
	 * carrito mostrando productos reales ("Tu carrito", no sólo el
	 * botón vacío) esa columna pasa a ser la más alta de lejos —
	 * confirmado en vivo: con 5 productos reales en el carrito, la
	 * columna 3 medía 800px contra 639px de la galería, y "Completa tu
	 * regalo" seguía esperando esos 800px completos, sin mejora real.
	 * `grid-row: 1 / 3` hace que la columna 3 pase a ocupar las filas 1
	 * Y 2 juntas — la fila 1 (galería + info) ya no la cuenta para su
	 * propia altura, así que termina antes; "Completa tu regalo"
	 * (fila 2) puede empezar ahí, MIENTRAS el carrito sticky sigue
	 * visible al lado (su área sticky ahora cubre ambas filas, sigue
	 * seguiéndote el scroll durante toda esa sección, no se suelta
	 * antes).
	 */
	grid-row: 1 / 3;
	position: sticky;
	top: var(--space-6);
	container-type: inline-size;
	container-name: amorea-buy-column;
}

.product-buy-card {
	max-width: 100%;
	background: var(--color-surface);
	border: 1px solid var(--color-accent);
	border-radius: var(--radius-lg);
	padding: var(--space-6);
	box-shadow: var(--shadow-sm);
}

.product-buy-card > .price {
	display: block;
	font-family: var(--font-body);
	font-size: var(--text-2xl);
	font-weight: 700;
	color: var(--color-primary);
	margin-bottom: var(--space-3);
}

.product-buy-card .stock {
	font-size: var(--text-sm);
	margin-bottom: var(--space-4);
}

.product-buy-card .stock.in-stock {
	color: var(--color-success);
}

.product-buy-card .stock.in-stock::before {
	content: "";
	display: inline-block;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--color-success);
	margin-right: 6px;
}

.product-buy-card .stock.out-of-stock {
	color: var(--gray-600);
}

/* Agotado: mismo punto que "1 disponibles", en plomo (antes texto rosado suelto). */
.product-buy-card .stock.out-of-stock::before {
	content: "";
	display: inline-block;
	width: 8px;
	height: 8px;
	border-radius: 50%;
	background: var(--gray-500);
	margin-right: 6px;
}

/* Insignia "AGOTADO" de la galería: plomo, misma familia que la de la tarjeta. */
.single-product .out-of-stock-badge {
	background: var(--gray-600);
	color: var(--color-surface);
	border-radius: var(--radius-pill);
	font-weight: 700;
	letter-spacing: 0.02em;
}

/*
 * Reportado en vivo con captura real ("Oso Gigante Amoroso", stock SIN
 * gestionar → selector de cantidad real y visible, `type="number"`, no
 * el `type="hidden"` que sale cuando el stock gestionado es 1 unidad):
 * el botón "Agregar al carrito" caía a una SEGUNDA línea entera, por
 * debajo del selector de cantidad, desbordando la altura fija de 44px
 * de esta fila. Causa real, medida con getBoundingClientRect(): el
 * selector de cantidad mide ~130px (ancho nativo de Blocksy para su UI
 * de +/-, `data-type="type-2"`) dentro de una columna de sólo ~152px
 * (mitad de `.product-buy-card__actions`, ver más abajo) — casi no
 * queda espacio real para el botón, y con `flex-wrap:wrap` acá, en vez
 * de ACHICARSE (el botón ya está preparado para eso: `flex:1 1 100%;
 * min-width:0` + elipsis real en `.product-buy-card__btn-label`, ver
 * más abajo) el navegador prefería envolver la fila entera a una
 * segunda línea. `nowrap` fuerza a que el botón se achique en su lugar
 * en vez de caer a una línea nueva — coherente además con un pedido
 * explícito ya documentado más abajo para el rango móvil ("se
 * mantienen en la MISMA fila también en móvil... nunca se apilan").
 */
.product-buy-card form.cart {
	display: flex;
	flex-wrap: nowrap;
	gap: var(--space-3);
}

/*
 * Blocksy trae un clearfix nativo (`.product[class*=gallery] form.cart:
 * before/:after { content:""; display:table }`) pensado para su layout
 * ORIGINAL basado en floats — nuestro producto sí matchea ese selector
 * (`ct-default-gallery` en la clase real del `.product`, comprobado en
 * vivo). Un clearfix no tiene sentido una vez que `form.cart` es
 * `display:flex` (arriba) — pero esos pseudo-elementos con
 * `content:""` SIGUEN generando cajas reales, y una caja real dentro de
 * un flex container cuenta como ítem propio para `flex-wrap`. Root
 * cause real (encontrado inspeccionando el layout con WebDriver, no a
 * simple vista) del desalineamiento vertical de 12px entre "Agregar al
 * carrito" y "Ver carrito" reportado en un rango de ancho específico
 * (~992px–1230px, tablet con el grid de 3 columnas retenido más
 * arriba): a esos anchos concretos, WebKit ubicaba a `.ct-cart-actions`
 * (con el botón adentro) en una SEGUNDA línea del flex-wrap, aunque los
 * pseudo-elementos midieran 0×0 — invisibles pero no inertes. Nunca
 * ocurría en escritorio ancho ni en el apilado <768px, sólo en ese
 * rango intermedio (confirmado desactivando el `content` en vivo: el
 * desalineamiento desaparecía por completo).
 *
 * `.product-buy-card .ct-product-add-to-cart form.cart::before/::after`
 * (sólo 2 clases) empata en especificidad real con la regla nativa
 * (`.product[class*=gallery] form.cart:before` — clase + atributo,
 * también 2 selectores de nivel clase) — comprobado en vivo que ese
 * primer intento NO alcanzaba: `single-product.css` carga ANTES que
 * `woocommerce.min.css` en el `<head>` real de esta página (orden de
 * encolado del propio tema, no algo a asumir), así que en un empate
 * gana la nativa por venir después, no la nuestra. Se agrega
 * `.product-buy-card-sticky-wrap` como clase adicional real (SÍ es un
 * ancestro genuino) para ganar por especificidad real, nunca por
 * depender del orden de carga.
 */
.product-buy-card-sticky-wrap .product-buy-card .ct-product-add-to-cart form.cart::before,
.product-buy-card-sticky-wrap .product-buy-card .ct-product-add-to-cart form.cart::after {
	content: none;
}

.product-buy-card form.cart .quantity {
	flex: 0 0 auto;
}

/*
 * Blocksy colorea sus propios controles (cantidad, select de
 * variación, botón) con --theme-palette-color-1 — que en este sitio es
 * el azul por defecto de Blocksy, nunca redefinido a nivel global
 * porque Carrito/Checkout ya resuelven su propio color por su cuenta
 * (mismo patrón: overrides puntuales, nunca uno sitewide). Acá se
 * repite ese mismo patrón, sólo dentro de la tarjeta de compra.
 */
.product-buy-card .quantity .qty {
	border-color: var(--color-border);
}

.product-buy-card .quantity .qty:focus {
	border-color: var(--color-primary);
	outline: none;
	box-shadow: var(--shadow-focus);
}

.product-buy-card .quantity .ct-increase,
.product-buy-card .quantity .ct-decrease {
	color: var(--color-primary);
}

/*
 * Reportado en vivo: el "azul" no era un anillo de foco (comprobado
 * moviendo el foco real con WebDriver: el clic en "+"/"-" nunca deja el
 * foco en ellos, cae en `<main>`, sin ningún outline visible ahí) —
 * era el FONDO en `:hover`, confirmado con `getComputedStyle` real
 * (`rgb(21,89,237)`, el azul nativo de Blocksy). Causa real:
 * `div.quantity .ct-increase:hover{background:var(--quantity-hover-
 * color, var(--theme-button-background-hover-color))}` — sin
 * `--quantity-hover-color` propio, cae al azul de tema. Se define esa
 * misma variable acá con el rosado Amorea (mismo mecanismo de override
 * que ya usa el resto de la ficha para este azul: flechas de galería,
 * botón del lightbox — nunca se toca el archivo nativo).
 */
.product-buy-card .quantity {
	--quantity-hover-color: var(--color-primary);
}

.product-buy-card .quantity .ct-increase:focus-visible,
.product-buy-card .quantity .ct-decrease:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

.product-buy-card select {
	border-color: var(--color-border);
	color: var(--color-text);
}

.product-buy-card select:focus {
	border-color: var(--color-primary);
	outline: none;
	box-shadow: var(--shadow-focus);
}

.product-buy-card .reset_variations {
	color: var(--color-primary);
}

/*
 * Modelo de caja EXPLÍCITO e idéntico al de "Ver carrito" — un
 * `<button>` nativo trae su propio `border-width`/`line-height`/
 * `box-sizing` de fábrica (heredado de WooCommerce/Blocksy), distinto
 * del de un `<a>` corriente; con sólo `min-height` igual en ambos,
 * cualquier diferencia de esos otros valores igual desalineaba los dos
 * botones. Acá se fija CADA propiedad del modelo de caja, no sólo la
 * altura, para que el resultado sea 100% predecible.
 */
/*
 * Reportado en vivo con captura real: "Agregar al carrito" (junto al
 * selector de cantidad real) se quedaba corto del ancho real de "Ver
 * carrito" de abajo — quedaba un hueco visible a la derecha, más
 * notorio en algunos anchos de escritorio que en otros (a 1920px, 28px
 * de hueco; a 1440px, por coincidencia, ningún hueco). Causa real:
 * Blocksy fija nativo `.ct-cart-actions :is(.single_add_to_cart_button,
 * .added_to_cart){max-width:var(--theme-button-max-width)}` — un techo
 * fijo (~147px reales acá) que no se mueve aunque `flex:1 1 100%` (ya
 * puesto acá) sí le pida crecer más. El hueco aparecía sólo en los
 * anchos donde el espacio real disponible superaba ese techo; por eso
 * "a veces se veía bien". `max-width:none` acá lo anula sin tocar el
 * archivo nativo.
 */
.product-buy-card .single_add_to_cart_button,
.product-buy-card .ct-product-add-to-cart .button {
	box-sizing: border-box;
	flex: 1 1 100%;
	width: 100%;
	max-width: none;
	min-width: 0;
	height: 44px;
	min-height: 44px;
	margin: 0;
	border-width: 2px;
	border-style: solid;
	border-radius: var(--radius-md);
	/* BUG REAL encontrado 2026-10-03 (reportado en vivo, captura de
	   cerca): `line-height:1` no deja margen vertical para la cola de
	   descendentes ("g", "j", "y") — combinado con `overflow:hidden`
	   en `.product-buy-card__btn-label` (abajo), la "g" de "Agregar"
	   se veía con la base cortada en seco. 1.3 deja el aire mínimo
	   necesario sin cambiar el centrado vertical (el botón sigue
	   centrando su contenido por flex, no por line-height). */
	line-height: 1.3;
	padding-inline: var(--space-3);
	font-size: var(--text-sm);
	font-weight: 700;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	background-color: var(--color-primary);
	border-color: var(--color-primary);
	color: #fff;
}

/*
 * `text-overflow:ellipsis` puesto directamente en el botón (arriba)
 * nunca dibuja el "…" real — comprobado en vivo con una captura de
 * cerca ("Agregar al ca" cortado en seco, sin puntos): el botón es
 * `display:flex` nativo de Blocksy y `text-overflow` no tiene efecto
 * sobre el texto de un contenedor flex (limitación real de la spec, no
 * de este archivo). single-product.js (wrapAddToCartButtonLabel())
 * envuelve el texto en este span propio, que sí puede truncarse — acá
 * sólo el `min-width:0` necesario para que se achique dentro del
 * botón flex (si no, vuelve a tomar el ancho "natural" del texto
 * completo y desborda) y `overflow:hidden; text-overflow:ellipsis`
 * (heredan el resto de tipografía/color del botón, no se repiten).
 */
.product-buy-card__btn-label {
	display: block;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: inherit;
}

.product-buy-card .single_add_to_cart_button:hover,
.product-buy-card .ct-product-add-to-cart .button:hover {
	background-color: var(--color-primary-hover);
	border-color: var(--color-primary-hover);
	color: #fff;
}

.product-buy-card .single_add_to_cart_button:focus-visible,
.product-buy-card .ct-product-add-to-cart .button:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

.product-buy-card .single_add_to_cart_button:disabled,
.product-buy-card .single_add_to_cart_button.disabled,
.product-buy-card .ct-product-add-to-cart .button:disabled,
.product-buy-card .ct-product-add-to-cart .button.disabled {
	background-color: var(--gray-300);
	border-color: var(--gray-300);
	color: var(--gray-600);
	cursor: not-allowed;
}

/*
 * WooCommerce (add-to-cart.js, núcleo, no se toca) agrega SOLO ese
 * enlace nativo "Ver carrito" después del botón la primera vez que se
 * usa con éxito, de forma PERMANENTE en el DOM por el resto de esa
 * carga de página — nunca desaparece aunque se quite el producto del
 * carrito después. Ya existe un "Ver carrito" propio y siempre visible
 * en el panel "Tu carrito" (mini-cart-panel.php) — este duplicado era
 * además una fuente de cambio de alto permanente e impredecible en la
 * columna de compra (spec del cliente: "Completa tu regalo" no debe
 * moverse nunca). Se oculta, no se quita del DOM (evita depender de
 * cuándo exactamente lo inserta ese script de WooCommerce).
 */
.product-buy-card .added_to_cart {
	display: none;
}

/*
 * "Ver carrito" junto al botón nativo — DISTINTO del enlace nativo
 * `.added_to_cart` que WooCommerce agrega solo tras un agregado
 * exitoso (ese sigue oculto, ver comentario arriba: causaba un cambio
 * de alto impredecible). Éste es propio, siempre visible desde la
 * carga de página, nunca cambia el alto de la tarjeta.
 *
 * CAUSA REAL de que apareciera debajo (ronda anterior): `display:flex;
 * flex-wrap:wrap` con `flex-basis` en píxeles fijos (220px/160px) — el
 * formulario nativo (cantidad + botón) tiene un ancho mínimo de
 * contenido real que, sumado al de "Ver carrito" más el gap, superaba
 * el ancho disponible de la tarjeta de compra en la mayoría de
 * viewports, así que el navegador los envolvía en dos filas en vez de
 * encogerlos. Se corrige con CSS Grid de dos columnas SIEMPRE iguales
 * (`1fr 1fr`) — nunca se envuelve, nunca depende de un ancho fijo ni
 * del viewport; `min-width:0` en ambas columnas permite que el
 * contenido interno (input de cantidad, texto del botón) se encoja en
 * vez de forzar un desbordamiento. Se mantiene igual en móvil a
 * propósito (ver media query de tipografía más abajo) — nunca se
 * apilan.
 */
/*
 * `align-items: end` (no `stretch`): la columna izquierda real trae el
 * texto de stock ("1 disponibles") ARRIBA del botón.
 *
 * CAUSA REAL de que "alinear las columnas" nunca alcanzara (2 intentos
 * anteriores): el botón real NO es hijo directo de `.ct-product-add-to-cart`
 * — vive dos niveles más adentro (`form.cart` → `.ct-cart-actions` →
 * `button`, marcado real de WooCommerce/Blocksy). Alinear ese `<div>`
 * wrapper como bloque nunca garantiza en qué altura EXACTA queda el
 * botón que hay adentro — `align-items`/`stretch` sobre el wrapper no
 * tiene forma de "empujar" un hijo nieto hacia el borde inferior por
 * sí solo.
 *
 * Solución real: `display: contents` en `.ct-product-add-to-cart`
 * saca ese `<div>` del cálculo de layout (sigue existiendo en el DOM,
 * deja de existir como caja) — sus hijos directos (`.stock`,
 * `form.cart`) pasan a ser celdas de ESTE MISMO grid, como si fueran
 * hermanos de "Ver carrito". Con filas explícitas, el stock va solo en
 * la fila 1 (columna izquierda) y `form.cart` + "Ver carrito" comparten
 * literalmente la fila 2 — CSS Grid garantiza que dos elementos en la
 * misma fila comparten el mismo alto de fila y `align-items: end` los
 * alinea al mismo borde inferior, sin depender de la estructura interna
 * de ninguno de los dos.
 */
.product-buy-card__actions {
	display: grid;
	grid-template-columns: 1fr 1fr;
	grid-template-rows: auto auto;
	align-items: end;
	gap: var(--space-2) var(--space-3);
	width: 100%;
}

.product-buy-card__actions .ct-product-add-to-cart {
	display: contents;
}

.product-buy-card__actions .ct-product-add-to-cart .stock {
	grid-column: 1;
	grid-row: 1;
	margin: 0;
}

.product-buy-card__actions .ct-product-add-to-cart form.cart {
	grid-column: 1;
	grid-row: 2;
	min-width: 0;
	align-self: stretch;
	height: 44px;
	display: flex;
	align-items: stretch;
}

/*
 * Blindaje adicional: `form.cart` ya queda en 44px (arriba), pero
 * `.ct-cart-actions` (el div real de Blocksy que envuelve cantidad +
 * botón, wc-content) es OTRO contenedor flex intermedio con su propio
 * alto "auto" — sin fijarlo también acá, cualquier padding/gap propio
 * de Blocksy en ese nivel podía dejarlo unos píxeles más bajo/alto que
 * los 44px reales del botón, corriendo visualmente el botón dentro de
 * su propia fila aunque la fila del grid ya estuviera bien calculada.
 * Se fija el mismo alto en LOS TRES niveles (form.cart → .ct-cart-actions
 * → botón) para que ninguno dependa de que el contenido interno del
 * siguiente nivel "burbujee" el alto correcto hacia arriba.
 */
/*
 * Reportado en vivo con captura real ("Oso Gigante Amoroso"): el
 * `nowrap` puesto en `form.cart` (arriba) no alcanzaba — causa real,
 * encontrada leyendo el CSS nativo real de Blocksy (woocommerce.min.css,
 * NO el archivo de WooCommerce): `form.cart` tiene un solo hijo directo,
 * este mismo `.ct-cart-actions` (cantidad + botón viven DENTRO de él,
 * no como hijos de `form.cart`) — el `flex-wrap:wrap` que de verdad
 * envolvía la fila era el de ESTE elemento
 * (`.ct-cart-actions{display:flex;flex-wrap:wrap;gap:10px}`, nativo),
 * uno más adentro de lo que se había tocado. Blocksy además fuerza
 * `width:100%` por defecto a CUALQUIER hijo directo de `.ct-cart-
 * actions` (`.ct-cart-actions>*{width:var(--item-width,100%)}`) — con
 * eso Y `flex-wrap:wrap`, cantidad y botón pedían 100% de ancho cada
 * uno, así que envolver a una segunda línea era la única forma de
 * cumplir ambos anchos. `nowrap` acá (el nivel real) + `flex:0 0 auto`
 * en `.quantity` (que ya no depende de ese 100% heredado) resuelve la
 * causa real, no el síntoma.
 */
.product-buy-card__actions .ct-product-add-to-cart form.cart .ct-cart-actions {
	height: 44px;
	align-items: stretch;
	flex-wrap: nowrap;
	margin: 0;
	min-width: 0;
	width: 100%;
}

/*
 * `width: auto` (intento anterior) desbordaba a ~337px — causa real:
 * sin el `width:100%` heredado (arriba) y sin ningún otro ancho propio,
 * el navegador caía al ancho nativo del `<input type=number">` sin
 * límite (`max-width:initial`, propio de Blocksy en ese input). El
 * ancho real que Blocksy sí quiere para este control (`data-type=
 * type-2`) es 130px (`--quantity-width`, ver comentario de arriba) —
 * se fija ese mismo valor en vez de `auto`, sólo sin el 100% heredado
 * que rompía el layout.
 */
.product-buy-card__actions .ct-product-add-to-cart form.cart .ct-cart-actions .quantity {
	flex: 0 0 auto;
	width: 130px;
}

.product-buy-card__view-cart {
	grid-column: 2;
	grid-row: 2;
	align-self: stretch;
}

/*
 * Reportado en vivo con captura real: incluso ya arreglado el
 * envolvido de línea de arriba, 130px de selector de cantidad + botón
 * "Agregar al carrito" real NUNCA entran cómodos en la mitad (columna
 * 1) de esta fila — es una limitación real de espacio, no otro bug de
 * wrap: la columna 1 mide ~152px a 1600px de ancho real, y sólo el
 * selector de cantidad ya pide 130px de eso. Cuando el selector de
 * cantidad es REAL (`.quantity` sin la clase `.hidden` que WooCommerce
 * le agrega en el HTML real cuando min=max=1, comprobado en vivo) "Ver
 * carrito" pasa a su PROPIA fila completa, debajo — le da toda la fila
 * 2 (315px reales) a cantidad+botón, que ahí sí entran cómodos. Cuando
 * el selector está oculto (caso ya aprobado y probado toda la sesión:
 * stock gestionado en 1 unidad), esta regla no aplica — sigue el mismo
 * layout de 2 columnas de siempre, sin cambios.
 */
.product-buy-card__actions:has(.quantity:not(.hidden)) {
	grid-template-columns: 1fr;
	grid-template-rows: auto auto auto;
}

.product-buy-card__actions:has(.quantity:not(.hidden)) .ct-product-add-to-cart form.cart {
	grid-column: 1;
	grid-row: 2;
}

.product-buy-card__actions:has(.quantity:not(.hidden)) .product-buy-card__view-cart {
	grid-column: 1;
	grid-row: 3;
	width: 100%;
}

.product-buy-card__view-cart {
	box-sizing: border-box;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 0;
	height: 44px;
	min-height: 44px;
	margin: 0;
	line-height: 1;
	padding: 0 var(--space-3);
	border-radius: var(--radius-md);
	border: 2px solid var(--color-primary);
	background: #fff;
	color: var(--color-primary);
	font-weight: 700;
	font-size: var(--text-sm);
	text-decoration: none;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	transition: background var(--transition-base), color var(--transition-base);
}

.product-buy-card__view-cart:hover {
	background: var(--color-primary);
	color: #fff;
}

.product-buy-card__view-cart:focus-visible {
	outline: 2px solid var(--color-primary);
	outline-offset: 2px;
}

/*
 * Estado "sin precio" — hallazgo real de la auditoría técnica final
 * (ver comentario en single-product-hooks.php, hook `price:before`
 * prioridad 11). Mismos tokens de color/tamaño ya usados en esta
 * misma tarjeta (`.product-buy-card .stock` para el tamaño del
 * título, `.product-buy-card__footer-text` para el texto secundario)
 * — ningún color, tamaño ni fuente nuevos, tal como se pidió.
 */
.product-buy-card__unavailable-title {
	font-family: var(--font-body);
	font-size: var(--text-base);
	font-weight: 700;
	color: var(--color-text);
	margin: 0 0 var(--space-2);
}

.product-buy-card__unavailable-text {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	margin: 0;
}

.product-buy-card__footer {
	margin-top: var(--space-5);
	padding-top: var(--space-5);
	border-top: 1px solid var(--color-border);
	display: flex;
	flex-direction: column;
	gap: var(--space-3);
}

.product-buy-card__footer-item {
	display: flex;
	align-items: flex-start;
	gap: var(--space-2);
}

.product-buy-card__footer-item img {
	flex: 0 0 auto;
	margin-top: 2px;
}

.product-buy-card__footer-title {
	font-weight: 700;
	font-size: var(--text-sm);
	margin: 0;
}

.product-buy-card__footer-text {
	color: var(--color-text-muted);
	font-size: var(--text-xs);
	margin: 0;
}

/* ==========================================================================
   "Completa tu regalo" — cáscara propia (las tarjetas/carrusel salen
   de .cart-forgot-card / initForgotCarousel(), ya en cart.css/cart.js)
   ========================================================================== */

.product-complete-gift {
	/*
	 * BUG REAL encontrado 2026-10-03 (pedido explícito de la usuaria):
	 * esta sección vive dentro de `.entry-summary.entry-summary-items`
	 * (`display:contents`, ver comentario junto a esa regla), así que
	 * YA participaba del mismo grid de 3 columnas que galería/info/
	 * carrito — confirmado en vivo inyectando una copia de prueba: sin
	 * ningún `grid-column`, el auto-placement de Grid la ubicaba sola
	 * en la COLUMNA 1 (el mismo ancho de la galería, fila 2, pegada
	 * justo debajo de las fotos) — nunca dependía del carrito
	 * (columna 3) para aparecer. El problema real que reportó no era
	 * la posición en sí sino el ancho: al ser tan angosta (sólo
	 * columna 1), se sentía "tapada" visualmente por el carrito
	 * sticky (columna 3) que sigue fijo al lado todo ese tramo de
	 * scroll. `grid-column:1/3` la ensancha al ancho combinado de
	 * galería + info central (pedido explícito: "no sólo el ancho de
	 * la galería, el de la galería y las características"), sin tocar
	 * ningún archivo PHP — el mismo mecanismo `display:contents` que
	 * ya resolvía esto para el resto de la ficha.
	 */
	grid-column: 1 / 3;
	padding-block: var(--space-4);
}

/*
 * Las tarjetas de "Completa tu regalo" son `flex: 0 0 <ancho fijo>`
 * en cart.css (pensado para el carrusel de 5 tarjetas del carrito,
 * NO se toca ese archivo — ver docblock arriba) — con menos de 5
 * productos reales disponibles, esas tarjetas se quedaban en su
 * tamaño chico de todos modos, dejando un hueco en blanco a la
 * derecha del último producto real en vez de ocupar el espacio
 * disponible. Se agrega sólo `flex-grow: 1` (de 0 a 1) acá: si hay
 * suficientes tarjetas para llenar la fila, no cambia nada (no sobra
 * espacio para crecer); si hay menos, se reparten el espacio libre y
 * lo llenan. El ancho fijo sigue siendo el mínimo (flex-shrink: 0
 * nativo no se toca), así que con muchas tarjetas el carrusel sigue
 * desbordando para el scroll horizontal, igual que antes.
 */
.product-complete-gift .cart-forgot-card {
	flex-grow: 1;
}

/*
 * BUG REAL encontrado 2026-10-03 (y corrección de un bug propio en el
 * primer intento): pedido explícito de la usuaria tras ver el ancho ya
 * acotado a galería+info (830px reales) — la fórmula de cart.css
 * (`/5`, nunca se toca ese archivo) está pensada para el Carrito a
 * ancho completo, y acá sólo entraban 5 tarjetas completas, dejando la
 * 6ª escondida detrás de la flecha. Se recalcula para 6 tarjetas por
 * fila SÓLO en escritorio real (≥950px, el mismo umbral que usa todo
 * este archivo para el grid de 3 columnas — ver `@media(min-width:
 * 950px)` repetido arriba): sin este freno, la primera versión de
 * esta regla aplicaba también en móvil, aplastando las 6 tarjetas a
 * un ancho ilegible ("G/A." en vez de "GATO", precio cortado) — mismo
 * error de "una sola regla para todos los anchos" que se viene
 * corrigiendo toda la sesión. Por debajo de 950px, el carrusel vuelve
 * a su comportamiento normal (cart.css, tamaño fijo + scroll).
 */
@media (min-width: 950px) {
	.product-complete-gift .cart-forgot-card {
		flex-basis: calc((100% - 5 * var(--space-4)) / 6);
	}
}

/*
 * Reportado en vivo con captura real: al agregar un ítem 'native'
 * (ej. "Tarjeta Amorea con Dedicatoria") desde acá, WooCommerce agrega
 * el enlace real "Ver carrito" (`.added_to_cart`, ver
 * woocommerce_template_loop_add_to_cart()) PERO deja el botón
 * original "Agregar" en el DOM (con la clase `.added` agregada) —
 * comprobado leyendo el HTML real tras el click: ninguna regla propia
 * lo ocultaba, así que quedaban los DOS visibles, uno debajo del otro,
 * agrandando la tarjeta entera y desalineándola de sus hermanas. Se
 * oculta acá el original una vez que WooCommerce le agrega `.added`
 * (mismo criterio nativo de WooCommerce/Storefront, sólo que Blocksy
 * no lo trae para esta estructura de tarjeta armada a mano).
 */
.product-complete-gift .cart-forgot-card__actions .add_to_cart_button.added {
	display: none;
}

/*
 * BUG REAL encontrado 2026-10-03: esta regla ya no hace falta. Era un
 * parche de una ronda anterior — con "Completa tu regalo" como
 * sección de ANCHO COMPLETO (fuera del grid, ver comentario de
 * `.product-complete-gift` arriba), este `max-width:73%` era la forma
 * de visualmente aproximar el ancho de galería+info sin tocar la
 * estructura. Hoy esa sección pasó a ser un ítem REAL del grid
 * (`grid-column:1/3`) — ya mide exactamente galería+info por sí sola,
 * sin necesitar ningún % aparte. Dejar este `max-width` encima
 * angostaba el panel AL 73% DE SU PROPIO CONTENEDOR YA ANGOSTO (830px
 * → 562px reales, medido en vivo), apretando las tarjetas hasta
 * cortar el precio ("S/ 250.0" en vez de "S/ 250.00"). Los 2 resets a
 * `max-width:none` en pantallas angostas (más abajo en este archivo)
 * quedan intactos — siguen siendo correctos, sólo redundantes ahora.
 */
.product-complete-gift__panel {
	/* Fondo rosado MUCHO más claro que el original (#fdeef4) — a pedido
	   del cliente, ese tono original ahora es el fondo propio de
	   `.product-complete-gift__dedication-notice` más abajo (ver ahí).
	   Radio/padding sin cambios (mismo criterio que .cart-forgot en
	   cart.css, ver docblock de complete-gift.php: nunca se toca ese
	   archivo, es la clave de un fragmento AJAX global). padding-top
	   reducido a pedido del cliente — sólo acá, nunca en cart.css. */
	background: #fef8fb;
	border-radius: var(--radius-lg);
	padding: var(--space-4) var(--space-6) var(--space-6);
}

/*
 * Título alineado arriba a la izquierda (no centrado) — a pedido del
 * cliente. `.cart-forgot__header`/`__title` vienen centrados de
 * cart.css (compartido con "¿Olvidaste algo?" en /cart/, que sí debe
 * seguir centrado) — se sobreescribe sólo acá, con el selector con
 * prefijo `.product-complete-gift`, nunca tocando cart.css.
 */
.product-complete-gift .cart-forgot__header {
	text-align: left;
}

.product-complete-gift .cart-forgot__title {
	justify-content: flex-start;
}

/*
 * `.cart-forgot__carousel` trae `margin: 0 var(--space-5)` en cart.css
 * — aire reservado a los dos lados para las flechas circulares
 * (position:absolute, se superponen al track, ver `.cart-forgot__arrow`)
 * cuando están visibles. La flecha "prev" arranca oculta (JS le pone
 * `[hidden]` mientras no haya nada hacia la izquierda), así que ese
 * margen izquierdo queda como aire suelto que desalinea la primera
 * tarjeta del título de arriba. Se quita sólo el margen IZQUIERDO acá
 * (nunca en cart.css — el carrito real sigue necesitando ese aire de
 * los dos lados) — el derecho queda igual, para la flecha "next".
 */
.product-complete-gift .cart-forgot__carousel {
	margin-left: 0;
}

/*
 * Ícono de regalo + un par de estrellitas amarillas alrededor, a la
 * izquierda del texto — puramente decorativo (aria-hidden en el
 * <span>, el texto real "Completa tu regalo" sigue siendo el único
 * contenido accesible del <h2>).
 */
.product-complete-gift__icon {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
}

.product-complete-gift__star {
	position: absolute;
	width: 10px;
	height: 10px;
	fill: #ffc736;
}

.product-complete-gift__star--top {
	top: -7px;
	right: -9px;
}

.product-complete-gift__star--bottom {
	width: 7px;
	height: 7px;
	bottom: -3px;
	left: -8px;
}

/*
 * Fondo propio (el mismo rosado #fdeef4 que antes tenía el panel de
 * arriba, ver .product-complete-gift__panel) + padding-left igual al
 * padding del panel (var(--space-6)) — a pedido del cliente, con dos
 * efectos: (1) look de "píldora" independiente para este aviso, según
 * la referencia visual que pasó el cliente; (2) el corazón queda
 * alineado con la primera tarjeta de "Completa tu regalo" de arriba,
 * que también empieza a var(--space-6) del borde izquierdo del panel
 * — antes este <p> no tenía padding propio y arrancaba pegado al
 * borde del contenedor, más a la izquierda que las tarjetas.
 */
.product-complete-gift__dedication-notice {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	background: #fdeef4;
	border-radius: var(--radius-pill);
	padding: var(--space-3) var(--space-6);
	color: var(--color-text-muted);
	font-size: var(--text-sm);
	margin-top: var(--space-4);
	margin-bottom: 0;
}

.product-complete-gift__dedication-notice svg {
	flex: 0 0 auto;
	color: var(--color-primary);
}

/* ==========================================================================
   Mini-carrito, justo debajo de la tarjeta de compra, DENTRO del mismo
   wrapper sticky (.product-buy-card-sticky-wrap, ver arriba) — así
   suben/bajan pegados como una sola unidad, nunca uno sobre el otro.
   Reutiliza el widget nativo de WooCommerce (woocommerce_mini_cart(),
   ver template-parts/single-product/mini-cart-panel.php); acá sólo el
   estilo visual, mismo lenguaje que .product-buy-card. Oculto por
   completo con carrito vacío (atributo `hidden`, ver PHP + JS
   setupMiniCartPanel() en single-product.js).
   ========================================================================== */

.product-mini-cart-panel {
	position: relative;
	width: 85%;
	margin-inline: auto;
	/*
	 * var(--space-4) (16px) — MISMO espaciado único entre TODAS las
	 * secciones apiladas de la ficha (ver .product-complete-gift, mismo
	 * criterio: 16px entre panel y aviso de dedicatoria). Reportado en
	 * vivo con captura real: acá quedaba en var(--space-16) (64px), 4
	 * veces más grande que cualquier otro espacio entre secciones —
	 * "sección precio" y "minicarrito" eran las únicas dos que no
	 * respetaban la regla. El motivo original de ese 64px (reservar
	 * lugar para el aviso verde "Se agregó correctamente", que flota
	 * con `position:absolute` JUSTO arriba de este panel — ver
	 * `.product-mini-cart-panel__notice` más abajo — sin ese aire
	 * terminaba superpuesto con "Pago 100% seguro" de la tarjeta de
	 * compra) se resuelve ahora del lado del propio aviso (box-shadow +
	 * fondo opaco, mismo criterio visual que un toast flotante real que
	 * SÍ puede superponerse brevemente a lo que tiene detrás mientras
	 * está visible) en vez de agrandar el espacio fijo entre secciones
	 * todo el tiempo, esté o no ese aviso visible en ese momento.
	 */
	margin-top: var(--space-4);
	background: var(--color-surface);
	border: 1px solid var(--color-accent);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
	padding: var(--space-5);
}

.product-mini-cart-panel__title {
	display: flex;
	align-items: baseline;
	gap: var(--space-2);
	font-family: var(--font-body);
	font-size: var(--text-base);
	font-weight: 800;
	margin: 0 0 var(--space-4);
	color: var(--color-text);
}

.product-mini-cart-panel__count {
	font-weight: 700;
	color: var(--color-text-muted);
}

/*
 * Sólo 2 productos visibles, el resto con scroll — misma técnica ya
 * usada en el resumen de la página de Confirmación
 * (.confirmation-products--scroll, assets/css/checkout.css). Primer
 * intento: sólo scrollbar fina nativa (scrollbar-width/color +
 * ::-webkit-scrollbar) sin la barra dibujada a mano — resultó
 * insuficiente: Safari/macOS oculta su scrollbar nativo hasta que se
 * está scrolleando activamente, así que no había NINGUNA señal visible
 * de que hubiera más productos (reportado por el cliente). Se agrega
 * la misma barra a mano que ya existe en Confirmación
 * (.amorea-mini-cart-scrollbar más abajo — ver
 * setupMiniCartScrollbar() en single-product.js).
 * max-height medido en vivo (2 filas reales, imagen 80px + nombre +
 * cm + precio + fila de cantidad, más el gap entre ambas ≈ 315px) —
 * un poco más chico (216px) cortaba la 2ª fila a la mitad en vez de
 * dejarla completa y recién scrollear desde la 3ª.
 */
.amorea-mini-cart-scroll-wrap {
	position: relative;
}

.product-mini-cart-panel .woocommerce-mini-cart {
	list-style: none;
	margin: 0 0 var(--space-4);
	/* var(--space-2) (8px) dejaba el tacho de basura casi pegado a la
	   barra de scroll — se sube a var(--space-5) (20px) para que
	   quede clara la separación. */
	padding: 0 var(--space-5) 0 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	max-height: 320px;
	overflow-y: scroll;
	scrollbar-width: thin;
	scrollbar-color: var(--color-primary) var(--color-canvas, #f7f2f2);
}

.product-mini-cart-panel .woocommerce-mini-cart::-webkit-scrollbar {
	width: 6px;
}

.product-mini-cart-panel .woocommerce-mini-cart::-webkit-scrollbar-track {
	background: var(--color-canvas, #f7f2f2);
	border-radius: var(--radius-pill);
}

.product-mini-cart-panel .woocommerce-mini-cart::-webkit-scrollbar-thumb {
	background: var(--color-primary);
	border-radius: var(--radius-pill);
}

/*
 * Barra dibujada a mano — copia exacta del patrón de
 * .confirmation-products-scrollbar (assets/css/checkout.css), sólo
 * con el prefijo de clase propio. Visible/oculta según haya o no
 * overflow real (atributo `hidden`, ver setupMiniCartScrollbar() en
 * single-product.js) — nunca un adorno fijo sin función real detrás.
 */
.amorea-mini-cart-scrollbar {
	position: absolute;
	top: 0;
	right: 0;
	bottom: 0;
	width: 8px;
	background: var(--color-canvas, #f7f2f2);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-pill);
	overflow: hidden;
	cursor: pointer;
	user-select: none;
	-webkit-user-select: none;
}

.amorea-mini-cart-scrollbar[hidden] {
	display: none;
}

.amorea-mini-cart-scrollbar__thumb {
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	background: var(--color-primary);
	border-radius: var(--radius-pill);
	cursor: grab;
}

.amorea-mini-cart-scrollbar__thumb:active {
	cursor: grabbing;
}

/*
 * Fila: imagen a la izquierda, columna de datos a la derecha (nombre,
 * cm, precio, cantidad+quitar) — reemplaza el <a> nativo que envolvía
 * imagen+nombre juntos (ver amorea_single_product_render_mini_cart_items(),
 * single-product-hooks.php: ahora son dos bloques propios,
 * `__media`/`__body`, ya separados en el PHP en vez de forzarlo con
 * CSS sobre el markup nativo).
 */
.woocommerce-mini-cart-item.amorea-mini-cart-item {
	display: flex;
	/* Sin esto, el default de flex (stretch) alarga `__media` para que
	   iguale el alto de `__body` cuando el nombre ocupa 2 líneas — la
	   imagen quedaba centrada verticalmente en esa caja más alta, más
	   abajo que el nombre. Con flex-start ambas columnas arrancan
	   arriba, alineadas entre sí (comprobado en vivo: "Tarjeta Amorea
	   con Dedicatoria", nombre a 2 líneas, la imagen quedaba 18px más
	   abajo que el título). Selector compuesto (dos clases) a propósito:
	   Blocksy trae `.product_list_widget li { align-items: center }` en
	   woocommerce.min.css (0,1,1 de especificidad) — más fuerte que
	   `.amorea-mini-cart-item` sola (0,1,0), así que ganaba pese a
	   cargar antes. Comprobado en vivo enumerando document.styleSheets:
	   esa era la única regla en conflicto. */
	align-items: flex-start;
	gap: var(--space-3);
	padding-bottom: var(--space-3);
	border-bottom: 1px solid var(--color-border);
}

.amorea-mini-cart-item:last-child {
	border-bottom: none;
	padding-bottom: 0;
}

/*
 * .amorea-mini-cart-item--just-added: la agrega PHP
 * (amorea_single_product_render_mini_cart_items(), single-product-hooks.php)
 * sólo en la fila que corresponde al último woocommerce_add_to_cart
 * real de este request (nunca en cambios de cantidad desde los +/-
 * del propio mini-carrito) — a propósito SIN estilo visual acá: es
 * sólo la marca que single-product.js lee para decidir cuándo mostrar
 * el aviso verde de "se agregó" (ver product-mini-cart-panel__notice
 * más abajo). Antes tenía un flash rosado en la fila; el cliente pidió
 * quitarlo y usar un aviso en su lugar.
 */

/*
 * Reportado en vivo con captura real: `position:absolute` (versión
 * anterior de este bloque) hacía que el aviso flotara ENCIMA de la
 * tarjeta de precio de arriba — se veía superpuesto/mal, aunque la
 * intención original fuera evitar que "Completa tu regalo" se moviera
 * mientras el aviso aparece/desaparece. El cliente pidió explícitamente
 * volver al comportamiento EN FLUJO: el aviso vive DENTRO del panel
 * "Tu carrito" (ya es su primer hijo real en el HTML, ver
 * mini-cart-panel.php — acá sólo cambia cómo se dibuja, nunca el
 * marcado), arriba del todo, colapsado a 0 de alto cuando no hay nada
 * que avisar (`max-height:0` + `overflow:hidden`, así "regresa a su
 * normalidad" sin dejar hueco) y expandiéndose a su alto real cuando
 * `single-product.js` le agrega `.is-visible` (ver
 * setupMiniCartJustAddedNotice()) — al expandirse empuja hacia abajo
 * el título "Tu carrito"/la lista de productos, y por lo tanto también
 * "Completa tu regalo" mientras dura (2.6s, después vuelve solo).
 */
.product-mini-cart-panel__notice {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	max-height: 0;
	margin-bottom: 0;
	padding: 0 var(--space-4);
	overflow: hidden;
	border-radius: var(--radius-sm);
	background: #eaf6f0;
	color: var(--color-success);
	font-size: var(--text-sm);
	font-weight: 700;
	opacity: 0;
	transition: max-height 0.3s ease, opacity 0.25s ease, padding 0.3s ease, margin-bottom 0.3s ease;
}

.product-mini-cart-panel__notice.is-visible {
	max-height: 60px;
	margin-bottom: var(--space-3);
	padding: var(--space-2) var(--space-4);
	opacity: 1;
}

.product-mini-cart-panel__notice svg {
	flex: 0 0 auto;
	width: 18px;
	height: 18px;
}

.amorea-mini-cart-item__media {
	flex: 0 0 auto;
}

.amorea-mini-cart-item__media img {
	width: 80px;
	height: 80px;
	object-fit: cover;
	border-radius: var(--radius-sm);
	display: block;
}

/*
 * Reportado en vivo por el cliente en pantallas chicas: `min-width: 0`
 * puro deja a esta columna encogerse SIN piso — en un panel ya
 * angosto (`.product-mini-cart-panel` es 85% de la tarjeta de
 * compra), el nombre del producto terminaba con menos de 100px reales
 * de ancho. `overflow-wrap: break-word` (heredado, no es de este
 * archivo) hace su trabajo tal como está pensado —evita que una
 * palabra larga desborde la caja— pero con tan poco espacio corta
 * palabras comunes como "Gigante" o "Amoroso" a la mitad, ilegible.
 * `min-width: 110px` en vez de `0`: alcanza de sobra para cualquier
 * palabra suelta de un nombre de producto en español a este tamaño de
 * fuente (comprobado en vivo).
 *
 * En los celulares MÁS angostos (hasta 374px, ver media query más
 * abajo) 110px ya no entra sin achicar algo más — la fila completa
 * (imagen 80px + gap + estos 110px) pasaba el ancho real disponible
 * y el sobrante empujaba los controles de cantidad (+/-) encima de
 * la barra de scroll, reportado en vivo por el cliente. Se achica ahí
 * la imagen (80px → 64px) para liberar espacio real en vez de seguir
 * exprimiendo el texto.
 */
.amorea-mini-cart-item__body {
	flex: 1 1 auto;
	min-width: 110px;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

@media ( max-width: 374px ) {
	.amorea-mini-cart-item__media img {
		width: 64px;
		height: 64px;
	}

	.amorea-mini-cart-item__body {
		min-width: 100px;
	}
}

/*
 * Columna angosta — CONTAINER QUERY (2026-10-03, reemplaza un media
 * query `950px–1099px` fijo). Causa real original: con el grid de 3
 * columnas activo, el panel "Tu carrito" (85% de una columna que en
 * ese rango apenas mide ~218-254px) dejaba muy poco espacio real para
 * imagen+texto de cada fila, y el "+"/"−" terminaban encima de la
 * barra de scroll. El techo fijo de 1099px dejaba un hueco real sin
 * cubrir justo después (1100-1199px de viewport, columna ~254-262px,
 * TODAVÍA angosta) — reportado en vivo, confirmado con DevTools
 * Protocol: el botón de eliminar quedaba fuera del área visible
 * exactamente ahí, en cualquier producto, nombre largo o corto.
 *
 * SEGUNDO HUECO encontrado (mismo día, auditoría de barrido continuo
 * con un escáner de desborde genérico, no sólo el síntoma puntual ya
 * conocido): el primer umbral de 290px apagaba el tratamiento angosto
 * (imagen 64px, piso de `.amorea-mini-cart-item__body` en 95px)
 * demasiado pronto — entre 291px y 308px de columna (viewport
 * ~1250-1320px) la imagen ya volvía a 80px pero la fila (`<li>`) TODAVÍA
 * no tenía los ~202px reales que ese tamaño de imagen + el piso de
 * 110px del cuerpo (el de fuera de este bloque) necesitan juntos —
 * `.amorea-mini-cart-item__body` desbordaba su propia fila hasta 17px,
 * medido con precisión barriendo de 10 en 10px. 315px de umbral (con
 * margen de sobra sobre el punto exacto donde el hueco se cierra solo,
 * 310px) cubre ese segundo hueco completo. Lección: verificar sólo el
 * síntoma ya conocido (el botón "desaparece") no alcanza — un barrido
 * genérico de desborde (comparar cada hijo contra el ancho real de su
 * padre, en vez de un chequeo puntual) encuentra huecos vecinos que un
 * chequeo dirigido no busca.
 *
 * Mismo criterio que el bloque de celulares de arriba (achicar la
 * imagen, no seguir exprimiendo el texto) más dos ajustes propios de
 * este caso: el panel pasa de 85% a 100% de su columna (ya no hace
 * falta el aire visual de sobra cuando la columna en sí es chica) y
 * se reduce el padding propio del panel y de la lista — liberan el
 * espacio real que faltaba.
 */
@container amorea-buy-column (max-width: 315px) {
	.product-mini-cart-panel {
		width: 100%;
		padding: var(--space-3);
	}

	.product-mini-cart-panel .woocommerce-mini-cart {
		padding-right: var(--space-3);
	}

	.amorea-mini-cart-item__media img {
		width: 64px;
		height: 64px;
	}

	.amorea-mini-cart-item__body {
		min-width: 95px;
	}
}

.amorea-mini-cart-item__name {
	font-size: var(--text-sm);
	font-weight: 700;
	color: var(--color-text);
	text-decoration: none;
}

.amorea-mini-cart-item__measure {
	font-size: var(--text-xs);
	color: var(--color-text-muted);
	margin: 0;
}

.amorea-mini-cart-item__price {
	font-size: var(--text-sm);
	font-weight: 700;
	color: var(--color-primary);
}

/*
 * El rosa de arriba no se aplicaba en vivo: el precio real vive
 * anidado adentro de `.woocommerce-Price-amount.amount` (markup
 * nativo de wc_price()), que trae su propio color heredado con más
 * peso que `.amorea-mini-cart-item__price` sola. Se apunta directo a
 * ese nodo anidado para ganarle.
 */
.amorea-mini-cart-item__price .woocommerce-Price-amount {
	color: var(--color-primary);
}

.amorea-mini-cart-item__controls {
	margin-top: auto;
	padding-top: var(--space-2);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-2);
}

/*
 * TERCER hueco encontrado (mismo barrido de desborde genérico que el
 * de los bloques de arriba): en el borde MÁS angosto del rango
 * anterior — columna ~218-225px, viewport 950-980px, justo donde el
 * grid de 3 columnas recién se activa — incluso con imagen 64px y el
 * piso de 95px ya aplicados, el contenido real de cantidad+espacio+
 * eliminar (112px, constante: 70px del selector + 8px de gap + 34px
 * del botón) seguía sin entrar en el espacio real disponible (104-111px,
 * medido con precisión barriendo de 5 en 5px) — el botón de eliminar
 * quedaba recortado 1 a 8px. Capa EXTRA, más angosta todavía, sólo
 * para este remanente: encoge el espacio ENTRE cantidad y eliminar
 * (gap) en vez de la cantidad misma (reducir el selector de cantidad
 * arriesgaba repetir un bug real YA corregido antes en este archivo:
 * "+"/"−" tapando un número de 2 dígitos). DESPUÉS de la regla base
 * (no antes, como un primer intento) — misma especificidad (1 clase),
 * necesita venir después en la cascada para ganar el empate, mismo
 * motivo ya documentado varias veces en este archivo.
 */
@container amorea-buy-column (max-width: 230px) {
	.amorea-mini-cart-item__controls {
		gap: 0;
	}
}

.amorea-mini-cart-item__controls .quantity {
	display: flex;
	align-items: center;
	gap: 6px;
}

.amorea-mini-cart-item__controls .quantity .qty,
.amorea-mini-cart-item__controls .quantity .amorea-qty-increase,
.amorea-mini-cart-item__controls .quantity .amorea-qty-decrease {
	appearance: none;
	-webkit-appearance: none;
	-moz-appearance: textfield;
}

.amorea-mini-cart-item__controls .quantity .qty::-webkit-inner-spin-button,
.amorea-mini-cart-item__controls .quantity .qty::-webkit-outer-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

.amorea-mini-cart-item__controls .quantity .amorea-qty-decrease,
.amorea-mini-cart-item__controls .quantity .amorea-qty-increase {
	width: 22px;
	height: 22px;
	border: 1px solid var(--color-border);
	border-radius: 50%;
	background: var(--color-surface);
	color: var(--color-primary);
	font-size: var(--text-sm);
	line-height: 1;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
}

.amorea-mini-cart-item__controls .quantity .amorea-qty-decrease:hover,
.amorea-mini-cart-item__controls .quantity .amorea-qty-increase:hover {
	background: var(--color-primary);
	color: #fff;
}

.amorea-mini-cart-item__controls .quantity .qty {
	width: 28px;
	border: none;
	background: transparent;
	text-align: center;
	font-size: var(--text-sm);
	font-weight: 700;
	color: var(--color-text);
	padding: 0;
}

.amorea-mini-cart-item__controls .quantity [aria-busy='true'] {
	opacity: 0.5;
	pointer-events: none;
}

/*
 * Selector reforzado con `.product-mini-cart-panel` como prefijo (no
 * sólo `.amorea-mini-cart-item__controls .remove`) por el mismo motivo
 * que el `align-items` de arriba: Blocksy carga de forma diferida
 * `cart-header-element-lazy.min.css` (JS, al primer clic en "Agregar
 * al carrito" — activa su propio dropdown de carrito del header) con
 * una regla `.woocommerce-mini-cart .remove` que reutiliza el mismo
 * nombre de clase nativo que este botón necesita conservar (para que
 * el `.remove_from_cart_button` nativo de WooCommerce siga
 * funcionando, ver amorea_single_product_render_mini_cart_items()).
 * Esa regla de Blocksy empata en especificidad (0,2,0) y llega
 * DESPUÉS en la cascada (se inyecta recién al usar el carrito) — por
 * eso "Tu carrito" se veía bien al cargar la página y se rompía sólo
 * después de agregar algo (comprobado en vivo enumerando
 * document.styleSheets). Con `.product-mini-cart-panel` de prefijo
 * (0,3,0) se gana siempre, sin depender del orden de carga; el
 * `position: static` de abajo deshace puntualmente el
 * `position: absolute` de Blocksy, que sacaba el tacho fuera del
 * flujo del flex y lo movía fuera de su fila.
 */
.product-mini-cart-panel .amorea-mini-cart-item__controls .remove {
	position: static;
	flex: 0 0 auto;
	width: 34px;
	height: 34px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	color: var(--color-text-muted);
	text-decoration: none;
}

.product-mini-cart-panel .amorea-mini-cart-item__controls .remove svg {
	width: 18px;
	height: 18px;
}

.product-mini-cart-panel .amorea-mini-cart-item__controls .remove:hover {
	background: var(--color-error);
	color: #fff;
}

.product-mini-cart-panel .woocommerce-mini-cart__empty-message {
	color: var(--color-text-muted);
	font-size: var(--text-sm);
	margin: 0;
}

.product-mini-cart-panel .woocommerce-mini-cart__total {
	display: flex;
	justify-content: space-between;
	font-size: var(--text-sm);
	font-weight: 700;
	text-align: left;
	text-transform: none;
	padding-top: var(--space-4);
	border-top: 1px solid var(--color-border);
	margin: 0 0 var(--space-4);
}

.product-mini-cart-panel .woocommerce-mini-cart__total .woocommerce-Price-amount {
	color: var(--color-primary);
}

.product-mini-cart-panel .woocommerce-mini-cart__buttons {
	/*
	 * Blocksy también trae, en la misma hoja diferida
	 * (cart-header-element-lazy.min.css, ver el comentario del `.remove`
	 * más arriba), una regla para ESTE mismo selector nativo
	 * (`.woocommerce-mini-cart__buttons`) con
	 * `border-top: 1px dashed ...` + `padding-top: 20px` — de ahí las
	 * líneas entrecortadas encima de "Ver carrito" y el salto hacia
	 * abajo (20px extra) que arrastraba también a "Completa tu regalo"
	 * (su margen depende de la altura de esta columna, ver
	 * setupCompleteGiftOverlap() en single-product.js). Como nunca
	 * declarábamos border/padding-top acá, esas dos propiedades
	 * quedaban resueltas sólo por la regla de Blocksy pese a que el
	 * resto de este bloque ya ganaba por especificidad.
	 */
	border-top: none;
	padding-top: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	margin: 0;
}

.product-mini-cart-panel .woocommerce-mini-cart__buttons .button {
	display: block;
	text-align: center;
	width: 100%;
	padding: 10px var(--space-4);
	border-radius: var(--radius-sm);
	font-weight: 700;
	font-size: var(--text-sm);
	text-decoration: none;
}

.product-mini-cart-panel .woocommerce-mini-cart__buttons .button.wc-forward {
	background: var(--color-surface);
	border: 1px solid var(--color-primary);
	color: var(--color-primary);
}

.product-mini-cart-panel .woocommerce-mini-cart__buttons .button:hover {
	/*
	 * Mismo "lift" global de Blocksy que en .cart-forgot-card__actions
	 * (cart.css, ver ese comentario) — `.button:hover` de main.min.css
	 * mueve 3px hacia arriba cualquier `.button` del sitio. A pedido
	 * del cliente, este botón debe quedarse quieto.
	 */
	transform: none;
	opacity: 0.9;
}

/* ==========================================================================
   Relacionados (nativo) — mismo problema de azul de Blocksy que la
   tarjeta de compra, mismo criterio de corrección puntual.
   ========================================================================== */

.related.products .button {
	background-color: var(--color-primary);
	border-color: var(--color-primary);
	color: #fff;
}

.related.products .button:hover {
	background-color: var(--color-primary-hover);
	border-color: var(--color-primary-hover);
	color: #fff;
}

/* ==========================================================================
   Barra de beneficios (5 columnas) — reutiliza .value-card de cards.css
   ========================================================================== */

.product-single-benefits {
	padding-block: var(--space-2) var(--space-10);
}

.product-single-benefits__grid {
	display: grid;
	grid-template-columns: repeat(5, 1fr);
	background: rgba(203, 37, 90, 0.05);
	border-radius: var(--radius-xl);
	padding: var(--space-5) var(--space-6);
}

/* ==========================================================================
   Responsive
   ========================================================================== */

/*
 * Tablet (768–1199px): a pedido del cliente, "Completa tu regalo" y
 * "Tu carrito" deben quedar en el MISMO lugar sin importar el tamaño
 * de pantalla — comprobado en vivo con datos reales de su navegador
 * (innerWidth: 980, un ancho que antes caía en un layout de 2
 * columnas con la tarjeta ya no-sticky, apilada normal, por eso nunca
 * podía verse al costado). Se elimina ese layout aparte: el mismo
 * grid de 3 columnas + `.product-buy-card-sticky-wrap` sticky de
 * escritorio sigue activo en todo este rango también — sólo se
 * ajustan detalles cosméticos menores (grilla de beneficios, cuántas
 * tarjetas del carrusel entran).
 */
@media (max-width: 1199px) and (min-width: 768px) {
	.product-single-benefits__grid {
		grid-template-columns: repeat(3, 1fr);
		gap: var(--space-4);
	}

	.value-card:nth-child(3n + 1) {
		border-left: none;
	}

	/* "Completa tu regalo" ~3 tarjetas visibles en este rango (ver
	   cart.css: por defecto son 5, y recién a 900px pasa a 3 — acá se
	   adelanta ese punto de quiebre para todo el rango tablet).
	   flex-grow en 1 (no 0): mismo motivo que la regla base de arriba,
	   para que con menos tarjetas reales no quede un hueco en blanco. */
	.product-complete-gift .cart-forgot-card {
		flex: 1 0 calc((100% - 2 * var(--space-4)) / 3);
	}

	/*
	 * "Agregar al carrito"/"Ver carrito": en este rango la columna 3
	 * sigue siendo sólo el 27% del viewport (arriba, grid de 3
	 * columnas retenido a propósito) — reportado en vivo con captura
	 * real: con el padding/tamaño de escritorio, cada botón sólo tenía
	 * ~100-115px reales y el texto "Agregar al carrito" (más largo que
	 * "Ver carrito") desbordaba su propia columna, tapando visualmente
	 * el inicio de "Ver carrito" (se leía sólo "carrito", sin "Ver").
	 * `min-width:0` (arriba, base) ya evita que ese desborde vuelva a
	 * tapar al botón vecino en cualquier ancho — acá, además, se
	 * achica texto/padding igual que en el bloque <768px de más abajo
	 * para que el recorte por elipsis sea mínimo en vez de agresivo.
	 */
	.product-buy-card .single_add_to_cart_button,
	.product-buy-card .ct-product-add-to-cart .button {
		padding-inline: var(--space-2);
		font-size: var(--text-xs);
	}

	.product-buy-card__view-cart {
		padding: 0 var(--space-2);
		font-size: var(--text-xs);
	}
}

/*
 * Tablet angosto (768px–949px): reportado por el cliente, el badge
 * "Más vendido" y el corazón de favoritos (ambos anclados a las
 * esquinas de la imagen principal) se pisan en este rango. Causa
 * estructural, no un detalle de esos dos elementos:
 *
 * 1) `.product-entry-wrapper` reparte 38.43% del viewport a la
 *    columna de la galería (ver `grid-template-columns` arriba).
 * 2) Blocksy activa sus miniaturas verticales (`.thumbs-left`) desde
 *    `min-width: 768px` — su propio breakpoint "md", 100% ajeno a
 *    nuestro grid, no se puede evitar bajándole el ancho a nada
 *    nuestro — y les reserva 100px + 15px fijos DENTRO de esa
 *    columna.
 *
 * Resultado comprobado en vivo: a la imagen le quedan ~150-175px
 * reales de ancho entre 768px y ~885px (menos con badges de texto
 * largo) — insuficiente para badge (118px+) y corazón (32px) más
 * márgenes sin pisarse. A partir de ~900px ya hay aire de sobra
 * (badge y corazón separados 20px+ en adelante) — 950px se usa acá
 * como techo con margen de seguridad; 1024px y 1440px, ya aprobados,
 * quedan intactos (siguen en el grid de 3 columnas de siempre).
 *
 * Solución: en vez de encoger badge/corazón (perdiendo legibilidad o
 * tacto — explícitamente descartado) o subirles el z-index (no
 * resuelve que sus áreas clicables se crucen, sólo tapa el síntoma
 * visual), este rango pasa a un layout de una sola columna real —
 * el mismo ya aprobado para <768px, extendido hasta acá — con la
 * galería a ancho completo. Las miniaturas verticales de Blocksy se
 * neutralizan sólo en este rango (vuelven a fila horizontal con
 * wrap, mismo tamaño de 100px de siempre, sin tocar ningún archivo
 * de Blocksy) para que la imagen recupere todo el ancho disponible.
 *
 * "5 miniaturas visibles sin scroll" es un requisito explícito sólo
 * de escritorio (1024px/1440px) — acá, con miniaturas horizontales y
 * wrap natural, entran todas sin necesidad de limitarlas ni de
 * scroll.
 */
@media (min-width: 768px) and (max-width: 949px) {
	/*
	 * `align-items: stretch` — reportado en vivo con captura real
	 * (`.product-buy-card`/`.product-central-info` mucho más angostos y
	 * pegados a la izquierda que la galería, con un hueco enorme a la
	 * derecha). Causa real, confirmada con `getComputedStyle()`: la
	 * regla base incondicional de `.product-entry-wrapper` (más arriba,
	 * la del grid de escritorio) fija `align-items: start` — en un grid
	 * de 3 columnas eso alinea verticalmente cada columna arriba, pero
	 * ese mismo `align-items` sigue existiendo acá cuando el wrapper
	 * pasa a `flex-direction: column`, y en un flex de columna
	 * `align-items` controla el ANCHO (eje transversal es horizontal):
	 * `start` encoge cada hijo a su contenido en vez de estirarlo al
	 * 100% del ancho disponible — sólo `.woocommerce-product-gallery`
	 * se salvaba porque trae su propio `width:100% !important`. Esta
	 * regla NUNCA se había reseteado en los bloques de abajo (sólo se
	 * tocaba `display`/`flex-direction`/`gap`), así que el bug persistía
	 * en TODOS los anchos de este rango y del rango <768px de más abajo.
	 */
	body.single-product .product-entry-wrapper {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: var(--space-5);
	}

	/*
	 * Reportado en vivo con captura real: entre la última fila de
	 * miniaturas y el título quedaba un salto de 68px (20px del `gap`
	 * de arriba + 48px de `margin-bottom` nativo de Blocksy en
	 * `.woocommerce-product-gallery`, comprobado con getComputedStyle
	 * — nunca declarado en este archivo) — mucho más que el espaciado
	 * de 16px ya usado como estándar entre el resto de las secciones
	 * (ver `.product-mini-cart-panel` más abajo). `margin-bottom: -4px`
	 * no es un valor arbitrario: cancela esos 48px nativos y le resta
	 * los 4px exactos al `gap` de 20px de arriba para que el total dé
	 * justo 16px, sin tocar ese `gap` (que sí sigue haciendo falta,
	 * sin cambios, entre información central y tarjeta de compra).
	 *
	 * `body.single-product` de prefijo a propósito — mismo motivo ya
	 * documentado varias veces en este archivo (miniaturas verticales,
	 * título angosto, etc.): la regla nativa de Blocksy vive en
	 * woocommerce.min.css, que carga DESPUÉS de este archivo, y con la
	 * MISMA especificidad (una sola clase) gana el empate por venir
	 * después, sin importar que esta regla esté dentro de una media
	 * query más específica en intención. Comprobado en vivo con
	 * getComputedStyle(): sin el prefijo, el margin-bottom seguía en
	 * 48px, esta regla nunca llegaba a aplicarse.
	 */
	body.single-product .woocommerce-product-gallery {
		margin-bottom: -4px;
	}

	/*
	 * Sticky pensado para "galería a la izquierda, tarjeta de compra
	 * flotando a la derecha mientras se hace scroll" — sin sentido en
	 * una sola columna apilada (mismo criterio que el bloque <768px
	 * de más abajo).
	 */
	.product-buy-card-sticky-wrap {
		position: static;
	}

	/*
	 * Mismo pedido/causa que en el bloque <767px de más abajo:
	 * `.product-complete-gift__panel`/`__dedication-notice` (73% fijo,
	 * ver la regla base) y `.product-mini-cart-panel` (85% fijo) quedan
	 * más angostos que el resto de las secciones apiladas en este rango
	 * también — faltaba repetir acá el mismo reseteo.
	 */
	.product-complete-gift__panel,
	.product-complete-gift__dedication-notice {
		max-width: none;
	}

	.product-mini-cart-panel {
		width: 100%;
	}

	/*
	 * Anula el posicionamiento vertical/absoluto que Blocksy aplica a
	 * `.thumbs-left` desde su propio breakpoint (768px) hacia arriba
	 * — sin esto, aunque el wrapper ya no sea grid de 3 columnas, las
	 * miniaturas seguirían flotando verticales sobre el borde
	 * izquierdo de la imagen. `initial` en las variables (no un
	 * valor fijo) para que cada una vuelva a resolver su propio
	 * fallback nativo (flexy.scss/default-gallery.scss), no un
	 * número inventado acá.
	 *
	 * Prefijo `body.single-product` a propósito (mismo mecanismo que
	 * ya usa este archivo para el botón del lightbox más arriba):
	 * `.thumbs-left .flexy-pills`/`.thumbs-left .flexy` son EXACTAMENTE
	 * la misma especificidad que las reglas nativas de Blocksy
	 * (default-gallery.scss, mismo selector) — y woocommerce.min.css
	 * de Blocksy carga DESPUÉS de single-product.css, así que en un
	 * empate gana la nativa pese a venir antes en este archivo
	 * (comprobado en vivo: sin el prefijo, las miniaturas seguían en
	 * columna absoluta). El prefijo suma especificidad real, no
	 * depende del orden de carga.
	 */
	body.single-product .thumbs-left .flexy-pills {
		position: relative;
		inset-inline-start: auto;
		inset-block: auto;
		height: auto;
		--pills-direction: row;
		--pills-wrap: wrap;
		--thumbs-left-spacing: initial;
		--thumbs-right-spacing: initial;
		--thumbs-container-margin: initial;
	}

	body.single-product .thumbs-left .flexy {
		margin-inline-start: 0;
	}

	/*
	 * El offset que compensa el ancho de las miniaturas verticales
	 * (ver `@media (min-width: 768px) { .thumbs-left
	 * .product-single-badge {...} }` más arriba) ya no aplica sin
	 * miniaturas a la izquierda — el badge vuelve a su posición
	 * simple de siempre.
	 */
	.thumbs-left .product-single-badge {
		left: 20px;
	}
}

/*
 * Móvil (<768px): todo apilado en una sola columna, en el orden real
 * del DOM (galería → información → tarjeta de compra — el breadcrumb
 * vive fuera de este wrapper, ya antes de la galería).
 */
@media (max-width: 767px) {
	/*
	 * `align-items: stretch` — mismo bug/causa real ya documentada en
	 * el bloque `@media (min-width:768px) and (max-width:949px)` de más
	 * arriba (la regla base del grid de escritorio deja `align-items:
	 * start` sin resetear, y en flex-column eso encoge cada columna a
	 * su contenido en vez de estirarla al ancho completo): a este ancho
	 * el bug es el mismo, sólo que este bloque nunca lo había
	 * reseteado tampoco.
	 */
	body.single-product .product-entry-wrapper {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: var(--space-5);
	}

	/*
	 * Mismo ajuste ya documentado arriba en el bloque
	 * `@media (min-width:768px) and (max-width:949px)`: cancela el
	 * `margin-bottom` nativo de Blocksy en la galería (48px) y deja el
	 * salto hacia el título en los mismos 16px que el resto de las
	 * secciones, restándole 4px al `gap` de 20px de arriba.
	 * `body.single-product` de prefijo, mismo motivo (Blocksy carga
	 * después con la misma especificidad).
	 */
	body.single-product .woocommerce-product-gallery {
		margin-bottom: -4px;
	}

	.product-reviews__body {
		grid-template-columns: 1fr;
		gap: var(--space-4);
	}

	.product-reviews__summary-col {
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		padding-right: 0;
		padding-bottom: var(--space-4);
		border-right: none;
		border-bottom: 1px solid var(--color-border);
	}

	.product-buy-card-sticky-wrap {
		position: static;
	}

	.product-buy-card {
		padding: var(--space-5);
	}

	/*
	 * Se mantienen en la MISMA fila también en móvil (pedido explícito
	 * — antes se apilaban acá) — sólo se ajusta tipografía/padding para
	 * que "Agregar al carrito" y "Ver carrito" quepan cómodos como dos
	 * columnas iguales sin desbordar ni envolver texto. Misma altura
	 * exacta que en escritorio (44px, fijada arriba) — nunca se
	 * modifica sólo en un breakpoint, para que ambos botones seres
	 * mantengan alineados en cualquier ancho.
	 */
	.product-buy-card__actions {
		gap: var(--space-2);
	}

	.product-buy-card .single_add_to_cart_button,
	.product-buy-card .ct-product-add-to-cart .button {
		padding-inline: var(--space-2);
		font-size: var(--text-xs);
	}

	.product-buy-card__view-cart {
		padding: 0 var(--space-2);
		font-size: var(--text-xs);
	}

	.product-central-info .product_title {
		font-size: clamp(1.375rem, 1.1rem + 3vw, 1.75rem);
	}

	.product-single-benefits__grid {
		grid-template-columns: repeat(2, 1fr);
	}

	.value-card:nth-child(2n + 1) {
		border-left: none;
	}

	/*
	 * Reportado en vivo con captura real: `.product-complete-gift__panel`/
	 * `__dedication-notice` traen `max-width: calc(73% + var(--space-6))`
	 * SIN media query (ver la regla base más arriba) — ese 73% replica el
	 * ancho combinado de las columnas 1+2 de escritorio (galería +
	 * información), a pedido explícito del cliente en su momento para
	 * que la sección NUNCA fuera ancho completo, ni en pantallas chicas.
	 * El cliente ahora pide lo contrario para el modo móvil apilado:
	 * centrado, sin ese hueco de ~90px en blanco a la derecha (medido en
	 * vivo a 390px de viewport: panel de 280px de ancho contra 351px que
	 * miden el título/características/precio a su lado). Se anula el
	 * `max-width` sólo en este rango para que el panel y el aviso rosado
	 * vuelvan a ocupar el mismo ancho que el resto de las secciones
	 * apiladas (mismo `.container` que ya las alinea a todas) — la regla
	 * de escritorio (>767px) no se toca.
	 */
	.product-complete-gift__panel,
	.product-complete-gift__dedication-notice {
		max-width: none;
	}

	/*
	 * Mismo pedido para el mini-carrito ("Tu carrito"): `width: 85%` (ver
	 * regla base más abajo) lo dejaba visiblemente más angosto que la
	 * tarjeta de compra justo arriba (297px contra 349px, medido en
	 * vivo) — acá pasa a 100%, igual de ancho que `.product-buy-card`,
	 * su hermano inmediato en el mismo wrapper sticky.
	 */
	.product-mini-cart-panel {
		width: 100%;
	}
}

@media (max-width: 480px) {
	.product-attr-pill {
		font-size: var(--text-xs);
		padding: 5px 10px;
	}

	/*
	 * `padding-inline` más chico a partir de 480px hacia abajo, en vez
	 * de tocar la altura (44px se mantiene igual que en escritorio,
	 * arriba, para que "Ver carrito" nunca se desalinee) — dos columnas
	 * angostas siguen necesitando algo menos de aire lateral para que
	 * "Agregar al carrito" no corte texto.
	 */
	.product-buy-card .single_add_to_cart_button,
	.product-buy-card .ct-product-add-to-cart .button {
		padding-inline: var(--space-1);
	}

	.product-buy-card__view-cart {
		padding-inline: var(--space-1);
	}

	.product-single-benefits__grid {
		grid-template-columns: 1fr;
	}

	.value-card {
		border-left: none !important;
		padding-inline: 0;
	}

	.product-complete-gift__panel {
		padding: var(--space-4);
	}

	/*
	 * El carrusel horizontal de "Completa tu regalo" (mismo mecanismo
	 * que .cart-forgot en el carrito) ya trae su propio scroll-snap +
	 * overflow-x en cart.css (~78% de ancho = 1 tarjeta + adelanto de la
	 * siguiente) — nada que agregar acá, sólo confirmar que no se le
	 * pone un ancho fijo que lo desborde.
	 */
	.product-complete-gift .cart-forgot__carousel {
		max-width: 100%;
	}
}
