body {
    background-color: #f8f9fa;
}

/* La tuile "pack" (catégorie products) suit le même modèle en ligne que les 9 autres catégories
   de composants, copié à l'identique de .processor-card (voir plus bas dans ce fichier) - même
   structure de grille card-body, même emplacement/taille de sélecteur, mêmes tailles de police.
   Seule différence : un radio plutôt qu'une checkbox (un pack à la fois, comme les composants
   "un seul choix" de ce tunnel) - ces derniers n'ayant pas d'équivalent -radio déjà stylé dans
   pc_components.css (qui n'utilise que des checkbox), ce même style est dupliqué ici sous le nom
   de classe -radio pour chacun des 8 sélecteurs à choix unique de ce tunnel. */
.product-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.product-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px;
}

.product-card .card-body > div:first-child {
    min-width: 0;
}

.product-card .card-body > div:nth-child(2) {
    justify-self: center;
}

.product-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px;
    flex-shrink: 0;
    position: relative;
}

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

.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);
}

.product-radio,
.processor-radio,
.memory-radio,
.storage-nvme-radio,
.storage-sata3-radio,
.graphic-card-radio,
.power-supply-radio,
.cpu-cooler-radio,
.modules-radio,
.operating-systems-radio {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
}

/* Boîtiers PC : seule catégorie de composant en grille d'image (reprise telle quelle de
   pc_configurator.css) plutôt que le modèle en ligne du reste du fichier (copié de
   pc_components.css) - un boîtier se choisit visuellement, pas la tuile pack elle-même. */
#tower-case-list > .col-md-6,
#tower-case-list > .col-lg-4 {
    margin-bottom: 1.5rem;
}

.tower-case-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    height: 100%;
}

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

.tower-case-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);
}

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

.tower-case-radio {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
}

.tower-case-image-wrapper {
    height: 250px;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    background-color: #fff;
    border-top-left-radius: 0.5rem;
    border-top-right-radius: 0.5rem;
    max-height: 100%;
    max-width: 100%;
    object-fit: contain;
    display: block;
    margin-top: 40px;
}

/* Badge promo pour les cartes "grille d'image" (boîtiers) : centré en haut de la carte plutôt que
   flottant à côté du sélecteur (voir .promo-badge-floating, pensé pour le modèle en ligne) - même
   principe que electronics_shop.css pour ses propres cartes en grille d'image. */
.promo-badge-top {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9;
    width: max-content;
    max-width: calc(100% - 64px);
    text-align: center;
}

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

/* 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 pc_components.js). */
.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() (pc_components.js) 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 col-md-4 qui le contient (voir pc_components.html) : le
   positionnement/l'ancrage sticky restent intacts, seul l'habillage visuel change. */

/* Fond bleu Datamotech très clair (au lieu du blanc plat, jugé "trop blanc") 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. Premier essai cassé : les
   items avaient border-left/border-right mis à 0 pour un rendu plus épuré, mais sans bordure de
   remplacement sur le conteneur lui-même, toute la liste se retrouvait sans bord gauche/droit -
   d'où les "bordures coupées sur les verticales". Ici 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 4 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 2 lignes ci-dessous : ô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 2 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 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 (#cart-tax-amount / #shipping-cost, 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;
}

/* Lignes "Inclus" (un composant du pack, sans prix propre - voir pc_pack.js updateCart) : plus
   discrètes que la ligne du pack lui-même (seule réellement facturée), padding réduit pour bien
   les regrouper visuellement sous elle plutôt que de leur donner le même poids. */
.cart-item-included {
    padding-top: 0.35rem;
    padding-bottom: 0.35rem;
}

.cart-item-included .cart-item-name {
    font-size: 0.85rem;
    font-weight: 400;
    color: #6c757d;
}

.cart-item-included .cart-item-subtotal {
    font-size: 0.85rem;
    font-weight: 400;
    font-style: italic;
}

