body {
    background-color: #f8f9fa;
}

/* Fix pour le panier sticky qui ne doit pas passer sous le header fixe */
.sticky-top {
    top: 70px !important; /* Hauteur du navbar + marge */
}

/* ===== Barre panier collée en bas (mobile uniquement) =====
   Sur desktop, .cart-card est dans une colonne latérale sticky toujours visible (voir
   .sticky-top ci-dessus) : pas besoin de cette barre, display:none par défaut. Sur mobile, cette
   colonne passe en pleine largeur et se retrouve tout en bas de la page, après tous les produits
   - donc invisible le reste du temps pendant qu'on fait son choix. Cette barre fixe comble ce
   manque : compteur + Total (HT) + Total TTC toujours visibles au fil du scroll, étiquetés
   explicitement (comme sur le reste du panier, jamais un montant nu) pour ne jamais laisser
   deviner lequel des deux est affiché - sans dupliquer le détail des articles (le tap renvoie
   vers la carte complète, voir le JS de chaque tunnel). */
.mobile-cart-bar {
    display: none;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1030;
    width: 100%;
    border: 0;
    border-top: 1px solid #9ec5fe;
    background-color: #0d6efd; /* bleu plein (pas le bleu "subtle" de la carte) : élément de
        navigation persistant, doit se détacher nettement du contenu de la page plutôt que s'y
        fondre - même traitement que les boutons .btn-primary déjà utilisés sur ce tunnel. */
    color: #fff;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    padding: 0.65rem 1.1rem;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.15);
}

.mobile-cart-bar-info {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    min-width: 0; /* autorise la troncature de .mobile-cart-bar-sub sans pousser le CTA hors du cadre */
}

.mobile-cart-bar-sub {
    font-size: 0.72rem;
    opacity: 0.85;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mobile-cart-bar-total {
    font-size: 1.05rem;
}

.mobile-cart-bar-cta {
    flex-shrink: 0;
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
}

@media (max-width: 767.98px) {
    /* .has-items est posé par updateCart() (JS du tunnel) dès que le panier contient un
       article, et retiré avec le padding-bottom du body quand il se vide - jamais de barre vide
       ni d'espace réservé inutile en bas de page. */
    .mobile-cart-bar.has-items {
        display: flex;
    }

    body.has-mobile-cart-bar {
        padding-bottom: 64px; /* hauteur approximative de la barre, pour qu'elle ne passe jamais
            par-dessus le bas de la carte panier ou du formulaire de commande */
    }

    /* .sticky-top (Bootstrap) n'est pas limité au desktop : sur mobile, la carte panier
       essayait quand même de s'épingler à top:70px une fois scrollée jusqu'à elle - en plus de
       n'avoir aucune utilité dans une mise en page 1 colonne (rien à "suivre" à côté), ça rendait
       scrollIntoView() peu fiable (un élément sticky déplace son propre point de mesure pendant
       qu'on scrolle vers lui, donc la cible bouge sous les pieds du calcul de défilement). */
    .cart-card.sticky-top {
        position: static !important;
    }
}

/* ===== Carte "Votre Panier" - redesign complet (en-tête / liste / récapitulatif) =====
   Un seul bloc cohérent, repensé dans son ensemble plutôt que ligne par ligne. Rien ici ne
   touche .sticky-top ni la colonne qui le contient : le positionnement/l'ancrage sticky restent
   intacts, seul l'habillage visuel change. */

/* Fond bleu Datamotech très clair (au lieu du blanc plat) sur le pourtour de la carte -
   en-tête, bordures, bloc des totaux - mais PAS sur les lignes du panier elles-mêmes, qui
   restent blanches (background-color propre à .list-group-item, non affectée par le fond de la
   carte) pour continuer à bien se détacher dessus. Les 3 teintes ci-dessous reprennent
   exactement le trio "subtle bg / subtle border / text emphasis" du bleu primaire de Bootstrap
   5.3 (déjà chargé via le CDN) : des couleurs conçues par Bootstrap spécifiquement pour rester
   lisibles ensemble, plutôt que des teintes choisies à l'œil. Le cyan vif de la navbar (#00ccff)
   est volontairement écarté ici : trop saturé pour un fond de texte, il ferait perdre du
   contraste aux écritures posées dessus. */
.cart-card {
    background-color: #cfe2ff; /* = --bs-primary-bg-subtle */
    border: 1px solid #9ec5fe; /* = --bs-primary-border-subtle */
}

.cart-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: 0.75rem;
    margin-bottom: 0.75rem;
    border-bottom: 1px solid #9ec5fe;
}

