/* ══════════════════════════════════════════════════════════════
   L'APPARENCE DU SITE : COULEURS, ESPACEMENTS, FORMES
   Ce fichier ne change rien au fonctionnement : il décrit uniquement
   à quoi ressemblent les éléments. Il vient par-dessus Bootstrap, la
   bibliothèque de mise en page utilisée par le site : quand une règle
   ci-dessous répète une couleur ou une taille, c'est pour remplacer
   celle de Bootstrap.
   Les sections suivent les grandes parties du site.
   ══════════════════════════════════════════════════════════════ */

:root {
    /* Le bleu de la barre du haut sert de couleur principale : boutons,
       liens, cases cochées et halos de saisie en découlent tous. */
    --bs-primary: #2668c4;
    --bs-primary-rgb: 38, 104, 196;
    --bs-primary-text-emphasis: #1c4f99;
    /* Le fond de la page. Un gris très pâle, et non du blanc : c'est lui qui
       détache la barre du haut et les cartes, toutes deux blanches. Les
       composants qui doivent rester blancs le redisent (.card, .table,
       .form-control), Bootstrap calquant leur fond sur celui-ci. */
    --bs-body-bg: #f5f6fa;
    --bs-body-color: #3a3f51;
    --bs-border-color: #e3e6ef;
    --bs-secondary-color: #5b6478;

    /* Prélevées sur MAIL_LOGO-original.png */
    --logo-bleu: #1670f0;
    --logo-cyan: #3fbdf2;

    /* ─── Les repères communs de l'habillage ───
       Une ombre est faite de deux couches : une très serrée qui donne le
       relief, une large et très diffuse qui détache l'élément du fond.
       Une seule couche donne toujours un rendu plat ou sale. */
    --ombre-douce: 0 1px 2px rgba(58, 63, 81, .04),
                   0 10px 26px -18px rgba(58, 63, 81, .25);
    --ombre-relief: 0 1px 2px rgba(58, 63, 81, .05),
                    0 18px 40px -20px rgba(58, 63, 81, .32);
    --ombre-flottante: 0 .75rem 2.25rem rgba(58, 63, 81, .16);

    --rayon-carte: 1rem;
    --rayon-champ: .55rem;

    /* ─── La barre du haut ───
       Un bleu nuit tiré du bleu du logo, et non un gris neutre : la barre
       appartient à la marque. Les trois autres valeurs en découlent, pour
       que le texte et les pastilles ne soient jamais écrits en dur. */
    --barre-fond: #2668c4;
    --barre-fond-clair: #46a2e6;
    --barre-texte: #eaf4ff;
    --barre-texte-vif: #ffffff;
    --barre-survol: rgba(255, 255, 255, .20);

    /* Un gris de bordure plus clair que celui des filets : une carte se
       détache par son ombre, pas par un trait marqué */
    --bordure-douce: #e9ecf5;
    --filet-tableau: #f1f3f9;
}

body {
    background-color: var(--bs-body-bg);

    /* Lissage des caractères : le texte est nettement plus net sur macOS,
       et les graisses ne paraissent plus épaissies */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* La couleur de sélection reprend le bleu du site plutôt que le bleu système */
::selection {
    background-color: rgba(var(--bs-primary-rgb), .18);
}

/* ══════════════════════════════════════════════════════════════
   LA BARRE DU HAUT ET LE LOGO
   La bande blanche présente sur toutes les pages, qui reste
   visible quand on descend dans la page.
   ══════════════════════════════════════════════════════════════ */

/* Barre de navigation horizontale : blanche, collée en haut de l'écran */
.app-navbar {
    /* Le dégradé part du bleu de la marque et s'éclaircit vers la droite, du
       côté du compte : la barre n'est pas un aplat, elle a un sens de lecture */
    background-color: var(--barre-fond);
    background-image: linear-gradient(100deg, var(--barre-fond) 30%, var(--barre-fond-clair) 160%);
    border-bottom: 0;
    padding-top: .55rem;
    padding-bottom: .55rem;

    /* Une ombre portée plutôt qu'un trait franc : la barre paraît posée
       au-dessus de la page, et non collée à elle */
    box-shadow: 0 1px 2px rgba(29, 85, 184, .14),
                0 10px 28px -18px rgba(29, 85, 184, .50);
}

/* Tant que la page est en haut, la barre reste posée à plat. Dès qu'on
   descend, du contenu passe dessous : l'ombre se renforce pour l'en
   détacher. La classe est posée par public/js/navbar-defilement.js. */
.app-navbar {
    transition: box-shadow .2s ease, background-color .2s ease;
}

.app-navbar.est-defilee {
    box-shadow: 0 2px 4px rgba(29, 85, 184, .18),
                0 18px 38px -22px rgba(29, 85, 184, .62);
}

/* Le liseré du logo, repris sur toute la largeur : c'est la signature de la
   barre. 2 px suffisent — au-delà, la bande devient un bandeau publicitaire. */
.app-navbar::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background-image: linear-gradient(90deg,
        rgba(255, 255, 255, .85) 0%, rgba(232, 246, 255, .55) 45%, rgba(255, 255, 255, 0) 100%);
    opacity: .8;
}

/* La barre étant collée en haut, le contenu défile dessous : un fond
   légèrement translucide et flouté laisse deviner ce passage au lieu de
   masquer la page derrière un bandeau opaque. Sans le @supports, les
   navigateurs qui ignorent le flou afficheraient un blanc transparent. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .app-navbar {
        background-color: rgba(38, 104, 196, .90);
        -webkit-backdrop-filter: saturate(160%) blur(14px);
        backdrop-filter: saturate(160%) blur(14px);
    }
}

/* La barre suit la même largeur utile que le contenu (voir .app-main) :
   toute la fenêtre, moins les marges latérales. Une largeur plafond laissait
   deux bandes vides sur un grand écran, le catalogue étant une grille de
   cartes : il a tout intérêt à prendre la place qu'on lui donne. */
.app-navbar > .container-fluid {
    max-width: none;
}

.app-navbar .navbar-brand {
    padding: 0;
    margin-right: .25rem;
}

/* Le contenu suit la même largeur utile que la barre.
   width: 100% est indispensable : sous « lg », body devient une colonne flex,
   et une marge auto sur un enfant flex annule le align-items: stretch. La
   largeur se calerait alors sur le contenu — le tableau rétrécirait à chaque
   filtre qui retire des lignes. */
.app-main {
    width: 100%;
    max-width: none;
}

.app-brand-logo {
    width: 38px;
    height: 38px;
    object-fit: contain;
    flex-shrink: 0;
}

.app-brand-text small {
    font-size: .76rem;
    line-height: 1;
}

/* Non limité à la sidebar : réutilisé tel quel sur la page de connexion */
.app-logo {
    color: var(--logo-bleu);
    font-weight: 700;
    font-style: italic;
    font-size: 1.4rem;
    letter-spacing: .02em;
}

/* Le dégradé du logo repris sur le mot « CRUD » */
@supports (background-clip: text) or (-webkit-background-clip: text) {
    .app-logo {
        background-image: linear-gradient(100deg, var(--logo-bleu), var(--logo-cyan) 135%);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }
}

.app-brand .app-logo {
    font-size: 1.15rem;
    line-height: 1.1;
}

/* ══════════════════════════════════════════════════════════════
   LES BANDEAUX DE MESSAGE
   Les rectangles vert (réussi) et rouge (erreur) en haut des pages.
   ══════════════════════════════════════════════════════════════ */

/* Alertes : palette adoucie + filet d'accent, via les variables Bootstrap */
.alert {
    --bs-alert-border: 1px solid var(--bs-alert-border-color);
    border-radius: .625rem;
    border-left-width: 3px;
    padding: .8rem .95rem;
    font-size: .875rem;
    line-height: 1.5;
}

.alert-danger {
    --bs-alert-bg: #fdf4f5;
    --bs-alert-border-color: #f0d7dc;
    --bs-alert-color: #9d3b4d;
    border-left-color: #d9758a;
}

.alert-success {
    --bs-alert-bg: #f2faf5;
    --bs-alert-border-color: #cee8da;
    --bs-alert-color: #1f6b4a;
    border-left-color: #57ab84;
}

/* Notre padding raccourci écrase celui de Bootstrap : on rend sa place au bouton */
.alert-dismissible {
    padding-right: 2.5rem;
}

.alert-dismissible .btn-close {
    padding: .9rem .8rem;
}

.alert-icone {
    flex-shrink: 0;
    margin-top: .1rem;
    opacity: .75;
}

.alert-titre {
    font-weight: 600;
}

.alert-liste {
    list-style: none;
    margin: .35rem 0 0;
    padding: 0;
}

.alert-liste li {
    position: relative;
    padding-left: .85rem;
    color: var(--bs-body-color);
}

.alert-liste li + li {
    margin-top: .25rem;
}

.alert-liste li::before {
    content: "";
    position: absolute;
    left: 0;
    top: .5rem;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background-color: currentColor;
    opacity: .45;
}

/* ══════════════════════════════════════════════════════════════
   LE MENU DE NAVIGATION
   Les entrées du menu : Produits, Utilisateurs, Paniers, Droits.
   L'entrée de la page en cours est mise en évidence.
   ══════════════════════════════════════════════════════════════ */

/* ─── Ce que la barre contient ───
   Le petit texte sous le logo et le nom du compte suivent la teinte de la
   barre, plutôt que d'ajouter une classe sur chaque élément du gabarit.
   Le nom n'est visé QUE dans le bouton : le menu déroulant le réaffiche,
   et là il est écrit sur un fond blanc. */
.app-navbar .app-brand-text small,
.app-navbar .btn-compte .nom-compte,
.app-navbar .btn-compte .bi-chevron-down {
    color: var(--barre-texte) !important;
}

.app-navbar .navbar-toggler {
    color: var(--barre-texte);
}

/* La pastille du compte porte le dégradé du logo : sur le bleu de la barre,
   un anneau blanc l'en détache */
.app-navbar .avatar-compte {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .60);
}

/* Le compteur du panier s'inverse : blanc à chiffre bleu, sinon un bleu
   sur du bleu ne se compte plus d'un coup d'œil */
.app-navbar .badge-panier {
    background-color: #ffffff !important;
    color: var(--barre-fond) !important;
    box-shadow: 0 1px 4px rgba(15, 27, 52, .25);
}

@supports (background-clip: text) or (-webkit-background-clip: text) {
    .app-navbar .app-logo {
        background-image: linear-gradient(100deg, #ffffff, #e8f6ff 130%);
    }
}

/* Entrées du menu : pastilles arrondies, code couleur repris de l'ancien menu latéral */
.app-navbar .navbar-nav {
    gap: .25rem;
}

.app-navbar .nav-link {
    display: flex;
    align-items: center;
    gap: .5rem;
    color: var(--barre-texte);
    border-radius: .5rem;
    font-size: .9rem;
    padding: .5rem .8rem;
}

.app-navbar .nav-link i {
    color: rgba(255, 255, 255, .85);
    font-size: 1rem;
}

.app-navbar .nav-link {
    transition: background-color .15s ease, color .15s ease;

    /* Entre 992 px et le confort, la barre se serre : sans cela « Tableau
       de bord » et le nom du compte se coupent en deux lignes et font
       grandir la barre */
    white-space: nowrap;
}

.app-navbar .btn-compte .nom-compte {
    white-space: nowrap;
}

.app-navbar .nav-link:hover,
.app-navbar .nav-link:focus {
    background-color: var(--barre-survol);
    color: var(--barre-texte-vif);
}

/* .show : Bootstrap la pose sur l'entrée dont le menu est ouvert.
   Le fond part du bleu du logo et s'éclaircit vers la droite : la pastille
   de la page ouverte se reconnaît d'un coup d'œil sans crier plus fort
   que le reste de la barre. */
.app-navbar .nav-link.active,
.app-navbar .nav-link.show {
    background-image: linear-gradient(100deg,
        rgba(255, 255, 255, .30), rgba(255, 255, 255, .18));
    color: var(--barre-texte-vif);
    font-weight: 600;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .45);
}

.app-navbar .nav-link.active i,
.app-navbar .nav-link.show i {
    color: var(--barre-texte-vif);
}

.app-navbar .dropdown-toggle::after {
    margin-left: .15rem;
    opacity: .5;
}

/* Bouton du menu repliable, sans le cadre par défaut de Bootstrap */
.app-navbar .navbar-toggler {
    border: 0;
    padding: .25rem .4rem;
    font-size: 1.35rem;
    line-height: 1;
    color: #5c6273;
}

.app-navbar .navbar-toggler:focus {
    box-shadow: none;
}

/* ══════════════════════════════════════════════════════════════
   LA BARRE DE RECHERCHE
   Le champ de la barre du haut, qui envoie vers le catalogue.
   Le balisage est dans resources/views/layout.blade.php.
   ══════════════════════════════════════════════════════════════ */

/* À partir de « lg », la recherche occupe l'espace laissé libre entre le
   menu et les boutons de droite. La largeur maximale l'empêche de devenir
   une bande démesurée sur un grand écran, et la marge automatique la
   rapproche alors des actions plutôt que de la laisser flotter au milieu. */
@media (min-width: 992px) {
    /* Le menu ne prend plus que la place de ses entrées : sans cela, il
       s'étire sur toute la barre et ne laisse rien à la recherche. */
    .app-navbar .navbar-collapse {
        order: 1;
        flex-grow: 0;
    }

    .recherche-navbar {
        order: 2;
        flex: 1 1 14rem;
        max-width: 26rem;
        margin-left: auto;
        margin-right: .75rem;
    }
}

