/* --------------------------------------------------------------------------
   Composants : boutons, champs, cartes, dialogues, selecteur de quantite,
   messages. Remplace les 153 classes Bootstrap utilisees par l'ancien front.
   -------------------------------------------------------------------------- */

/* --- Boutons ------------------------------------------------------------ */

.bouton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--espace-xs);
    min-height: var(--cible);
    padding: 0 var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
    color: var(--texte);
    font: inherit;
    text-decoration: none;
    cursor: pointer;
}

.bouton:hover:not(:disabled) {
    background: var(--fond-doux);
}

.bouton:disabled {
    opacity: 0.5;
    cursor: default;
}

.bouton-primaire {
    border-color: var(--marque);
    background: var(--marque);
    color: var(--marque-contraste);
}

.bouton-primaire:hover:not(:disabled) {
    background: color-mix(in srgb, var(--marque) 85%, black);
}

.bouton-discret {
    border-color: transparent;
    background: transparent;
}

.bouton-rond {
    /* Cible tactile pleine, meme si le glyphe est petit. */
    width: var(--cible);
    min-width: var(--cible);
    padding: 0;
    border-radius: 50%;
    font-size: 1.25rem;
    line-height: 1;
}

/* --- Champs ------------------------------------------------------------- */

.champ {
    display: block;
    margin-bottom: var(--espace-s);
}

.champ > span {
    display: block;
    margin-bottom: calc(var(--pas) * 1);
    font-size: 0.9rem;
    color: var(--texte-discret);
}

.champ input,
.stepper-valeur {
    width: 100%;
    min-height: var(--cible);
    padding: 0 var(--espace-xs);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
    color: inherit;
    font: inherit;
    /* Jamais en dessous de 1rem : voir base.css. */
    font-size: 1rem;
}

.champ input:user-invalid {
    border-color: var(--echec);
}

.erreur {
    margin-bottom: var(--espace-xs);
    color: var(--echec);
    font-size: 0.9rem;
}

/* --- Carte produit ------------------------------------------------------ */

.produit {
    display: flex;
    flex-direction: column;
    gap: var(--espace-xs);
    padding: var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
    box-shadow: var(--ombre);
}

.produit-image {
    width: 100%;
    /* Carre, et l'image entiere dedans : l'ancien front la bornait dans une
       boite de 300 sur 300 sans la rogner. Un bocal photographie en hauteur
       perdait son etiquette avec object-fit: cover. */
    aspect-ratio: 1 / 1;
    object-fit: contain;
    border-radius: calc(var(--rayon) - 2px);
    background: var(--fond-doux);
}

.produit-infos {
    flex: 1;
}

.produit-nom {
    font-size: 1rem;
}

.produit-detail {
    color: var(--texte-discret);
    font-size: 0.85rem;
}

.produit-description {
    color: var(--texte-discret);
    font-size: 0.85rem;
    /* Les retours a la ligne des descriptions sont rendus par le CSS : le front
       actuel les convertissait en <br/> par remplacement de chaine, ce qui
       imposait de construire du HTML. */
    white-space: pre-line;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.produit-achat {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--espace-xs);
}

.produit-prix {
    font-weight: 600;
}

/* Le prix du lot sous le prix unitaire : plus discret, sur sa propre ligne,
   parce qu'il complete le prix sans le remplacer. */
.produit-prix-lot {
    display: block;
    font-weight: normal;
    font-size: 0.8rem;
}

.produit-prix-lot:empty {
    display: none;
}

.recap-lot {
    display: block;
    font-weight: normal;
    font-size: 0.85rem;
}

/* --- Selecteur de quantite ---------------------------------------------- */

mp-stepper {
    display: inline-flex;
    align-items: center;
    gap: calc(var(--pas) * 1);
}

.stepper-valeur {
    width: 3.5rem;
    text-align: center;
    /* Les fleches natives sont trop petites pour une cible tactile. */
    appearance: textfield;
}

.stepper-valeur::-webkit-outer-spin-button,
.stepper-valeur::-webkit-inner-spin-button {
    appearance: none;
    margin: 0;
}

mp-stepper.stepper-actif .stepper-valeur {
    border-color: var(--marque);
    font-weight: 600;
}

/* --- Dialogues ---------------------------------------------------------- */

.dialogue {
    width: min(28rem, calc(100vw - 2 * var(--espace-m)));
    padding: 0;
    border: none;
    border-radius: var(--rayon);
    box-shadow: 0 10px 40px rgb(0 0 0 / 25%);
}

.dialogue::backdrop {
    background: rgb(0 0 0 / 40%);
}

.dialogue-corps {
    padding: var(--espace-m);
}

/* La version en ligne, pas la verticale : en dessous de cent cinquante pixels
   de large la signature de celle-ci n'est plus qu'une trame grise. */
.dialogue-marque {
    display: block;
    width: min(300px, 100%);
    height: auto;
    margin: 0 auto var(--espace-m);
}

.dialogue-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--espace-xs);
    margin-top: var(--espace-s);
}

/* « Voir le catalogue » ne partage pas le bord droit avec « Se connecter » :
   ce n'est pas une variante de l'action, c'est l'autre sortie. Il tient donc
   le bord gauche, en face d'elle, et l'espace entre les deux dit qu'on ne
   choisit pas entre deux nuances de la meme chose. */
.dialogue-actions .bouton-visiteur {
    margin-right: auto;
}

/* --- Messages ephemeres ------------------------------------------------- */

/* Les messages ephemeres se posent au-dessus de la barre de rappel quand elle
   est la : sinon le dernier message recouvre le bouton qui l'a declenche. */
body:has(.rappel:not([hidden])) .alertes {
    bottom: calc(var(--cible) + var(--espace-m) + var(--espace-s));
}

.alertes {
    position: fixed;
    left: 50%;
    bottom: var(--espace-m);
    z-index: 20;
    display: flex;
    flex-direction: column;
    gap: var(--espace-xs);
    width: min(30rem, calc(100vw - 2 * var(--espace-m)));
    transform: translateX(-50%);
    pointer-events: none;
}

.alerte {
    margin: 0;
    padding: var(--espace-xs) var(--espace-s);
    border-radius: var(--rayon);
    background: var(--texte);
    color: var(--fond);
    box-shadow: var(--ombre);
    opacity: 1;
    transition: opacity 0.4s ease;
}

.alerte-echec {
    background: var(--echec);
}

.alerte.disparait {
    opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
    .alerte {
        transition: none;
    }
}

/* --- Rappel de commande -------------------------------------------------- */
/*
   Une barre au bas de la fenetre, a toutes les largeurs : ce qui est au panier
   ne doit jamais quitter l'ecran, et une commande oubliee au panier n'est pas
   commandee.

   Un rail lateral avait ete essaye : il montrait le detail des lignes, mais il
   prend une colonne entiere du catalogue en permanence pour une information
   qu'on ne consulte que par intermittence. La barre coute soixante pixels de
   hauteur, et rien de plus.
*/

.rappel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9;
    display: flex;
    align-items: center;
    gap: var(--espace-s);
    padding: var(--espace-xs) var(--espace-m);
    background: var(--fond);
    border-top: 1px solid var(--bordure);
    color: var(--texte);
    box-shadow: 0 -8px 24px -18px rgb(22 32 26 / 45%);

    --rappel-teinte: var(--alerte);
}