.cart-count-badge {
    min-width: 1.75rem;
    text-align: center;
}

/* Même teinte "text emphasis" que .cart-summary-grand-total (voir plus bas) plutôt que
   .text-primary : titre en grand texte (24px), donc le seuil AA seul (3:1) suffirait déjà avec
   le bleu standard, mais réutiliser la même teinte foncée partout où du texte bleu repose sur ce
   fond garde une palette cohérente plutôt que deux bleus différents sur la même carte. */
.cart-title-accent {
    color: #052c65;
}

/* Liste : UNE bordure englobante sur le conteneur + coins arrondis, plutôt que de compter sur
   les bordures individuelles par défaut de chaque .list-group-item. Le conteneur porte la SEULE
   bordure extérieure (haut/bas/gauche/droite), et chaque item ne porte plus qu'un border-bottom
   comme séparateur interne (absent sur le dernier) : plus aucun bord manquant possible. Bordure
   extérieure en bleu subtle (cohérente avec le fond de carte qui l'entoure), séparateurs
   internes en gris neutre (entre des lignes blanches, pas de fond bleu à raccorder à cet
   endroit). */
.cart-list {
    background-color: #fff; /* même couleur que les items (voir .list-group-item, fond blanc par
        défaut de Bootstrap) : même si un artefact d'anti-aliasing sub-pixel du navigateur laisse
        passer un liseré au clipping du coin arrondi malgré le rayon identique ci-dessous, ce
        liseré est alors blanc sur blanc (invisible) plutôt que bleu sur blanc (très visible). */
    border: 1px solid #9ec5fe !important;
    border-radius: 0.6rem !important; /* Bootstrap a une règle .card>.list-group:last-child qui
        force (entre autres) la largeur de la bordure du bas à 0 et les coins du bas à
        var(--bs-card-inner-border-radius) (5px), avec une spécificité supérieure à ce sélecteur
        seul, SI cette liste venait à devenir le dernier enfant direct de sa .card (ce n'est pas
        le cas ici, .cart-summary suit toujours la liste, mais voir checkout.css pour le détail
        du bug que ça a causé là où c'était bien le cas) - mieux vaut s'en prémunir ici aussi
        plutôt que de dépendre de cet ordre pour toujours rester vrai. */
    overflow: hidden; /* clippe proprement les coins malgré les séparateurs internes */
}

.cart-list .list-group-item {
    border: 0;
    border-radius: 0; /* Bootstrap arrondit par défaut les coins du 1er/dernier item (~0.375rem,
        plus petit que les 0.6rem du conteneur) : le coin de l'item "mordait" alors en-deçà du
        coin du conteneur. overflow:hidden sur .cart-list le masque en théorie, mais le
        chevauchement de deux rayons différents laisse parfois une fine amorce visible selon le
        rendu/zoom du navigateur - les coins du 1er/dernier item reprennent donc EXACTEMENT le
        même rayon que le conteneur ci-dessous, pour qu'il n'y ait plus deux arrondis différents
        à faire coïncider du tout. */
    border-bottom: 1px solid #e9ecef;
}

.cart-list .list-group-item:first-child {
    border-top-left-radius: 0.6rem;
    border-top-right-radius: 0.6rem;
}