/* Entre « lg » et « xl », tout est déjà déplié mais la place manque : le
   menu, la recherche et le compte se disputent la largeur, et la barre
   finit par déborder. On retire donc l'accessoire — le sous-titre du logo
   et le nom du compte, que l'avatar remplace — et on resserre le reste. */
@media (min-width: 992px) and (max-width: 1199.98px) {
    /* !important : le gabarit pose « d-block » et « d-md-inline » sur ces
       deux éléments, et les classes d'affichage de Bootstrap sont elles-mêmes
       en !important — sans cela, la règle ci-dessous ne passerait pas */
    .app-navbar .app-brand-text small,
    .app-navbar .btn-compte .nom-compte {
        display: none !important;
    }

    .app-navbar .nav-link {
        padding-left: .6rem;
        padding-right: .6rem;
    }

    .recherche-navbar {
        flex-basis: 10rem;
        max-width: 16rem;
    }
}

/* Sous « lg », la recherche passe sur sa propre ligne, sur toute la
   largeur : à côté du logo et du menu, il ne lui resterait qu'un timbre
   illisible. order la place sous la première ligne mais au-dessus du
   menu déroulé, pour rester à portée de pouce. */
@media (max-width: 991.98px) {
    .recherche-navbar {
        order: 2;
        flex: 0 0 100%;
        margin-top: .5rem;
    }

    .app-navbar .navbar-collapse {
        order: 3;
    }
}

/* Le cadre du champ : gris au repos pour se fondre dans la barre blanche,
   il passe au blanc dès qu'on écrit dedans. La loupe et la croix sont
   posées dans ce cadre, le champ occupe ce qui reste. */
