/* ══════════════════════════════════════════════════════════════
   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 {
    --bs-primary: #6c7fe0;
    --bs-primary-rgb: 108, 127, 224;
    --bs-primary-text-emphasis: #4c5cc4;
    --bs-body-bg: #ffffff;
    --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;

    /* 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 {
    background-color: #ffffff;
    border-bottom: 1px solid var(--bs-border-color);
    padding-top: .5rem;
    padding-bottom: .5rem;
    box-shadow: 0 1px 2px rgba(58, 63, 81, .05);
}

/* 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(255, 255, 255, .82);
        -webkit-backdrop-filter: saturate(180%) blur(14px);
        backdrop-filter: saturate(180%) blur(14px);
    }
}

/* La barre suit la même largeur utile que le contenu (voir .app-main) :
   sur un écran large, le logo et le menu du compte restent alignés sur la
   page au lieu de s'écarter jusqu'aux bords de la fenêtre */
.app-navbar > .container-fluid {
    max-width: 1600px;
}

.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: 1600px;
    margin-inline: auto;
}

.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.
   ══════════════════════════════════════════════════════════════ */

/* 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: #5c6273;
    border-radius: .5rem;
    font-size: .9rem;
    padding: .5rem .8rem;
}

.app-navbar .nav-link i {
    color: #6b7488;
    font-size: 1rem;
}

.app-navbar .nav-link:hover,
.app-navbar .nav-link:focus {
    background-color: #f4f5fa;
    color: var(--bs-body-color);
}

/* .show : Bootstrap la pose sur l'entrée dont le menu est ouvert */
.app-navbar .nav-link.active,
.app-navbar .nav-link.show {
    background-color: #eef0fc;
    color: var(--bs-primary-text-emphasis);
    font-weight: 600;
}

.app-navbar .nav-link.active i,
.app-navbar .nav-link.show i {
    color: var(--bs-primary-text-emphasis);
}

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

/* 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 transparent;
    border-radius: 2rem;
    background-color: #f4f5fa;
    transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease;
}

.champ-recherche:hover {
    background-color: #eef0f7;
}

/* :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(var(--bs-primary-rgb), .45);
    box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .12);
}

/* 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;
}

/* 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: #eef0fc;
    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;
}

/* 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: #5c6ed2;
    --bs-btn-hover-border-color: #5c6ed2;
    --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;
}

/* 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: #eef0fc;
    color: var(--bs-primary-text-emphasis);
}

/* Actions de droite : icônes en gris neutre, séparateur avant le compte */
.app-navbar .btn-icon {
    color: #6c7280;
}

.app-navbar .btn-icon:hover,
.app-navbar .btn-icon:focus,
.app-navbar .btn-icon.active {
    background-color: #f4f5fa;
    color: #3a3f51;
}

.app-navbar-divider {
    width: 1px;
    height: 1.5rem;
    background-color: var(--bs-border-color);
}

/* 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 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: 1.9rem;
    height: 1.9rem;
    font-size: .95rem;
}

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

/* ══════════════════════════════════════════════════════════════
   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 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;
}

.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: #eef0fc;
    --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;
}

/* 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;
}

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

/* 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;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .9rem;
}

.carte-produit-prix strong {
    font-size: 1.05rem;
    letter-spacing: -.01em;
}


/* ══════════════════════════════════════════════════════════════
   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;
    }

    .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 */
    .table-cartes tbody .cellule-nom {
        justify-content: flex-start;
        text-align: left;
        font-size: 1rem;
        font-weight: 600;
        padding-bottom: .35rem;
    }

    .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-family: var(--bs-font-monospace);
    font-size: .82rem;
    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);
}

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

/* ─── 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-family: var(--bs-font-monospace);
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .01em;
    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 la référence PAN-… */
.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.
   ══════════════════════════════════════════════════════════════ */

.panier-article {
    display: flex;
    gap: 1rem;
    padding: 1rem 0;
    border-top: 1px solid var(--bs-border-color);
}

.panier-article-visuel {
    flex-shrink: 0;
    width: 110px;
    height: 110px;
    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 {
    margin-top: .35rem;
    font-size: .82rem;
    color: #1a7f4b;
}

.panier-article-rupture {
    margin-top: .35rem;
    font-size: .82rem;
    color: var(--bs-danger);
}

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

.panier-quantite {
    display: flex;
    align-items: center;
    gap: .25rem;
}

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

/* Un bouton qui a l'apparence d'un lien : le formulaire de suppression
   a besoin d'un <button>, mais visuellement c'est un lien discret */
.panier-lien-action {
    padding: 0;
    border: 0;
    background: none;
    color: var(--bs-primary);
    font-size: .85rem;
}

.panier-lien-action:hover {
    color: var(--bs-primary-text-emphasis);
    text-decoration: underline;
}

.panier-article-prix {
    flex-shrink: 0;
    text-align: right;
}

.panier-article-montant {
    font-size: 1.05rem;
    font-weight: 700;
    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-visuel {
        width: 74px;
        height: 74px;
    }

    .panier-article {
        flex-wrap: wrap;
    }

    .panier-article-prix {
        width: 100%;
        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 confirmé » ─── */
/* 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 : violet du site, orange, 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: #6c7fe0;
    --tdb-c2: #eb6834;
    --tdb-c3: #1baf7a;
    --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;
    }

    /* Le prix et l'état du stock passent l'un sous l'autre s'ils
       ne tiennent pas : plus de chiffre coupé en deux */
    .carte-produit-prix {
        flex-wrap: wrap;
        row-gap: .35rem;
    }

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

    /* 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,
    .panier-lien-action {
        min-width: 2.5rem;
        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: #eef0fc;
        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: #eef0fc;
    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;
    }
}