.cart-list .list-group-item:last-child {
    border-bottom: 1px solid transparent; /* même épaisseur que les séparateurs des autres lignes
        (border-bottom: 1px solid #e9ecef ci-dessus), juste invisible : un border-bottom:0 pur
        retirait 1px à la hauteur de la boîte, rendant cette dernière ligne 1px plus courte que
        toutes les autres plutôt que simplement sans trait de séparation visible. */
    border-bottom-left-radius: 0.6rem;
    border-bottom-right-radius: 0.6rem;
}

/* Récapitulatif des totaux : lignes secondaires sobres, Total (HT) et Total TTC mis en avant
   (vert/bleu vifs) comme les deux chiffres-clés de la carte plutôt que des lignes visuellement
   équivalentes. Vert/bleu "vifs" standards (#198754 / #0d6efd) et non les variantes "emphasis"
   plus foncées : sur le fond bleu clair de la carte, ces couleurs vives tombent à ~3.45:1 de
   contraste - insuffisant pour du texte "normal" (seuil AA 4.5:1) mais conforme à l'exception
   WCAG "grand texte" (seuil 3:1, applicable à partir de 18.66px en gras). D'où la taille et le
   gras systématiquement forcés ensemble avec la couleur sur ces lignes : ôter l'un des deux
   ferait retomber sous le seuil requis. */
.cart-summary-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 0.88rem;
    color: #495057;
    padding: 0.3rem 0;
}

/* Même taille que .cart-summary-ht-total, sans graisse ni couleur : contrairement aux lignes de
   totaux, celles-ci gardent la couleur discrète héritée de .cart-summary-row (#495057, déjà
   ~6.2:1 de contraste sur ce fond - aucune contrainte de taille/graisse liée à l'accessibilité
   ici, juste un agrandissement visuel pur). */
.cart-summary-row-lg {
    font-size: 1.17rem;
}

/* Libellés (hors montants) des lignes secondaires (ex: TVA / Frais de port), en gras noir pour
   les distinguer du montant en face, qui lui reste en gris normal hérité de .cart-summary-row.
   Le sélecteur ne vise que le 1er span de la ligne (le libellé) : le 2e (le montant) n'est
   jamais touché. */
.cart-summary-row-lg > span:first-child {
    font-weight: 700;
    color: #000;
}

.cart-summary-ht-total {
    font-size: 1.17rem; /* 18.7px : tout juste au-dessus des 18.66px requis par l'exception WCAG
        "grand texte" - impossible de réduire davantage sans repasser sous ce seuil et perdre la
        conformité du vert/bleu vifs (voir note plus haut). */
    font-weight: 700;
    color: #198754; /* = --bs-success (vif), cf. note ci-dessus sur la taille/graisse requises */
}

.cart-summary-grand-total {
    margin-top: 0.35rem;
    padding-top: 0.75rem;
    border-top: 1px solid #9ec5fe;
    font-size: 1.17rem; /* 18.7px, même limite qu'au-dessus */
    font-weight: 700;
    color: #0d6efd; /* = --bs-primary (vif), cf. note ci-dessus sur la taille/graisse requises */
}

.cart-summary-grand-total strong {
    font-size: 1.25rem; /* valeur plus grande que le libellé, pour rester le chiffre le plus visible de la carte - plus de marge ici pour réduire que le libellé, pas contrainte par le même seuil plancher */
}

/* Ligne de panier : grille 3 colonnes fixes (pastille qté / infos / sous-total) plutôt qu'un
   simple flex. La pastille a VRAIMENT besoin de sa propre colonne de largeur fixe : avec un
   flex où elle suivait le nom dans le même flux de texte, un nom qui se coupait sur 2 lignes
   réservait une largeur d'item égale à sa ligne la PLUS LONGUE (pas celle, plus courte, où la
   pastille essayait de s'insérer), ce qui la renvoyait seule sur une 3e ligne - cassé et
   différent d'un article à l'autre selon la longueur du nom. En colonne de grille séparée et
   figée (30px), la pastille garde une position fixe quelle que soit cette longueur. La colonne
   infos (1fr, min-width:0) peut se replier sur autant de lignes que nécessaire sans jamais
   perturber les deux autres colonnes, et le sous-total (auto, jamais compressé) reste toujours
   ancré en haut à droite via align-items:start sur la grille. */