.rappel[data-etat="validee"] { --rappel-teinte: var(--succes); }
.rappel[data-etat="vide"] { --rappel-teinte: var(--texte-discret); }

.rappel-etat {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    margin: 0;
    color: var(--rappel-teinte);
    font-weight: 700;
    font-size: 0.85rem;
}

.rappel-etat:empty { display: none; }

/* Rien ne se replie dans une barre : un libelle sur trois lignes la ferait
   grandir, et elle mange deja le bas de la page. */
.rappel-etat,
.rappel-compte,
.rappel-somme {
    white-space: nowrap;
}

.rappel-etat::before {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 999px;
    background: currentColor;
    flex: none;
}

.rappel-vide {
    flex: 1;
    margin: 0;
    color: var(--texte-discret);
    font-size: 0.9rem;
}

.rappel-pied {
    display: flex;
    align-items: center;
    gap: var(--espace-s);
    margin-left: auto;
}

/* Le nombre d'articles : dans une barre, c'est ce qui reste du detail des
   lignes. Il dit qu'on n'a pas oublie la moitie de sa commande. */
.rappel-compte {
    color: var(--texte-discret);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.rappel-compte:empty { display: none; }

.rappel-total {
    display: flex;
    align-items: baseline;
    gap: var(--espace-xs);
    margin: 0;
    font-family: var(--police-titre);
    font-size: 1.35rem;
    letter-spacing: 0.01em;
}

/* Le mot « Total » ne sert a rien a cote d'un montant en gros, dans une barre
   qui ne contient que ca. Il reste pour les lecteurs d'ecran. */
.rappel-total > span:first-child {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.rappel-somme { font-variant-numeric: tabular-nums; }

/* Panier vide : ni compte, ni total, ni boutons. Un « 0,00 € » n'apprend rien,
   et le rappel se contente alors de dire qu'il n'y a rien. */
.rappel[data-etat="vide"] .rappel-total { display: none; }

.rappel-voir,
.rappel-valider,
.rappel-aide { white-space: nowrap; }

/* Le bouton de validation s'agite au meme instant que l'icone du panier dans
   l'entete. Plus court et plus discret qu'elle : l'icone est petite et loin,
   ce bouton est large et deja sous les yeux -- lui donner la meme amplitude
   ferait sauter toute la barre.

   Trois pixels et un degre : assez pour que l'oeil l'attrape, trop peu pour
   qu'on ait a le suivre. */
@keyframes valider-secousse {
    0%, 100% { transform: translateX(0) rotate(0); }
    20%      { transform: translateX(-3px) rotate(-1.2deg); }
    45%      { transform: translateX(3px) rotate(1.2deg); }
    70%      { transform: translateX(-2px) rotate(-0.8deg); }
}

.valider-secousse {
    animation: valider-secousse 0.6s ease-out;
}

/* Le nombre de taches sur le bouton d'aide : la pastille de l'entete a la meme
   forme, et dit la meme chose -- il reste quelque chose a faire. */
.rappel-aide:not([data-libres="0"]) .pastille {
    background: var(--nuit);
    color: var(--orange);
}

/* La barre couvre le bas de la page : sans cette reserve, le dernier produit du
   catalogue reste inatteignable. */
.catalogue:has(.rappel:not([hidden])) {
    padding-bottom: calc(var(--cible) + var(--espace-l));
}

/* Sur un telephone, il ne reste que l'essentiel : l'etat, le montant et le
   geste. Le decompte et le raccourci vers le recapitulatif sautent -- ce dernier
   est deja dans l'entete, sur la pastille du panier. */
@media (max-width: 30rem) {
    .rappel {
        padding: var(--espace-xs) var(--espace-s);
        gap: var(--espace-xs);
    }

    .rappel-compte,
    .rappel-voir {
        display: none;
    }

    /* Deux boutons sur une barre de telephone : celui de l'aide se reduit a
       son pictogramme. Le libelle reste dans aria-label. */
    .rappel-aide {
        width: var(--cible);
        min-width: var(--cible);
        padding: 0;
        justify-content: center;
    }

    .rappel-aide-mot { display: none; }

    /* La pastille se pose sur le pictogramme faute de place a cote. */
    .rappel-aide .pastille {
        position: absolute;
        transform: translate(14px, -12px);
        font-size: 0.7rem;
        min-width: 1.3em;
    }

    .rappel-aide { position: relative; }

    .rappel-pied { gap: var(--espace-xs); }
    .rappel-total { font-size: 1.2rem; }
    .rappel-valider { padding: 0 var(--espace-s); }
}

/* Sur grand ecran, la barre se regroupe au centre plutot que de coller aux deux
   bords : le regard n'a pas a traverser tout l'ecran entre l'etat et le bouton. */
@media (min-width: 62rem) {
    .rappel {
        justify-content: center;
    }

    .rappel-vide { flex: none; }

    .rappel-pied {
        margin-left: var(--espace-l);
    }
}

/* --- Recapitulatif de commande ------------------------------------------ */

.panier {
    max-width: 50rem;
    margin: 0 auto;
    padding: var(--espace-m);
}

.panier-entete {
    margin-bottom: var(--espace-m);
}

.tableau {
    width: 100%;
    border-collapse: collapse;
    background: var(--fond);
    border-radius: var(--rayon);
    overflow: hidden;
    box-shadow: var(--ombre);
}

.tableau th,
.tableau td {
    padding: var(--espace-xs) var(--espace-s);
    border-bottom: 1px solid var(--bordure);
    text-align: left;
    font-weight: normal;
}

.tableau thead th {
    color: var(--texte-discret);
    font-size: 0.85rem;
    font-weight: 600;
}

.tableau .nombre {
    text-align: right;
    white-space: nowrap;
}

.tableau tfoot th,
.tableau tfoot td {
    border-bottom: none;
    font-weight: 600;
}

.recap-nom {
    font-weight: 500;
}

/* Sur mobile, la colonne du prix unitaire est la moins utile. */
@media (max-width: 30rem) {
    .recap th:nth-child(2),
    .recap td:nth-child(2) {
        display: none;
    }

    .tableau th,
    .tableau td {
        padding: var(--espace-xs);
    }
}

.etat-commande {
    margin: var(--espace-s) 0;
    color: var(--texte-discret);
}

.etat-commande.etat-valide {
    color: var(--succes);
    font-weight: 600;
}

.panier-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-xs);
    justify-content: flex-end;
}

/* Pleine largeur sur telephone : des boutons de largeurs differentes alignes a
   droite se lisent mal, et la cible est plus facile a atteindre. */
@media (max-width: 30rem) {
    .panier-actions .bouton {
        flex: 1 1 100%;
    }
}

/* --- Carte produit : la zone cliquable ---------------------------------- */

/* Le lien est le conteneur de l'image et du texte, plutot qu'un display:
   contents : celui-ci a longtemps sorti les liens de l'arbre d'accessibilite. */
.produit-lien {
    display: flex;
    flex-direction: column;
    gap: var(--espace-xs);
    flex: 1;
    color: inherit;
    text-decoration: none;
}

.produit:hover .produit-nom {
    text-decoration: underline;
}

/* --- Fiches produit et producteur --------------------------------------- */

.fiche {
    max-width: 50rem;
    margin: 0 auto;
    padding: var(--espace-m);
}