/* 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épassait du conteneur (.cart-list, overflow:hidden) faute de place réservée
   au-dessus, et se retrouvait 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. */
.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 (contrairement à une 1ère version de cette règle) : un badge en item
       flex séparé se retrouvait rejeté tout seul sur sa propre ligne dès que le nom + le badge ne
       tenaient plus ensemble sur la ligne courante - y compris quand le nom à lui seul tenait sur
       1 ligne - au lieu de rester à la suite du texte comme en flux inline normal. Voir
       electronics_shop.css pour le même correctif et son détail. */
}

/* Description (stockages) sur sa propre ligne : tous les articles suivent désormais le même
   squelette (nom, description optionnelle, prix unitaire) empilé verticalement dans la même
   colonne, qu'ils aient ou non de description - aucune divergence structurelle entre catégories. */
.cart-item-desc {
    margin-top: 0.1rem;
}

.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), donc rien ne change pour
        les lignes sans promo. */
    align-self: center; /* centre le prix HT sur la hauteur réelle de la ligne (nom + éventuelle
        description + prix unitaire), plutôt que de rester calé en haut comme les deux autres
        colonnes de la grille (qui gardent align-items:start sur .cart-item - seule cette colonne
        est concernée, donc aucun risque de réintroduire le bug de pastille qté isolée sur une 3e
        ligne que align-items:start évitait). Pousse par la même occasion l'ancien prix barré
        (.cart-promo-old-subtotal, ancré en absolu juste au-dessus via bottom:100%) un peu plus
        bas, puisqu'il suit son ancre. */
    white-space: nowrap;
    font-weight: 700;
    padding-top: 0.1rem;
}

/* ===== Promo sur une ligne de panier (purement additif, voir pc_components.js) =====
   Même principe que les badges/ancien prix déjà utilisés sur les cartes produit
   (promo_badge_inline / promo_old_price) pour l'ancien prix du sous-total : positionné en absolu
   au-dessus du nouveau, retiré du flux normal, donc il n'élargit ni ne décale jamais la colonne à
   largeur fixe du sous-total. Voir electronics_shop.css pour le détail du badge (inline normal,
   pas de margin-left : l'espace avec le nom vient d'un espace texte dans pc_components.js, qui
   disparaît tout seul si le badge est rejeté seul au début de la ligne suivante - un margin, lui,
   serait resté et aurait décalé le badge vers la droite par rapport au nom sur la ligne du dessus). */
/* Espace nom/badge via un span dédié autour d'un unique caractère espace (texte, pas margin -
   voir note ci-dessus), avec un word-spacing qui l'élargit sans l'empêcher de disparaître
   normalement si ce même espace tombe en bout de ligne (le navigateur rogne tout le caractère,
   pas juste sa largeur visuelle). Un word-spacing posé directement sur .cart-item-name aurait
   aussi élargi les espaces internes au nom du produit lui-même - d'où ce span isolé. */
.cart-promo-gap {
    word-spacing: 0.1rem;
}

.cart-promo-badge {
    font-size: 0.65rem;
    font-weight: 700;
    white-space: nowrap;
    vertical-align: middle;
    position: relative;
    top: -2px; /* voir electronics_shop.css pour le détail de cette valeur mesurée. */
}

.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%;
}

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

/* Badge flottant (7 catégories sans image) : positionné en absolu par rapport au sélecteur de
   quantité (voir position:relative sur .xxx-quantity-selector), donc ne participe jamais à la
   grille - ni espace réservé quand il n'y a pas de promo, ni décalage du sélecteur quand il y en
   a une. Vient simplement s'insérer visuellement dans l'espace blanc juste avant le sélecteur. */
.promo-badge-floating {
    position: absolute;
    right: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-right: 1.5rem;
}

/* Enrobage serré autour du prix normal (valeur + devise) uniquement : comme l'ancien prix est
   positionné en absolu (donc retiré du flux), ce span se redimensionne exactement à la largeur
   du nouveau prix, indépendamment de la largeur min-width réservée par la colonne pour l'aligner
   entre les cartes. C'est ce sur quoi l'ancien prix se centre (voir .promo-old-price), pour un
   centrage exact au-dessus du nouveau prix quel que soit le nombre de chiffres. */
