/* ==========================================================================
   Codfy — Tienda de addons: listado (/shop) y ficha del producto.

   Reviste el ecommerce NATIVO de website_sale con el lenguaje visual del
   sitio (tokens --cf-* de style.css, tipografía Altone, azul #0053FF). No se
   reescribe ninguna plantilla del listado: se trabaja sobre el marcado que
   ya pinta Odoo, así que las actualizaciones del módulo no rompen nada y el
   carrito sigue siendo el nativo.

   TODO va acotado a `.o_wsale_products_page` (el listado) o a
   `#product_detail` (la ficha). Sin ese acotado, las mismas clases de
   Bootstrap tocarían el carrito y el checkout, que aquí NO se tocan.

   El tile de Odoo 19 se gobierna con variables `--o-wsale-card-*`
   (website_sale/static/src/scss/product_tile.scss); donde existe una, se
   prefiere pisar la variable antes que pelearse con el selector.
   ========================================================================== */

/* ==========================================================================
   1. LISTADO — contenedor y cabecera
   ========================================================================== */

.o_wsale_products_page {
    --cf-shop-radius: 16px;
    --cf-shop-thumb: #f7f8fa;
    font-family: var(--cf-font);
}

/* Los últimos rincones que se resistían a la tipografía del sitio.

   `body { font-family: Altone !important }` (style.css) llega por HERENCIA, y
   la herencia es lo más débil del cascade: en cuanto Odoo pone la fuente de su
   tema sobre el propio elemento —lo hace en las clases de encabezado `.h1`…
   `.h6` y en los importes—, gana ella por muy `!important` que sea la de body.
   Hay que reenganchar esos elementos uno a uno.

   El monoespaciado NO entra aquí: el nombre de la carpeta del addon
   (`.font-monospace`) y el editor simulado van así queriendo. */
:is(.o_wsale_products_page, #product_detail) :is(
    .h1, .h2, .h3, .h4, .h5, .h6,
    .oe_price, .oe_currency_value, .oe_default_price,
    .css_editable_mode_hidden
) {
    font-family: var(--cf-font);
}

/* El listado nativo se ciñe al ancho del resto del sitio. */
.o_wsale_products_page .o_wsale_products_main_row {
    max-width: var(--cf-max);
    margin-inline: auto;
    padding-inline: clamp(16px, 3vw, 24px);
}

.o_wsale_products_page #o_wsale_products_header {
    padding-top: clamp(24px, 4vw, 44px) !important;
    padding-bottom: 8px !important;
}
.o_wsale_products_page .o_wsale_shop_title {
    font-family: var(--cf-font);
    font-size: clamp(1.5rem, 2.6vw, 2rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--cf-text);
    margin-bottom: 0.15em;
}

/* ==========================================================================
   2. LISTADO — barra de categorías (filmstrip)

   Odoo la pinta como una tira horizontal con desplazamiento; con nueve
   categorías se cortaba por la derecha sin ninguna pista de que hubiera más.
   Se convierte en pastillas que fluyen a varias líneas: nada se esconde y
   deja de hacer falta el desplazamiento.
   ========================================================================== */

.o_wsale_products_page .o_wsale_filmstrip_container,
.o_wsale_products_page .o_wsale_filmstrip_wrapper {
    overflow: visible !important;
}
/* Clave para que envuelva: el wrapper es un elemento flexible cuyo
   `min-width: auto` le impedía encogerse por debajo de su contenido, así que
   medía lo que la tira entera (1096px dentro de un contenedor de 802) y el
   `flex-wrap` del <ul> no llegaba a activarse nunca. */
.o_wsale_products_page .o_wsale_filmstrip_wrapper {
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
}
.o_wsale_products_page .o_wsale_filmstrip {
    flex-wrap: wrap;
    gap: 8px;
    /* La tira nativa se dimensiona por su contenido (`min-width: max-content`)
       para poder desplazarse; con eso puesto, envolver es imposible. */
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
}
/* Las flechas de desplazamiento ya no pintan nada. */
.o_wsale_products_page .o_wsale_filmstrip_container > button,
.o_wsale_products_page .o_wsale_filmstrip_arrow {
    display: none !important;
}