.fiche-retour {
    margin-bottom: var(--espace-s);
}

.fiche-titre {
    margin-bottom: 0;
}

.fiche-titre-producteur {
    text-transform: uppercase;
}

.fiche-categorie {
    margin-bottom: var(--espace-m);
}

.fiche-corps {
    display: grid;
    grid-template-columns: minmax(12rem, 18rem) 1fr;
    gap: var(--espace-m);
    align-items: start;
}

@media (max-width: 40rem) {
    .fiche-corps {
        grid-template-columns: 1fr;
    }
}

.fiche-visuel {
    position: relative;
}

.fiche-image {
    width: 100%;
    border-radius: var(--rayon);
    background: var(--fond-doux);
}

.fiche-bio {
    position: absolute;
    top: var(--espace-xs);
    left: var(--espace-xs);
}

.fiche-description {
    /* Les retours a la ligne sont rendus par le CSS, pas par des <br> injectes. */
    white-space: pre-line;
}

.fiche-producteur {
    margin: var(--espace-s) 0;
}

.fiche-prix {
    margin-bottom: 0;
    font-size: 1.5rem;
    font-weight: 600;
}

.fiche-prix .discret {
    font-size: 1rem;
    font-weight: normal;
}

.fiche-achat {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    margin-top: var(--espace-s);
}

.fiche-contact {
    display: flex;
    flex-direction: column;
    gap: calc(var(--pas) * 1);
    margin-top: var(--espace-s);
    padding-left: var(--espace-s);
    border-left: 2px solid var(--bordure);
    font-style: normal;
}

.fiche-sous-titre {
    margin-top: var(--espace-l);
}

.liste-liens {
    margin: 0;
    padding-left: var(--espace-m);
}

.liste-liens li {
    margin-bottom: calc(var(--pas) * 1);
}

/* Les produits d'un producteur portent une miniature : la puce ferait double
   emploi avec l'image, et le nom se cale sur le milieu de la vignette. */
.liste-produits {
    list-style: none;
    padding-left: 0;
}

.lien-produit {
    display: flex;
    align-items: center;
    gap: var(--espace-s);
}

/* --- Retour visuel du panier -------------------------------------------- */

/* Un ajout doit se voir : la pastille est dans l'entete, la carte cliquee peut
   etre tout en bas de page. */
@keyframes pastille-bouge {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.45); }
    100% { transform: scale(1); }
}

/* Confirmation discrete que la commande est bien partie au serveur. */
@keyframes pastille-enregistree {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--marque) 60%, transparent); }
    100% { box-shadow: 0 0 0 0.6rem transparent; }
}

.pastille {
    position: relative;
    transition: opacity 0.2s ease;
}

.pastille-bouge {
    animation: pastille-bouge 0.35s ease;
}

/* Le halo de confirmation est porte par un pseudo-element : deux classes qui
   declarent toutes deux la propriete raccourcie animation ne peuvent pas
   coexister, la derniere declaree gagnant. Or les deux se chevauchent. */
.pastille::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
}

.pastille-enregistree::after {
    animation: pastille-enregistree 0.6s ease-out;
}

/* Pendant l'envoi : la pastille s'attenue, sans bloquer l'interface. */
.pastille-en-cours {
    opacity: 0.55;
}

/* --- Etats du panier ----------------------------------------------------- */

/* Une commande a valider n'est pas une commande : le serveur ne la retient pas
   dans les bons de distribution. La couleur d'alerte le dit en permanence, le
   rappel periodique le redit a qui a quitte la page des yeux. */
.pastille[data-etat="a-valider"] {
    background: var(--alerte);
    color: var(--alerte-contraste);
}

.pastille[data-etat="validee"] {
    background: var(--succes);
    color: var(--marque-contraste);
}

/* La coche marque l'etat sans effacer le nombre d'articles. */
.pastille[data-etat="validee"]::before {
    content: "✓";
    margin-right: 0.15em;
    font-weight: 700;
}

/* Rappel : une onde qui part de la pastille, plus lente et plus ample que la
   confirmation d'enregistrement, pour ne pas se confondre avec elle. */
@keyframes pastille-rappel {
    0%   { transform: scale(1); box-shadow: 0 0 0 0 color-mix(in srgb, var(--alerte) 70%, transparent); }
    30%  { transform: scale(1.2); }
    60%  { transform: scale(1); }
    100% { transform: scale(1); box-shadow: 0 0 0 0.9rem transparent; }
}

.pastille-rappel {
    animation: pastille-rappel 1.1s ease-out;
}

/* L'icone s'agite avec la pastille, comme le bouton du panier de l'ancien
   front. Cinq pixels de part et d'autre : l'amplitude d'origine, assez pour
   attirer l'oeil sans faire sauter l'entete. */
@keyframes panier-secousse {
    0%, 100% { transform: translateX(0); }
    20%      { transform: translateX(-5px); }
    40%      { transform: translateX(5px); }
    60%      { transform: translateX(-5px); }
    80%      { transform: translateX(5px); }
}

.panier-secousse .icone {
    animation: panier-secousse 0.5s ease;
}

/* Le rappel penche le panier plutot que de le secouer : plus lent, plus
   ample, il se distingue de l'accuse de reception d'un ajout. */
@keyframes panier-penche {
    0%, 100% { transform: rotate(0); }
    15%      { transform: rotate(-9deg); }
    45%      { transform: rotate(7deg); }
    70%      { transform: rotate(-4deg); }
}

.panier-penche .icone {
    animation: panier-penche 1.1s ease-out;
    transform-origin: 50% 70%;
}

/* Un fond, et pas seulement un mouvement : le mouvement passe, le fond reste.
   Qui arrive sur la page apres coup doit voir qu'une action est attendue. */
[data-action="ouvrir-panier"][data-etat="a-valider"] {
    background: color-mix(in srgb, var(--alerte) 14%, transparent);
    border-color: color-mix(in srgb, var(--alerte) 45%, transparent);
    color: var(--alerte);
}

[data-action="ouvrir-panier"][data-etat="validee"] {
    background: color-mix(in srgb, var(--succes) 12%, transparent);
    border-color: color-mix(in srgb, var(--succes) 35%, transparent);
}

/* Au rappel, le fond s'affirme puis retombe : la couleur porte le message
   quand le regard est ailleurs, l'a-coup le ramene. */
@keyframes panier-fond {
    0%   { background-color: color-mix(in srgb, var(--alerte) 14%, transparent); }
    35%  { background-color: color-mix(in srgb, var(--alerte) 55%, transparent); }
    100% { background-color: color-mix(in srgb, var(--alerte) 14%, transparent); }
}

.panier-penche {
    animation: panier-fond 1.1s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    .pastille-bouge,
    .pastille-rappel,
    .pastille-enregistree::after,
    .panier-secousse .icone,
    .panier-penche .icone,
    .panier-penche,
    .valider-secousse {
        animation: none;
    }

    .pastille {
        transition: none;
    }
}

/* --- Administration ------------------------------------------------------ */

.admin {
    max-width: 70rem;
    margin: 0 auto;
    padding: var(--espace-m);
}

/* Le choix des produits aligne trente-neuf cartes de producteurs : c'est le seul
   ecran d'administration dont le contenu gagne a s'etaler. Sa grille est en
   minmax(20rem, 1fr), donc une quatrieme colonne demande 4 x 20 + 3 x 0,75
   d'ecart + 2 x 1,25 de marge interieure, soit 84,75rem. On prend 88 : la
   colonne passe avec un peu d'aise, et il reste une marge sur un ecran de
   1440 points. */