.champ-recherche {
    display: flex;
    align-items: center;
    gap: .25rem;
    padding: 0 .35rem 0 .15rem;
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 2rem;
    background-color: rgba(255, 255, 255, .20);
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.champ-recherche:hover {
    background-color: rgba(255, 255, 255, .30);
}

/* Le texte saisi et son indication, sur le bleu de la barre */
.champ-recherche .recherche-saisie {
    color: var(--barre-texte-vif);
}

.champ-recherche .recherche-saisie::placeholder {
    color: rgba(255, 255, 255, .82);
}

.champ-recherche .recherche-loupe,
.champ-recherche .recherche-vider {
    color: var(--barre-texte);
}

/* :focus-within — le cadre réagit quand le curseur est dans le champ,
   alors que :focus ne concernerait que le cadre lui-même, qui ne se
   sélectionne pas. Le halo reprend celui des champs du site. */
.champ-recherche:focus-within {
    background-color: #ffffff;
    border-color: rgba(255, 255, 255, .90);
    box-shadow: 0 0 0 .2rem rgba(255, 255, 255, .30);
}

/* Le champ actif est blanc : son contenu repasse en foncé */
.champ-recherche:focus-within .recherche-saisie {
    color: var(--bs-body-color);
}

.champ-recherche:focus-within .recherche-saisie::placeholder,
.champ-recherche:focus-within .recherche-loupe,
.champ-recherche:focus-within .recherche-vider {
    color: var(--bs-secondary-color);
}

/* La loupe : un bouton, mais sans rien de ce qui fait un bouton à l'écran */
.recherche-loupe {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 2rem;
    height: 2rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: #6b7488;
    font-size: .95rem;
    line-height: 1;
}

.recherche-loupe:hover,
.champ-recherche:focus-within .recherche-loupe {
    color: var(--bs-primary-text-emphasis);
}

.recherche-loupe:focus-visible {
    outline: 2px solid rgba(var(--bs-primary-rgb), .5);
    outline-offset: 1px;
}

/* Le champ lui-même : pas de cadre à lui, c'est le bloc autour qui en tient
   lieu. min-width: 0 est indispensable, sinon la largeur par défaut d'un
   input empêche le champ de rétrécir dans la barre. */
.recherche-saisie {
    flex: 1 1 auto;
    min-width: 0;
    padding: .45rem .15rem;
    border: 0;
    background: transparent;
    color: var(--bs-body-color);
    font-size: .875rem;
}

.recherche-saisie:focus {
    outline: none;
}

.recherche-saisie::placeholder {
    color: #6b7488;
}

/* Les navigateurs WebKit ajoutent leur propre croix dans un champ de type
   « search » : on la retire, la nôtre est juste à côté et mène au catalogue. */
.recherche-saisie::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

/* La croix qui vide la recherche */
.recherche-vider {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 50%;
    color: #6b7488;
    font-size: .7rem;
    text-decoration: none;
}

.recherche-vider:hover,
.recherche-vider:focus-visible {
    background-color: #e3e6ef;
    color: var(--bs-body-color);
}

/* ══════════════════════════════════════════════════════════════
   L'EN-TÊTE DE PAGE
   Le bandeau de titre en haut de chaque page : pastille d'icône,
   titre, phrase d'explication, et à droite les boutons ou le
   compteur. Le balisage vient de resources/views/components/entete.blade.php.
   ══════════════════════════════════════════════════════════════ */

.entete-page {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;

    /* Un filet fin sépare l'en-tête du contenu, sans alourdir */
    margin-bottom: 1rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.entete-page-titre {
    display: flex;
    align-items: center;
    gap: .9rem;

    /* Laisse le titre se réduire au lieu de pousser les boutons dehors */
    min-width: 0;
}

/* Pastille d'icône, dans les bleus du logo */
.entete-page-icone {
    flex: none;
    width: 44px;
    height: 44px;
    display: grid;
    place-items: center;
    border-radius: 14px;
    font-size: 1.2rem;
    color: var(--logo-bleu);
    background-image: linear-gradient(135deg,
        rgba(22, 112, 240, .10), rgba(63, 189, 242, .18));
    box-shadow: inset 0 0 0 1px rgba(22, 112, 240, .10);
}

.entete-page-texte {
    min-width: 0;
}

.entete-page-nom {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: -.01em;

    /* Un titre trop long est coupé par « … » plutôt que de tout décaler */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.entete-page-sous {
    margin: .15rem 0 0;
    font-size: .8125rem;
    line-height: 1.35;
    color: var(--bs-secondary-color);
}

.entete-page-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

/* Compteur du genre « 212 produits », posé à droite du titre */
.entete-page-compteur {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .35rem .75rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 999px;
    background-color: #ffffff;
    font-size: .8125rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

.entete-page-compteur strong {
    font-weight: 600;
    color: var(--bs-body-color);
}

/* Sur petit écran, le titre et les boutons passent l'un sous l'autre */
@media (max-width: 575.98px) {
    .entete-page-icone {
        width: 38px;
        height: 38px;
        border-radius: 12px;
        font-size: 1.05rem;
    }

    .entete-page-nom {
        font-size: 1rem;
    }
}


/* ══════════════════════════════════════════════════════════════
   LES TABLEAUX ET LES CARTES
   L'apparence commune à toutes les listes du site.
   ══════════════════════════════════════════════════════════════ */

/* Tableaux : Bootstrap calque --bs-table-bg sur --bs-body-bg (même souci que .card) */
.table {
    --bs-table-bg: #ffffff;

    /* Les filets entre les lignes sont volontairement très pâles : dans une
       longue liste, ils guident l'œil sans quadriller la page */
    --bs-table-border-color: var(--filet-tableau);
    --bs-table-hover-bg: #fafbff;
}

.table thead th {
    padding-top: .65rem;
    padding-bottom: .65rem;
    font-size: .74rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: #5f6981;
    font-weight: 600;
    border-bottom-width: 1px;

    /* Un titre tient sur une ligne. Coupé en deux, il déforme la hauteur de
       l'en-tête et se lit mal en capitales. Si le tableau devient trop large,
       c'est .table-responsive qui le fait défiler ; sous 768 px les lignes
       deviennent des fiches et les titres ne s'affichent plus. */
    white-space: nowrap;
}

/* ─── Le nom d'un compte, dans la liste des utilisateurs ───
   Sans plafond, le tableau élargit la colonne jusqu'à tenir la plus longue
   des lignes en entier — « Super administrateur (vous) En attente » — et
   prend la place des autres colonnes. Avec ce plafond, la pastille passe
   simplement dessous. */
.cellule-nom {
    max-width: 11rem;
}

/* ─── La liste des droits d'un compte ───
   Une grille de quatre colonnes plutôt qu'un simple retour à la ligne : les
   badges se rangent quatre par quatre, et ceux de la rangée du dessous
   tombent sous ceux du dessus au lieu de se décaler au hasard des longueurs.
   max-content garde à chaque badge sa largeur, justify-items l'empêche
   d'être étiré jusqu'au bord de sa colonne. */
.cellule-droits {
    display: grid;
    grid-template-columns: repeat(4, max-content);
    justify-items: start;
    gap: .3rem;
}

/* Des lignes plus hautes : c'est le premier réglage qui fait la différence
   entre une liste dense et une liste confortable */
.table > tbody > tr > td {
    padding-top: .8rem;
    padding-bottom: .8rem;
}

/* Les colonnes alignées à droite portent des nombres (prix, quantités).
   La chasse fixe les aligne chiffre sous chiffre d'une ligne à l'autre. */
.table td.text-end,
.table th.text-end {
    font-variant-numeric: tabular-nums;
}

/* Cartes : bordure très pâle et ombre en deux couches plutôt qu'un simple
   cadre. Fond blanc explicite : Bootstrap calque --bs-card-bg sur
   --bs-body-bg, qu'on a mis en gris clair pour la page. */
.card {
    --bs-card-bg: #ffffff;
    --bs-card-border-color: var(--bordure-douce);
    --bs-card-border-radius: var(--rayon-carte);
    --bs-card-inner-border-radius: calc(var(--rayon-carte) - 1px);
    box-shadow: var(--ombre-douce);
}

/* ══════════════════════════════════════════════════════════════
   LA PAGE CATALOGUE : FILTRES À GAUCHE, TABLEAU À DROITE
   Toute la mise en page de la liste des produits : le panneau
   de filtres, ses blocs repliables, ses options et ses compteurs.
   Sur petit écran, le panneau repasse au-dessus du tableau.
   ══════════════════════════════════════════════════════════════ */

/* Catalogue : panneau de filtres à gauche, tableau à droite.
   minmax(0, 1fr) et non 1fr : sans ça, un tableau large pousse la colonne
   au lieu de défiler dans son propre conteneur. */
.grille-catalogue {
    display: grid;
    grid-template-columns: 17rem minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
}

.panneau-filtres {
    background-color: #ffffff;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    overflow: hidden;

    /* Reste sous la barre de navigation quand la liste défile */
    position: sticky;
    top: 5rem;
}

/* Champ de recherche, loupe posée dans le champ */
.filtres-recherche {
    position: relative;
    padding: .75rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.filtres-recherche .bi-search {
    position: absolute;
    top: 50%;
    left: 1.4rem;
    transform: translateY(-50%);
    font-size: .8rem;
    color: #6b7488;
    pointer-events: none;
}

.filtres-recherche .form-control {
    padding-left: 2rem;
    border-radius: 999px;
    font-size: .8125rem;
}

/* Étiquettes des filtres posés */
.filtres-puces {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
    padding: .75rem;
    background-color: #fbfcfe;
    border-bottom: 1px solid var(--bs-border-color);
}

.puce-filtre {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    max-width: 100%;
    padding: .2rem .5rem;
    border-radius: 999px;
    background-color: rgba(var(--bs-primary-rgb), .10);
    color: var(--bs-primary-text-emphasis);
    font-size: .75rem;
    text-decoration: none;
}

.puce-filtre:hover {
    background-color: #dfe3fa;
    color: var(--bs-primary-text-emphasis);
}

.puce-filtre .bi-x-lg {
    font-size: .6rem;
    opacity: .7;
}

.filtres-vider {
    margin-left: auto;
    font-size: .75rem;
    color: var(--bs-secondary-color);
    text-decoration: none;
}

.filtres-vider:hover {
    color: var(--bs-primary-text-emphasis);
    text-decoration: underline;
}

/* Un bloc = une facette repliable */
.bloc-filtre + .bloc-filtre {
    border-top: 1px solid var(--bs-border-color);
}

.bloc-filtre-titre {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: .75rem .875rem;
    border: 0;
    background-color: transparent;
    font-size: .75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bs-body-color);
}

.bloc-filtre-titre .bi-chevron-down {
    font-size: .7rem;
    color: #6b7488;
    transition: transform .15s ease-in-out;
}

/* Bootstrap pose .collapsed sur le bouton quand le bloc est fermé */
.bloc-filtre-titre.collapsed .bi-chevron-down {
    transform: rotate(-90deg);
}

.bloc-filtre-corps {
    padding: 0 .875rem .875rem;
}

.bloc-filtre-corps > div + div {
    margin-top: .75rem;
}

/* Une option : case ou bouton radio, libellé, puis le nombre de produits */
.option-filtre {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .3rem 0;
    font-size: .8125rem;
    color: #5c6273;
    cursor: pointer;
}

.option-filtre:hover {
    color: var(--bs-body-color);
}

.option-filtre .form-check-input {
    flex-shrink: 0;
    margin: 0;
}

/* « Tous les prix » / « Tous les articles » : un lien qui décoche la section.
   Le retrait pèse moins qu'une case, d'où le texte seul, aligné sur les autres */
.option-filtre-tout {
    padding-left: 1.6rem;
    text-decoration: none;
}

.option-filtre-tout.option-filtre-active {
    color: var(--bs-primary);
    font-weight: 600;
}

/* Facette sans résultat avec les filtres en cours : visible mais hors d'atteinte */
.option-filtre.option-vide {
    opacity: .45;
    cursor: default;
}

.option-filtre-nom {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.option-filtre-compte {
    margin-left: auto;
    font-size: .7rem;
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
}

/* Même langage que les en-têtes de tableau : petites capitales espacées */
.etiquette-filtre {
    display: block;
    margin-bottom: .25rem;
    font-size: .74rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 600;
    color: var(--bs-secondary-color);
}

/* Sous « lg » le panneau repasse au-dessus du tableau : les facettes se rangent
   côte à côte au lieu de s'empiler, sinon le tableau part sous la ligne de flottaison. */
@media (max-width: 991.98px) {
    .grille-catalogue {
        grid-template-columns: minmax(0, 1fr);
    }

    .panneau-filtres {
        position: static;
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    }

    .filtres-recherche,
    .filtres-puces {
        grid-column: 1 / -1;
    }

    /* Le filet passe au-dessus de chaque facette, plus entre deux voisines */
    .bloc-filtre,
    .bloc-filtre + .bloc-filtre {
        border-top: 1px solid var(--bs-border-color);
    }
}

/* À partir de « lg », l'écran est figé : la barre, le titre et le tableau ne
   bougent plus, seul le panneau de filtres défile de son côté. En dessous, le
   panneau passe au-dessus du tableau et la page défile normalement.

   min-height: 0 est répété à chaque étage : sans lui, un enfant flex refuse de
   rétrécir sous sa taille naturelle et c'est la page entière qui défilerait. */
@media (min-width: 992px) {

    /* :has limite l'écran figé à la seule page qui contient la grille, c'est-à-dire
       la liste des produits. Partout ailleurs la page défile normalement. */
    body:has(.grille-catalogue) {
        height: 100vh;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .app-navbar {
        flex-shrink: 0;
    }

    body:has(.grille-catalogue) .app-main {
        display: flex;
        flex-direction: column;
        flex: 1;
        min-height: 0;
    }

    /* stretch, et non le start du réglage de base : sinon les deux colonnes
       gardent leur hauteur naturelle et plus rien ne se contraint */
    .grille-catalogue {
        flex: 1;
        min-height: 0;
        align-items: stretch;
    }

    /* Le sticky n'a plus d'objet une fois la hauteur bornée */
    .panneau-filtres {
        position: static;
        min-height: 0;
        overflow-y: auto;
    }

    .grille-catalogue > .card {
        display: flex;
        flex-direction: column;
        min-height: 0;
    }

    /* Catalogue en cartes : même principe que la carte du tableau.
       Les cartes défilent, la pagination reste en bas. */
    .colonne-cartes {
        display: flex;
        flex-direction: column;
        min-height: 0;
    }

    .colonne-cartes > .row {
        flex: 1;
        min-height: 0;
        overflow-y: auto;

        /* les marges négatives de la .row déborderaient sur les côtés */
        overflow-x: hidden;

        /* Sans cela, les rangées de cartes se partagent toute la hauteur
           libre de la colonne : après un filtre qui ne laisse qu'un ou deux
           produits, la seule rangée restante s'étire jusqu'en bas de l'écran,
           et la carte suit (elle est en h-100). D'où le grand vide entre le
           prix et le bouton « Ajouter au panier », que le mt-auto pousse
           tout en bas. Ici les rangées gardent la hauteur de leur contenu et
           se rangent en haut ; le surplus reste de l'espace vide, pas de la
           carte étirée. align-items reste à stretch : dans une même rangée,
           les cartes s'alignent toujours sur la plus haute. */
        align-content: flex-start;
    }

    /* Les lignes défilent ici, la pagination du pied reste en place */
    .grille-catalogue .table-responsive {
        flex: 1;
        min-height: 0;
        overflow: auto;
    }

    /* En-tête maintenu pendant que les lignes défilent. Fond opaque obligatoire,
       sinon les lignes se voient au travers. */
    .grille-catalogue .table thead th {
        position: sticky;
        top: 0;
        z-index: 1;
        background-color: var(--bs-table-bg);
    }
}

/* ══════════════════════════════════════════════════════════════
   LES CHAMPS DE FORMULAIRE
   Zones de saisie, menus déroulants, cases à cocher.
   ══════════════════════════════════════════════════════════════ */

/* Champs de formulaire : même souci que .card/.table, plus une bordure trop
   proche du gris de fond pour être visible. Fond blanc + bordure plus marquée. */
.form-control,
.form-select {
    background-color: #ffffff;
    border-color: #d5dae9;
    border-radius: var(--rayon-champ);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color .15s ease, box-shadow .15s ease;
}

.form-control:hover:not(:focus),
.form-select:hover:not(:focus) {
    border-color: #c2c9de;
}

/* Même remarque pour les champs : le halo de focus et les cases cochées restaient
   au bleu vif. Le fond blanc est répété ici car .form-control:focus de Bootstrap
   le repasse à --bs-body-bg, qui est le gris de la page. */
/* Halo de focus resserré et plus transparent : il signale le champ actif
   sans le noyer dans une tache bleue */
.form-control:focus,
.form-select:focus {
    background-color: #ffffff;
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

.form-check-input:checked {
    background-color: var(--bs-primary);
    border-color: var(--bs-primary);
}

.form-check-input:focus {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

/* ══════════════════════════════════════════════════════════════
   LES BOUTONS
   Bouton principal bleu, bouton secondaire gris, bouton rouge de
   suppression, et les boutons en icône seule des tableaux.
   ══════════════════════════════════════════════════════════════ */

/* Forme commune à tous les boutons : coins accordés aux champs, et une
   transition qui couvre l'ombre et le léger soulèvement du survol */
.btn {
    --bs-btn-border-radius: var(--rayon-champ);
    font-weight: 500;
    transition: transform .12s ease, box-shadow .18s ease,
                background-color .15s ease, border-color .15s ease, color .15s ease;
}

/* Les boutons pleins portent une ombre teintée de leur propre couleur :
   une ombre grise sous un bouton coloré paraît toujours sale */
.btn-primary,
.btn-danger,
.btn-secondary {
    box-shadow: 0 6px 14px -9px rgba(58, 63, 81, .9);
}

.btn-primary {
    box-shadow: 0 6px 14px -9px rgba(var(--bs-primary-rgb), 1);
}

.btn-primary:hover,
.btn-danger:hover,
.btn-secondary:hover,
.btn-outline-secondary:hover {
    transform: translateY(-1px);
}

.btn-primary:hover {
    box-shadow: 0 10px 20px -10px rgba(var(--bs-primary-rgb), 1);
}

/* Le bouton redescend quand on appuie : le geste devient physique */
.btn:active {
    transform: translateY(0);
}

.btn:disabled,
.btn.disabled {
    box-shadow: none;
    transform: none;
}

/* Bootstrap fige les couleurs de .btn-* en dur à la compilation : contrairement
   aux badges ou à .text-primary, redéfinir --bs-primary ne les atteint pas.
   On réécrit donc leurs variables pour les ramener dans la palette du site. */
.btn-primary {
    --bs-btn-bg: var(--bs-primary);
    --bs-btn-border-color: var(--bs-primary);
    --bs-btn-hover-bg: #1f58ad;
    --bs-btn-hover-border-color: #1f58ad;
    --bs-btn-active-bg: var(--bs-primary-text-emphasis);
    --bs-btn-active-border-color: var(--bs-primary-text-emphasis);
    --bs-btn-disabled-bg: var(--bs-primary);
    --bs-btn-disabled-border-color: var(--bs-primary);
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

/* Gris bleuté, accordé aux gris du site (#3a3f51, #7d879e) plutôt qu'au gris
   neutre de Bootstrap, qui jurait à côté du bleu-violet */
.btn-secondary {
    --bs-btn-bg: #6b7488;
    --bs-btn-border-color: #6b7488;
    --bs-btn-hover-bg: #5d6577;
    --bs-btn-hover-border-color: #5d6577;
    --bs-btn-active-bg: #545b6b;
    --bs-btn-active-border-color: #545b6b;
    --bs-btn-disabled-bg: #6b7488;
    --bs-btn-disabled-border-color: #6b7488;
    --bs-btn-focus-shadow-rgb: 107, 116, 136;
}

.btn-outline-secondary {
    --bs-btn-color: #5c6273;
    --bs-btn-border-color: #c7cce0;
    --bs-btn-hover-bg: #6b7488;
    --bs-btn-hover-border-color: #6b7488;
    --bs-btn-active-bg: #545b6b;
    --bs-btn-active-border-color: #545b6b;
    --bs-btn-focus-shadow-rgb: 107, 116, 136;
}

/* Bouton de retour en arrière (« Continuer mes achats »). Clair et cerclé
   plutôt que gris plein : c'est une sortie de page, elle ne doit pas peser
   autant que l'action principale. La flèche glisse vers la gauche au survol,
   le geste du retour. */
.btn-gris {
    --bs-btn-color: #434a5f;
    --bs-btn-bg: #ffffff;
    --bs-btn-border-color: #ccd3e6;
    --bs-btn-hover-color: var(--bs-primary);
    --bs-btn-hover-bg: #eef2fb;
    --bs-btn-hover-border-color: #b9c6e4;
    --bs-btn-active-color: var(--bs-primary);
    --bs-btn-active-bg: #e3eafa;
    --bs-btn-active-border-color: #adbcdf;
    --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);

    display: inline-flex;
    align-items: center;
    border-radius: 999px;
    padding: .45rem 1.05rem;
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(58, 63, 81, .07);
}

.btn-gris:hover {
    box-shadow: 0 6px 14px -8px rgba(var(--bs-primary-rgb), .9);
}

/* La flèche recule d'un cheveu : le bouton dit où il mène */
.btn-gris .bi {
    transition: transform .15s ease;
}

.btn-gris:hover .bi {
    transform: translateX(-3px);
}

/* Rouge accordé au rose des alertes (#d9758a / #9d3b4d) : moins criard que celui
   de Bootstrap, assez soutenu pour rester lu comme une action destructrice */
.btn-danger {
    --bs-btn-bg: #c9455c;
    --bs-btn-border-color: #c9455c;
    --bs-btn-hover-bg: #b13d51;
    --bs-btn-hover-border-color: #b13d51;
    --bs-btn-active-bg: #9d3b4d;
    --bs-btn-active-border-color: #9d3b4d;
    --bs-btn-disabled-bg: #c9455c;
    --bs-btn-disabled-border-color: #c9455c;
    --bs-btn-focus-shadow-rgb: 201, 69, 92;
}

/* Actions de tableau en icônes seules (Modifier / Supprimer) */
.btn-icon {
    width: 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: .5rem;
    background-color: transparent;
    color: var(--bs-primary);
    box-shadow: none;
}

.btn-icon:hover,
.btn-icon:focus {
    background-color: rgba(var(--bs-primary-rgb), .10);
    color: var(--bs-primary-text-emphasis);
}

/* La variante rouge : retirer, supprimer. Discrète au repos — un fond
   plein sur chaque ligne d'un panier ferait une colonne de taches rouges —
   et franchement rouge dès qu'on la survole. */
.btn-icon-danger {
    color: var(--bs-danger);
}

.btn-icon-danger:hover,
.btn-icon-danger:focus {
    background-color: rgba(var(--bs-danger-rgb), .12);
    color: var(--bs-danger-text-emphasis);
}

/* Actions de droite : icônes claires sur le bleu nuit de la barre,
   séparateur avant le compte */
.app-navbar .btn-icon {
    color: var(--barre-texte);
}

.app-navbar .btn-icon:hover,
.app-navbar .btn-icon:focus,
.app-navbar .btn-icon.active {
    background-color: var(--barre-survol);
    color: var(--barre-texte-vif);
}

.app-navbar-divider {
    width: 1px;
    height: 1.5rem;
    background-color: rgba(255, 255, 255, .16);
}

/* Bascule Connexion / Inscription (page d'authentification) */
#onglet-connexion,
#onglet-inscription {
    color: var(--bs-secondary-color);
    background-color: transparent;
    border: none;
}

#onglet-connexion.active,
#onglet-inscription.active {
    background-color: #ffffff;
    color: var(--bs-body-color);
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .06);
}

/* ══════════════════════════════════════════════════════════════
   LE CHAMP MOT DE PASSE ET SON ŒIL
   Le bouton d'affichage posé à l'intérieur du champ.
   ══════════════════════════════════════════════════════════════ */

/* Champ mot de passe : le bouton « afficher / masquer » se pose dans le champ,
   sans bordure ni fond, pour ne pas alourdir le formulaire (page Mon profil) */
.champ-mdp {
    position: relative;
}

.champ-mdp .form-control {
    padding-right: 2.5rem;
}

/* Sinon l'icône rouge de validation Bootstrap se superpose à l'œil */
.champ-mdp .form-control.is-invalid {
    background-image: none;
}

.champ-mdp .bascule-mdp {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 2.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    background-color: transparent;
    color: #6b7488;
    border-radius: 0 .375rem .375rem 0;
    transition: color .15s ease-in-out;
}

.champ-mdp .bascule-mdp:hover {
    color: var(--bs-body-color);
}

.champ-mdp .bascule-mdp:focus-visible {
    color: var(--bs-body-color);
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

/* ══════════════════════════════════════════════════════════════
   LE PANIER ET LES QUANTITÉS
   La pastille de l'icône panier, le champ « combien j'en veux »
   et ses boutons − et +.
   ══════════════════════════════════════════════════════════════ */

/* Pastille du nombre d'articles, sur l'icône panier de la barre du haut.
   Alignée sur le haut de l'icône (et non centrée sur son coin, sinon elle
   dépasse au-dessus d'une barre aussi basse).
   Sélecteur en deux parties : Bootstrap applique « .btn .badge » aux badges
   posés dans un bouton, et une classe seule ne ferait pas le poids. */
.app-navbar .badge-panier {
    position: absolute;
    top: 0;
    left: 100%;
    transform: translateX(-70%);

    /* Compteur de notification et non pastille d'état : elle garde le fond
       plein, et la largeur plancher des badges ne s'applique pas ici */
    min-width: 0;
    font-size: .74rem;
    line-height: 1;
    padding: .25rem .35rem;
}

.modal-content {
  --bs-modal-border-radius: 0rem;
}

/* Champ « combien j'en veux » de la liste des produits */
.quantite-panier {
    width: 2.75rem;
    padding: .15rem .25rem;
    font-size: .8rem;
    text-align: center;
    appearance: textfield;
}

/* Les flèches natives élargiraient le champ */
.quantite-panier::-webkit-inner-spin-button,
.quantite-panier::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Boutons − et + encadrant le champ quantité */
.btn-pas {
    width: 1.4rem;
    height: 1.6rem;
    padding: 0;
    border: 1px solid var(--bs-border-color);
    border-radius: .25rem;
    background-color: #ffffff;
    color: #6c7280;
    font-size: .8rem;
    line-height: 1;
}

.btn-pas:hover:not(:disabled) {
    background-color: #f4f5fa;
    color: var(--bs-body-color);
}

.btn-pas:disabled {
    opacity: .4;
}

/* ─── Les trois commandes soudées en un seul bloc ───
   Un bouton, un champ et un bouton posés côte à côte se lisent comme
   trois objets sans rapport. Réunis dans un cadre blanc à coins arrondis,
   ils forment un seul réglage, qu'on repère d'un coup d'œil. */
.quantite-groupe {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--rayon-champ);
    background-color: #ffffff;

    /* Un creux très léger : le bloc paraît enfoncé, donc réglable */
    box-shadow: inset 0 1px 2px rgba(58, 63, 81, .05);

    /* Les angles des enfants dépasseraient du cadre arrondi */
    overflow: hidden;
}

/* Dans le bloc, les boutons abandonnent leur cadre et leur fond : le
   cadre du groupe et les deux filets du champ les délimitent déjà */
.quantite-groupe .btn-pas {
    border: 0;
    border-radius: 0;
    background-color: transparent;
    font-weight: 600;
}

/* Au survol, le bleu du site plutôt qu'un gris : la commande répond */
.quantite-groupe .btn-pas:hover:not(:disabled) {
    background-color: rgba(var(--bs-primary-rgb), .10);
    color: var(--bs-primary);
}

.quantite-groupe .btn-pas:active:not(:disabled) {
    background-color: rgba(var(--bs-primary-rgb), .18);
}

.quantite-groupe .btn-pas:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: -2px;
}

/* Le champ n'est plus une boîte de saisie mais le centre du bloc :
   deux filets le séparent des boutons, rien de plus */
.quantite-groupe .quantite-panier {
    border: 0;
    border-right: 1px solid var(--filet-tableau);
    border-left: 1px solid var(--filet-tableau);
    border-radius: 0;
    background-color: transparent;
    font-weight: 600;
    color: var(--bs-body-color);
    box-shadow: none;
}

/* Le halo bleu de Bootstrap déborderait du cadre : le fond teinté le
   remplace, et c'est le groupe entier qui porte le halo */
.quantite-groupe .quantite-panier:focus {
    background-color: rgba(var(--bs-primary-rgb), .07);
    box-shadow: none;
}

.quantite-groupe:focus-within {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

/* Bloc entier grisé quand le produit ne peut pas être commandé */
.quantite-groupe:has(.quantite-panier:disabled) {
    background-color: #f7f8fc;
}

/* ─── Le réglage sur une carte du catalogue ───
   Un panneau clair qui porte l'intitulé et le bloc : posé juste au-dessus
   du bouton bleu, il annonce qu'on choisit une quantité avant d'ajouter.
   Le dégradé est celui du visuel des fiches produit, pour rester dans la
   même famille que le reste du site. */
.quantite-carte {
    padding: .4rem .4rem .4rem .75rem;
    border: 1px solid #ccd7ec;
    border-radius: var(--rayon-champ);
    background: linear-gradient(160deg, #e4ebf8, #cfdaf0);
}

/* Même traitement que les en-têtes de tableau et les intitulés de fiche */
.quantite-carte-libelle {
    font-size: .76rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bs-secondary-color);
}

/* Les mêmes commandes, sur une carte du catalogue : un peu plus grandes,
   la carte a la place et on vise plus facilement au doigt */
.quantite-carte .btn-pas {
    width: 2rem;
    height: 2rem;
    font-size: 1rem;
}

.quantite-carte .quantite-panier {
    width: 3.25rem;
    height: 2rem;
    font-size: .9rem;
}

/* Sur la carte, le bloc est posé sur le panneau : il garde son relief
   vers l'extérieur, et non le creux de la version compacte */
.quantite-carte .quantite-groupe {
    background-color: #ffffff;
    box-shadow: 0 1px 2px rgba(58, 63, 81, .06);
}


/* ══════════════════════════════════════════════════════════════
   LA FICHE DÉTAILLÉE D'UN PRODUIT
   La fenêtre qui s'ouvre en cliquant sur une ligne du catalogue.
   ══════════════════════════════════════════════════════════════ */

/* Lignes de la liste des produits : le clic ouvre la fiche détaillée */
.ligne-produit {
    cursor: pointer;
}

/* Fiche détaillée d'un produit */
.fiche-produit {
    border: 0;
    border-radius: var(--rayon-carte);
    overflow: hidden;
    box-shadow: 0 1.5rem 3rem rgba(58, 63, 81, .2);
}

.fiche-produit-fermer {
    position: absolute;
    top: .75rem;
    right: .75rem;
    z-index: 2;
    padding: .6rem;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, .85);
}

/* Le visuel occupe toute la hauteur de la colonne de gauche */
.fiche-produit-visuel {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    min-width: 0;
    overflow: hidden;
    background: linear-gradient(160deg, #f7f8fc, #e6ebf7);
}

/* La photo reste dans sa colonne : une image large était bornée en hauteur
   seulement, donc elle débordait sur la marge et touchait le bord de la fiche */
.fiche-produit-visuel img {
    max-width: 100%;
    max-height: 15rem;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: calc(var(--rayon-carte) / 2);
    filter: drop-shadow(0 .75rem 1.25rem rgba(58, 63, 81, .18));
}

/* Même traitement que les en-têtes de tableau, pour les intitulés de la fiche */
.fiche-produit-etiquette {
    font-size: .76rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--bs-secondary-color);
    font-weight: 600;
}

.fiche-produit-prix {
    font-size: 1.6rem;
    font-weight: 600;
    line-height: 1;
}

.fiche-produit-meta {
    font-size: .875rem;
    border-top: 1px solid var(--bs-border-color);
}

.fiche-produit-meta > div {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: .5rem 0;
    border-bottom: 1px dashed var(--bs-border-color);
}

.fiche-produit-meta > div:last-child {
    border-bottom: 0;
}





/* ─── Le badge de stock, et son pop-up de dépôts ─── */

/* La boîte porte le <template> : le JS y retrouve le contenu du pop-up */
.badge-stock-boite {
    display: inline-block;
}

/* Un <button> déguisé en badge : bordure et fond d'origine effacés */
.badge-stock {
    border: 0;
    cursor: pointer;
    font: inherit;
    font-size: .75em;
    font-weight: 700;
    line-height: 1;
}

.badge-stock:hover {
    filter: brightness(1.06);
}

.badge-stock:focus-visible {
    outline: 2px solid var(--bs-primary);
    outline-offset: 2px;
}

.popover-depots {
    --bs-popover-max-width: 18rem;
    --bs-popover-border-color: var(--bordure-douce);
    --bs-popover-border-radius: var(--rayon-champ);
    --bs-popover-body-padding-x: .85rem;
    --bs-popover-body-padding-y: .7rem;
    box-shadow: var(--ombre-flottante);
}

.depots-titre {
    margin-bottom: .5rem;
    color: var(--bs-secondary-color);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.table-depots {
    width: 100%;
    font-size: .85rem;
}

.table-depots th {
    padding-bottom: .35rem;
    border-bottom: 1px solid var(--bs-border-color);
    color: var(--bs-secondary-color);
    font-size: .75rem;
    font-weight: 600;
}

.table-depots td {
    padding: .35rem 0;
}

.table-depots tbody tr + tr td {
    border-top: 1px solid var(--filet-tableau);
}

/* Les quantités alignées à droite, chiffres de même largeur */
.table-depots th:last-child,
.depots-qte {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.table-depots tfoot td {
    border-top: 1px solid var(--bs-border-color);
    font-weight: 700;
}

.depots-ecart {
    margin-top: .5rem;
    color: #9d3b4d;
    font-size: .75rem;
}


/* ══════════════════════════════════════════════════════════════
   LE MENU DU COMPTE
   Le bouton en haut à droite, avec l'initiale de la personne,
   et le menu déroulant qui en sort.
   ══════════════════════════════════════════════════════════════ */

/* Bouton du compte, dans la barre du haut */
.btn-compte {
    padding: .3rem .5rem;
    border: 0;
    border-radius: .5rem;
    background-color: transparent;
    line-height: 1.2;
}

.btn-compte:hover,
.btn-compte[aria-expanded="true"] {
    background-color: #f4f5fa;
}

/* Le survol reprend le bleu très pâle du reste de la barre */
.app-navbar .btn-compte:hover,
.app-navbar .btn-compte[aria-expanded="true"] {
    background-color: var(--barre-survol);
}

.nom-compte {
    font-size: .875rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--bs-body-color);
}

.btn-compte .bi-chevron-down {
    font-size: .7rem;
    color: var(--bs-secondary-color);
}

/* Pastille portant l'initiale du compte, dans le dégradé du logo */
.avatar-compte {
    width: 1.9rem;
    height: 1.9rem;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background-image: linear-gradient(135deg, var(--logo-bleu), var(--logo-cyan));
    color: #ffffff;
    font-size: .8rem;
    font-weight: 600;
}

/* Menus déroulants (menu principal et compte) : les variables du composant
   suffisent à les redimensionner */
.menu-nav,
.menu-compte {
    --bs-dropdown-font-size: .8125rem;
    --bs-dropdown-padding-x: .35rem;
    --bs-dropdown-padding-y: .35rem;
    --bs-dropdown-item-padding-y: .45rem;
    --bs-dropdown-item-padding-x: .6rem;
    --bs-dropdown-border-radius: .625rem;
    --bs-dropdown-border-color: var(--bs-border-color);
    --bs-dropdown-box-shadow: 0 .5rem 1.5rem rgba(58, 63, 81, .14);
    --bs-dropdown-link-hover-bg: #f4f5fa;
    --bs-dropdown-link-active-bg: rgba(var(--bs-primary-rgb), .10);
    --bs-dropdown-link-active-color: var(--bs-primary-text-emphasis);
    margin-top: .35rem;
}

.menu-nav {
    --bs-dropdown-min-width: 12.5rem;
}

.menu-compte {
    --bs-dropdown-min-width: 13rem;
}

.menu-nav .dropdown-item,
.menu-compte .dropdown-item {
    display: flex;
    align-items: center;
    border-radius: .375rem;
}

.menu-nav .dropdown-item i,
.menu-compte .dropdown-item i {
    color: #6b7488;
}

.menu-nav .dropdown-item.active i,
.menu-compte .dropdown-item.active i {
    color: var(--bs-primary-text-emphasis);
}

/* Rappel de qui est connecté, en tête du menu du compte */
.entete-compte {
    padding: .4rem .6rem .3rem;
    line-height: 1.35;
}

.entete-compte .nom-compte {
    display: block;
    font-size: .8125rem;
}

.entete-compte .text-secondary {
    font-size: .75rem;
}

/* ══════════════════════════════════════════════════════════════
   LA FENÊTRE D'AJOUT / MODIFICATION D'UN PRODUIT
   Le formulaire produit et sa zone photo, où l'on peut déposer
   une image par glisser-déposer.
   ══════════════════════════════════════════════════════════════ */

/* Le <form> de la modale produit enveloppe le corps et le pied : il s'intercale
   entre .modal-content et .modal-body et coupe la chaîne flex sur laquelle repose
   modal-dialog-scrollable. Sans ça, le corps ne se réduit pas et le pied — donc
   les boutons Annuler / Sauvegarder — sort de l'écran sur une fenêtre basse.
   min-height: 0 : sinon un enfant flex refuse de rétrécir sous sa taille naturelle. */
.modal-dialog-scrollable .modal-content > form {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

/* Le cadre qui porte la zone d'image et sa corbeille */
.zone-image-boite {
    position: relative;
}

/* La corbeille, posée dans le coin de la photo */
.zone-image-supprimer {
    position: absolute;
    top: .5rem;
    right: .5rem;
    background-color: #ffffff;
    border: 1px solid var(--bs-border-color);
    box-shadow: 0 1px 3px rgba(58, 63, 81, .12);
}

.zone-image-supprimer:hover,
.zone-image-supprimer:focus {
    background-color: var(--bs-danger);
    border-color: var(--bs-danger);
    color: #ffffff;
}

/* Zone d'image des formulaires produit : rectangle cliquable, « + » tant qu'aucune image */
.zone-image {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 11rem;
    padding: .5rem;
    border: 2px dashed #c7cce0;
    border-radius: .5rem;
    background-color: #ffffff;
    cursor: pointer;
    overflow: hidden;
}

/* Dans la colonne de droite du formulaire produit, la zone occupe toute la hauteur utile */
@media (min-width: 992px) {
    .zone-image-haute {
        height: 20rem;
    }
}

.zone-image:hover,
.zone-image.survol {
    border-color: var(--bs-primary);
    background-color: #f7f8fc;
}

.zone-image.est-invalide {
    border-color: var(--bs-danger);
}

.zone-image-invite {
    font-size: 1.75rem;
    line-height: 1;
    color: var(--bs-secondary-color);
}

.zone-image-apercu {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* ══════════════════════════════════════════════════════════════
   LES NUMÉROS DE PAGE
   La ligne 1 2 3 … sous les tableaux.
   ══════════════════════════════════════════════════════════════ */

/* Pagination : Bootstrap fige ses couleurs à la compilation, elle ignore
   --bs-primary. On repasse par ses variables pour retrouver la palette du thème. */
.pagination {
    --bs-pagination-padding-x: .5rem;
    --bs-pagination-padding-y: .25rem;
    --bs-pagination-font-size: .8125rem;
    --bs-pagination-color: var(--bs-secondary-color);
    --bs-pagination-bg: #ffffff;
    --bs-pagination-border-color: var(--bs-border-color);
    --bs-pagination-border-radius: .375rem;
    --bs-pagination-hover-color: var(--bs-body-color);
    --bs-pagination-hover-bg: #f4f5fa;
    --bs-pagination-hover-border-color: #c7cce0;
    --bs-pagination-focus-color: var(--bs-body-color);
    --bs-pagination-focus-bg: #f4f5fa;
    --bs-pagination-focus-box-shadow: 0 0 0 .2rem rgba(108, 127, 224, .2);
    --bs-pagination-active-color: #ffffff;
    --bs-pagination-active-bg: var(--bs-primary);
    --bs-pagination-active-border-color: var(--bs-primary);
    --bs-pagination-disabled-color: #b9bfd0;
    --bs-pagination-disabled-bg: #ffffff;
    --bs-pagination-disabled-border-color: var(--bs-border-color);

    gap: .25rem;
    margin-bottom: 0;
}

/* Pastilles autonomes plutôt que segments collés : Bootstrap chevauche les
   bordures et n'arrondit que les extrémités, on annule les deux. */
.pagination .page-item .page-link {
    min-width: 2rem;
    height: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-left: 0;
    border-radius: var(--bs-pagination-border-radius);
    font-weight: 500;
    transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}

.pagination .page-item.active .page-link {
    box-shadow: 0 .125rem .375rem rgba(108, 127, 224, .35);
}

/* Les « … » entre deux plages de pages : un repère, pas un bouton */
.pagination .page-item.disabled:not(:first-child):not(:last-child) .page-link {
    background-color: transparent;
    border-color: transparent;
}

/* Pied de carte : la pagination est rattachée au tableau qu'elle commande */
.card-footer {
    padding: .7rem 1.1rem;
    margin-top: 3px;
    background-color: #fbfcfe;
    border-top-color: var(--bordure-douce);
}

/* ══════════════════════════════════════════════════════════════
   LES CARTES DU CATALOGUE
   ══════════════════════════════════════════════════════════════ */

.carte-produit {
    cursor: pointer;
    transition: box-shadow .15s ease, transform .15s ease;
}

.carte-produit:hover {
    transform: translateY(-3px);
    border-color: #dfe3f0;
    box-shadow: var(--ombre-relief);
}

/* La photo est posée sur un fond neutre et reste entière */
.carte-produit-visuel {
    height: 190px;
    padding: .75rem;
    background-color: #f6f7fb;
    border-bottom: 1px solid var(--bordure-douce);
    /* Suit l'arrondi de la carte : un coin d'image plus carré que son
       cadre se voit immédiatement */
    border-radius: calc(var(--rayon-carte) - 1px) calc(var(--rayon-carte) - 1px) 0 0;

    /* filet de sécurité : quoi qu'il arrive, la photo ne déborde pas sur le texte */
    overflow: hidden;
}

/* width et height à 100 % plutôt que max-* : la contrainte ne dépend
   alors d'aucun calcul de contexte. contain garde la photo entière. */
.carte-produit-visuel img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

/* Un nom trop long est coupé par « … » : toutes les cartes gardent la même allure */
.carte-produit-nom {
    margin-bottom: .35rem;
    font-size: .95rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Le code article, juste sous le nom : plus petit, mais aussi lisible que lui */
.carte-produit-reference {
    margin-bottom: .4rem;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--bs-body-color);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Deux lignes de description au maximum, hauteur réservée pour aligner les prix */
.carte-produit-description {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: 2.5em;
    margin-bottom: .75rem;
    font-size: .8125rem;
    line-height: 1.25;
    color: var(--bs-secondary-color);
}

.carte-produit-prix {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem .5rem;
    margin-bottom: .9rem;
}

/* flex-basis à 100 % : le montant garde sa ligne pour lui seul, jamais coupé
   en deux, et les badges se rangent tous en dessous. Sans cela un badge long
   descend et un badge court reste à côté : les cartes d'une même rangée
   n'ont plus la même hauteur. */
.carte-produit-prix strong {
    flex: 1 0 100%;
    font-size: 1.05rem;
    letter-spacing: -.01em;
    white-space: nowrap;
}

/* Un badge tient sur une ligne, quitte à être large */
.carte-produit-prix .badge {
    white-space: nowrap;
}


/* ══════════════════════════════════════════════════════════════
   LES TABLEAUX DEVIENNENT DES FICHES SUR TÉLÉPHONE ET TABLETTE
   Sous 992 px, un tableau de 8 colonnes est illisible : on ne peut que
   le faire glisser de gauche à droite. Les tableaux marqués
   « table-cartes » se réorganisent donc en fiches empilées : une ligne
   = une fiche, et chaque cellule affiche « Intitulé ....... valeur ».
   L'intitulé vient de l'attribut data-libelle posé dans les vues.
   ══════════════════════════════════════════════════════════════ */

@media (max-width: 991.98px) {

    /* Les titres de colonnes n'ont plus de sens, sauf la ligne de
       recherche de la liste des utilisateurs, que l'on garde */
    .table-cartes thead tr:not(.ligne-filtres) {
        display: none;
    }

    /* La fiche n'a pas la largeur de quatre badges : ils reprennent un simple
       retour à la ligne, au fil du texte */
    .cellule-droits {
        display: flex;
        flex-wrap: wrap;
    }

    .table-cartes,
    .table-cartes tbody,
    .table-cartes tr,
    .table-cartes td {
        display: block;
        width: 100%;
    }

    /* Une fiche = une ligne du tableau.
       En colonne, pour pouvoir remonter l'image et le nom en tête de fiche
       (avec « order ») sans toucher à l'ordre des colonnes du tableau. */
    .table-cartes tbody tr {
        display: flex;
        flex-direction: column;
        padding: .85rem 1rem;
        border-bottom: 1px solid var(--bs-border-color);
    }

    .table-cartes tbody .cellule-image { order: -2; }
    .table-cartes tbody .cellule-nom   { order: -1; }

    .table-cartes tbody tr:last-child {
        border-bottom: 0;
    }

    /* Une cellule = « intitulé ....... valeur » */
    .table-cartes tbody td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        padding: .3rem 0;
        border: 0;
        text-align: right;

        /* annule les text-nowrap du tableau : ici on va à la ligne */
        white-space: normal;
    }

    .table-cartes tbody td::before {
        content: attr(data-libelle);
        flex: none;
        text-align: left;
        font-size: .7rem;
        font-weight: 600;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: var(--bs-secondary-color);
    }

    /* Le nom sert de titre à la fiche : pas d'intitulé, plus gros.
       La fiche occupe toute la largeur, le plafond du tableau n'a plus lieu d'être. */
    .table-cartes tbody .cellule-nom {
        justify-content: flex-start;
        text-align: left;
        font-size: 1rem;
        font-weight: 600;
        padding-bottom: .35rem;
        max-width: none;
    }

    .table-cartes tbody .cellule-nom::before {
        content: none;
    }

    /* L'image se place en haut de la fiche, un peu plus grande */
    .table-cartes tbody .cellule-image {
        justify-content: flex-start;
        padding-bottom: .5rem;
    }

    .table-cartes tbody .cellule-image::before {
        content: none;
    }

    .table-cartes tbody .cellule-image img {
        width: 64px;
        height: 64px;
    }

    /* Les boutons d'action se rangent en bas à droite de la fiche */
    .table-cartes tbody .cellule-actions {
        justify-content: flex-end;
        flex-wrap: wrap;
        padding-top: .6rem;
    }

    .table-cartes tbody .cellule-actions::before {
        content: none;
    }

    /* Le total en pied de tableau reprend la même présentation */
    .table-cartes tfoot,
    .table-cartes tfoot tr {
        display: block;
    }

    .table-cartes tfoot th {
        display: inline-block;
        width: auto;
        border: 0;
    }

    /* La ligne de recherche de la liste des utilisateurs : un champ par ligne */
    .table-cartes .ligne-filtres {
        display: block;
        padding: .75rem 1rem;
    }

    .table-cartes .ligne-filtres th {
        display: block;
        width: 100%;
        padding: .25rem 0;
        text-align: left;
    }

    /* Le message « aucun résultat » reste centré sur toute la largeur */
    .table-cartes tbody td[colspan]::before {
        content: none;
    }

    .table-cartes tbody td[colspan] {
        justify-content: center;
        text-align: center;
    }
}


/* ══════════════════════════════════════════════════════════════
   LE PANNEAU DE FILTRES SE REPLIE SUR TÉLÉPHONE
   Déplié, il mesure plus d'un écran et demi : sans repli, il faudrait
   le faire défiler en entier avant d'apercevoir le premier produit.
   Le bouton n'apparaît que sous 992 px ; au-dessus, tout reste ouvert.
   ══════════════════════════════════════════════════════════════ */

.bouton-filtres {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* La flèche pivote quand le panneau est ouvert */
.bouton-filtres .bi-chevron-down {
    transition: transform .2s ease;
}

.bouton-filtres[aria-expanded="true"] .bi-chevron-down {
    transform: rotate(180deg);
}

@media (max-width: 991.98px) {

    /* Le panneau n'est plus une colonne : il se pose au-dessus du tableau
       et ses facettes se rangent côte à côte quand la place le permet */
    .corps-filtres {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
    }
}


/* ══════════════════════════════════════════════════════════════
   ADAPTATION AUX PETITS ÉCRANS
   Ce qui change sur tablette et téléphone : le menu se replie,
   les éléments passent les uns sous les autres.
   ══════════════════════════════════════════════════════════════ */

/* Sous « lg », le menu se replie sous la barre : les entrées passent en colonne
   et les sous-menus s'affichent en retrait, sans cadre ni ombre. */
@media (max-width: 991.98px) {
    .app-navbar .navbar-collapse {
        padding-top: .5rem;
    }

    .app-navbar .navbar-nav {
        gap: .15rem;
    }

    .menu-nav {
        border: 0;
        box-shadow: none;
        margin-top: 0;
        padding-left: 1.75rem;
    }
}

/* ══════════════════════════════════════════════════════════════
   LA PAGE PANIERS DES CLIENTS (SUIVI)
   Les trois compteurs en haut, puis une carte par client : son nom
   en grand dans l'en-tête, et une ligne par produit en dessous.
   ══════════════════════════════════════════════════════════════ */

.suivi-compteurs {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

/* Un compteur : la pastille d'icône à gauche, le chiffre et son mot à droite */
.suivi-compteur {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: .6rem .9rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .6rem;
    background-color: #ffffff;
}

.suivi-compteur-icone {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    height: 2.2rem;
    flex-shrink: 0;
    border-radius: .5rem;
    background-color: rgba(var(--bs-primary-rgb), .1);
    color: var(--bs-primary);
    font-size: 1.05rem;
}

.suivi-compteur-valeur {
    font-size: 1.05rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
}

.suivi-compteur-libelle {
    font-size: .75rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

/* Le numéro de panier, présenté comme une référence de commande */
.suivi-reference {
    font-size: .85rem;
    font-weight: 600;
    color: var(--bs-primary);
}

/* Les pastilles d'état, en couleurs douces */
.badge-suivi {
    display: inline-block;
    padding: .3rem .7rem;
    border-radius: 999px;
    font-size: .76rem;
    font-weight: 600;
    white-space: nowrap;
}

.badge-suivi-bleu {
    background-color: rgba(var(--bs-primary-rgb), .12);
    color: var(--bs-primary-text-emphasis);
}

.badge-suivi-vert {
    background-color: #e3f7ed;
    color: #1a7f4b;
}

.badge-suivi-gris {
    background-color: #eceef4;
    color: var(--bs-secondary-color);
}

/* Livraison partielle : une étape entre le validé et le livré */
.badge-suivi-partiel {
    background-color: #fdf0dd;
    color: #8a5300;
}

/* Livré : l'état final d'une commande, le stock physique a été déduit */
.badge-suivi-livre {
    background-color: #e4f1fb;
    color: #0d5c8f;
}

/* En instance : la pastille clignote en rouge, c'est l'état qui demande une action */
.badge-suivi-instance {
    background-color: #fdeaea;
    color: #b02a2a;
    animation: badge-clignote 1.2s ease-in-out infinite;
}

@keyframes badge-clignote {
    50% { opacity: .35; }
}

/* Un clignotement permanent gêne la lecture de qui l'a demandé au système */
@media (prefers-reduced-motion: reduce) {
    .badge-suivi-instance {
        animation: none;
    }
}

/* ─── La carte d'un client ─── */

/* Les cartes du site sont volontairement plates (voir .card plus haut).
   Celle-ci fait exception : une ombre douce en deux couches, une très
   proche pour le relief, une large et diffuse pour le détacher du fond */
.panier-client {
    margin-bottom: 1.25rem;
    border: 1px solid var(--bordure-douce);
    border-radius: var(--rayon-carte);
    overflow: hidden;
    box-shadow: var(--ombre-douce);
    transition: box-shadow .18s ease, border-color .18s ease;
}

.panier-client:hover {
    border-color: #dfe3f0;
    box-shadow: var(--ombre-relief);
}

/* L'en-tête se distingue du corps par un fond très légèrement teinté,
   qui s'éclaircit vers le bas */
.panier-client-entete {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid #eef0f7;
    background-image: linear-gradient(180deg, #fbfcff, #ffffff);
}

.panier-client-identite {
    display: flex;
    align-items: center;
    gap: .9rem;
}

/* La pastille d'initiale, dans le dégradé du logo */
.panier-client-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.9rem;
    height: 2.9rem;
    flex-shrink: 0;
    border-radius: .8rem;
    background-image: linear-gradient(135deg, var(--logo-bleu), var(--logo-cyan));
    color: #ffffff;
    font-size: 1.15rem;
    font-weight: 700;
    box-shadow: 0 6px 14px -6px rgba(22, 112, 240, .55);
}

/* Le nom du client : c'est le titre de la carte, il doit se voir de loin.
   L'interlettrage resserré est ce qui donne son aplomb à un gros titre */
.panier-client-nom {
    margin: 0 0 .15rem;
    font-size: 1.3rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -.015em;
}

.panier-client-email {
    font-size: .82rem;
    color: var(--bs-secondary-color);
}

.panier-client-meta {
    text-align: right;
}

/* La ligne référence · date · état, au-dessus du montant */
.panier-client-repere {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    align-items: center;
    gap: .45rem;
    margin-bottom: .45rem;
}

/* La référence prend la forme d'une étiquette, et non d'un texte nu */
.suivi-reference {
    display: inline-block;
    padding: .18rem .5rem;
    border-radius: .4rem;
    background-color: rgba(var(--bs-primary-rgb), .08);
    font-size: .8rem;
    font-weight: 600;
    white-space: nowrap;
    color: var(--bs-primary-text-emphasis);
}

.panier-client-date {
    font-size: .78rem;
    color: var(--bs-secondary-color);
}

/* Les chiffres d'argent en chasse fixe : les montants d'une colonne
   s'alignent alors parfaitement, chiffre sous chiffre */
.chiffre,
.panier-client-montant {
    font-variant-numeric: tabular-nums;
}

.panier-client-montant {
    font-size: 1.45rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -.02em;
    white-space: nowrap;
}

.panier-client-compte {
    margin-top: .15rem;
    font-size: .78rem;
    color: var(--bs-secondary-color);
}

/* ─── Les lignes de produits ─── */

.panier-client-lignes thead th {
    padding: .6rem 1.5rem;
    border-bottom: 1px solid #eef0f7;
    background-color: #fafbfe;
    font-size: .74rem;
    font-weight: 600;
    color: #5f6981;
    text-transform: uppercase;
    letter-spacing: .07em;
}

.panier-client-lignes tbody td {
    padding: .85rem 1.5rem;
    border-bottom-color: #f1f3f9;
}

/* Pas de trait sous la dernière ligne : le tableau se termine
   proprement sur le bord de la carte */
.panier-client-lignes tbody tr:last-child td {
    border-bottom: 0;
}

.panier-client-lignes tbody tr:hover td {
    background-color: #fafbff;
}

.panier-produit {
    display: flex;
    align-items: center;
    gap: .9rem;
}

/* La vignette est posée dans un cadre clair avec une marge intérieure :
   la photo respire, et toutes les lignes ont la même hauteur */
.panier-produit-visuel {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    flex-shrink: 0;
    padding: .25rem;
    border: 1px solid #edeff6;
    border-radius: .65rem;
    background-color: #ffffff;
}

.panier-produit-visuel img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.panier-produit-nom {
    font-size: .95rem;
    font-weight: 600;
    line-height: 1.25;
}

/* Le code article devient une petite étiquette grise */
.panier-produit-code {
    display: inline-block;
    margin-top: .15rem;
    padding: .1rem .4rem;
    border-radius: .3rem;
    background-color: #f4f5fa;
    font-family: var(--bs-font-monospace);
    font-size: .7rem;
    color: var(--bs-secondary-color);
}

/* La quantité en pastille : elle se lit d'un coup d'œil dans la colonne */
.panier-quantite-pastille {
    display: inline-block;
    min-width: 2.5rem;
    padding: .2rem .55rem;
    border-radius: 999px;
    background-color: #f4f5fa;
    font-size: .8rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--bs-body-color);
}

.panier-ligne-montant {
    font-size: .98rem;
    font-weight: 700;
}

/* ─── L'assignation : représentant → client destinataire ───
   Une bande fine entre l'en-tête et les produits */
.panier-assignation {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem 1.25rem;
    padding: .8rem 1.5rem;
    border-bottom: 1px solid #eef0f7;
    background-color: #fafbfe;
}

.panier-assignation-bloc {
    display: flex;
    align-items: center;
    gap: .6rem;
    min-width: 0;
}

.panier-assignation-icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    flex-shrink: 0;
    border-radius: .55rem;
    background-color: rgba(var(--bs-primary-rgb), .1);
    color: var(--bs-primary-text-emphasis);
    font-size: .95rem;
}

.panier-assignation-libelle {
    font-size: .74rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--bs-secondary-color);
}

.panier-assignation-valeur {
    font-size: .88rem;
    font-weight: 600;
}

/* Le code, en étiquette comme le numéro de commande */
.panier-assignation-code {
    display: inline-block;
    margin-right: .3rem;
    padding: .08rem .4rem;
    border-radius: .35rem;
    background-color: #eceef4;
    font-family: var(--bs-font-monospace);
    font-size: .74rem;
    font-weight: 600;
}

.panier-assignation-fleche {
    color: var(--bs-secondary-color);
}

.panier-assignation-attente {
    font-weight: 500;
    font-style: italic;
    color: var(--bs-secondary-color);
}

/* Le point coloré devant l'état */
.badge-suivi .point {
    display: inline-block;
    width: .38rem;
    height: .38rem;
    margin-right: .35rem;
    border-radius: 50%;
    background-color: currentColor;
    vertical-align: middle;
}

/* Sur téléphone, l'en-tête passe en colonne et le montant se recale à gauche */
@media (max-width: 575.98px) {
    .panier-client-entete {
        padding: 1rem 1.1rem;
    }

    .panier-client-meta {
        text-align: left;
    }

    /* La flèche n'a plus de sens quand les deux blocs passent l'un sous l'autre */
    .panier-assignation {
        padding: .75rem 1.1rem;
    }

    .panier-assignation-fleche {
        display: none;
    }

    .panier-client-repere {
        justify-content: flex-start;
    }

    .panier-client-lignes thead th,
    .panier-client-lignes tbody td {
        padding-left: 1.1rem;
        padding-right: 1.1rem;
    }
}


/* ══════════════════════════════════════════════════════════════
   LA PAGE MON PANIER
   Deux colonnes : les articles à gauche, le récapitulatif à droite.
   Chaque article suit la même disposition : photo, description au
   milieu, prix calé à droite.
   ══════════════════════════════════════════════════════════════ */

/* L'en-tête de la carte des articles */
.panier-articles-entete {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: .75rem;
    margin-bottom: .25rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.panier-articles-compte {
    color: #434a5f;
    font-size: .78rem;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
}

/* Une ligne du panier : la photo, le corps, puis le montant.
   Chaque ligne est un bloc à part entière, qui se détache au survol. */
.panier-article {
    display: flex;
    gap: 1rem;
    padding: 1rem;
    border-radius: .65rem;
    transition: background-color .15s ease;
}

.panier-article + .panier-article {
    border-top: 1px solid var(--filet-tableau, var(--bs-border-color));
}

.panier-article:hover {
    background-color: var(--bs-tertiary-bg, #f7f8fc);
}

.panier-article-visuel {
    flex-shrink: 0;
    width: 96px;
    height: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: .35rem;
    border: 1px solid var(--bs-border-color);
    border-radius: .5rem;
    background-color: #ffffff;
}

.panier-article-visuel img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* min-width: 0 — sans lui, un nom de produit très long empêcherait
   la colonne de rétrécir et pousserait le prix hors de la carte */
.panier-article-corps {
    flex: 1;
    min-width: 0;
}

.panier-article-nom {
    font-size: 1.02rem;
    font-weight: 600;
    line-height: 1.3;
}

.panier-article-stock,
.panier-article-rupture {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .45rem;
    padding: .15rem .55rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 600;
}

.panier-article-stock {
    color: #06381f;
    background-color: #b6ddc8;
}

.panier-article-rupture {
    color: #611018;
    background-color: #f0bfc4;
}

.panier-article-commandes {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    margin-top: .85rem;
}

/* Le panneau de réglage d'une ligne : le même que sur les cartes du catalogue
   (.quantite-carte), posé dans la ligne et un peu plus compact */
.panier-reglage {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .3rem .3rem .3rem .65rem;
    margin: 0;
}

/* Seul le panneau du dépôt se borne à la largeur offerte : les deux autres
   gardent leur taille, leurs boutons ne supportent pas d'être rétrécis */
.panier-reglage:has(.depot-champ) {
    max-width: 100%;
    min-width: 0;
}

.panier-reglage .btn-pas {
    width: 1.8rem;
    height: 1.8rem;
    font-size: .95rem;
}

.panier-reglage .quantite-panier {
    width: 3rem;
    height: 1.8rem;
    font-size: .88rem;
}

/* Le bloc est posé sur le panneau : relief vers l'extérieur, comme sur la carte */
.panier-reglage .quantite-groupe {
    background-color: #ffffff;
    box-shadow: 0 1px 2px rgba(58, 63, 81, .06);
}

/* La remise : le champ et son « % » dans le même bloc blanc */
.panier-reglage .remise-champ {
    width: 3.6rem;
    text-align: right;
}

.remise-unite {
    display: inline-flex;
    align-items: center;
    padding: 0 .4rem;
    color: #434a5f;
    font-size: .82rem;
    font-weight: 600;
}

/* Le dépôt de retrait : une liste posée sur le panneau, au même relief
   que le bloc blanc de la quantité et de la remise */
.panier-reglage .depot-champ {
    width: auto;
    /* min-width: 0 autorise le rétrécissement sous la longueur du nom :
       sans lui, « DEPT SAPINO » pousse le panneau hors de la carte */
    min-width: 0;
    max-width: 100%;
    flex: 0 1 auto;
    text-overflow: ellipsis;
    height: 1.8rem;
    padding: 0 1.6rem 0 .5rem;
    border-color: var(--bs-border-color);
    border-radius: var(--rayon-champ);
    background-position: right .4rem center;
    font-size: .82rem;
    font-weight: 600;
    color: var(--bs-body-color);
    box-shadow: 0 1px 2px rgba(58, 63, 81, .06);
}

.panier-reglage .depot-champ:focus {
    border-color: var(--bs-primary);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}

/* La coche de validation, posée à côté du panneau */
.btn-icon-valider {
    color: #434a5f;
}

.btn-icon-valider:hover {
    color: var(--bs-primary);
}

/* Le trait vertical qui sépare la quantité du lien Supprimer */
.panier-separateur {
    width: 1px;
    height: 1.1rem;
    background-color: var(--bs-border-color);
}

/* La colonne de droite : le montant en haut, la corbeille en bas.
   space-between les écarte sur toute la hauteur de la ligne, si bien que la
   corbeille tombe d'elle-même à la hauteur des commandes de quantité, qui
   ferment la colonne du milieu. */
.panier-article-prix {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-between;
    gap: .5rem;
    text-align: right;
}

.panier-article-supprimer {
    flex-shrink: 0;
}

.panier-champ-libelle {
    color: #434a5f;
    font-size: .7rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.panier-article-montant {
    font-size: 1.15rem;
    font-weight: 700;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.panier-article-unitaire {
    color: var(--bs-secondary-color);
    font-size: .78rem;
    white-space: nowrap;
}

.panier-soustotal {
    padding-top: 1rem;
    border-top: 1px solid var(--bs-border-color);
    text-align: right;
    font-size: 1.05rem;
}

/* Le récapitulatif reste visible pendant qu'on fait défiler les articles.
   Le décalage laisse la place à la barre du haut, elle-même collante. */
@media (min-width: 992px) {
    .panier-recap {
        position: sticky;
        top: 5.5rem;
    }
}

.panier-recap-total {
    font-size: 1.1rem;
    margin-bottom: .9rem;
}

.panier-confirmer {
    padding-top: .6rem;
    padding-bottom: .6rem;
    font-weight: 600;
}

.panier-recap-note {
    margin: .6rem 0 0;
    font-size: .78rem;
    color: var(--bs-secondary-color);
    text-align: center;
}

.panier-recap-detail > div {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: .25rem 0;
    font-size: .88rem;
    color: var(--bs-secondary-color);
}

.panier-recap-detail-total {
    margin-top: .35rem;
    padding-top: .5rem !important;
    border-top: 1px solid var(--bs-border-color);
    font-size: 1rem !important;
    font-weight: 700;
    color: var(--bs-body-color) !important;
}

/* Le grand panier barré de la page vide */
.panier-vide-icone {
    font-size: 3rem;
    line-height: 1;
    color: var(--bs-secondary-color);
    opacity: .45;
}

/* Sur téléphone, la photo rétrécit et le prix passe sous la description */
@media (max-width: 575.98px) {
    .panier-article {
        flex-wrap: wrap;
        --photo-panier: 74px;
        --ecart-photo: 1rem;
    }

    .panier-article-visuel {
        width: var(--photo-panier);
        height: var(--photo-panier);
    }

    /* Les réglages repassent sous la photo et reprennent toute la largeur de
       la carte : dans la seule colonne du texte ils n'avaient pas la place et
       sortaient du cadre, boutons et noms de dépôt coupés */
    .panier-article-commandes {
        width: calc(100% + var(--photo-panier) + var(--ecart-photo));
        margin-left: calc(-1 * (var(--photo-panier) + var(--ecart-photo)));
    }

    /* Un réglage par rangée : intitulé à gauche, commandes à droite */
    .panier-article-commandes > .panier-reglage {
        display: flex;
        width: 100%;
    }

    .panier-article-commandes > .panier-reglage > .quantite-carte-libelle {
        margin-right: auto;
    }

    /* La liste prend la place laissée libre : le nom du dépôt tient en entier */
    .panier-reglage .depot-champ {
        flex: 1 1 auto;
    }

    /* La colonne du prix devient une rangée à elle seule, sous la description :
       le montant à gauche, la corbeille à l'autre bout. En colonne, elle
       écraserait le nom du produit, qui se couperait lettre par lettre. */
    .panier-article-prix {
        width: 100%;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        text-align: left;
    }
}

/* ══════════════════════════════════════════════════════════════
   LES FINITIONS COMMUNES
   Les derniers détails qui s'appliquent partout : fenêtres,
   pastilles, barres de défilement, contour de navigation au
   clavier. Rien ici ne concerne une page en particulier.
   ══════════════════════════════════════════════════════════════ */

/* ─── Les fenêtres modales ─── */

.modal-content {
    border: 0;
    border-radius: var(--rayon-carte);
    box-shadow: var(--ombre-flottante);
}

.modal-header,
.modal-footer {
    border-color: var(--bordure-douce);
}

.modal-header {
    padding: 1rem 1.25rem;
}

.modal-footer {
    padding: .85rem 1.25rem;
}

/* Le voile derrière la fenêtre : plus clair et légèrement bleuté,
   la page reste lisible au lieu de disparaître dans le noir */
.modal-backdrop.show {
    background-color: #2b3047;
    opacity: .42;
}

/* ─── Les pastilles ─── */

.badge {
    padding: .38em .65em;
    font-weight: 600;
    letter-spacing: .01em;

    /* Une largeur plancher : sans elle, « 3 » donne un rond et « 128 » un
       ovale allongé. Les pastilles d'une même colonne gardent la même forme. */
    min-width: 2.3rem;
    font-variant-numeric: tabular-nums;
}

/* Les pastilles d'état (stock, expiration, compte en attente) reprennent le
   principe des bandeaux d'alerte : fond très pâle et texte soutenu, plutôt
   que le vert et le jaune pleins de Bootstrap, trop criards à côté du
   bleu-violet du site. Le sens reste lisible, le contraste du texte aussi. */
/* !important est indispensable : .text-bg-* sont des classes utilitaires
   de Bootstrap, qui posent leurs couleurs en !important. Sans lui, la
   règle ci-dessous serait ignorée quelle que soit sa spécificité. */
.badge.text-bg-success {
    background-color: #e3f7ed !important;
    color: #1a7f4b !important;
}

.badge.text-bg-warning {
    background-color: #fdf1da !important;
    color: #8f6310 !important;
}

.badge.text-bg-danger {
    background-color: #fdeaee !important;
    color: #a83b50 !important;
}


/* ─── Les menus déroulants ─── */

.dropdown-menu {
    --bs-dropdown-border-color: var(--bordure-douce);
    --bs-dropdown-box-shadow: var(--ombre-flottante);
    --bs-dropdown-border-radius: .75rem;
}

/* ─── Les barres de défilement ─── */
/* Les barres du système sont larges et grises ; les affiner et les
   teinter est l'un des détails qui se remarquent le moins, et qui
   change pourtant l'impression d'ensemble. */

* {
    scrollbar-width: thin;
    scrollbar-color: #cfd5e6 transparent;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

/* La bordure transparente + background-clip amincissent la poignée
   tout en gardant une zone de clic confortable */
::-webkit-scrollbar-thumb {
    border: 3px solid transparent;
    border-radius: 999px;
    background-color: #cfd5e6;
    background-clip: content-box;
}

::-webkit-scrollbar-thumb:hover {
    background-color: #b9c0d6;
}

/* ─── La navigation au clavier ─── */
/* Un contour net pour qui se déplace à la touche Tab. Les éléments qui
   ont déjà leur propre halo (boutons, champs, pagination) en sont
   exclus, sinon les deux se superposeraient. */

a:focus-visible,
.dropdown-item:focus-visible,
.btn-compte:focus-visible,
.navbar-toggler:focus-visible {
    outline: 2px solid rgba(var(--bs-primary-rgb), .55);
    outline-offset: 2px;
    border-radius: .35rem;
}

/* ─── Les animations, en option ─── */
/* Certaines personnes règlent leur système pour supprimer les animations
   (mal des transports, troubles vestibulaires). On respecte ce choix :
   les couleurs continuent de changer, plus rien ne bouge. */

@media (prefers-reduced-motion: reduce) {
    .btn,
    .btn:hover,
    .carte-produit,
    .carte-produit:hover,
    .panier-client {
        transform: none;
        transition-property: background-color, border-color, color, box-shadow;
    }
}

/* ─── La pastille verte de la fenêtre « Panier validé » ─── */
/* Le halo est fait d'une ombre portée sans flou : un seul élément
   suffit à dessiner le cercle clair autour de la pastille. */
.fenetre-succes-icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 50%;
    background-color: #d8f2e4;
    color: #1a7f4b;
    font-size: 1.75rem;
    box-shadow: 0 0 0 .5rem #eef8f2;
}

/* ─── Un article du panier devenu inachetable ─── */
/* La ligne est estompée et sa photo décolorée : elle reste lisible, mais
   se distingue au premier coup d'œil des articles qui, eux, partiront. */
.panier-article-bloque .panier-article-visuel img {
    filter: grayscale(1);
    opacity: .55;
}

.panier-article-bloque .panier-article-nom,
.panier-article-bloque .panier-article-prix {
    opacity: .6;
}

.panier-article-bloque .panier-article-visuel {
    border-color: #f3d9df;
}

/* ═══════════════════════════════════════════════════════════════
   LE CODE PROFIL, DANS LA CRÉATION D'UN COMPTE
   Le champ du code, sans cadre ni aplat.
   ═══════════════════════════════════════════════════════════════ */

/* Un code se lit caractère par caractère : chasse fixe et lettres espacées */
.bloc-code-profil #code_profil {
    font-family: var(--bs-font-monospace);
    letter-spacing: .06em;
    text-transform: uppercase;
}

.bloc-code-profil #code_profil::placeholder {
    font-family: var(--bs-body-font-family);
    letter-spacing: normal;
    text-transform: none;
}

/* L'écart entre le code représentant et le code client, seulement quand les deux sont affichés :
   seul, le champ client doit rester aligné sur la colonne voisine */
.bloc-code-representant + .bloc-code-client {
    margin-top: 1rem;
}



/* ══════════════════════════════════════════════════════════════
   LE TABLEAU DE BORD (préfixe tdb-)
   ══════════════════════════════════════════════════════════════ */

/* ─── Les quatre chiffres clés ─── */
.tdb-tuiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 1rem;
    margin-bottom: 1rem;
}

.tdb-tuile {
    padding: 1.1rem 1.25rem;
    border: 1px solid var(--bordure-douce);
    border-radius: var(--rayon-carte);
    background-color: #ffffff;
    box-shadow: var(--ombre-douce);
}

.tdb-tuile-entete {
    display: flex;
    align-items: center;
    gap: .55rem;
    font-size: .8rem;
    font-weight: 600;
    color: var(--bs-secondary-color);
}

.tdb-tuile-icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: .5rem;
    background-color: rgba(var(--bs-primary-rgb), .1);
    color: var(--bs-primary-text-emphasis);
}

/* Le chiffre : c'est lui qu'on lit en premier */
.tdb-tuile-valeur {
    margin-top: .6rem;
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.tdb-tuile-detail {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .75rem;
    margin-top: .35rem;
    font-size: .78rem;
    color: var(--bs-secondary-color);
}

/* ─── Les titres de carte ─── */
.tdb-titre {
    margin: 0 0 .15rem;
    font-size: 1rem;
    font-weight: 700;
}

.tdb-sous-titre {
    margin-bottom: 1rem;
    font-size: .78rem;
    color: var(--bs-secondary-color);
}

.tdb-lien {
    font-size: .8rem;
    font-weight: 600;
    text-decoration: none;
}

.tdb-vide {
    margin: 1rem 0 0;
    font-size: .85rem;
    color: var(--bs-secondary-color);
}

/* ─── Une ligne de classement : le nom, le chiffre, puis la barre ─── */
.tdb-rang {
    padding: .5rem 0;
}

.tdb-rang + .tdb-rang {
    border-top: 1px solid var(--filet-tableau);
}

.tdb-rang-texte {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .75rem;
    font-size: .85rem;
}

.tdb-rang-nom {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tdb-rang-valeur {
    flex-shrink: 0;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.tdb-rang-detail {
    margin-top: .15rem;
    font-size: .74rem;
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
}

/* La barre : un rail très clair, et la part pleine dans la couleur du site */
.tdb-barre {
    height: 6px;
    margin-top: .35rem;
    border-radius: 999px;
    background-color: #eef0f7;
    overflow: hidden;
}

.tdb-barre span {
    display: block;
    width: var(--largeur, 0);
    height: 100%;
    min-width: 4px;
    border-radius: 999px;
    background-color: var(--bs-primary);
    transition: width .9s cubic-bezier(.22, .61, .36, 1);
    transition-delay: calc(.35s + var(--i, 0) * 90ms);
}

/* Au chargement, le script pose .tdb-depart puis la retire : les barres
   partent de 0 et grandissent jusqu'à --largeur */
.tdb-depart .tdb-barre span {
    width: 0;
    transition: none;
}

.tdb-rang:hover .tdb-barre span {
    background-color: var(--bs-primary-text-emphasis);
}

/* ─── Les états de stock : toujours une icône et un mot ─── */
.tdb-etat {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: .76rem;
    font-weight: 600;
    white-space: nowrap;
}

.tdb-etat-rupture {
    color: #b4233c;
}

.tdb-etat-faible {
    color: #a15c00;
}

/* ─── La liste des produits à réapprovisionner ─── */
.tdb-stock {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: .75rem;
    padding: .55rem 0;
    font-size: .85rem;
}

.tdb-stock + .tdb-stock {
    border-top: 1px solid var(--filet-tableau);
}

.tdb-stock-nom {
    min-width: 0;
}

.tdb-stock-nom a {
    color: inherit;
    font-weight: 600;
    text-decoration: none;
}

.tdb-stock-nom a:hover {
    color: var(--bs-primary-text-emphasis);
}

.tdb-stock-nom .panier-produit-code {
    display: table;   /* sous le nom, mais juste à la taille du code */
    margin-top: .15rem;
}

.tdb-table {
    margin-top: .5rem;
    font-size: .85rem;
}


/* ─── Le tableau de bord animé : couleurs des graphiques ───
   Anneau des statuts : bleu du site, orange, prune, vert d'eau (validés
   ensemble, lisibles aussi pour les daltoniens). Anneau du stock : couleurs
   d'état, toujours accompagnées d'une icône et d'un mot dans la légende. */
.tdb {
    --tdb-c1: #2668c4;
    --tdb-c2: #eb6834;
    --tdb-c4: #9b5bb8;
    --tdb-c3: #1baf7a;
    --tdb-c5: #7a8798;
    --tdb-bon: #0ca30c;
    --tdb-alerte: #fab219;
    --tdb-critique: #d03b3b;
    --tdb-grille: #edf0f6;
    --tdb-encre-discrete: #6b7488;
}

/* Les cartes montent doucement, l'une après l'autre */
.tdb-apparait {
    animation: tdb-apparition .55s cubic-bezier(.22, .61, .36, 1) both;
    animation-delay: calc(var(--i, 0) * 70ms);
}

@keyframes tdb-apparition {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}

.tdb-tuile {
    transition: transform .2s ease, box-shadow .2s ease;
}

.tdb-tuile:hover {
    transform: translateY(-2px);
    box-shadow: var(--ombre-relief);
}

.tdb-carte-entete {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: .5rem 1rem;
}

.tdb-resume {
    text-align: right;
}

.tdb-resume-valeur {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -.01em;
}

.tdb-resume-libelle {
    font-size: .75rem;
    color: var(--bs-secondary-color);
}

.tdb-titre-petit {
    margin-top: 1.25rem;
    font-size: .9rem;
}

/* ─── La courbe ─── */
.tdb-courbe {
    position: relative;
    height: 230px;
}

.tdb-courbe svg {
    display: block;
    overflow: visible;
}

.tdb-grille {
    stroke: var(--tdb-grille);
    stroke-width: 1;
}

.tdb-axe {
    stroke: #d5dae6;
    stroke-width: 1;
}

.tdb-graduation {
    font-size: 11px;
    fill: var(--tdb-encre-discrete);
    font-variant-numeric: tabular-nums;
}

.tdb-courbe-ligne {
    fill: none;
    stroke: var(--tdb-c1);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.tdb-courbe-surface {
    fill: var(--tdb-c1);
    fill-opacity: .1;
}

/* Le point : couleur de la série, anneau blanc de 2 px */
.tdb-courbe-point {
    fill: var(--tdb-c1);
    stroke: #ffffff;
    stroke-width: 2;
}

.tdb-courbe-etiquette {
    font-size: 11px;
    font-weight: 700;
    fill: var(--bs-body-color);
}

.tdb-repere {
    stroke: #c5cbd9;
    stroke-width: 1;
}

.tdb-capteur {
    fill: transparent;
    cursor: crosshair;
    outline: none;
}

.tdb-capteur:focus-visible {
    stroke: var(--bs-primary);
    stroke-width: 1;
}

.tdb-bulle {
    position: absolute;
    z-index: 2;
    padding: .45rem .65rem;
    border: 1px solid var(--bordure-douce);
    border-radius: .55rem;
    background-color: #ffffff;
    box-shadow: var(--ombre-flottante);
    pointer-events: none;
    white-space: nowrap;
}

.tdb-bulle-valeur {
    font-size: .9rem;
    font-weight: 700;
}

.tdb-bulle-libelle {
    font-size: .76rem;
    color: var(--bs-secondary-color);
}

.tdb-details {
    margin-top: .75rem;
    font-size: .82rem;
}

.tdb-details summary {
    color: var(--bs-primary-text-emphasis);
    font-weight: 600;
    cursor: pointer;
}

/* ─── Les anneaux ─── */
.tdb-anneau-bloc {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.25rem;
}

.tdb-anneau {
    position: relative;
    width: 168px;
    height: 168px;
    flex-shrink: 0;
    margin: 0 auto;
}

.tdb-anneau svg {
    width: 100%;
    height: 100%;
    overflow: visible;
}

.tdb-anneau-rail {
    fill: none;
    stroke: #f0f2f8;
    stroke-width: 14;
}

.tdb-anneau-segment {
    fill: none;
    stroke-width: 14;
    stroke-dasharray: 0 400;
    cursor: pointer;
    transition: stroke-width .2s ease, opacity .2s ease;
}

.tdb-anneau-segment.actif {
    stroke-width: 18;
}

.tdb-anneau-segment.attenue {
    opacity: .35;
}

.tdb-anneau-centre {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 0 30px;
    text-align: center;
    pointer-events: none;
}

.tdb-anneau-valeur {
    font-size: 1.7rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -.02em;
}

.tdb-anneau-libelle {
    font-size: .76rem;
    line-height: 1.25;
    color: var(--bs-secondary-color);
}

.tdb-legende {
    flex: 1 1 180px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.tdb-legende-ligne {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .5rem;
    border-radius: .5rem;
    font-size: .84rem;
    cursor: default;
    outline: none;
    transition: background-color .15s ease;
}

.tdb-legende-ligne.actif,
.tdb-legende-ligne:focus-visible {
    background-color: #f4f6fb;
}

.tdb-legende-pastille {
    width: .7rem;
    height: .7rem;
    flex-shrink: 0;
    border-radius: .2rem;
}

.tdb-legende-icone {
    color: var(--tdb-encre-discrete);
    font-size: .8rem;
}

.tdb-legende-nom {
    flex: 1;
}

.tdb-legende-valeur {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.tdb-legende-part {
    min-width: 2.6rem;
    text-align: right;
    font-size: .75rem;
    color: var(--bs-secondary-color);
    font-variant-numeric: tabular-nums;
}

/* Réglage système « réduire les animations » : tout s'affiche directement */
@media (prefers-reduced-motion: reduce) {
    .tdb-apparait,
    .tdb-barre span,
    .tdb-tuile,
    .tdb-anneau-segment {
        animation: none;
        transition: none;
    }
}


/* Recherche en direct : pendant le chargement, les résultats précédents
   restent affichés en plus pâle — pas de clignotement, pas de saut */
.recherche-en-cours {
    opacity: .5;
    transition: opacity .15s ease;
}


/* ══════════════════════════════════════════════════════════════
   LES PHOTOS RESTENT ENTIÈRES
   Une pièce détachée photographiée en longueur ne doit jamais être
   rognée : on voit la pièce en entier, quitte à laisser de l'air
   autour. « contain » montre tout, « cover » remplit et coupe.
   ══════════════════════════════════════════════════════════════ */
.vignette-produit {
    object-fit: contain;
    background-color: #f6f7fb;
    padding: 2px;
}

/* ══════════════════════════════════════════════════════════════
   TÉLÉPHONE : LE CATALOGUE EN CARTES
   Une carte par ligne : la photo a toute la largeur, on lui donne donc
   plus de hauteur, et le nom peut tenir sur deux lignes au lieu d'être
   coupé par « … ».
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 575.98px) {
    .carte-produit-visuel {
        height: 240px;
        padding: .9rem;
    }

    /* Le nom entier, sur deux lignes au maximum */
    .carte-produit-nom {
        white-space: normal;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        -webkit-box-orient: vertical;
        font-size: 1rem;
        line-height: 1.3;
    }

    .carte-produit-prix strong {
        font-size: 1.15rem;
    }

    /* Les boutons prennent toute la largeur : cible confortable au doigt */
    .carte-produit .card-body .btn,
    .carte-produit .card-body form .btn {
        width: 100%;
    }

    /* La fiche détaillée : la photo garde une hauteur raisonnable */
    .fiche-produit-visuel {
        padding: 1.25rem;
    }

    .fiche-produit-visuel img {
        max-height: 11rem;
    }
}

/* ══════════════════════════════════════════════════════════════
   TÉLÉPHONE : DES CIBLES CONFORTABLES AU DOIGT
   Un bouton de moins de 40 px se rate une fois sur deux. On agrandit
   aussi la vignette des fiches produit, trop petite sur petit écran.
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 575.98px) {
    .btn-icon,
    .btn-pas {
        min-width: 2.5rem;
        min-height: 2.5rem;
    }

    /* Le champ suit les boutons agrandis, sinon les deux filets qui
       l'encadrent s'arrêtent avant le bord du bloc */
    .quantite-groupe .quantite-panier {
        height: auto;
        min-height: 2.5rem;
    }

    /* Les actions d'une fiche : espacées, alignées à droite */
    .table-cartes tbody .cellule-actions .d-flex {
        gap: .5rem !important;
    }

    /* La vignette du produit, dans la liste devenue fiche */
    .table-cartes tbody .cellule-image img {
        width: 4.5rem !important;
        height: 4.5rem !important;
    }

    /* La photo d'un article du panier suit le même principe */
    .panier-article-visuel img {
        object-fit: contain;
    }
}


/* ══════════════════════════════════════════════════════════════
   TÉLÉPHONE ET TABLETTE : LE PANNEAU DE FILTRES
   Sous 992 px il se replie derrière un bouton. Tout y est agrandi :
   une case à cocher de 13 px et une ligne de 24 px de haut se ratent
   au doigt.
   ══════════════════════════════════════════════════════════════ */
@media (max-width: 991.98px) {

    /* Le bouton « Filtres » devient une vraie barre cliquable */
    .bouton-filtres {
        min-height: 3rem;
        padding: .75rem .875rem;
        border-radius: var(--rayon-champ);
        background-color: #f4f6fb;
        font-size: .82rem;
    }

    .bouton-filtres[aria-expanded="true"] {
        background-color: rgba(var(--bs-primary-rgb), .10);
        color: var(--bs-primary-text-emphasis);
    }

    .bloc-filtre:has(.bouton-filtres) {
        padding: .5rem;
        border-top: 0;
    }

    /* Les titres de facettes : plus hauts et plus lisibles */
    .bloc-filtre-titre {
        min-height: 3rem;
        font-size: .82rem;
    }

    /* Une option = une ligne confortable, cochable partout sur la ligne */
    .option-filtre {
        min-height: 2.75rem;
        padding: .45rem .25rem;
        gap: .75rem;
        font-size: .95rem;
    }

    .option-filtre .form-check-input {
        width: 1.25rem;
        height: 1.25rem;
        margin-top: 0;
        flex-shrink: 0;
    }

    .option-filtre-compte {
        font-size: .85rem;
    }

    /* Les étiquettes des filtres posés, plus faciles à retirer */
    .puce-filtre {
        padding: .4rem .7rem;
        font-size: .85rem;
    }

    .puce-filtre .bi-x-lg {
        font-size: .75rem;
        opacity: .9;
    }

    .filtres-vider {
        font-size: .85rem;
    }

    /* Les champs de la recherche avancée : taille de doigt, et 16px de
       police pour que l'iPhone n'agrandisse pas la page à la saisie */
    .bloc-filtre-corps .form-control-sm {
        min-height: 2.75rem;
        font-size: 1rem;
    }

    .etiquette-filtre {
        font-size: .85rem;
    }

    /* Le bouton qui referme le panneau, en bas de la liste des filtres */
    .filtres-fin {
        position: sticky;
        bottom: 0;
        padding: .75rem;
        border-top: 1px solid var(--bs-border-color);
        background-color: #ffffff;
        grid-column: 1 / -1;
    }

    .filtres-fin .btn {
        min-height: 2.75rem;
    }
}


/* ══════════════════════════════════════════════════════════════
   LE PANNEAU DE FILTRES, HABILLÉ
   Le bouton « Filtres » (visible sous 992 px) devient une vraie tuile :
   pastille d'icône, intitulé avec sa ligne d'état, compteur de filtres
   actifs et chevron dans un rond. Les options cochées se repèrent d'un
   coup d'œil grâce à un fond teinté.
   ══════════════════════════════════════════════════════════════ */

.bouton-filtres {
    display: flex;
    align-items: center;
    gap: .7rem;
    width: 100%;
    padding: .7rem .8rem;
    border: 1px solid var(--bordure-douce);
    border-radius: .85rem;
    background-color: #ffffff;
    box-shadow: var(--ombre-douce);
    color: var(--bs-body-color);
    text-align: left;
    text-transform: none;
    letter-spacing: normal;
    transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
}

.bouton-filtres:hover {
    border-color: #cfd6f5;
    box-shadow: var(--ombre-relief);
}

/* Ouvert : la tuile prend la couleur du site, pour dire « c'est ici que ça se passe » */
.bouton-filtres[aria-expanded="true"] {
    border-color: rgba(var(--bs-primary-rgb), .35);
    background-color: #f7f8ff;
}

.bouton-filtres-icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    flex-shrink: 0;
    border-radius: .65rem;
    background-image: linear-gradient(135deg, var(--logo-bleu), var(--logo-cyan));
    color: #ffffff;
    font-size: 1rem;
    box-shadow: 0 6px 14px -8px rgba(22, 112, 240, .7);
}

.bouton-filtres-texte {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    font-size: .95rem;
    font-weight: 600;
}

.bouton-filtres-texte small {
    font-size: .78rem;
    font-weight: 400;
    color: var(--bs-secondary-color);
}

/* Le nombre de filtres posés, en pastille pleine */
.bouton-filtres-compte {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.5rem;
    margin-left: auto;
    padding: 0 .4rem;
    border-radius: 999px;
    background-color: var(--bs-primary);
    color: #ffffff;
    font-size: .78rem;
    font-weight: 700;
}

.bouton-filtres-fleche {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    flex-shrink: 0;
    margin-left: auto;
    border-radius: 50%;
    background-color: #f1f3f9;
    color: #5f6981;
    font-size: .7rem;
    transition: transform .2s ease, background-color .15s ease;
}

/* Quand un compteur est présent, il occupe la place de « marge auto » */
.bouton-filtres-compte + .bouton-filtres-fleche {
    margin-left: .35rem;
}

.bouton-filtres[aria-expanded="true"] .bouton-filtres-fleche {
    transform: rotate(180deg);
    background-color: rgba(var(--bs-primary-rgb), .14);
    color: var(--bs-primary-text-emphasis);
}

/* ─── Les options : la ligne entière réagit, la coche se voit ─── */
.option-filtre {
    padding: .35rem .5rem;
    border-radius: .55rem;
    transition: background-color .12s ease;
}

.option-filtre:hover {
    background-color: #f5f7fd;
}

/* L'option choisie garde un fond teinté : on voit ce qui est actif sans relire les cases */
.option-filtre:has(.form-check-input:checked) {
    background-color: rgba(var(--bs-primary-rgb), .10);
    color: var(--bs-body-color);
    font-weight: 600;
}

/* Le nombre de produits, en pastille discrète */
.option-filtre-compte {
    padding: .1rem .45rem;
    border-radius: 999px;
    background-color: #f1f3f9;
    color: #5f6981;
    font-size: .74rem;
    font-weight: 600;
}

.option-filtre:has(.form-check-input:checked) .option-filtre-compte {
    background-color: rgba(var(--bs-primary-rgb), .16);
    color: var(--bs-primary-text-emphasis);
}


/* Sous 992 px, le panneau ne garde plus son propre cadre : la tuile
   « Filtres » se pose directement sur la page, et c'est la liste des
   filtres qui devient une carte quand on l'ouvre. */
@media (max-width: 991.98px) {
    .panneau-filtres {
        border: 0;
        background-color: transparent;
        overflow: visible;
    }

    .bloc-filtre:has(.bouton-filtres) {
        padding: 0;
    }

    /* La liste dépliée : sa propre carte, sous la tuile */
    .corps-filtres.show,
    .corps-filtres.collapsing {
        margin-top: .6rem;
        border: 1px solid var(--bordure-douce);
        border-radius: .85rem;
        background-color: #ffffff;
        box-shadow: var(--ombre-douce);
        overflow: hidden;
    }

    /* Le premier bloc n'a plus besoin de son filet du haut */
    .corps-filtres > .bloc-filtre:first-of-type {
        border-top: 0;
    }

    /* Les étiquettes des filtres actifs, posées sur la page elles aussi */
    .filtres-puces {
        padding: 0 0 .6rem;
        border-bottom: 0;
        background-color: transparent;
    }
}