.o_wsale_products_page .o_wsale_filmstrip > li {
    width: auto !important;
    min-width: 0 !important;
    flex: 0 0 auto !important;
}
.o_wsale_products_page .o_wsale_filmstrip_link {
    border-radius: 999px;
}
.o_wsale_products_page .o_wsale_filmstrip_item {
    display: inline-flex !important;
    align-items: center;
    width: auto !important;
    height: auto !important;
    padding: 8px 16px;
    border: 1px solid var(--cf-border);
    border-radius: 999px;
    background: var(--cf-bg);
    color: var(--cf-muted);
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.2;
    white-space: nowrap;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.o_wsale_products_page .o_wsale_filmstrip_link:hover .o_wsale_filmstrip_item {
    border-color: var(--cf-primary);
    color: var(--cf-primary);
}
/* El hueco de la imagen de categoría sobra: ninguna tiene portada y dejaba
   un cuadro gris dentro de cada pastilla. */
.o_wsale_products_page .o_wsale_filmstrip_image,
.o_wsale_products_page .o_wsale_filmstrip_placeholder {
    display: none !important;
}
/* Categoría activa: el radio nativo va oculto, así que se marca por :checked. */
.o_wsale_products_page .o_wsale_filmstrip .btn-check:checked + .o_wsale_filmstrip_item {
    background: var(--cf-primary);
    border-color: var(--cf-primary);
    color: var(--cf-on-primary);
}

/* ==========================================================================
   3. LISTADO — buscador
   ========================================================================== */

.o_wsale_products_page .o_wsale_products_searchbar_form .form-control,
.o_wsale_products_page .o_wsale_products_searchbar_form input[type="search"],
.o_wsale_products_page .o_wsale_products_searchbar_form input[name="search"] {
    height: 44px;
    border: 1px solid var(--cf-border);
    border-radius: 12px;
    padding-inline: 16px;
    font-size: 0.925rem;
    background: var(--cf-bg);
    box-shadow: none;
}
.o_wsale_products_page .o_wsale_products_searchbar_form .form-control:focus {
    border-color: var(--cf-primary);
    box-shadow: 0 0 0 3px rgba(0, 83, 255, 0.12);
}

/* ==========================================================================
   4. LISTADO — barra lateral de filtros (Versión de Odoo y Rango de precio)
   ========================================================================== */

/* El carril nativo mide `vh-100` con desplazamiento propio: con solo dos
   filtros dejaba una columna vacía de una pantalla de alto y una barra de
   desplazamiento permanente al lado del contenido. Que mida lo que ocupa. */
.o_wsale_products_page .o_wsale_products_grid_before_rail {
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
}

/* «Limpiar filtros»: solo aparece cuando hay algún filtro puesto. Odoo lo
   pinta como `btn-light`, un bloque gris que pesaba más que los propios
   filtros; se rebaja a un enlace discreto en rojo suave, que es lo que es. */
.o_wsale_products_page aside .btn-light[href="/shop"] {
    justify-content: space-between;
    padding: 9px 14px !important;
    border: 1px solid var(--cf-border);
    border-radius: 10px;
    background: transparent;
    color: var(--cf-muted);
    transition: border-color 0.15s ease, color 0.15s ease;
}
.o_wsale_products_page aside .btn-light[href="/shop"]:hover {
    border-color: #d92d20;
    background: rgba(217, 45, 32, 0.05);
    color: #d92d20;
}
.o_wsale_products_page aside .btn-light[href="/shop"] small {
    margin: 0 !important;
    font-size: 0.8rem;
}
.o_wsale_products_page aside .btn-light[href="/shop"] b { font-weight: 600; }

/* ----- Títulos de cada filtro ----- */
.o_wsale_products_page aside .accordion-item,
.o_wsale_products_page aside .accordion {
    border-color: var(--cf-border) !important;
    background: transparent;
}
.o_wsale_products_page aside .accordion-button {
    padding-block: 14px;
}
.o_wsale_products_page aside .accordion-button b {
    font-family: var(--cf-font);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--cf-text);
}
.o_wsale_products_page aside .accordion-button::after {
    width: 0.9rem;
    height: 0.9rem;
    background-size: 0.9rem;
    opacity: 0.45;
}
.o_wsale_products_page aside .accordion-button:not(.collapsed) {
    color: inherit;
    background: transparent;
    box-shadow: none;
}