.cart-item {
    display: grid;
    grid-template-columns: 30px 1fr auto;
    column-gap: 0.65rem;
    align-items: start;
    padding-top: 0.65rem;
    padding-bottom: 0.65rem;
    transition: background-color 0.15s ease;
}

/* Une ligne en promo affiche l'ancien prix du sous-total au-dessus du nouveau (voir
   .cart-promo-old-subtotal, position:absolute bottom:100%) : sur la toute 1ère ligne de la
   liste, ce texte dépasserait du conteneur (.cart-list, overflow:hidden) faute de place réservée
   au-dessus, et se retrouverait rogné. Padding-top agrandi UNIQUEMENT quand la ligne contient
   réellement cet ancien prix (:has), donc les lignes sans promo gardent exactement le même
   padding qu'avant - zéro changement sur l'existant. Voir pc_components.css pour le détail. */
.cart-item:has(.cart-promo-old-subtotal) {
    padding-top: 1.1rem;
}

.cart-item:hover {
    background-color: #f8f9fc;
}

.cart-item-qty {
    display: flex;
    justify-content: center;
    padding-top: 0.15rem; /* aligne optiquement avec la 1ère ligne du nom */
}

/* Pastille recolorée en teinte primaire douce (au lieu du gris neutre .bg-secondary) pour
   s'accorder avec le reste de l'identité du tunnel plutôt que de trancher dessus. */
.cart-qty-badge {
    background-color: rgba(13, 110, 253, 0.12);
    color: #0d6efd;
    font-size: 0.72rem;
    font-weight: 700;
    padding: 0.32em 0.55em;
    min-width: 1.6em;
}

.cart-item-info {
    min-width: 0; /* autorise le retour à la ligne du nom sans repousser le sous-total */
}

.cart-item-name {
    font-size: 0.92rem;
    font-weight: 600;
    word-break: break-word;
    /* PAS de display:flex ici contrairement à pc_components.css : les noms y sont courts et
       tiennent sur 1 ligne, donc le badge en item flex se centre proprement sur le texte. Ici les
       noms sont souvent longs et se replient déjà sur 2 lignes à eux seuls - un badge en item
       flex séparé se retrouvait alors rejeté tout seul sur une 3ème ligne, sous le nom entier, au
       lieu de continuer à la suite du dernier mot. En flux inline normal (badge inline-block +
       vertical-align ci-dessous sur .cart-promo-badge), il reprend sa place naturelle à la suite
       du texte, sur la ligne où il y a de la place. */
}

.cart-item-subtotal {
    position: relative; /* ancre pour .cart-promo-old-subtotal ci-dessous, positionné en absolu -
        sans effet quand il n'y a pas de promo (pas d'enfant à ancrer). */
    align-self: center; /* centre le prix HT sur la hauteur réelle de la ligne, voir
        pc_components.css pour le détail (pousse aussi l'ancien prix barré vers le bas). */
    white-space: nowrap;
    font-weight: 700;
    padding-top: 0.1rem;
}

/* ===== Promo sur une ligne de panier (purement additif, voir electronics_shop.js) =====
   Mêmes règles que pc_components.css : voir ce fichier pour le détail de chaque choix. */
.cart-promo-gap {
    word-spacing: 0.1rem; /* élargit l'espace nom/badge sans l'empêcher de disparaître normalement
        si ce caractère espace tombe en bout de ligne (badge rejeté seul) - voir pc_components.css
        pour le détail de ce choix face à margin-left. */
}