.admin-large {
    max-width: 88rem;
}

.admin-entete {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-s);
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--espace-m);
}

.admin-entete h1 {
    margin: 0;
}

.tableau .actions {
    display: flex;
    flex-wrap: wrap;
    gap: calc(var(--pas) * 1);
    justify-content: flex-end;
}

.tableau .actions .bouton {
    min-height: 2.25rem;
    padding: 0 var(--espace-xs);
    font-size: 0.9rem;
}

/* L'etat se lit d'un coup d'oeil dans une liste longue. */
.etiquette {
    display: inline-block;
    padding: 0.1em 0.6em;
    border-radius: 999px;
    background: var(--fond-doux);
    border: 1px solid var(--bordure);
    font-size: 0.85rem;
    white-space: nowrap;
}

.etiquette[data-statut="1"] {
    border-color: var(--marque);
    background: color-mix(in srgb, var(--marque) 12%, white);
    color: color-mix(in srgb, var(--marque) 80%, black);
    font-weight: 600;
}

.etiquette[data-statut="3"] {
    color: var(--texte-discret);
}

/* Sur telephone, le lieu et les horaires passent apres l'essentiel. */
/* Repli seulement : sur un ecran large, l'adresse et le numero ont leurs
   colonnes, et les repeter sous le nom les dirait deux fois. La regle de base
   passe avant la requete media -- a specificite egale, c'est la derniere ecrite
   qui gagne, et l'inverse masquait la ligne partout. */
.membre-contact {
    display: none;
}

/* Sur telephone, un tableau d'administration ne tient pas ses cinq colonnes.
   Cedent celles qui sont marquees, et non les deuxieme et troisieme quelles
   qu'elles soient : cette regle-la, positionnelle, masquait le prix du
   catalogue et -- sur les archives producteurs, qui n'ont que trois colonnes --
   la colonne Actions elle-meme. */
@media (max-width: 45rem) {
    .tableau .colonne-repliable {
        display: none;
    }

    /* Ce qu'une colonne repliee emportait de necessaire revient sous le nom.
       L'adresse et le numero d'un membre sont ce qu'on vient chercher sur un
       telephone, et leurs liens restent actionnables d'un doigt. */
    .membre-contact {
        display: flex;
        flex-wrap: wrap;
        gap: var(--espace-s);
        margin-top: calc(var(--pas) * 1);
        font-weight: normal;
    }
}


.note {
    margin-top: var(--espace-s);
    font-size: 0.85rem;
}

/* --- Reglage a bascule ---------------------------------------------------- */

/* Un interrupteur plutot qu'une case a cocher : le reglage prend effet au clic,
   sans formulaire ni bouton d'enregistrement, et c'est ce qu'annonce la forme
   d'un interrupteur. La case native reste dessous, invisible mais bien la : elle
   porte le focus, le clavier et l'etat pour les technologies d'assistance. */
.reglage {
    margin-bottom: var(--espace-m);
    padding: var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
}

.interrupteur {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    min-height: var(--cible);
    cursor: pointer;
}

/* Retiree du flux mais pas du DOM : display:none la sortirait de l'ordre de
   tabulation et lui oterait le focus. */
.interrupteur input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
}

.interrupteur-piste {
    position: relative;
    flex: none;
    width: 2.75rem;
    height: 1.5rem;
    border: 1px solid var(--bordure);
    border-radius: 999px;
    background: var(--fond-doux);
    transition: background-color 0.15s ease-out;
}

.interrupteur-piste::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 50%;
    background: var(--texte-discret);
    transition: transform 0.15s ease-out, background-color 0.15s ease-out;
}

.interrupteur input:checked + .interrupteur-piste {
    background: var(--marque);
    border-color: var(--marque);
}

.interrupteur input:checked + .interrupteur-piste::after {
    transform: translateX(1.25rem);
    background: var(--marque-contraste);
}

.interrupteur input:focus-visible + .interrupteur-piste {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
}

.interrupteur input:disabled + .interrupteur-piste {
    opacity: 0.5;
}

@media (prefers-reduced-motion: reduce) {
    .interrupteur-piste,
    .interrupteur-piste::after {
        transition: none;
    }
}

/* --- Le visiteur -------------------------------------------------------- */

/* L'orange de la charte : la couleur de ce qu'on propose, distincte du vert de
   l'action courante. Deux boutons de meme couleur dans un dialogue, ce sont deux
   appels de meme poids, et l'on ne sait plus lequel est le chemin normal.

   Le texte est en blanc, comme sur l'affiche : c'est la combinaison de la charte
   pour un aplat orange. Meme regle que le bouton « Aider ». */
.bouton-visiteur {
    border-color: var(--orange);
    background: var(--orange);
    color: var(--orange-contraste);
    font-weight: 600;
}

.bouton-visiteur:hover:not(:disabled) {
    background: color-mix(in srgb, var(--orange) 88%, black);
    border-color: color-mix(in srgb, var(--orange) 88%, black);
}

/* Meme place que la barre de rappel d'un membre -- ce qui compte reste sous les
   yeux -- mais un composant a part : celle-la est branchee sur le panier, et un
   visiteur n'en a pas. */
.visiteur-barre {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--espace-s);
    padding: var(--espace-xs) var(--espace-m);
    background: var(--fond);
    border-top: 1px solid var(--bordure);
    box-shadow: 0 -2px 12px rgb(0 0 0 / 8%);
}

.visiteur-message {
    margin: 0;
}

/* Les messages ephemeres se posent au-dessus, comme ils le font au-dessus de la
   barre de rappel : sinon le dernier message recouvre le bouton. */
body:has(.visiteur-barre:not([hidden])) .alertes {
    bottom: calc(var(--cible) + var(--espace-m) + var(--espace-s));
}

@media (max-width: 30rem) {
    .visiteur-message {
        font-size: 0.9rem;
    }
}

/* --- Ecran des parametres ----------------------------------------------- */

/* Les reglages sont groupes par ce qu'ils concernent, et non alignes a la
   file : l'apercu parle des membres en attente, la vitrine de gens qui n'ont pas
   de compte du tout. Un titre par groupe suffit a le dire. */
.parametres-titre {
    margin: var(--espace-l) 0 var(--espace-xs);
    font-size: 1.1rem;
}

.parametres-titre:first-of-type {
    margin-top: var(--espace-m);
}

.parametres-intro {
    margin: 0 0 var(--espace-s);
    max-width: 46rem;
}

/* Un reglage que la vitrine fermee rend sans effet : eteint plutot que retire.
   Le retirer ferait sauter la mise en page a chaque bascule, et cacherait a
   l'administrateur ce qu'il obtiendra en ouvrant. */
.reglage-sans-effet {
    opacity: 0.55;
}

/* --- Fiches a corriger --------------------------------------------------- */

.fiches-a-corriger {
    margin: 0 0 var(--espace-m);
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--espace-xs);
}

.fiche-a-corriger {
    padding: var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
}

.fiche-a-corriger-entete {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    flex-wrap: wrap;
}

/* Ce qu'on retirerait, mot pour mot : la seule facon de decider en connaissance
   de cause. En police a chasse fixe, parce que ce sont des adresses et des
   numeros, et qu'on veut les lire caractere par caractere. */