/* ----- Versión de Odoo: pastillas en vez de casillas -----

   Son tres valores cortos ("17.0", "18.0", "19.0") apilados en una columna:
   ocupaban tres renglones para decir muy poco y la casilla gris de Bootstrap
   desentonaba con el resto. Se ponen en fila como pastillas seleccionables.

   La casilla NO se quita del DOM, solo se hace invisible: sigue siendo la que
   lleva el `name="attribute_value"` que envía el formulario `js_attributes`,
   y el <label for> hace que pulsar la pastilla la marque igual. Se conserva
   `:focus-visible` para que siga siendo navegable con el teclado. */
.o_wsale_products_page .js_attributes .flex-column {
    display: flex;
    flex-direction: row !important;
    flex-wrap: wrap;
    gap: 8px;
}
.o_wsale_products_page .js_attributes .form-check {
    display: block;
    min-height: 0;
    padding-left: 0;
    margin-bottom: 0 !important;
}
.o_wsale_products_page .js_attributes .form-check-input {
    position: absolute;
    width: 0;
    height: 0;
    opacity: 0;
    margin: 0;
    pointer-events: none;
}
.o_wsale_products_page .js_attributes .form-check-label {
    display: inline-block;
    margin: 0;
    padding: 7px 15px;
    border: 1px solid var(--cf-border);
    border-radius: 999px;
    background: var(--cf-bg);
    color: var(--cf-muted);
    font-size: 0.85rem;
    font-weight: 500;
    line-height: 1.2;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.o_wsale_products_page .js_attributes .form-check-label:hover {
    border-color: var(--cf-primary);
    color: var(--cf-primary);
}
.o_wsale_products_page .js_attributes .form-check-input:checked + .form-check-label {
    border-color: var(--cf-primary);
    background: var(--cf-primary);
    color: var(--cf-on-primary);
    font-weight: 600;
}
.o_wsale_products_page .js_attributes .form-check-input:focus-visible + .form-check-label {
    outline: 2px solid var(--cf-primary);
    outline-offset: 2px;
}

/* ----- Rango de precio -----

   El widget nativo son DOS `input[type=range]` superpuestos (`.original` y
   `.ghost`) dentro de `.multirange-wrapper`, que lleva en línea las variables
   `--low` y `--high` con el tramo elegido en tanto por ciento. Se aprovechan
   esas variables para pintar la pista de una vez con un degradado, en lugar
   de montar elementos extra: se hereda a los pseudoelementos y el navegador
   la repinta sola al arrastrar. */
.o_wsale_products_page .multirange-wrapper {
    margin-bottom: 2.75rem !important;
}
.o_wsale_products_page .multirange-wrapper .multirange-min,
.o_wsale_products_page .multirange-wrapper .multirange-max {
    opacity: 1 !important;
    padding: 3px 10px;
    border: 1px solid var(--cf-border);
    border-radius: 8px;
    background: var(--cf-bg);
    color: var(--cf-text);
    font-size: 0.75rem;
    font-weight: 600;
}

.o_wsale_products_page input.multirange {
    height: 22px;
    background: transparent;
}
/* La pista solo la pinta el `ghost`; si la pintaran los dos, la de arriba
   taparía a la de abajo y el tramo azul no se vería. */
.o_wsale_products_page input.multirange.ghost::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 999px;
    background: linear-gradient(
        to right,
        var(--cf-border) 0, var(--cf-border) var(--low),
        var(--cf-primary) var(--low), var(--cf-primary) var(--high),
        var(--cf-border) var(--high), var(--cf-border) 100%);
}
.o_wsale_products_page input.multirange.ghost::-moz-range-track {
    height: 6px;
    border-radius: 999px;
    background: linear-gradient(
        to right,
        var(--cf-border) 0, var(--cf-border) var(--low),
        var(--cf-primary) var(--low), var(--cf-primary) var(--high),
        var(--cf-border) var(--high), var(--cf-border) 100%);
}
.o_wsale_products_page input.multirange.original::-webkit-slider-runnable-track {
    height: 6px;
    background: transparent;
}
.o_wsale_products_page input.multirange.original::-moz-range-track {
    height: 6px;
    background: transparent;
}