.price-value-group {
    position: relative;
    display: inline-block;
}

/* Ancien prix barré, réduit par rapport à la taille "small" de Bootstrap (0.875em) utilisée
   auparavant - seule modification demandée sur la taille du texte. Positionné en absolu
   au-dessus du prix normal (et non plus à sa gauche) pour ne jamais chevaucher le bouton "+"
   du sélecteur, quelle que soit la longueur du prix. La carte a de la marge verticale libre
   au-dessus/en dessous de sa ligne de contenu (align-items:center sur une hauteur fixe), donc
   cet ajout ne fait jamais grandir la tuile. Centré horizontalement par rapport à .price-value-
   group (voir ci-dessus), donc centré au-dessus du nouveau prix même si le nombre de chiffres
   diffère. line-height:1 pour ne garder que la hauteur réelle du texte (sans l'interligne par
   défaut) : l'espace dispo au-dessus du prix normal est très réduit sur ces tuiles compactes,
   donc chaque pixel gagné ici sert à mieux centrer visuellement l'ancien prix entre la
   description et le nouveau prix. */
.promo-old-price {
    font-size: 0.7rem;
    line-height: 1;
    position: absolute;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 0.3rem;
    white-space: nowrap;
}

.motherboard-currency {
    display: inline;
}

.motherboard-currency.hidden {
    display: none;
}

/* Motherboard Card Styles - Identical to Processor Card Styles */
.motherboard-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.motherboard-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.motherboard-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.motherboard-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.motherboard-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

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

.motherboard-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);
}

.motherboard-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

.tower-case-currency {
    display: inline;
}

.tower-case-currency.hidden {
    display: none;
}

/* Processor Card Styles */
.processor-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.processor-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.processor-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.processor-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.processor-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

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

.processor-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);
}

.processor-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
}

/* Sélecteur de quantité pour les processeurs */
.processor-quantity-selector {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    position: relative; /* ancre pour le badge promo, positionné en absolu (voir .promo-badge-floating) */
}

/* Enrobage des 3 boutons -/valeur/+ (voir .promo-badge-floating pour pourquoi il est séparé du
   badge promo) : leur donne leur propre ligne flex, indépendante du badge, notamment utile en
   pile mobile où badge et boutons doivent s'empiler sans que le badge n'hérite de leur largeur. */
.qty-controls {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.processor-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;
}

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

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

.processor-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 */
.processor-qty-input::-webkit-outer-spin-button,
.processor-qty-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

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

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

.processor-currency {
    display: inline;
}

.processor-currency.hidden {
    display: none;
}

/* Sélecteurs de quantité - Styles génériques pour tous les composants, y compris le pack
   (.product-*) : même widget -/nombre/+ que sur les autres tunnels, voir pc_pack.js pour son
   comportement (couplé au radio de sélection du pack plutôt qu'à une checkbox). */
.motherboard-quantity-selector,
.memory-quantity-selector,
.storage-quantity-selector,
.graphic-card-quantity-selector,
.power-supply-quantity-selector,
.cpu-cooler-quantity-selector,
.modules-quantity-selector,
.product-quantity-selector {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    position: relative; /* ancre pour le badge promo, positionné en absolu (voir .promo-badge-floating) */
}

.motherboard-qty-btn,
.memory-qty-btn,
.storage-qty-btn,
.graphic-card-qty-btn,
.power-supply-qty-btn,
.cpu-cooler-qty-btn,
.modules-qty-btn,
.product-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;
}