.fiche-a-corriger-trouve {
    margin: var(--espace-xs) 0;
    font-family: Consolas, "SF Mono", Menlo, monospace;
    font-size: 0.85rem;
    color: var(--texte-discret);
    overflow-wrap: anywhere;
}

.fiche-a-corriger-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-xs);
}

.fiche-a-corriger-actions .bouton {
    min-height: 2.25rem;
    padding: 0 var(--espace-xs);
    font-size: 0.9rem;
}

/* --- Les familles d'actions d'une distribution -------------------------- */

/* La rangee en aligne trois : ce qui prepare la distribution -- ses produits,
   ses taches --, les documents qu'on en sort -- bon de commande producteur, bon
   de distribution --, puis ce qui la modifie ou la fait avancer. Quatre boutons
   gris a la file se lisent comme une seule famille ; un ecart ouvre chacune.
   Quatre fois le pas ordinaire de la rangee : assez pour se voir, pas assez
   pour trouer la ligne. Les transitions n'en ont pas besoin, leur teinte les
   detache deja. */
.actions [data-debut-groupe] {
    margin-left: var(--espace-s);
}

/* --- Les transitions d'une distribution --------------------------------- */

/* Une teinte, pas un aplat. Ces quatre boutons voisinent avec six autres sur la
   meme ligne : un bouton plein en ferait l'action principale de l'ecran, alors
   qu'ils sont au meme rang que « Produits » ou « Modifier ».
   La couleur dit ou va la distribution -- vert quand elle prend les commandes,
   ambre quand elles s'arretent, rouge quand elle quitte la liste courante -- et
   non si l'action est risquee : toutes les quatre se defont.
   Meme recette que le rappel du panier : 14 % de fond, 45 % de bordure, et la
   couleur pleine pour le texte, qui est ce qui doit rester lisible. Les seuils
   sont mesures par parcours-admin.mjs, sur les styles calcules. */
.actions [data-action="open"],
.actions [data-action="reopen"] {
    background: color-mix(in srgb, var(--marque) 14%, transparent);
    border-color: color-mix(in srgb, var(--marque) 45%, transparent);
    color: var(--marque);
}

/* L'ambre de la charte tombe a 4,31 sur sa propre teinte -- mesure, pas estime.
   Le libelle est donc assombri, du meme geste que `.bouton-primaire:hover` :
   c'est la meme couleur, plus foncee, et elle repasse a 5,8. */
.actions [data-action="close"] {
    background: color-mix(in srgb, var(--alerte) 14%, transparent);
    border-color: color-mix(in srgb, var(--alerte) 45%, transparent);
    color: color-mix(in srgb, var(--alerte) 85%, black);
}

.actions [data-action="archive"] {
    background: color-mix(in srgb, var(--echec) 14%, transparent);
    border-color: color-mix(in srgb, var(--echec) 45%, transparent);
    color: var(--echec);
}

/* Le survol appuie la meme teinte. Sans ces regles, `.bouton:hover` -- plus
   specifique -- remettrait le fond neutre et la couleur disparaitrait au
   moment precis ou l'on vise le bouton. */
.actions [data-action="open"]:hover:not(:disabled),
.actions [data-action="reopen"]:hover:not(:disabled) {
    background: color-mix(in srgb, var(--marque) 24%, transparent);
}

.actions [data-action="close"]:hover:not(:disabled) {
    background: color-mix(in srgb, var(--alerte) 24%, transparent);
}

.actions [data-action="archive"]:hover:not(:disabled) {
    background: color-mix(in srgb, var(--echec) 24%, transparent);
}

/* --- Choix des produits d'une distribution ------------------------------ */

.admin-actions {
    display: flex;
    align-items: center;
    gap: var(--espace-s);
}

/* Trois colonnes larges plutot que quatre etroites : un nom de producteur va
   jusqu'a cinquante caracteres, et c'est lui qu'on parcourt du regard.

   Le minimum ne fixe pas la largeur d'une colonne -- le 1fr l'etire -- il decide
   seulement combien il en tient. Dans les 85,5rem utiles de la page elargie, une
   quatrieme colonne demanderait un minimum sous 20,8rem ; au-dessus, il y en a
   trois, etirees a 28rem chacune. On prend 23 plutot que 26 : la borne haute
   compte pour les ecrans plus etroits, ou 23 garde trois colonnes jusqu'a
   1200 points quand 26 retombe a deux des 1360. */
.groupes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(23rem, 1fr));
    gap: var(--espace-s);
    align-items: start;
}

.groupe {
    margin: 0;
    padding: var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
}

/* La legende est une boite flex : le nom d'un producteur peut alors passer sur
   deux lignes sans emporter le chevron avec lui. En flux de ligne, un libelle
   trop long occupait toute la largeur et renvoyait le bouton dessous, ou il
   rouvrait la bande vide que le groupe replie cherche a eviter -- et la vignette
   du producteur, qui prend quarante pixels par colonne, a fait basculer cinq
   groupes de plus dans ce cas. */
.groupe-titre {
    display: flex;
    align-items: center;
    /* Une legende s'ajuste a son contenu et ne prend pas la largeur de son
       cadre : sans cette largeur, la marge automatique du chevron n'a aucun
       espace a distribuer, et il reste colle au compte -- a une place qui saute
       d'une carte a l'autre selon la longueur du nom. */
    width: 100%;
    /* L'ecart minimal entre le titre et le chevron, quand un nom long ne laisse
       plus rien a la marge automatique de celui-ci. */
    gap: var(--espace-xs);
    padding: 0 var(--espace-xs);
    font-weight: 600;
}

/* Replie, le groupe n'est plus que son titre : le cadre gardait sous le nom une
   bande vide, qui donnait a chaque carte l'air d'un champ a moitie rendu. */
.groupe:has(> .choix-liste[hidden]) {
    padding-top: 0;
    padding-bottom: 0;
}

/* Une boite flex refuse par defaut de descendre sous la largeur de son contenu :
   sans ce zero, le nom ne se replierait pas et deborderait de la carte. */
.groupe-titre .choix {
    min-width: 0;
}

/* Le compte garde sa largeur et ne se coupe pas : reparti sur les items du
   libelle, le retrecissement ecrivait « 0 / 44 » sur trois lignes. C'est le nom,
   et lui seul, qui se replie. */
.groupe-titre .choix .discret {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Trente-neuf producteurs et quatre cents produits : les listes sont repliees,
   et c'est le compte du groupe qui dit ou sont les produits retenus. */
.groupe-bascule {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Le chevron garde sa taille quoi qu'il arrive : c'est le nom qui se replie.
       La marge automatique le pose au bord droit de la carte : sinon il suivait
       le compte, et sa position sautait d'une carte a l'autre dans la grille. */
    flex: 0 0 auto;
    margin-left: auto;
    padding: 0;
    /* La cible tactile reste entiere : le chevron ne fait que vingt pixels. */
    min-width: var(--cible);
    min-height: var(--cible);
    border: 1px solid transparent;
    border-radius: var(--rayon);
    background: none;
    /* L'orange de la charte : la couleur de ce qui attend un geste. Il ne
       conviendrait pas a un paragraphe, il convient a un element graphique. */
    color: var(--orange);
    cursor: pointer;
}

.groupe-bascule:hover {
    border-color: var(--orange);
}

.groupe-bascule svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 0.15s ease;
}