.o_wsale_products_page input.multirange::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 18px;
    height: 18px;
    margin-top: -6px;          /* centra el tirador sobre la pista de 6px */
    border: 2px solid var(--cf-primary);
    border-radius: 50%;
    background: var(--cf-bg);
    box-shadow: 0 2px 6px rgba(14, 13, 50, 0.20);
    cursor: grab;
    transition: transform 0.12s ease;
}
.o_wsale_products_page input.multirange::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border: 2px solid var(--cf-primary);
    border-radius: 50%;
    background: var(--cf-bg);
    box-shadow: 0 2px 6px rgba(14, 13, 50, 0.20);
    cursor: grab;
}
.o_wsale_products_page input.multirange::-webkit-slider-thumb:hover { transform: scale(1.12); }
.o_wsale_products_page input.multirange:active::-webkit-slider-thumb { cursor: grabbing; }
.o_wsale_products_page input.multirange:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px rgba(0, 83, 255, 0.18);
}

/* ==========================================================================
   5. LISTADO — la tarjeta del addon

   Alturas iguales dentro de una fila y el precio siempre abajo del todo: sin
   esto, descripciones de distinto largo dejaban las tarjetas escalonadas.
   ========================================================================== */

:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product {
    height: 100%;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_cart {
    height: 100%;
    flex-direction: column;
    background: var(--cf-bg);
    border: 1px solid var(--cf-border);
    border-radius: var(--cf-shop-radius);
    overflow: hidden;
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_cart:hover {
    transform: translateY(-3px);
    border-color: rgba(0, 83, 255, 0.35);
    box-shadow: 0 14px 34px rgba(14, 13, 50, 0.10);
}

/* ----- Icono del addon ----- */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_image {
    background: var(--cf-shop-thumb);
    border-bottom: 1px solid var(--cf-border);
    aspect-ratio: 4 / 3;
    width: 100%;
}
/* `contain` y no `cover`: son iconos cuadrados de 128px, recortarlos les
   comía el logotipo. El padding evita que toquen el borde de la tarjeta. */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_image_img,
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_image img {
    object-fit: contain !important;
    padding: 18px;
}

/* ----- Textos ----- */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_information {
    display: flex;
    flex-direction: column;
    padding: 16px 18px 18px;
    gap: 2px;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_products_item_title {
    font-family: var(--cf-font);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -0.01em;
    margin-bottom: 2px;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_products_item_title a { color: var(--cf-text); }
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_product_cart:hover .o_wsale_products_item_title a {
    color: var(--cf-primary);
}

/* Ficha técnica (c_apps_sync): nombre de carpeta y descargas, en una línea. */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .codfy_ficha_tecnica {
    font-size: 0.75rem;
    margin-bottom: 4px;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .codfy_ficha_tecnica .font-monospace {
    color: var(--cf-muted);
}

/* Pastilla de categoría. */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .codfy_cat_chip {
    align-self: flex-start;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(0, 83, 255, 0.08);
    color: var(--cf-primary);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    margin-bottom: 8px;
}

/* Descripción: dos líneas y puntos suspensivos, para que la altura no baile. */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .oe_subdescription {
    font-size: 0.8125rem;
    line-height: 1.5;
    color: var(--cf-muted);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ----- Precio y acciones, pegados abajo ----- */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_sub {
    margin-top: auto;
    padding-top: 14px;
    align-items: center;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .product_price {
    font-family: var(--cf-font);
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--cf-text);
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_btn .btn,
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_sub .btn-primary {
    background: var(--cf-primary);
    border-color: var(--cf-primary);
    border-radius: 10px;
    font-weight: 600;
    font-size: 0.85rem;
    padding: 9px 14px;
}
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_btn .btn:hover,
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_product_sub .btn-primary:hover {
    background: var(--cf-primary-2);
    border-color: var(--cf-primary-2);
}

/* El botón aparece al pasar el ratón (opción `actions_onhover` de Odoo).
   En pantallas táctiles NO hay hover, así que ahí se deja siempre visible:
   si no, el botón de comprar sería inalcanzable en móvil. */
@media (hover: none) {
    .o_wsale_products_page .o_wsale_product_btn { opacity: 1 !important; }
}

/* ----- Cintas (¡Nuevo! / Gratis!!!) ----- */
:is(.o_wsale_products_page, .s_dynamic_snippet_products) .o_wsale_badge,
#product_detail .o_wsale_badge {
    top: 12px;
    left: 12px;
    padding: 5px 12px;
    border-radius: 999px;
    font-family: var(--cf-font);
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    box-shadow: 0 4px 12px rgba(14, 13, 50, 0.18);
}

/* ==========================================================================
   6. LISTADO — paginación
   ========================================================================== */

.o_wsale_products_page .products_pager .page-link {
    min-width: 40px;
    height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0 3px;
    border: 1px solid var(--cf-border);
    border-radius: 10px;
    color: var(--cf-muted);
    font-weight: 600;
    font-size: 0.9rem;
}
.o_wsale_products_page .products_pager .page-link:hover {
    border-color: var(--cf-primary);
    color: var(--cf-primary);
    background: transparent;
}
.o_wsale_products_page .products_pager .page-item.active .page-link {
    background: var(--cf-primary);
    border-color: var(--cf-primary);
    color: var(--cf-on-primary);
}

/* ==========================================================================
   7. FICHA DEL ADDON
   ========================================================================== */

#product_detail {
    font-family: var(--cf-font);
}
#product_detail .o_wsale_breadcrumb {
    font-size: 0.8125rem;
}
#product_detail .o_wsale_breadcrumb a { color: var(--cf-muted); text-decoration: none; }
#product_detail .o_wsale_breadcrumb a:hover { color: var(--cf-primary); }

#product_detail h1 {
    font-family: var(--cf-font);
    font-size: clamp(1.6rem, 3vw, 2.15rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--cf-text);
}

/* Imagen: mismo tratamiento que en la rejilla, contenida y sobre gris. */
#product_detail .o_wsale_product_images .oe_product_image_img,
#product_detail #o-carousel-product img,
#product_detail .o_wsale_product_page_image img {
    object-fit: contain !important;
    background: var(--cf-shop-thumb, #f7f8fa);
    border-radius: var(--cf-shop-radius, 16px);
    padding: clamp(20px, 4vw, 48px);
}

#product_detail .o_wsale_product_details_content_section_price .oe_price,
#product_detail .product_price .oe_price {
    font-size: clamp(1.5rem, 2.4vw, 1.9rem);
    font-weight: 700;
    color: var(--cf-text);
}

/* Selector de versión de Odoo: pastillas en vez de radios sueltos. */
#product_detail .js_attribute_value label,
#product_detail .o_wsale_product_attribute label {
    font-size: 0.9rem;
    color: var(--cf-muted);
}
#product_detail .form-check-input:checked {
    background-color: var(--cf-primary);
    border-color: var(--cf-primary);
}