.cart-promo-badge {
    font-size: 0.65rem;
    font-weight: 700;
    white-space: nowrap;
    /* Pas de margin-left pour l'espace avec le nom : un margin resterait même si le badge est
       rejeté seul au début de la ligne suivante (nom déjà plein sur sa propre ligne), décalant le
       badge vers la droite par rapport au nom au-dessus au lieu de rester flush à gauche. L'espace
       vient du .cart-promo-gap ci-dessus, qui lui disparaît tout seul dans ce cas, comme n'importe
       quel espace en bout de ligne. */
    vertical-align: middle; /* .cart-item-name reste en flux inline normal ici (voir plus haut) :
        le badge est un inline-block classique dans ce texte, pas un item flex. */
    position: relative;
    top: -2px; /* vertical-align:middle seul aligne le milieu du badge sur baseline + demi-x-height
        du texte parent, pas sur le centre réel de la ligne (boîte pleine) - toujours ~2-3px trop
        bas avec ce padding de badge. Décalage mesuré empiriquement (Playwright) pour faire
        coïncider le centre du badge avec celui de la ligne de texte où il tombe, qu'elle soit la
        1ère (nom court) ou la dernière d'un nom qui se replie sur 2 lignes. */
}

.cart-promo-old-price {
    margin-right: 0.3rem;
    opacity: 0.85;
}

.cart-promo-old-subtotal {
    position: absolute;
    bottom: 100%;
    right: 0;
    font-size: 0.65rem;
    font-weight: 400;
    white-space: nowrap;
    margin-bottom: 0.1rem;
}

/* Styles du header du website */
.navbar {
    background: linear-gradient(180deg, rgba(10, 14, 39, 0.98) 0%, rgba(26, 26, 62, 0.95) 100%) !important;
    backdrop-filter: blur(15px);
    border-bottom: 2px solid rgba(0, 204, 255, 0.2);
    box-shadow: 0 8px 32px rgba(0, 102, 255, 0.15);
}

.navbar-brand {
    color: #00ccff !important;
    font-weight: 800;
    font-size: 1.5rem;
    letter-spacing: 2px;
    text-shadow: 0 0 10px rgba(0, 204, 255, 0.5);
}

.navbar-nav .nav-link {
    color: rgba(255, 255, 255, 0.8) !important;
    transition: all 0.3s ease;
    position: relative;
}