/* Le chevron pointe vers le bas quand il reste a ouvrir, vers le haut quand il
   ne reste qu'a replier. */
.groupe-bascule[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
    .groupe-bascule svg { transition: none; }
}

.choix-liste {
    margin: var(--espace-xs) 0 0;
    padding: 0;
    list-style: none;
}

/* La ligne entiere est cliquable : une case a cocher seule est une cible
   minuscule sur telephone. */
.choix {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    min-height: 2.25rem;
    padding: 0 var(--espace-xs);
    border-radius: calc(var(--rayon) - 2px);
    cursor: pointer;
}

.choix:hover {
    background: var(--fond-doux);
}

.choix input {
    width: 1.1rem;
    height: 1.1rem;
    flex: none;
    accent-color: var(--marque);
}

.choix-nom {
    flex: 1;
}

.choix-masque .choix-nom {
    color: var(--texte-discret);
    font-style: italic;
}

/* --- Administration : navigation et membres ----------------------------- */

.admin-nav {
    display: flex;
    gap: var(--espace-m);
    margin-bottom: var(--espace-m);
    border-bottom: 1px solid var(--bordure);
}

.admin-nav a {
    padding: var(--espace-xs) 0;
    color: var(--texte-discret);
    text-decoration: none;
    border-bottom: 2px solid transparent;
}

.admin-nav a:hover {
    color: var(--texte);
    border-bottom-color: var(--bordure);
}

/* Sur telephone le menu defile plutot que de deborder : cinq onglets ne
   tiennent pas dans 390 points, « Parametres » etait coupe au bord, et c'est
   lui qui donnait a toute la page sa barre horizontale. L'ascenseur reste
   mince mais visible -- une barre qui n'apparait qu'au defilement ne dit pas
   qu'il y a quelque chose a droite. La marge basse lui laisse sa place sans
   recouvrir le trait de l'onglet courant.

   Meme parti que les filtres du catalogue, et le meme seuil. */
@media (max-width: 640px) {
    .admin-nav {
        flex-wrap: nowrap;
        overflow-x: auto;
        padding-bottom: var(--espace-xs);
        scrollbar-width: thin;
    }

    /* Les onglets gardent leur largeur : un libelle coupe en deux lignes
       serait pire que le defilement. */
    .admin-nav a {
        flex: 0 0 auto;
        white-space: nowrap;
    }
}

/* --- Barre d'action des ecrans d'administration -------------------------- */
/*
   Meme parti que le rappel de commande du catalogue : ce qui compte reste au
   bas de la fenetre. Sur le choix des produits d'une distribution, le compte
   des produits retenus et l'enregistrement etaient dans l'entete, hors de vue
   des la premiere avancee dans quatre cents cases.
*/
.barre-action {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 9;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--espace-m);
    padding: var(--espace-xs) var(--espace-m);
    background: var(--fond);
    border-top: 1px solid var(--bordure);
    box-shadow: 0 -8px 24px -18px rgb(22 32 26 / 45%);
}

/* Rien ne se replie dans une barre : un libelle sur deux lignes la ferait
   grandir, et elle mange deja le bas de la page. */
.barre-action .discret {
    white-space: nowrap;
}

/* La barre couvre le bas de la page : sans cette reserve, le dernier groupe
   reste inatteignable. */
.admin-avec-barre {
    padding-bottom: calc(var(--cible) + var(--espace-l));
}

/* Les archives tiennent deux listes sans rapport l'une avec l'autre. Les
   onglets reprennent la barre d'administration : meme repere, meme geste. */
.onglets {
    display: flex;
    gap: var(--espace-m);
    margin-bottom: var(--espace-m);
    border-bottom: 1px solid var(--bordure);
}

.onglet {
    min-height: var(--cible);
    padding: var(--espace-xs) 0;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    color: var(--texte-discret);
    font: inherit;
    cursor: pointer;
}

.onglet:hover {
    color: var(--texte);
    border-bottom-color: var(--bordure);
}

.onglet[aria-selected="true"] {
    color: var(--texte);
    border-bottom-color: var(--marque);
}

.pagination {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--espace-s);
    margin-top: var(--espace-s);
}

/* La date de la demande suit l'etiquette sans la bousculer : sur telephone elle
   passe a la ligne plutot que d'etirer la colonne. */
.membre-demande {
    display: inline-block;
    margin-left: var(--espace-xs);
    font-size: 0.85rem;
}

.champ-recherche {
    margin-bottom: 0;
    min-width: 16rem;
}

.bouton-danger {
    border-color: var(--echec);
    background: var(--echec);
    color: var(--fond);
}

.bouton-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--echec) 85%, black);
}

.etiquette[data-membre="0"] {
    border-color: var(--echec);
    background: color-mix(in srgb, var(--echec) 10%, white);
    color: color-mix(in srgb, var(--echec) 85%, black);
    font-weight: 600;
}


/* --- Taches benevoles ---------------------------------------------------- */

.taches {
    margin: 0;
    padding: 0;
    list-style: none;
    background: var(--fond);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    box-shadow: var(--ombre);
}

.tache {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--espace-xs);
    padding: var(--espace-xs) var(--espace-s);
    border-bottom: 1px solid var(--bordure);
}

.tache:last-child {
    border-bottom: none;
}

.tache-libelle,
.tache-champ {
    flex: 1;
    min-width: 12rem;
}

.tache-champ {
    min-height: var(--cible);
    padding: 0 var(--espace-xs);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    font: inherit;
    font-size: 1rem;
}

.tache-mienne {
    background: color-mix(in srgb, var(--marque) 8%, white);
}

.ajout-tache {
    display: flex;
    align-items: flex-end;
    gap: var(--espace-xs);
    margin-top: var(--espace-s);
}

/* Le champ porte la liste de completion : il lui faut un contenant positionne,
   sinon la liste pousserait le bouton « Ajouter » vers le bas a chaque frappe. */
.ajout-champ {
    position: relative;
    flex: 1;
    min-width: 0;
}

.suggestions {
    position: absolute;
    z-index: 20;
    top: calc(100% + var(--pas));
    left: 0;
    right: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    max-height: 16rem;
    overflow-y: auto;
    background: var(--fond);
    border: 1px solid var(--marque);
    border-radius: var(--rayon);
    box-shadow: var(--ombre);
}

.suggestion-choix {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--espace-s);
    width: 100%;
    min-height: var(--cible);
    padding: var(--espace-xs) var(--espace-s);
    border: 0;
    border-bottom: 1px solid var(--bordure);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.suggestion:last-child .suggestion-choix {
    border-bottom: 0;
}

.suggestion-choix:hover,
.suggestion[aria-selected="true"] .suggestion-choix {
    background: color-mix(in srgb, var(--marque) 10%, var(--fond));
}

/* --- Nuage des taches deja employees ------------------------------------- */

.nuage-taches {
    margin-top: var(--espace-m);
}

.nuage-titre {
    margin: 0 0 var(--espace-xs);
    color: var(--texte-discret);
    font-size: 0.9rem;
}

.nuage-pastilles {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-xs);
}

/* Les libelles vont jusqu'a soixante-quatre caracteres : une pastille doit
   pouvoir se replier sur deux lignes plutot que deborder de l'ecran. */