#product_detail #add_to_cart,
#product_detail .o_wsale_product_details_content_section_cta .btn-primary {
    background: var(--cf-primary);
    border-color: var(--cf-primary);
    border-radius: 12px;
    padding: 13px 26px;
    font-weight: 600;
    font-size: 1rem;
    transition: background 0.15s ease, transform 0.15s ease;
}
#product_detail #add_to_cart:hover,
#product_detail .o_wsale_product_details_content_section_cta .btn-primary:hover {
    background: var(--cf-primary-2);
    border-color: var(--cf-primary-2);
    transform: translateY(-1px);
}

#product_detail .codfy_ficha_tecnica {
    font-size: 0.8125rem;
    margin-bottom: 10px;
}

/* Selector de cantidad: fuera. Nadie compra tres veces el mismo addon para la
   misma serie de Odoo, y el «− 1 +» al lado del botón solo mete ruido.
   Se OCULTA, no se borra de la plantilla: el `input[name=add_qty]` con valor 1
   tiene que seguir en el DOM porque es el que lee `js_add_cart_json` al
   añadir al carrito. */
#product_detail .css_quantity {
    display: none !important;
}
#product_detail .o_wsale_product_details_content_section_cta .btn-primary {
    width: 100%;
}

/* ----- Datos del addon (c_apps_sync) ----- */
#product_detail .codfy_datos {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 10px 20px;
    margin: 0;
    padding: 18px 20px;
    border: 1px solid var(--cf-border);
    border-radius: var(--cf-shop-radius, 16px);
    background: var(--cf-shop-thumb, #f7f8fa);
    font-size: 0.85rem;
}
#product_detail .codfy_datos dt {
    margin: 0;
    color: var(--cf-muted);
    font-weight: 500;
    white-space: nowrap;
}
#product_detail .codfy_datos dd {
    margin: 0;
    color: var(--cf-text);
    font-weight: 600;
    text-align: right;
    overflow-wrap: anywhere;
}