.motherboard-qty-btn:hover:not(:disabled),
.memory-qty-btn:hover:not(:disabled),
.storage-qty-btn:hover:not(:disabled),
.graphic-card-qty-btn:hover:not(:disabled),
.power-supply-qty-btn:hover:not(:disabled),
.cpu-cooler-qty-btn:hover:not(:disabled),
.modules-qty-btn:hover:not(:disabled),
.product-qty-btn:hover:not(:disabled) {
    background-color: #0d6efd;
    color: #fff;
    transform: scale(1.05);
}

.motherboard-qty-btn:disabled,
.memory-qty-btn:disabled,
.storage-qty-btn:disabled,
.graphic-card-qty-btn:disabled,
.power-supply-qty-btn:disabled,
.cpu-cooler-qty-btn:disabled,
.modules-qty-btn:disabled,
.product-qty-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    border-color: #dee2e6;
    color: #6c757d;
}

.motherboard-qty-input,
.memory-qty-input,
.storage-qty-input,
.graphic-card-qty-input,
.power-supply-qty-input,
.cpu-cooler-qty-input,
.modules-qty-input,
.product-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 */
.motherboard-qty-input::-webkit-outer-spin-button,
.motherboard-qty-input::-webkit-inner-spin-button,
.memory-qty-input::-webkit-outer-spin-button,
.memory-qty-input::-webkit-inner-spin-button,
.storage-qty-input::-webkit-outer-spin-button,
.storage-qty-input::-webkit-inner-spin-button,
.graphic-card-qty-input::-webkit-outer-spin-button,
.graphic-card-qty-input::-webkit-inner-spin-button,
.power-supply-qty-input::-webkit-outer-spin-button,
.power-supply-qty-input::-webkit-inner-spin-button,
.cpu-cooler-qty-input::-webkit-outer-spin-button,
.cpu-cooler-qty-input::-webkit-inner-spin-button,
.modules-qty-input::-webkit-outer-spin-button,
.modules-qty-input::-webkit-inner-spin-button,
.product-qty-input::-webkit-outer-spin-button,
.product-qty-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.motherboard-qty-input[type=number],
.memory-qty-input[type=number],
.storage-qty-input[type=number],
.graphic-card-qty-input[type=number],
.power-supply-qty-input[type=number],
.cpu-cooler-qty-input[type=number],
.modules-qty-input[type=number],
.product-qty-input[type=number] {
    -moz-appearance: textfield;
}

.motherboard-qty-input:focus,
.memory-qty-input:focus,
.storage-qty-input:focus,
.graphic-card-qty-input:focus,
.power-supply-qty-input:focus,
.cpu-cooler-qty-input:focus,
.modules-qty-input:focus,
.product-qty-input:focus {
    outline: none;
    border-color: #0d6efd;
    box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.25);
    z-index: 10;
}

/* Memory Card Styles - Identical to Processor Card Styles */
.memory-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.memory-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.memory-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.memory-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.memory-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

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

.memory-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);
}

.memory-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

.memory-currency {
    display: inline;
}

.memory-currency.hidden {
    display: none;
}

/* Graphic Card Card Styles - Identical to Processor Card Styles */
.graphic-card-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.graphic-card-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.graphic-card-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.graphic-card-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.graphic-card-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

.graphic-card-price {
    margin-right: 0.25rem;
}

.graphic-card-currency {
    display: inline;
}

.graphic-card-currency.hidden {
    display: none;
}

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

.graphic-card-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);
}

.graphic-card-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

/* Power Supply Card Styles - Identical to Graphic Card Card Styles */
.power-supply-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.power-supply-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.power-supply-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.power-supply-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.power-supply-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

.power-supply-price {
    margin-right: 0.25rem;
}

.power-supply-currency {
    display: inline;
}

.power-supply-currency.hidden {
    display: none;
}

.power-supply-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.power-supply-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);
}

.power-supply-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

/* CPU Cooler Card Styles - Identical to Graphic Card Card Styles */
.cpu-cooler-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.cpu-cooler-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.cpu-cooler-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.cpu-cooler-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.cpu-cooler-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

.cpu-cooler-price {
    margin-right: 0.25rem;
}

.cpu-cooler-currency {
    display: inline;
}