.pastille-tache {
    max-width: 100%;
    padding-top: var(--espace-xs);
    padding-bottom: var(--espace-xs);
    text-align: left;
    white-space: normal;
}

.pastille-tache::before {
    content: "+";
    margin-right: 0.4em;
    font-weight: 700;
    color: var(--marque);
}

.pastille-tache:hover::before {
    color: inherit;
}

/* Sous 640 px, une pastille de trente-huit caracteres prend sa ligne entiere :
   les quinze feraient quinze rangees, soit un nuage plus haut que la liste des
   taches qu'il sert. On n'en montre plus que dix -- les autres restent
   atteignables par la completion, qui cherche dans la liste entiere. */
@media (max-width: 640px) {
    .pastille-tache:nth-child(n + 11) {
        display: none;
    }
}

/* --- Commandes passees --------------------------------------------------- */

.commandes-passees {
    margin-top: var(--espace-l);
    padding-top: var(--espace-m);
    border-top: 1px solid var(--bordure);
}

.commandes-passees h2 {
    margin: 0 0 var(--espace-s);
    font-size: 1.1rem;
}

.commandes-passees ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

/* --- Filtres par categorie ----------------------------------------------- */

.filtres {
    margin-bottom: var(--espace-m);
}

.filtres-rang {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-xs);
}

.filtres-sous {
    margin-top: var(--espace-xs);
    padding-top: var(--espace-xs);
    border-top: 1px dashed var(--bordure);
}

.filtre {
    min-height: var(--cible);
    padding: 0 var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: 999px;
    background: var(--fond);
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.filtre:hover {
    border-color: var(--marque);
}

.filtre[aria-pressed="true"] {
    background: var(--marque);
    border-color: var(--marque);
    color: var(--marque-contraste);
}

.filtre-compte {
    opacity: 0.6;
    margin-left: 0.3em;
}

.filtre[aria-pressed="true"] .filtre-compte {
    opacity: 0.8;
}

/* Sur mobile, les filtres defilent horizontalement plutot que d'occuper
   plusieurs lignes et de repousser les produits hors de l'ecran. */
@media (max-width: 640px) {
    .filtres-rang {
        flex-wrap: nowrap;
        overflow-x: auto;
        padding-bottom: var(--espace-xs);
        scrollbar-width: thin;
    }

    .filtre {
        flex: 0 0 auto;
    }
}

/* Une liste longue dans un dialogue defile a l'interieur du dialogue, pas de la
   page : sans cela le dialogue depasse l'ecran et ses boutons deviennent
   inatteignables. */
.tableau-defilant {
    max-height: 50vh;
    overflow-y: auto;
    margin-bottom: var(--espace-s);
}

/* --- Administration du catalogue ----------------------------------------- */

.catalogue-producteur {
    margin-bottom: var(--espace-s);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
}

.catalogue-producteur > summary {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--espace-s);
    min-height: var(--cible);
    padding: var(--espace-s) var(--espace-m);
    cursor: pointer;
}

.catalogue-producteur > summary:focus-visible {
    outline: 2px solid var(--marque);
    outline-offset: -2px;
}

.catalogue-producteur-nom {
    font-weight: 600;
}

.catalogue-producteur-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-xs);
    padding: 0 var(--espace-m) var(--espace-s);
}

.catalogue-producteur .tableau {
    margin: 0;
}

/* L'archivage est mis a l'ecart des actions courantes : il n'a pas le meme
   poids que corriger une description, et se trouver sous le doigt qui vise le
   bouton voisin n'est pas la place d'un geste qui vide un catalogue. */
.catalogue-producteur-actions [data-action="archiver-producteur"] {
    margin-left: auto;
}

/* Un dialogue de formulaire est plus large : trois champs par ligne y tiennent
   sur un ecran de bureau, un seul sur telephone. */
.dialogue-large {
    width: min(44rem, calc(100vw - 2 * var(--espace-m)));
}

.champ-groupe {
    display: flex;
    flex-wrap: wrap;
    gap: var(--espace-s);
}

.champ-groupe > .champ {
    flex: 1 1 10rem;
}

.champ-groupe > .choix {
    flex: 0 0 auto;
}

.dialogue-corps textarea,
.dialogue-corps select {
    width: 100%;
    min-height: var(--cible);
    padding: var(--espace-xs);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
    color: inherit;
    font: inherit;
    /* 1rem au minimum : en dessous, iOS zoome sur le champ actif. */
    font-size: 1rem;
}

.dialogue-corps textarea {
    resize: vertical;
}

.dialogue-corps input[type="file"] {
    font: inherit;
    font-size: 1rem;
}

/* --- Ecrans publics : inscription, mot de passe oublie -------------------- */

/* La meme marge interieure que .catalogue, .admin et .panier : sans elle le
   titre touchait le bandeau, qui est colle en haut. */
.formulaire-public {
    max-width: 28rem;
    margin: 0 auto;
    padding: var(--espace-m);
}

.formulaire-public h1 {
    margin-bottom: var(--espace-xs);
}

.formulaire-public .discret {
    margin-bottom: var(--espace-m);
}

.dialogue-liens {
    display: flex;
    justify-content: space-between;
    gap: var(--espace-s);
    margin: var(--espace-s) 0 0;
    font-size: 0.9rem;
}

/* Les lignes rejetees d'un import : une liste courte, defilante si le fichier
   etait mauvais de bout en bout. */
.liste-rejets {
    max-height: 40vh;
    overflow-y: auto;
    margin: 0 0 var(--espace-s);
    padding-left: var(--espace-m);
    font-size: 0.9rem;
}

.dialogue-corps code {
    font-size: 0.85em;
    word-break: break-word;
}

/* --- Vignettes ----------------------------------------------------------- */
/* Le catalogue d'administration, et la liste des produits d'une fiche
   producteur. */

.vignette {
    width: 40px;
    height: 40px;
    flex: 0 0 auto;
    object-fit: cover;
    border-radius: var(--rayon);
    background: var(--fond-doux);
}

.produit-avec-vignette {
    display: flex;
    align-items: center;
    gap: var(--espace-s);
}

/* Sur une ligne de titre, la vignette se range sur la hauteur du texte plutot
   que sur celle d'une rangee de tableau. */
.catalogue-producteur > summary .vignette,
.groupe-titre .vignette {
    width: 32px;
    height: 32px;
}

.catalogue-producteur-contact {
    margin: 0 var(--espace-m) var(--espace-s);
}

/* La description dans sa propre carte : elle vient d'ailleurs -- c'est le texte
   du producteur, pas une donnee de gestion -- et le cadre le montre sans avoir
   a l'ecrire. Fond doux plutot que blanc : la fiche est deja sur du blanc. */
.catalogue-producteur-description {
    margin: 0 var(--espace-m) var(--espace-s);
    padding: var(--espace-s) var(--espace-m);
    border: 1px solid var(--bordure);
    border-left: 3px solid var(--marque);
    border-radius: var(--rayon);
    background: var(--fond-doux);
}

.catalogue-producteur-description:empty {
    /* Sans texte, la carte se reduit a une ligne : elle reste visible, mais ne
       prend pas la place d'une description. */
    color: var(--texte-discret);
    font-style: italic;
}

.catalogue-producteur-description:empty::before {
    content: 'Aucune description.';
}