/* ----- Selector de versión en pastillas -----

   El atributo usa `display_type = 'pills'`, nativo de Odoo 19. La pastilla es
   el <li class="o_variant_pills btn">, no el <label>: el radio de dentro va
   con `opacity-0` y es el <li> el que recibe `.active`. Odoo la marca con las
   utilidades `bg-primary-subtle border-primary text-primary-emphasis`, que son
   selectores de una clase; basta con ganarles en especificidad. */
#product_detail .o_variant_pills {
    border: 1px solid var(--cf-border) !important;
    border-radius: 999px;
    padding: 8px 20px;
    background: var(--cf-bg);
    color: var(--cf-muted);
    font-size: 0.9rem;
    font-weight: 500;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
#product_detail .o_variant_pills:hover {
    border-color: var(--cf-primary) !important;
    color: var(--cf-primary);
}
/* Los `!important` no son capricho: Odoo marca la activa con las utilidades
   `bg-primary-subtle` y `text-primary-emphasis`, y las utilidades de Bootstrap
   5 se generan TODAS con !important, así que sin esto ganan ellas por mucha
   especificidad que se le eche. */
#product_detail .o_variant_pills.active,
#product_detail .o_variant_pills.active label,
#product_detail .o_variant_pills.active span {
    border-color: var(--cf-primary) !important;
    background: var(--cf-primary) !important;
    color: var(--cf-on-primary) !important;
    font-weight: 600;
}
#product_detail .o_variant_pills .o_variant_pills_input_value { margin: 0; }
#product_detail .attribute_name span {
    font-family: var(--cf-font);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--cf-text);
}

/* ==========================================================================
   8. CARRUSEL DE PRODUCTOS (snippet `s_dynamic_snippet_products`)

   Es el bloque que se arrastra al final de la ficha. Trae SU PROPIA plantilla
   de tarjeta (`website_sale.dynamic_filter_template_product_product_products_item`),
   no reutiliza la de /shop, pero usa las mismas clases por dentro: por eso las
   reglas de la tarjeta del apartado 5 se le aplican también, con `:is()`, en
   vez de repetirlas.

   OJO: la hoja solo se carga bajo /shop. Si algún día este mismo bloque se
   arrastra a la portada o a otra página, habrá que mover el apartado 5 a
   style.css o ampliar la condición de carga en `layout_assets`.
   ========================================================================== */

.s_dynamic_snippet_products {
    --cf-shop-radius: 16px;
    --cf-shop-thumb: #f7f8fa;
    /* El bloque viene con la opción `thumb_cover`, que RECORTA la imagen: los
       iconos de los addons salían cortados por arriba y por abajo. Aquí la
       imagen no es un <img> sino un <span> con `background-image`, así que no
       vale `object-fit`; el SCSS de Odoo usa esta misma variable para las dos
       cosas (`object-fit` y `background-size`), así que se corrige de una. */
    --o-wsale-card-thumb-fill-mode: contain;
}

/* La imagen necesita aire igual que en la rejilla, pero al ser fondo y no
   <img> el `padding` del apartado 5 no la encoge: se recorta con el propio
   `background-size`. */
.s_dynamic_snippet_products .oe_product_image_img {
    background-repeat: no-repeat;
    background-position: center;
    background-size: 72% !important;
}

.s_dynamic_snippet_products .s_dynamic_snippet_title h4 {
    font-family: var(--cf-font);
    font-size: clamp(1.35rem, 2.2vw, 1.75rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--cf-text);
}
.s_dynamic_snippet_products .s_dynamic_snippet_title .lead {
    font-size: 0.95rem;
    color: var(--cf-muted);
}

/* ----- Flechas -----

   OJO con cuál es el botón. El marcado es:

       <a class="carousel-control-prev">
           <span class="oi oi-chevron-left rounded-pill p-3 bg-700"/>
       </a>

   y el círculo que se ve es el <span> de dentro (48x48 por el `p-3`), NO el
   <a>, que mide 65x40. Dar el fondo y el borde al <a> dejaba un óvalo blanco
   asomando por detrás del círculo, porque el hijo es MÁS GRANDE que el padre.
   Así que el <a> se deja transparente y solo posiciona; todo lo visual va al
   <span>.

   Los `!important` son necesarios: `bg-700`, `p-3` y `rounded-pill` son
   utilidades y en Bootstrap 5 todas se generan con !important. */