.cpu-cooler-currency.hidden {
    display: none;
}

.cpu-cooler-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.cpu-cooler-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);
}

.cpu-cooler-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

/* Modules Card Styles - Identical to CPU Cooler Card Styles */
.modules-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.modules-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le checkbox */
}

.modules-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.modules-card .card-body > div:nth-child(2) {
    /* Sélecteur de quantité */
    justify-self: center;
}

.modules-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px; /* Largeur FIXE (pas min-width) : le contenu (prix + devise + libellé HT/TTC)
        change de longueur au changement de mode, une largeur seulement minimale laissait alors la
        colonne s'élargir en TTC et décaler le sélecteur vers la gauche. 145px couvre le cas le
        plus large observé (ex: "898.56 € TTC") avec de la marge. */
    flex-shrink: 0;
    position: relative; /* ancre pour l'ancien prix barré, positionné en absolu (voir .promo-old-price) */
}

.modules-price {
    margin-right: 0.25rem;
}

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

.modules-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);
}

.modules-checkbox {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

.modules-currency {
    display: inline;
}

.modules-currency.hidden {
    display: none;
}

/* Operating Systems Card Styles - Identical to Modules Card Styles */
.operating-systems-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.operating-systems-card .card-body {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 1.25rem 2rem;
    gap: 3rem;
    padding-left: 50px; /* Espace pour le radio button */
}

.operating-systems-card .card-body > div:first-child {
    flex: 1;
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.operating-systems-card .card-body > div:last-child {
    text-align: center;
    flex-shrink: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 0.25rem;
}

.operating-systems-price {
    margin-right: 0.25rem;
}

.operating-systems-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.operating-systems-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);
}

.operating-systems-radio {
    width: 18px;
    height: 18px;
    cursor: pointer;
    accent-color: #0d6efd;
    position: absolute;
    top: 50%;
    left: 12px;
    transform: translateY(-50%);
    z-index: 10;
}

.operating-systems-currency {
    display: inline;
}

.operating-systems-currency.hidden {
    display: none;
}

/* Storage Card Styles (NVMe / Sata3) - deux groupes de boutons radio indépendants (voir
   pc_pack.html / pc_pack.js), chacun identique au modèle Processor Card Styles. */
.storage-nvme-card,
.storage-sata3-card {
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s, background-color 0.2s;
    cursor: pointer;
    border: 2px solid #dee2e6;
    position: relative;
    min-height: 80px;
    display: flex;
    align-items: center;
    margin-bottom: 1.5rem;
}

.storage-nvme-card .card-body,
.storage-sata3-card .card-body {
    display: grid;
    grid-template-columns: 1fr auto auto;
    align-items: center;
    column-gap: 2rem;
    width: 100%;
    padding: 1.25rem 2rem;
    padding-left: 50px; /* Espace pour le radio */
}

.storage-nvme-card .card-body > div:first-child,
.storage-sata3-card .card-body > div:first-child {
    min-width: 0; /* Permet la troncature du texte si nécessaire */
}

.storage-nvme-card .card-body > div:nth-child(2),
.storage-sata3-card .card-body > div:nth-child(2) {
    justify-self: center;
}

.storage-nvme-card .card-body > div:last-child,
.storage-sata3-card .card-body > div:last-child {
    text-align: right;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
    width: 145px;
    flex-shrink: 0;
    position: relative;
}

.storage-nvme-card:hover,
.storage-sata3-card:hover {
    transform: translateY(-3px);
    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}

.storage-nvme-card.selected,
.storage-sata3-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);
}

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

/* Sur mobile, la grille "1fr auto auto" (texte / quantité / prix) laisse une colonne de
   texte proche de 0px une fois les deux colonnes fixes et les paddings soustraits de la
   largeur de l'écran, ce qui force un retour à la ligne caractère par caractère. On empile
   la carte en dessous de 768px (breakpoint Bootstrap déjà utilisé dans les templates) ;
   le rendu desktop n'est pas concerné par cette media query. */