.navbar-nav .nav-link::before {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 0;
    height: 2px;
    background: linear-gradient(90deg, #00ccff, transparent);
    transition: width 0.3s ease;
}

.navbar-nav .nav-link:hover {
    color: #00ccff !important;
    text-shadow: 0 0 8px rgba(0, 204, 255, 0.6);
}

.navbar-nav .nav-link:hover::before {
    width: 100%;
}

.sticky-top {
    top: 20px; /* Ajout d'une petite marge */
}

body {
    padding-top: 70px;
    background-color: #f8f9fa;
}

/* ===== BARRE DE FILTRES PAR CATÉGORIE ===== */
/* Marge basse conditionnelle : par défaut (pas de sous-filtre affiché), 1.5rem déclaré jusqu'à
   la grille de produits - exactement la même valeur que .shop-subfilter-bar ci-dessous (déjà en
   place avant ce correctif, rendu jugé "parfait"). #shop-product-grid (.row.g-3) porte un
   margin-top:-1rem propre au mécanisme de gouttières Bootstrap, qui fusionne avec cette marge et
   la réduit à 8px de rendu réel - c'est ce rendu de 8px (pas les 1.5rem déclarés) qui doit rester
   identique des deux côtés, donc la même valeur déclarée ici suffit, sans compensation.
   .has-subfilter (posée par JS avec .shop-subfilter-bar.is-visible, voir initCategoryFilter()
   dans electronics_shop.js) la réduit à 0.5rem pour l'écart vers les sous-filtres (élément
   normal, sans marge négative, donc sans ce piège de fusion) - pour que cet écart égale celui
   entre les lignes de filtres elles-mêmes (gap ci-dessous). */
.shop-filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.shop-filter-bar.has-subfilter {
    margin-bottom: 0.5rem;
}

.shop-filter-pill {
    border: 2px solid #dee2e6;
    background-color: #fff;
    color: #495057;
    border-radius: 999px;
    padding: 0.4rem 1rem;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.shop-filter-pill:hover {
    border-color: #0d6efd;
    color: #0d6efd;
}

.shop-filter-pill.active {
    background-color: #0d6efd;
    border-color: #0d6efd;
    color: #fff;
}

/* Sous-filtres (Impression 3D uniquement) : masqués par défaut, affichés par JS seulement quand
   le filtre principal "Impression 3D" est actif (voir initCategoryFilter() dans
   electronics_shop.js) - jamais visibles pour les autres catégories, qui n'ont pas de sous-type.
   Pastilles plus discrètes que les pilules principales (teinte neutre, pas de bleu plein à
   l'état actif) pour bien marquer qu'il s'agit d'un niveau de filtre secondaire. */
.shop-subfilter-bar {
    display: none;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 1.5rem; /* même valeur déclarée que .shop-filter-bar sans sous-filtre - voir
        la note au-dessus de .shop-filter-bar : les deux fusionnent avec le même margin-top
        négatif de #shop-product-grid et rendent donc le même écart réel (8px). */
}

.shop-subfilter-bar.is-visible {
    display: flex;
}

.shop-subfilter-pill {
    border: 1px solid #dee2e6;
    background-color: #f8f9fa;
    color: #495057;
    border-radius: 999px;
    padding: 0.3rem 0.85rem;
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
}

.shop-subfilter-pill:hover {
    border-color: #0d6efd;
    color: #0d6efd;
}

.shop-subfilter-pill.active {
    background-color: #e7f1ff;
    border-color: #0d6efd;
    color: #0d6efd;
}

/* ===== GRILLE BOUTIQUE (carte produit unifiée pour toutes les catégories) ===== */
.shop-grid-item {
    margin-bottom: 1.5rem;
}

.shop-product-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    border-radius: 1rem;
    overflow: hidden;
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
}

.shop-product-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.shop-product-card.selected {
    border-color: #0d6efd;
    background-color: rgba(13, 110, 253, 0.05);
    box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
}

.shop-product-card-header {
    position: absolute;
    top: 10px;
    left: 10px;
    z-index: 10;
}

/* Badge promo en overlay centré, dans la même bande réservée en haut de la carte que la case à
   cocher (voir margin-top sur .shop-product-image) - ne modifie jamais la hauteur du footer, donc
   aucun risque de désalignement entre cartes en promo et cartes sans promo dans une même rangée.
   max-width laisse une marge par rapport à la case à cocher (en haut à gauche) ; sur les cartes
   étroites (2 par rangée en mobile), un libellé long (ex: "DESTOCKAGE -27%") peut quand même
   dépasser ponctuellement du badge, donc le texte est autorisé à passer sur 2 lignes (white-space:
   normal ci-dessous) plutôt que de déborder par-dessus la case à cocher. */
.promo-badge-top {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9;
    width: max-content; /* largeur naturelle du texte tant qu'il tient sur une ligne (voir
        max-width ci-dessous) - sans ça, la largeur "shrink-to-fit" d'un bloc absolu imbriqué peut
        se calculer plus étroite que nécessaire et faire passer même un texte court sur 2 lignes */
    max-width: calc(100% - 64px); /* le badge est centré, donc la marge (10px + 18px de case à
        cocher + respiration) doit être réservée des deux côtés à la fois pour éviter tout contact
        avec la case à cocher */
    text-align: center;
}

.promo-badge-top .badge {
    white-space: normal;
    font-size: 0.75rem;
    line-height: 1.2;
}

.promo-badge {
    white-space: nowrap;
}

/* Enrobage serré autour du prix normal (valeur + devise + libellé) : comme l'ancien prix est
   positionné en absolu (donc retiré du flux), ce span se redimensionne exactement à la largeur du
   nouveau prix. C'est ce sur quoi l'ancien prix se centre (voir .promo-old-price ci-dessous), pour
   un centrage exact au-dessus du nouveau prix quel que soit le nombre de chiffres - et son propre
   white-space:nowrap évite qu'il se coupe en plein milieu sur les cartes étroites. */
.price-value-group {
    position: relative;
    display: inline-block;
    white-space: nowrap;
}

/* Ancien prix barré, réduit par rapport à la taille "small" de Bootstrap (0.875em) utilisée
   auparavant. Positionné en absolu au-dessus du prix normal (et non plus sur la même ligne) et
   centré (voir .price-value-group) : ne participe jamais à la hauteur de la ligne de prix, donc
   le sélecteur de quantité (au-dessus, dans la même colonne) reste toujours à la même position,
   qu'il y ait une promo ou non - même principe que pc_components.css. */
.promo-old-price {
    font-size: 0.75rem;
    line-height: 1;
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 0.3rem;
    white-space: nowrap;
}

.shop-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
}