.s_dynamic_snippet_products .carousel-control-prev,
.s_dynamic_snippet_products .carousel-control-next {
    border: 0;
    background: transparent;
    box-shadow: none;
    opacity: 1;
    /* El <a> mide más que su botón y, al quedarse transparente, era una zona
       invisible que se tragaba los clics sobre el borde de la tarjeta de al
       lado (28px de solape medidos). Acotarlo por `width` no sirve: la
       utilidad `w-auto` que Odoo le pone en la plantilla gana igualmente. Se
       resuelve por eventos: el <a> no recibe puntero y su botón sí. El clic
       en el botón burbujea hasta el <a>, que es donde Bootstrap tiene el
       `data-bs-slide`, así que el carrusel sigue pasando de página. */
    pointer-events: none;
}
.s_dynamic_snippet_products .carousel-control-prev > .oi,
.s_dynamic_snippet_products .carousel-control-next > .oi {
    pointer-events: auto;
    cursor: pointer;
}
/* El posicionado solo a partir de `md`, que es donde Odoo pone el control en
   absoluto (`position-md-absolute`); por debajo va en el flujo y `top` no
   pintaría nada.

   Se le fija el tamaño al del botón: por defecto el <a> mide 65x242 —tan alto
   como la tarjeta—, y al quedarse transparente se convertía en una zona
   invisible que se tragaba los clics sobre el borde de la primera y la última
   tarjeta. */
@media (min-width: 768px) {
    .s_dynamic_snippet_products .carousel-control-prev,
    .s_dynamic_snippet_products .carousel-control-next {
        /* `!important` por la utilidad `w-auto` que Odoo le pone en la
           plantilla, que también lo lleva. */
        width: 42px !important;
        height: 42px;
        /* `calc()` en vez de `top:50%` + `translateY(-50%)`: el transform no
           llegaba a aplicarse y el botón quedaba 21px por debajo del centro.
           Restando la mitad de la altura no depende de nada más. */
        top: calc(50% - 21px);
        bottom: auto;
    }
    .s_dynamic_snippet_products .carousel-control-prev { left: -21px; }
    .s_dynamic_snippet_products .carousel-control-next { right: -21px; }
}

.s_dynamic_snippet_products .carousel-control-prev > .oi,
.s_dynamic_snippet_products .carousel-control-next > .oi {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    margin: 0 !important;
    padding: 0 !important;
    border: 1px solid var(--cf-border);
    border-radius: 50% !important;
    background-color: var(--cf-bg) !important;
    color: var(--cf-text);
    font-size: 0.95rem;
    box-shadow: 0 6px 18px rgba(14, 13, 50, 0.12);
    transition: border-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.s_dynamic_snippet_products .carousel-control-prev:hover > .oi,
.s_dynamic_snippet_products .carousel-control-next:hover > .oi {
    border-color: var(--cf-primary);
    color: var(--cf-primary);
    box-shadow: 0 8px 22px rgba(0, 83, 255, 0.18);
}

/* ==========================================================================
   9. RESPONSIVO

   Regla del addon: todo bloque tiene que verse bien en móvil, tablet y
   escritorio antes de darlo por hecho.
   ========================================================================== */

@media (max-width: 991.98px) {
    .o_wsale_products_page .o_wsale_products_main_row { padding-inline: 16px; }
    .o_wsale_products_page .o_wsale_filmstrip { gap: 6px; }
    .o_wsale_products_page .o_wsale_filmstrip_item {
        padding: 7px 13px;
        font-size: 0.8125rem;
    }
}

@media (max-width: 575.98px) {
    /* Una sola columna en teléfono. Odoo deja `g-col-6` (dos por fila) en la
       rejilla de 12, y con nombre, carpeta, categoría, descripción y precio
       dentro, a 390px cada tarjeta se queda en ~170px: el título se partía en
       tres líneas y la categoría no cabía. */
    .o_wsale_products_page .o_wsale_products_grid_table > .oe_product {
        grid-column: span 12 !important;
    }
    .o_wsale_products_page .o_wsale_product_information { padding: 14px 15px 16px; }
    .o_wsale_products_page .o_wsale_products_item_title { font-size: 0.95rem; }
    .o_wsale_products_page .oe_product_image_img,
    .o_wsale_products_page .oe_product_image img { padding: 14px; }
    .o_wsale_products_page .products_pager .page-link {
        min-width: 36px;
        height: 36px;
        margin: 0 2px;
    }
}