.catalogue-producteur-edition {
    margin: 0 var(--espace-m) var(--espace-s);
}

.catalogue-producteur-edition textarea {
    width: 100%;
    padding: var(--espace-xs);
    border: 1px solid var(--bordure);
    border-radius: var(--rayon);
    background: var(--fond);
    color: inherit;
    font: inherit;
    /* 1rem au minimum : en dessous, iOS zoome sur le champ actif. */
    font-size: 1rem;
    resize: vertical;
}

/* Les coordonnees du producteur : sur une ligne quand la place le permet, en
   colonne sur telephone, ou une cible tactile ne doit pas en toucher une autre. */
.catalogue-producteur-contact {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--espace-xs) var(--espace-s);
    font-style: normal;
}

.catalogue-producteur-contact a {
    min-height: var(--cible);
    display: inline-flex;
    align-items: center;
}

/* Une coordonnee dans un tableau : la cible tactile occupe la cellule, sans
   quoi il faut viser le texte lui-meme. */
.tableau td > a[href^="mailto:"],
.tableau td > a[href^="tel:"] {
    display: inline-flex;
    align-items: center;
    min-height: var(--cible);
}

/* --- Annonce de la distribution ------------------------------------------ */

/* L'information que l'adherent vient chercher : quand, ou, et jusqu'a quand
   commander. Elle etait reduite a une ligne grise sous un titre. */
.annonce {
    margin-bottom: var(--espace-m);
    padding: var(--espace-m);
    border: 1px solid var(--bordure);
    border-left: 4px solid var(--marque);
    border-radius: var(--rayon);
    background: var(--fond);
    box-shadow: var(--ombre);
}

.annonce p {
    margin: 0;
}

.annonce-distribution {
    font-size: 1.25rem;
    line-height: 1.4;
}

.annonce-distribution strong {
    color: var(--marque);
}

.annonce-commande {
    margin-top: var(--espace-xs) !important;
    color: var(--texte-discret);
}

@media (max-width: 640px) {
    .annonce-distribution {
        font-size: 1.1rem;
    }
}

/* --- Appel a l'aide, dans le bandeau ------------------------------------- */

.annonce-aide {
    margin-top: var(--espace-s) !important;
}

/* Le meme appel en tete de l'ecran de commande : il y a sa propre marge, le
   bloc d'annonce du catalogue ne l'entourant pas ici. */
.panier-entete .annonce-aide {
    margin-bottom: 0 !important;
}

/* L'appel a l'aide prend l'orange de la charte : « ce qui attend un geste ».
   Il se distingue ainsi du vert de la commande -- deux appels de couleurs
   differentes ne se disputent pas, ils se repartissent.

   Le texte y est en blanc, comme sur l'affiche : c'est la combinaison de la
   charte pour un aplat orange, et le libelle d'un bouton se lit comme un
   element d'interface. */
.bouton-aide[data-libres]:not([data-libres="0"]) {
    background: var(--orange);
    border-color: var(--orange);
    color: var(--orange-contraste);
}

.bouton-aide[data-libres]:not([data-libres="0"]):hover:not(:disabled) {
    background: color-mix(in srgb, var(--orange) 88%, black);
    border-color: color-mix(in srgb, var(--orange) 88%, black);
}

.bouton-aide {
    display: inline-flex;
    align-items: center;
    gap: var(--espace-xs);
}

.bouton-aide svg {
    width: 20px;
    height: 20px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* --- Plusieurs distributions ouvertes ------------------------------------
 *
 * Rien ne s'affiche tant qu'une seule est ouverte : le bandeau garde alors la
 * place qu'il a toujours eue. A partir de deux, une ligne discrete sous
 * l'annonce, et un rail qu'elle deplie -- deux rendez-vous a la fois, chevrons
 * au-dela. Les libelles vont jusqu'a quarante-six caracteres dans la base : ils
 * ont besoin de toute la largeur, d'ou la liste plutot que des onglets.
 */
.autres {
    margin-top: calc(var(--espace-m) * -1 + var(--espace-xs));
    margin-bottom: var(--espace-m);
}

.autres-ligne {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    flex-wrap: wrap;
    padding: var(--espace-xs) var(--espace-s);
    border-radius: var(--rayon);
    font-size: 0.94rem;
    color: var(--texte-discret);
}

/* Une autre commande ouverte dont le panier est vide : c'est le seul cas ou
   l'oubli coute quelque chose, et le seul ou la ligne se fait remarquer. */
.autres-ligne[data-ton="alerte"] {
    background: color-mix(in srgb, var(--alerte) 12%, transparent);
    color: color-mix(in srgb, var(--alerte) 85%, black);
}

.autres-lien {
    font: inherit;
    font-weight: 700;
    background: none;
    border: none;
    padding: 0;
    margin-left: auto;
    color: var(--marque);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    min-height: var(--cible);
}

.autres-rail {
    display: flex;
    align-items: center;
    gap: var(--espace-xs);
    margin-top: var(--espace-xs);
}

.autres-cartes {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--espace-xs);
    flex: 1;
    min-width: 0;
}

/* Sur un telephone, la rangee devient une colonne et les chevrons se rangent
   au-dessus et en dessous : deux libelles longs ne tiennent pas cote a cote. */
@media (max-width: 640px) {
    .autres-rail { flex-direction: column; align-items: stretch; }
    .autres-cartes { grid-template-columns: 1fr; }
    .autres-chevron { align-self: center; }
}

.autres-chevron {
    flex: none;
    width: var(--cible);
    height: var(--cible);
    display: grid;
    place-items: center;
    border: 1px solid var(--bordure);
    border-radius: 50%;
    background: var(--fond);
    color: var(--texte);
    cursor: pointer;
}

.autres-chevron svg {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.autres-chevron[disabled] { opacity: 0.35; cursor: default; }

.choix-distribution {
    display: flex;
    align-items: flex-start;
    gap: var(--espace-xs);
    text-align: left;
    font: inherit;
    color: var(--texte);
    background: var(--fond);
    border: 1px solid var(--bordure);
    border-left: 4px solid var(--bordure);
    border-radius: var(--rayon);
    padding: var(--espace-s);
    cursor: pointer;
    min-width: 0;
}

.choix-distribution[data-courante="true"] {
    border-left-color: var(--marque);
    box-shadow: var(--ombre);
    cursor: default;
}

/* Le rond plein dit laquelle est montree sans reposer sur la seule couleur du
   liesere -- il reste lisible pour qui ne distingue pas le vert. */
.choix-marqueur {
    flex: none;
    width: 17px;
    height: 17px;
    margin-top: 3px;
    border: 2px solid var(--bordure);
    border-radius: 50%;
    display: grid;
    place-items: center;
}

.choix-distribution[data-courante="true"] .choix-marqueur { border-color: var(--marque); }

.choix-distribution[data-courante="true"] .choix-marqueur::after {
    content: "";
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--marque);
}

.choix-corps {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.choix-quand { font-weight: 700; line-height: 1.25; }
.choix-ou { font-size: 0.88rem; color: var(--texte-discret); }

.choix-etat {
    margin-top: var(--pas);
    align-self: flex-start;
    font-size: 0.78rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--fond-doux);
    color: var(--texte-discret);
}

.choix-distribution[data-courante="true"] .choix-etat {
    background: color-mix(in srgb, var(--marque) 13%, transparent);
    color: var(--marque);
}