.shop-product-image {
    height: 200px;
    width: 100%;
    object-fit: contain;
    background-color: #fff;
    border-bottom: 1px solid #f1f1f1;
    margin-top: 40px;
}

.shop-product-card .card-body {
    display: flex;
    flex-direction: column;
    flex: 1;
}

.shop-category-badge {
    align-self: flex-start;
    max-width: 100%;
    background-color: #e7f1ff;
    color: #0d6efd;
    font-weight: 600;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    margin-bottom: 0.5rem;
    white-space: normal;
    text-align: center;
}

.shop-product-card .card-title {
    margin-bottom: 0.5rem;
}

.shop-product-card .card-text {
    margin-bottom: 0.75rem;
    flex: 1 1 auto; /* Permet à la description de grandir et pousser le footer vers le bas */
}

.hidden {
    display: none;
}

/* Footer de la carte avec sélecteur de quantité et prix */
.shop-product-footer {
    display: flex;
    flex-direction: column;
    gap: 1.75rem; /* doit rester assez grand pour laisser la place au prix barré, positionné en
        absolu au-dessus du prix normal (voir .promo-old-price) : sinon il chevauche le sélecteur
        du dessus quand une promo est active - même principe que .motherboard-footer. */
    margin-top: auto;
    padding-top: 0.5rem;
    border-top: 1px dashed #eee;
}

/* Centré plutôt qu'aligné à gauche : reste toujours à la même position horizontale (au centre de
   la carte), qu'il y ait un prix barré ou non - voir .promo-old-price pour la même logique côté
   prix. */
.shop-quantity-selector {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}

/* Sélecteur de variante (ex: couleur de filament) : uniquement affiché si le produit a
   has_variants=true côté WebUIAPI (voir shop_card()) - les produits sans variante ne rendent rien
   du tout ici, donc aucune tuile existante n'est modifiée, et le sélecteur de quantité (voir
   .shop-quantity-selector ci-dessus) reste strictement identique à l'existant, sans rien y
   ajouter. Rendu sur sa PROPRE ligne, juste au-dessus du séparateur pointillé du footer (voir
   .shop-variant-row), quitte à agrandir légèrement la tuile pour les produits à variantes - accepté
   tant que les descriptions restent courtes sur ce type de produit.

   Ce n'est PAS un <select> natif : la liste déroulante d'un <select> est dessinée par le système
   d'exploitation et ne peut quasiment pas être stylée (aucune cohérence possible avec le design du
   site). C'est donc un menu personnalisé (bouton + <ul> positionnée en absolu, pilotés en JS - voir
   initVariantDropdowns() dans electronics_shop.js), avec une valeur courante conservée dans un
   input caché (.shop-variant-value, toujours de classe {{category}}-variant-select pour rester
   compatible avec la lecture du panier). Le bouton reprend le même style que les boutons -/+ (voir
   .shop-qty-btn) : bordure bleue, coins arrondis, flèche custom. Sa largeur est calculée une fois
   en JS à partir de la valeur la plus longue de la liste, puis fixée - elle ne bouge donc jamais
   selon la valeur choisie. */