@media (max-width: 767.98px) {
    .motherboard-card .card-body,
    .processor-card .card-body,
    .memory-card .card-body,
    .graphic-card-card .card-body,
    .power-supply-card .card-body,
    .cpu-cooler-card .card-body,
    .modules-card .card-body,
    .storage-nvme-card .card-body,
    .storage-sata3-card .card-body,
    .product-card .card-body {
        grid-template-columns: 1fr;
        row-gap: 0.75rem;
        padding: 1rem 1rem 1rem 50px;
    }

    .motherboard-card .card-body > div:nth-child(2),
    .processor-card .card-body > div:nth-child(2),
    .memory-card .card-body > div:nth-child(2),
    .graphic-card-card .card-body > div:nth-child(2),
    .power-supply-card .card-body > div:nth-child(2),
    .cpu-cooler-card .card-body > div:nth-child(2),
    .modules-card .card-body > div:nth-child(2),
    .storage-nvme-card .card-body > div:nth-child(2),
    .storage-sata3-card .card-body > div:nth-child(2),
    .product-card .card-body > div:nth-child(2) {
        justify-self: start;
    }

    .motherboard-card .card-body > div:last-child,
    .processor-card .card-body > div:last-child,
    .memory-card .card-body > div:last-child,
    .graphic-card-card .card-body > div:last-child,
    .power-supply-card .card-body > div:last-child,
    .cpu-cooler-card .card-body > div:last-child,
    .modules-card .card-body > div:last-child,
    .storage-nvme-card .card-body > div:last-child,
    .storage-sata3-card .card-body > div:last-child,
    .product-card .card-body > div:last-child {
        justify-content: flex-start;
        min-width: 0;
    }

    .operating-systems-card .card-body {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.75rem;
        padding: 1rem 1rem 1rem 50px;
    }

    .operating-systems-card .card-body > div:last-child {
        justify-content: flex-start;
    }

    /* Le badge promo flottant (voir .promo-badge-floating) est normalement positionné en absolu
       à gauche du sélecteur (right:100%) pour s'insérer dans l'espace blanc du rendu desktop 3
       colonnes. En pile mobile (1 colonne), ce même positionnement absolu fait déborder le badge
       hors de la carte - et le repositionner en absolu au-dessus du sélecteur reste risqué : la
       description au-dessus peut faire 1 ou 2 lignes selon sa longueur, donc une position absolue
       fixe peut chevaucher son texte. On le remet donc dans le flux normal, au-dessus des boutons
       -/+ : le sélecteur passe en colonne (badge et .qty-controls empilés, chacun avec sa propre
       largeur naturelle - voir .qty-controls, qui regroupe les 3 boutons séparément du badge pour
       que celui-ci ne s'étire jamais à leur largeur ni à celle de la carte). */
    .motherboard-quantity-selector,
    .processor-quantity-selector,
    .memory-quantity-selector,
    .storage-quantity-selector,
    .graphic-card-quantity-selector,
    .power-supply-quantity-selector,
    .cpu-cooler-quantity-selector,
    .modules-quantity-selector,
    .product-quantity-selector {
        flex-direction: column;
        align-items: flex-start;
    }

    .promo-badge-floating {
        position: static;
        transform: none;
        margin: 0 0 0.5rem 0;
    }

    /* Même principe que le badge ci-dessus pour le prix barré (voir .promo-old-price) : en pile
       mobile, il est juste au-dessus de la ligne de prix, donc juste en dessous du sélecteur -
       son positionnement absolu (pensé pour flotter dans la marge verticale d'une ligne desktop
       compacte) le fait chevaucher les boutons -/+ du sélecteur. On le remet dans le flux normal,
       sur sa propre ligne au-dessus du nouveau prix, centré. */
    .promo-old-price {
        position: static;
        transform: none;
        display: block;
        text-align: center;
        margin: 0 0 0.2rem 0;
    }
}