.shop-variant-row {
    margin-bottom: 0.6rem;
    display: flex;
    justify-content: center;
}

.shop-variant-dropdown {
    position: relative;
    display: inline-block;
}

.shop-variant-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    height: 32px;
    min-width: 70px;
    border: 2px solid #0d6efd;
    border-radius: 4px;
    background-color: #fff;
    color: #0d6efd;
    font-size: 0.78rem;
    font-weight: 600;
    padding: 0 8px;
    cursor: pointer;
    transition: background-color 0.15s;
}

.shop-variant-toggle:hover {
    background-color: rgba(13, 110, 253, 0.06);
}

.shop-variant-toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
}

.shop-variant-toggle-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.shop-variant-chevron {
    flex-shrink: 0;
    transition: transform 0.15s;
}

.shop-variant-dropdown.is-open .shop-variant-chevron {
    transform: rotate(180deg);
}

/* Le menu est déplacé en JS vers la fin de <body> à l'initialisation (voir initVariantDropdowns
   dans electronics_shop.js), donc positionné en "fixed" (coordonnées calculées en JS à
   l'ouverture, relatives à la fenêtre) plutôt qu'en "absolute" dans la carte : le <div class="card">
   Bootstrap a overflow:hidden par défaut (pour les coins arrondis des images), ce qui rognait
   silencieusement le menu dès qu'il dépassait la carte - invisible/inaccessible au-delà d'une
   poignée de valeurs. En sortant le menu de la carte, il ne peut plus jamais être coupé, quel que
   soit le nombre de valeurs de la liste. */
.shop-variant-menu {
    list-style: none;
    margin: 0;
    padding: 4px;
    position: fixed;
    max-height: 220px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 8px;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.14);
    z-index: 1050;
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.12s ease;
}

.shop-variant-menu.is-open {
    visibility: visible;
    opacity: 1;
    pointer-events: auto;
}

.shop-variant-option {
    padding: 6px 10px;
    font-size: 0.8rem;
    color: #212529;
    border-radius: 5px;
    cursor: pointer;
    white-space: nowrap;
    text-align: center;
}

.shop-variant-option:hover {
    background-color: rgba(13, 110, 253, 0.08);
}

.shop-variant-option.is-selected {
    background-color: #0d6efd;
    color: #fff;
    font-weight: 600;
}

.shop-product-price {
    text-align: center;
}

.shop-qty-btn {
    width: 32px;
    height: 32px;
    border: 2px solid #0d6efd;
    background-color: #fff;
    color: #0d6efd;
    font-size: 1.25rem;
    font-weight: bold;
    border-radius: 4px;
    cursor: pointer;
    transition: all 0.2s;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    line-height: 1;
}

.shop-qty-btn:hover:not(:disabled) {
    background-color: #0d6efd;
    color: #fff;
    transform: scale(1.05);
}

.shop-qty-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    border-color: #dee2e6;
    color: #6c757d;
}

.shop-qty-input {
    width: 50px;
    height: 32px;
    text-align: center;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    font-weight: bold;
    font-size: 1rem;
    background-color: #f8f9fa;
    cursor: default;
}

/* Masquer les flèches du input number */
.shop-qty-input::-webkit-outer-spin-button,
.shop-qty-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.shop-qty-input[type=number] {
    -moz-appearance: textfield;
}

.shop-qty-input:focus {
    outline: none;
    border-color: #0d6efd;
    box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
    z-index: 10;
}
