/* Mobile-first : les règles de base ciblent le téléphone (rail de nav en
   bas, catégories en puces horizontales). La media query en bas de fichier
   (min-width: 800px) reprend fidèlement le design de l'app de bureau
   (ui/style.py, ui/recipes_tab.py) : rail d'icônes doré à gauche, barre du
   haut noire/or, panneau de catégories en arborescence, tableaux à en-tête
   sombre, sections non repliables mais visuellement identiques. */

* { box-sizing: border-box; }

/* Un attribut hidden doit toujours masquer l'élément, même quand une classe
   pose déjà un display explicite (ex: .champ-ligne { display: block }) —
   sans ce !important, la règle de la feuille de style de l'auteur l'emporte
   toujours sur le display:none par défaut du navigateur pour [hidden],
   quelle que soit la spécificité, et l'élément reste visible malgré
   l'attribut. */
[hidden] { display: none !important; }

/* overflow-x uniquement sur html, jamais aussi sur body : les poser tous
   les deux revient à laisser le navigateur aussi transformer le
   overflow-y (resté "visible") de body en "auto" (comportement hérité de
   CSS2.1) — body devient alors lui-même un conteneur défilant, ce qui
   casse position:sticky partout ailleurs dans l'appli (un élément sticky
   se cale sur son plus proche ANCÊTRE défilant ; si c'est ce body-là,
   jamais scrollé pour de vrai puisque le défilement réel se fait sur html,
   l'élément ne bouge jamais et semble "pas sticky du tout"). Un seul des
   deux avec overflow-x suffit à empêcher le défilement horizontal. */
html {
    overflow-x: hidden; /* garde-fou : des noms de recette/catégorie longs ne
                           doivent jamais faire apparaître de défilement
                           horizontal sur mobile */
}
html, body {
    margin: 0;
    padding: 0;
    background: var(--gris-panneau);
    color: var(--texte);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
}

a { color: inherit; text-decoration: none; }

.app-shell { min-height: 100vh; }
.colonne-principale { min-height: 100vh; display: flex; flex-direction: column; }

/* ---------- Barre du haut (noire par défaut, façon topbar de l'app) ---------- */
.barre-haut {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--noir);
    color: var(--blanc);
    padding: 14px 16px;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 10px;
}
.barre-haut-cote { display: flex; align-items: center; gap: 8px; min-width: 22px; }
.barre-haut-droite { justify-content: flex-end; }
.barre-haut-titre {
    text-align: center;
    font-weight: 700;
    font-size: 16px;
    letter-spacing: 0.02em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Variante "fiche détail / formulaire" : barre dorée, comme barreDetail
   dans l'app de bureau (ouverture d'une recette/composition). */
.barre-haut-or {
    background: var(--or);
    color: var(--noir);
}
.barre-haut-or .barre-haut-titre {
    color: var(--noir);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}
.barre-haut-fermer {
    background: transparent;
    border: none;
    font-size: 20px;
    font-weight: 700;
    color: inherit;
    line-height: 1;
    padding: 4px 6px;
    cursor: pointer;
}
.barre-haut-fermer:hover { color: var(--brique); }
.barre-haut-action {
    background: transparent;
    border: 1px solid rgba(0,0,0,0.25);
    color: var(--noir);
    font-size: 12px;
    font-weight: 700;
    padding: 6px 12px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
}
.barre-haut-action:hover { background: rgba(0,0,0,0.08); }

/* ---------- Bouton "Imprimer" (menu déroulant Étiquette de production /
   Ticket recette) ---------- */
.menu-imprimer { position: relative; display: inline-block; }
.bouton-imprimer { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.icone-imprimer-mobile { display: none; }
.menu-imprimer-liste {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.18);
    z-index: 20;
    min-width: 200px;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.menu-imprimer-liste button {
    background: transparent;
    border: none;
    text-align: left;
    padding: 10px 12px;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte);
    cursor: pointer;
}
.menu-imprimer-liste button:hover { background: var(--gris-panneau); }
@media (max-width: 799px) {
    /* Le bouton "Imprimer" devient une icône seule sur mobile (comme
       "Réimprimer" dans l'historique de traçabilité — même icône), qu'il
       s'agisse de la pilule de la barre du haut ou du petit bouton inline
       dans le tableau d'une composition. */
    .bouton-imprimer { width: 34px; height: 34px; padding: 0; border-radius: 8px; flex: 0 0 auto; }
    .bouton-imprimer .texte-desktop-imprimer { display: none; }
    .bouton-imprimer .icone-imprimer-mobile {
        display: inline-block;
        width: 16px; height: 16px;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 16px;
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' width='18' height='18'%3E%3Crect x='5' y='1.2' width='8' height='5' rx='0.6' stroke='%231a1a1a' stroke-width='1.3' fill='none'/%3E%3Crect x='2' y='6' width='14' height='6.2' rx='1.6' stroke='%231a1a1a' stroke-width='1.3' fill='none' stroke-linejoin='round'/%3E%3Ccircle cx='12.3' cy='9.1' r='0.8' fill='%231a1a1a'/%3E%3Crect x='5' y='11' width='8' height='5.6' rx='0.6' stroke='%231a1a1a' stroke-width='1.3' fill='none'/%3E%3C/svg%3E");
    }
}

/* ---------- Bouton "Exporter en PDF" (texte desktop / icône + "PDF"
   mobile) ---------- */
.bouton-exporter-pdf { display: inline-flex; align-items: center; }
.texte-mobile-pdf { display: none; align-items: center; gap: 4px; }
.icone-telechargement-mobile {
    display: inline-block;
    width: 14px; height: 14px;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 14px;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' width='18' height='18'%3E%3Cpath d='M9 2.2v8' stroke='%231a1a1a' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3Cpath d='M5.7 7.3L9 10.6l3.3-3.3' stroke='%231a1a1a' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M3 13v1.3a1.2 1.2 0 001.2 1.2h9.6a1.2 1.2 0 001.2-1.2V13' stroke='%231a1a1a' stroke-width='1.4' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
@media (max-width: 799px) {
    /* Même hauteur (34px) et mêmes coins arrondis (8px) que .bouton-imprimer
       juste au-dessus — les deux boutons doivent avoir le même gabarit. */
    .bouton-exporter-pdf {
        height: 34px;
        padding: 0 10px;
        border-radius: 8px;
        box-sizing: border-box;
    }
    .bouton-exporter-pdf .texte-desktop-pdf { display: none; }
    .bouton-exporter-pdf .texte-mobile-pdf { display: inline-flex; }
}

/* ---------- Aperçu d'étiquette dans la fenêtre modale (création +
   réimpression) ---------- */
.apercu-etiquette-conteneur {
    margin-top: 14px;
    border: 1px solid var(--gris-bordure);
    border-radius: 12px;
    overflow: hidden;
    background: #ececec;
}
.iframe-apercu-etiquette { width: 100%; height: 220px; border: none; display: block; }

.bouton-favori {
    background: transparent;
    border: none;
    color: var(--noir);
    padding: 4px 6px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
}
.bouton-favori svg path { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; }
.bouton-favori.est-favori { color: var(--brique); }
.bouton-favori.est-favori svg path { fill: currentColor; }

/* ---------- Navigation : rail fixe en bas sur mobile (icônes + libellé),
   défilable horizontalement — 6 sections ne tiennent pas toutes sur un
   écran de téléphone. ---------- */
.rail {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 10;
    display: flex;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    background: var(--blanc);
    border-top: 1px solid var(--gris-bordure);
    padding: 4px 0 max(4px, env(safe-area-inset-bottom)) 0;
}
@media (max-width: 799px) {
    /* Look "verre liquide" (façon barre d'onglets iOS 26) : impossible à
       reproduire à l'identique en CSS (le vrai Liquid Glass réagit à
       l'inclinaison de l'appareil, effet natif SwiftUI/UIKit sans
       équivalent web) — on se rapproche avec un fond translucide flouté
       (backdrop-filter, déjà natif sous Safari depuis très longtemps) +
       un léger reflet en haut du rail pour simuler le bord du verre.
       color-mix garde la teinte du thème actuel (clair ou personnalisé)
       au lieu de blanc en dur, qui jurerait sur un thème sombre. */
    .rail {
        background: color-mix(in srgb, var(--blanc) 55%, transparent);
        backdrop-filter: blur(20px) saturate(180%);
        -webkit-backdrop-filter: blur(20px) saturate(180%);
        border-top: 1px solid color-mix(in srgb, var(--blanc) 45%, transparent);
        box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.10);
    }
    .rail::before {
        content: "";
        position: absolute;
        top: 0;
        left: 8%;
        right: 8%;
        height: 1px;
        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.75), transparent);
        pointer-events: none;
    }
}
.rail-lien {
    flex: 0 0 auto;
    min-width: 76px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: 6px 4px;
    font-size: 11px;
    color: var(--texte-attenue);
    font-weight: 600;
}
.rail-icone {
    width: 20px;
    height: 20px;
    object-fit: contain;
}
/* Pastille rouge sur la cloche "Notifications" (nombre de demandes de
   collaboration + partages en attente) — positionnée par rapport à ce
   conteneur plutôt qu'à .rail-lien entier, pour rester collée au coin de
   l'icône même si le libellé texte en dessous est plus large qu'elle. */
.rail-icone-conteneur { position: relative; display: inline-flex; }
.pastille-notification {
    position: absolute;
    top: -4px;
    right: -6px;
    min-width: 14px;
    height: 14px;
    padding: 0 3px;
    border-radius: 999px;
    background: var(--brique);
    color: var(--blanc);
    font-size: 9px;
    font-weight: 700;
    line-height: 14px;
    text-align: center;
}
.rail-lien.actif { color: var(--or-fonce); }

.contenu {
    padding: 16px;
    /* laisse la place au rail fixe en bas, plus la zone protégée en bas de
       l'écran (barre d'accueil de l'iPhone) quand l'app est ajoutée sur
       l'écran d'accueil : le rail grandit alors de env(safe-area-inset-bottom)
       (voir .rail plus bas), donc .contenu doit grandir d'autant pour ne
       pas laisser du contenu caché dessous. */
    padding-bottom: calc(84px + env(safe-area-inset-bottom));
    max-width: 1100px;
    margin: 0 auto;
    width: 100%;
}

/* ---------- Recherche / filtre ---------- */
/* Sticky en haut de page (mobile : colle au haut du viewport puisque toute
   la page défile d'un bloc ; ordinateur : colle en haut de .catalogue-contenu,
   qui défile indépendamment — voir plus bas). top:0 par défaut ici ; le
   décalage exact (marge négative + padding-top + top, tous trois égaux au
   padding-top RÉEL du conteneur ambiant) est recalculé en JS au chargement
   et au redimensionnement (voir ajusterBarresRecherche dans app.js), car ce
   padding change selon la page : 16px sur les pages catalogue, 28px sur
   Traçabilité/Paramètres qui n'ont pas cet agencement. Une valeur unique
   figée en dur ne pouvait être juste que sur l'un des deux cas — mauvaise
   sur l'autre, elle y laissait soit un vide au-dessus une fois collée
   (valeur trop petite), soit empêchait carrément le collage (valeur trop
   grande, la marge négative dépassant le padding réel disponible). Fond =
   celui de l'appli (--gris-panneau, pas blanc) pour se fondre avec la page. */
.barre-recherche {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 14px;
    position: sticky;
    top: 0;
    z-index: 4;
    background: var(--gris-panneau);
    padding-bottom: 10px;
}
.champ-select-tri { flex: 1 1 200px; min-width: 0; }
.barre-recherche input[type="search"] {
    flex: 1;
    padding: 10px 14px;
    border: 1px solid var(--gris-bordure);
    border-radius: 10px;
    background: var(--gris-panneau);
    font-size: 15px;
    color: var(--texte);
}
.barre-recherche input[type="search"]:focus {
    outline: none;
    border: 1.5px solid var(--or);
    background: var(--blanc);
}
/* Enveloppe posée en JS (voir app.js) autour du <input type="search"> pour y
   loger la croix d'effacement — reprend le flex:1 que portait l'input seul. */
.champ-recherche-effacable {
    position: relative;
    flex: 1;
    min-width: 0;
}
.champ-recherche-effacable input[type="search"] {
    width: 100%;
    box-sizing: border-box;
}
.champ-recherche-effacable.a-texte input[type="search"] {
    padding-right: 36px;
}
.barre-recherche input[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}
.bouton-effacer-recherche {
    display: none;
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: none;
    background: transparent;
    border-radius: 999px;
    color: var(--texte-attenue);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
}
.champ-recherche-effacable.a-texte .bouton-effacer-recherche {
    display: flex;
}
.bouton-effacer-recherche:hover {
    background: var(--gris-bordure);
    color: var(--texte);
}

/* Remplacées sur mobile par le panneau de catégories coulissant (voir
   .panneau-lateral.ouvert plus bas) : une seule ligne à défilement
   horizontal masquait facilement des catégories (rien n'indique qu'on peut
   scroller) et écrasait la hiérarchie en préfixes "›" dans le texte. */
.puces-categories {
    display: none;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
    margin-bottom: 14px;
    -webkit-overflow-scrolling: touch;
}
.puce-categorie {
    flex: 0 0 auto;
    padding: 6px 14px;
    border-radius: 999px;
    border: 1px solid var(--gris-bordure);
    background: var(--blanc);
    font-size: 13px;
    font-weight: 600;
    color: var(--texte-attenue);
    white-space: nowrap;
}
.puce-categorie.actif {
    background: var(--or);
    border-color: var(--or-fonce);
    color: var(--noir);
}

.compteur {
    font-size: 13px;
    color: var(--texte-attenue);
    margin-bottom: 10px;
    font-style: italic;
}

/* ---------- Bouton "+ Nouvelle…" flottant (mobile) ---------- */
.bouton-flottant {
    display: inline-block;
    background: var(--or);
    color: var(--noir);
    border: 1px solid var(--or-fonce);
    padding: 8px 16px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 700;
}
/* "+ Nouvelle…" et "Sélectionner" côte à côte plutôt que chacun sur sa
   propre ligne — évite de perdre une ligne entière juste pour ce bouton. */
.ligne-boutons-catalogue {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 14px;
}
.ligne-boutons-catalogue .bouton-flottant,
.ligne-boutons-catalogue form {
    margin-bottom: 0;
}
/* "Tout sélectionner" : caché tant que le mode sélection n'est pas actif
   (voir .visible, posée par JS en même temps que "Sélectionner" devient
   "Annuler"). */
.bouton-tout-selectionner { display: none; margin-bottom: 0; }
.bouton-tout-selectionner.visible { display: inline-block; }

/* ---------- Bouton "Action" flottant (mobile uniquement) : regroupe
   "+ Nouvelle…"/"Catégorie"/"Sélectionner" (habituellement en haut de page,
   hors de portée du pouce sur un grand téléphone tenu à une main) dans un
   bouton rond en bas à droite, au-dessus du rail de nav. Un tap déploie un
   petit menu de pastilles ; l'icône "+" tourne de 45° pour former une croix
   tant qu'il est ouvert — voir .fab-actions.ouvert plus bas et le JS dans
   app.js. .ligne-boutons-catalogue (la ligne du haut) reste affichée sur
   ordinateur uniquement : ces actions y restent, inchangées. ---------- */
.fab-actions { display: none; }
@media (max-width: 799px) {
    .ligne-boutons-catalogue { display: none; }
    .fab-actions { display: block; }
}
.fab-bouton {
    position: fixed;
    right: 16px;
    bottom: calc(60px + max(4px, env(safe-area-inset-bottom)) + 12px);
    width: 56px;
    height: 56px;
    padding: 0;
    border-radius: 50%;
    background: var(--or);
    border: 1px solid var(--or-fonce);
    box-shadow: 0 8px 20px rgba(0,0,0,0.3);
    z-index: 70;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
.fab-icone { width: 22px; height: 22px; transition: transform 0.2s ease; }
.fab-actions.ouvert .fab-icone { transform: rotate(45deg); }
.fab-menu {
    position: fixed;
    right: 16px;
    bottom: calc(60px + max(4px, env(safe-area-inset-bottom)) + 12px + 56px + 10px);
    z-index: 70;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    opacity: 0;
    pointer-events: none;
    transform: translateY(8px);
    transition: opacity 0.15s ease, transform 0.15s ease;
}
.fab-actions.ouvert .fab-menu { opacity: 1; pointer-events: auto; transform: translateY(0); }
.fab-menu-item {
    background: var(--blanc);
    color: var(--noir);
    border: 1px solid var(--gris-bordure);
    border-radius: 999px;
    padding: 10px 18px;
    font-size: 14px;
    font-weight: 700;
    white-space: nowrap;
    box-shadow: 0 4px 14px rgba(0,0,0,0.18);
}
/* "+ Nouvelle…" reprend le fond du bouton rond (même couleur d'accent que
   choisie dans Paramètres, ex. l'or de "Or & Anthracite") plutôt que le
   blanc des autres pastilles du menu, pour rester cohérent avec le reste
   des boutons de création de l'appli. */
.fab-menu-item.fab-menu-item-principal { background: var(--or); border-color: var(--or-fonce); color: var(--noir); }

/* Bouton rond "+" (page Collaborateurs) : contrairement à .fab-bouton
   ci-dessus (qui déploie un menu et n'existe que sur mobile), celui-ci ouvre
   directement la modale "Ajouter un collaborateur" et reste visible à
   toutes les tailles d'écran — la page Collaborateurs n'a pas d'autre
   endroit où loger cette action sur ordinateur. */
.fab-bouton-simple {
    position: fixed;
    right: 16px;
    bottom: calc(60px + max(4px, env(safe-area-inset-bottom)) + 12px);
    width: 56px;
    height: 56px;
    padding: 0;
    border-radius: 50%;
    background: var(--or);
    border: 1px solid var(--or-fonce);
    box-shadow: 0 8px 20px rgba(0,0,0,0.3);
    z-index: 70;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
@media (min-width: 800px) {
    .fab-bouton-simple { bottom: 24px; }
}

/* ---------- Bouton rond de sélection (ordinateur uniquement) : remplace
   l'ancien bouton texte "Sélectionner" (retiré de .ligne-boutons-catalogue)
   par un bouton flottant en bas à droite, même esprit que le bouton
   "Action" mobile — icône curseur plutôt que "+" puisqu'il n'ouvre pas de
   sous-menu, un clic bascule directement le mode sélection (classe
   .bouton-mode-selection réutilisée telle quelle : la logique JS de
   app.js s'applique sans changement). Le rail passant à gauche sur
   ordinateur (pas de barre de nav en bas), bottom:18px suffit — même valeur
   que .barre-actions-groupe en bas, pour un alignement visuel cohérent.
   Sélecteur à DEUX classes (.bouton-mode-selection.bouton-selection-desktop)
   plutôt qu'une seule : ".bouton-mode-selection { display: inline-block; }"
   existe ailleurs (pour l'ancien bouton texte, plus bas dans ce fichier) —
   à spécificité égale (une seule classe), cette règle-là l'emportait sur
   mon "display:none" ci-dessous, y compris sur MOBILE (elle n'est pas
   scopée à un media query), ce qui laissait ce bouton s'afficher vide, en
   haut de page, hors de sa position fixed. Deux classes gagne dans tous
   les cas, peu importe l'ordre. */
.bouton-mode-selection.bouton-selection-desktop { display: none; }
@media (min-width: 800px) {
    .bouton-mode-selection.bouton-selection-desktop {
        display: flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        right: 16px;
        bottom: 18px;
        width: 56px;
        height: 56px;
        padding: 0;
        border: 1px solid var(--or-fonce);
        border-radius: 50%;
        background: var(--or);
        box-shadow: 0 8px 20px rgba(0,0,0,0.3);
        z-index: 70;
        cursor: pointer;
        color: transparent;
        text-indent: -9999px;
        overflow: hidden;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 22px;
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='5 3 14 14'%3E%3Cpath d='M5 3l14 6-6.2 2.2L10.6 17 5 3z' fill='%231a1a1a'/%3E%3C/svg%3E");
    }
    .bouton-mode-selection.bouton-selection-desktop.mode-actif { background-color: var(--brique); border-color: var(--brique); }
}

/* ---------- Barre alphabet (Recettes/Compositions/Ingrédients) : filtre en
   direct la liste déjà affichée sur la première lettre du nom (voir le JS
   dans app.js). Une seule ligne à défilement horizontal (demandé
   explicitement) plutôt que de passer à la ligne. */
.barre-alphabet {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    margin-bottom: 14px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 4px;
}
.lettre-alphabet {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    padding: 0;
    border-radius: 6px;
    border: 1px solid var(--gris-bordure);
    background: var(--blanc);
    color: var(--texte);
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
    /* Certaines lettres sont des <a> (Recettes/Compositions, navigation
       ?lettre= réelle) plutôt que des <button> (Ingrédients, filtre en
       direct) : un <a> est "inline" par défaut, qui ignore width/height et
       ne centre pas son texte tout seul comme le fait un <button> — d'où
       ce display+centrage explicite, valable pour les deux cas. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}
.lettre-alphabet:hover { background: var(--or-clair); }
.lettre-alphabet.actif { background: var(--or); border-color: var(--or-fonce); color: var(--noir); }
.lettre-alphabet.indisponible { opacity: 0.3; cursor: default; pointer-events: none; }
.bouton-lettre-favoris { display: flex; align-items: center; justify-content: center; }
.bouton-lettre-favoris svg path { fill: var(--brique); }
.bouton-lettre-favoris.actif { background: var(--or); border-color: var(--or-fonce); }

/* ---------- Catalogue : panneau latéral (desktop) + contenu ---------- */
.catalogue { display: block; }
.panneau-lateral { display: none; }

/* ---------- Panneau de catégories coulissant (mobile) : réutilise le même
   <aside class="panneau-lateral"> que le panneau latéral fixe d'ordinateur
   (même arborescence, même lien "Gérer les catégories") plutôt que de
   dupliquer le HTML — juste présenté en feuille remontant du bas, ouverte/
   fermée par JS (.ouvert) via le bouton "Catégories" à côté de la barre de
   recherche. ---------- */
.bouton-categories-mobile { display: inline-flex; }
.panneau-lateral-fond {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(33, 29, 22, 0.5);
    z-index: 900;
}
.panneau-lateral-fond.visible { display: block; }
.panneau-lateral-fermer {
    display: none;
    position: absolute;
    top: 14px;
    right: 14px;
    flex: 0 0 auto;
    background: var(--gris-panneau);
    border: none;
    border-radius: 50%;
    width: 28px;
    height: 28px;
    font-size: 15px;
    color: var(--texte-attenue);
    cursor: pointer;
}
@media (max-width: 799px) {
    .panneau-lateral.ouvert {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 901;
        max-height: 75vh;
        background: var(--blanc);
        border-radius: 16px 16px 0 0;
        box-shadow: 0 -8px 30px rgba(0,0,0,0.25);
        padding: 20px 16px calc(20px + env(safe-area-inset-bottom));
        overflow-y: auto;
    }
    /* "+ Nouvelle…" est déjà accessible juste au-dessus de la barre de
       recherche (.bouton-flottant) — inutile en double dans la feuille. */
    .panneau-lateral.ouvert .bouton-nouveau { display: none; }
    .panneau-lateral.ouvert .panneau-lateral-fermer { display: block; }
}

.panneau-lateral-titre {
    font-weight: 700;
    font-size: 13px;
    color: var(--texte);
    margin: 4px 0 10px 2px;
}
.bouton-nouveau {
    display: block;
    width: 100%;
    text-align: center;
    background: var(--or);
    color: var(--noir);
    border: none;
    border-radius: 16px;
    padding: 9px 12px;
    font-weight: 700;
    font-size: 14px;
    margin-bottom: 14px;
}
.bouton-nouveau:hover { background: var(--or-fonce); }

.arborescence-categories { display: flex; flex-direction: column; gap: 2px; }
.categorie-lien {
    display: block;
    padding: 7px 10px;
    padding-left: calc(10px + (var(--prof, 0) * 14px));
    border-radius: 8px;
    font-size: 13px;
    color: var(--texte);
}
.categorie-lien:hover { background: #faf7f0; }
.categorie-lien.actif {
    background: var(--or-clair);
    color: var(--texte);
    font-weight: 600;
}
.lien-gerer-categories {
    display: block;
    margin-top: 10px;
    padding: 7px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--texte-attenue);
}
.lien-gerer-categories:hover { color: var(--or-fonce); }

/* ---------- Page de gestion des catégories ---------- */
.liste-gestion-categories { display: flex; flex-direction: column; gap: 6px; }
.ligne-gestion-categorie {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    padding-left: calc(10px + (var(--prof, 0) * 18px));
    border-radius: 8px;
    border: 1px solid var(--gris-bordure);
}
.nom-gestion-categorie { font-weight: 700; flex: 1 1 140px; min-width: 0; }
.form-gestion-categorie { display: flex; align-items: center; gap: 6px; }
.champ-texte-compact { width: 140px; padding: 6px 8px; }
.champ-select-compact { width: 170px; padding: 6px 8px; }

/* ---------- Grille de cartes (recettes / compositions) ---------- */
.grille-cartes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
}
.carte {
    display: block;
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
    border-radius: 14px;
    padding: 12px;
}
.carte:hover, .carte:active { border-color: var(--or); }

/* ---------- Cartes catalogue (recettes/compositions/productions) : case à
   cocher + glisser-déposer vers une catégorie, en plus du lien classique.
   Structure : .carte-catalogue > .case-carte + .carte-lien-catalogue
   (empêche d'imbriquer une case dans un <a>, invalide en HTML). ---------- */
.carte-catalogue { position: relative; padding: 0; }
.carte-catalogue.glisse-en-cours { opacity: 0.4; }
/* La case à cocher elle-même ne s'affiche jamais, y compris en mode
   sélection — la carte cochée se distingue déjà par son surlignage (bordure
   + fond au survol ci-dessous), une case en plus serait redondante. Elle
   reste dans le DOM (juste invisible) : c'est elle qui est réellement
   cochée/décochée en JS et envoyée avec le formulaire. */
.case-carte, .case-fusion { display: none; }
.carte-catalogue:has(.case-carte input:checked) { border-color: var(--or); }
.carte-lien-catalogue { display: block; padding: 12px; }

/* ---------- Mode sélection (bouton "Sélectionner"/"Annuler") : toute la
   carte devient cliquable pour cocher/décocher, plus pratique qu'une case
   minuscule à viser (surtout au doigt) — voir case-carte/case-fusion
   ci-dessus, jamais affichées. Les cartes non cochées sont légèrement
   délavées (pas encore sélectionnées) ; une carte cochée reprend sa
   luminosité normale et se distingue par son contour doré (déjà posé plus
   haut via :has(input:checked), pas besoin de le redéfinir ici). ---------- */
.mode-selection .carte-catalogue,
.mode-selection .carte-ligne-fusionnable,
.mode-selection .bloc-etiquette {
    cursor: pointer;
    opacity: 0.5;
    transition: opacity 0.12s ease;
}
/* Bouton Imprimer d'une carte de liste (voir ingredients_liste.html) : masqué
   en mode sélection, où cliquer n'importe où sur la carte doit cocher/décocher
   plutôt que déclencher une action individuelle. */
.mode-selection .carte-ligne-fusionnable .bouton-imprimer { display: none; }
.mode-selection .carte-catalogue:has(.case-carte input:checked),
.mode-selection .carte-ligne-fusionnable:has(.case-fusion input:checked),
.mode-selection .bloc-etiquette:has(.case-fusion input:checked) {
    opacity: 1;
}
.bloc-etiquette:has(.case-fusion input:checked) { border-color: var(--or); }

/* ---------- Vue liste du catalogue : cartes en ligne plutôt qu'en grille ---------- */
.grille-cartes.vue-liste { grid-template-columns: 1fr; gap: 8px; }
.grille-cartes.vue-liste .carte-catalogue { overflow: hidden; }
.grille-cartes.vue-liste .carte-lien-catalogue {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
}
.grille-cartes.vue-liste .carte-vignette { width: 48px; height: 48px; flex: 0 0 auto; margin-bottom: 0; font-size: 18px; }
.grille-cartes.vue-liste .carte-texte { flex: 1; min-width: 0; }
.grille-cartes.vue-liste .carte-titre,
.grille-cartes.vue-liste .carte-categorie,
.grille-cartes.vue-liste .carte-meta {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------- Bascule Grille/Liste ---------- */
.bascule-vue { display: inline-flex; flex: 0 0 auto; border-radius: 10px; overflow: hidden; border: 1px solid var(--gris-bordure); }
.bouton-vue {
    /* border:none + appearance:none : sans ça, un <button> (contrairement à
       un <a>) garde le relief/biseau natif du navigateur par-dessus ce
       style — invisible pour Grille/Liste (des <a>), mais visible et signalé
       comme "moche" sur la bascule Production/INCO de Traçabilité, qui
       utilise de vrais <button>. */
    border: none;
    appearance: none;
    -webkit-appearance: none;
    font-family: inherit;
    cursor: pointer;
    padding: 10px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte-attenue);
    background: var(--blanc);
}
.bouton-vue.actif { background: var(--or); color: var(--noir); font-weight: 700; }
/* Bascule Production/INCO en haut de la page Traçabilité — même composant
   visuel que .bascule-vue (Grille/Liste des catalogues), juste avec de la
   marge en dessous puisqu'elle n'est pas accolée à la barre de recherche. */
.bascule-tracabilite { margin-bottom: 14px; }

/* ---------- Mobile : tri et bascule grille/liste réduits à des icônes,
   pour laisser le plus de place possible au champ de recherche. Le select
   de tri reste un vrai <select> natif (fonctionnel, accessible) — juste son
   texte est masqué (color transparent) au profit d'un pictogramme, même
   principe que la flèche des autres <select> de l'appli. Pour grille/liste,
   les deux liens existent déjà dans le HTML (utiles tels quels sur
   ordinateur) : seul celui qui n'est PAS la vue actuelle reste visible ici,
   comme icône seule — pas besoin de dupliquer le balisage. ---------- */
@media (max-width: 799px) {
    .champ-select.champ-select-tri {
        flex: 0 0 40px;
        width: 40px;
        min-width: 40px;
        padding: 0;
        border: 1px solid var(--gris-bordure);
        border-radius: 10px;
        color: transparent;
        text-indent: -9999px;
        background-color: var(--gris-panneau);
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Crect x='2' y='4' width='14' height='2' rx='1' fill='%236b6558'/%3E%3Crect x='2' y='8' width='10' height='2' rx='1' fill='%236b6558'/%3E%3Crect x='2' y='12' width='6' height='2' rx='1' fill='%236b6558'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
    }
    .bascule-vue:has(.bouton-vue-grille, .bouton-vue-liste) { border: none; }
    .bouton-vue-grille, .bouton-vue-liste { display: none; }
    .bouton-vue-grille:not(.actif), .bouton-vue-liste:not(.actif) {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 40px;
        height: 40px;
        padding: 0;
        font-size: 0;
        background: var(--gris-panneau);
        border-radius: 10px;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
    }
    .bouton-vue-grille:not(.actif) {
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Crect x='2' y='2' width='6' height='6' rx='1' fill='%236b6558'/%3E%3Crect x='10' y='2' width='6' height='6' rx='1' fill='%236b6558'/%3E%3Crect x='2' y='10' width='6' height='6' rx='1' fill='%236b6558'/%3E%3Crect x='10' y='10' width='6' height='6' rx='1' fill='%236b6558'/%3E%3C/svg%3E");
    }
    .bouton-vue-liste:not(.actif) {
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Crect x='2' y='3' width='14' height='2.5' rx='1' fill='%236b6558'/%3E%3Crect x='2' y='7.5' width='14' height='2.5' rx='1' fill='%236b6558'/%3E%3Crect x='2' y='12' width='14' height='2.5' rx='1' fill='%236b6558'/%3E%3C/svg%3E");
    }
}

/* ---------- Cible de glisser-déposer (catégories du panneau latéral) ---------- */
.categorie-cible-drop.survol-drop { background: var(--or-clair); outline: 1.5px dashed var(--or-fonce); outline-offset: -2px; }

.barre-actions-form-groupe { display: flex; gap: 10px; flex-wrap: wrap; }
.barre-actions-form-groupe .bouton-secondaire-inline, .barre-actions-form-groupe .bouton-danger-inline { width: auto; flex: 1 1 200px; }
.bouton-danger-inline { width: auto; }

/* ---------- Barre d'actions de la sélection multiple : petites pilules
   flottantes, fixées en bas de l'écran (indépendantes du scroll — pas
   seulement "sticky" dans la page), masquées tant que rien n'est coché. ---------- */
.barre-actions-groupe {
    position: fixed;
    left: 50%;
    /* Sur mobile, le rail de navigation est fixé en bas de l'écran (voir
       .rail plus haut) : sans cette marge la barre de pilules (z-index plus
       haut) se serait affichée PAR-DESSUS ses icônes. 84px+12px puis
       84px+24px se sont révélés trop généreux (mesuré : la hauteur réelle
       du rail sur mobile est d'environ 56px, contre un total de 96-108px
       pour ces deux essais — un vide flottant bien trop grand au-dessus).
       60px + max(4px, env(...)) reprend exactement la hauteur réelle du
       rail (52px de padding/contenu fixes + son propre padding-bottom bas,
       lui-même max(4px, la zone protégée iPhone)) et n'ajoute que 8px de
       vrai espacement visuel. */
    bottom: calc(60px + max(4px, env(safe-area-inset-bottom)));
    z-index: 50;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 10px;
    background: transparent;
    max-width: calc(100vw - 32px);
    transform: translateX(-50%) translateY(calc(100% + 30px));
    opacity: 0;
    pointer-events: none;
    transition: transform 0.18s ease, opacity 0.18s ease;
}
/* .barre-actions-form (partagée avec les lignes de bouton "Enregistrer"
   classiques, non fixées) pose margin:18px 0 plus bas dans ce fichier —
   même spécificité qu'un simple ".barre-actions-groupe", donc l'ordre
   d'apparition dans le fichier suffisait à faire gagner cette règle-là.
   Ici la barre est fixed et "bottom" positionne sa boîte de MARGE, pas son
   bord visible : sans annuler ce margin-bottom, un vide de 18px
   supplémentaire et non voulu s'ajoutait au-dessus du rail. Sélecteur à
   deux classes pour gagner face à ".barre-actions-form" quel que soit
   l'ordre dans le fichier. */
.barre-actions-form.barre-actions-groupe { margin: 0; }
.barre-actions-groupe.visible { transform: translateX(-50%) translateY(0); opacity: 1; pointer-events: auto; }
.barre-actions-groupe > .bouton-secondaire-inline,
.barre-actions-groupe > .bouton-danger-inline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    height: 40px;
    margin: 0;
    flex: 0 0 auto;
    border: none;
    border-radius: 999px;
    padding: 0 18px;
    font-size: 13px;
    font-weight: 700;
    white-space: nowrap;
    box-shadow: 0 8px 20px rgba(0,0,0,0.22);
}
.barre-actions-groupe > .bouton-secondaire-inline { background: var(--blanc); color: var(--noir); }
.barre-actions-groupe > .bouton-secondaire-inline:hover { background: var(--or-clair); }
.barre-actions-groupe > .bouton-danger-inline { background: var(--brique); color: var(--blanc); }
.barre-actions-groupe > .bouton-danger-inline:hover { background: #c0392b; }

/* Le bouton "coeur" (ajouter aux favoris) de la sélection groupée reste une
   icône seule à toutes les tailles d'écran (jamais de texte "Ajouter aux
   favoris" en toutes lettres sur ordinateur, contrairement à Partager/
   Supprimer) — demandé explicitement. */
.barre-actions-groupe > .bouton-favori-selection {
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    margin: 0;
    flex: 0 0 auto;
    border: none;
    border-radius: 999px;
    background: var(--blanc);
    color: var(--noir);
    cursor: pointer;
    box-shadow: 0 8px 20px rgba(0,0,0,0.22);
}
.barre-actions-groupe > .bouton-favori-selection:hover { background: var(--or-clair); }
.barre-actions-groupe > .bouton-favori-selection svg path { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linejoin: round; }

/* La barre elle-même s'affiche dès l'entrée en mode sélection (voir
   .visible plus haut, désormais lié à ce mode et non au nombre de cases
   cochées — voir app.js), avec seulement "Annuler la sélection" et "Tout
   sélectionner" dedans (celui-ci garde sa propre bascule .visible, posée
   par app.js au même moment que l'entrée en mode sélection — exclu ici
   plutôt que régi par .a-des-coches). Dès qu'au moins un élément est coché
   (.a-des-coches) : "Tout sélectionner" cède la place aux autres actions
   (Supprimer, Fusionner, Marque/fournisseur), qui elles n'ont de sens
   qu'une fois quelque chose sélectionné. Vaut aussi bien sur mobile que
   sur ordinateur. */
.barre-actions-groupe > *:not(.bouton-annuler-selection):not(.bouton-tout-selectionner) { display: none; }
.barre-actions-groupe.a-des-coches > .bouton-secondaire-inline,
.barre-actions-groupe.a-des-coches > .bouton-danger-inline,
.barre-actions-groupe.a-des-coches > .bouton-favori-selection { display: inline-flex; }
.barre-actions-groupe.a-des-coches > .bouton-tout-selectionner { display: none; }

/* Barre de sélection multiple sur mobile : "Annuler"/"Supprimer" (présents
   sur TOUTES les pages à sélection — Recettes/Compositions/Ingrédients/
   Production/Traçabilité) deviennent des pastilles rondes à icône plutôt
   que du texte, pour prendre moins de place. Ingrédients a 2 boutons de
   plus (Fusionner, Marque/fournisseur) : Marque/fournisseur disparaît sur
   mobile (ordinateur seulement) et Fusionner suit le même traitement
   icône — scopé à #form-selection-ingredients puisque ces deux-là n'existent
   que sur cette page.
   Sélecteurs ".barre-actions-groupe > .bouton-xxx" (et pas juste
   ".bouton-xxx" seul) : même spécificité à deux classes que la règle de
   base plus haut (padding/largeur en texte) — sans ça, cette dernière
   l'emportait malgré son ordre plus tôt dans le fichier. */
@media (max-width: 799px) {
    .barre-actions-groupe > .bouton-annuler-selection,
    .barre-actions-groupe > .bouton-supprimer-selection {
        width: 40px;
        padding: 0;
        color: transparent;
        text-indent: -9999px;
        overflow: hidden;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
        box-shadow: 0 8px 20px rgba(0,0,0,0.22);
    }
    .barre-actions-groupe > .bouton-annuler-selection {
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Cpath d='M4 4l10 10M14 4L4 14' stroke='%231a1a1a' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
    }
    .barre-actions-groupe > .bouton-supprimer-selection {
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Cpath d='M3 5h12M7 5V3.5a1 1 0 011-1h2a1 1 0 011 1V5M4.5 5l.7 9a1 1 0 001 .9h5.6a1 1 0 001-.9l.7-9' stroke='%23ffffff' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3Cpath d='M7.5 8v4M10.5 8v4' stroke='%23ffffff' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E");
    }

    #form-selection-ingredients .bouton-marque-fournisseur { display: none; }
    .barre-actions-groupe > .bouton-fusionner-selection {
        width: 40px;
        padding: 0;
        color: transparent;
        text-indent: -9999px;
        overflow: hidden;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
        box-shadow: 0 8px 20px rgba(0,0,0,0.22);
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Ccircle cx='7' cy='9' r='5' fill='none' stroke='%231a1a1a' stroke-width='1.5'/%3E%3Ccircle cx='11' cy='9' r='5' fill='none' stroke='%231a1a1a' stroke-width='1.5'/%3E%3C/svg%3E");
    }
    .barre-actions-groupe > .bouton-partager-selection {
        width: 40px;
        padding: 0;
        color: transparent;
        text-indent: -9999px;
        overflow: hidden;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
        box-shadow: 0 8px 20px rgba(0,0,0,0.22);
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Ccircle cx='13' cy='4.5' r='1.8' fill='none' stroke='%231a1a1a' stroke-width='1.4'/%3E%3Ccircle cx='5' cy='9' r='1.8' fill='none' stroke='%231a1a1a' stroke-width='1.4'/%3E%3Ccircle cx='13' cy='13.5' r='1.8' fill='none' stroke='%231a1a1a' stroke-width='1.4'/%3E%3Cpath d='M6.6 8.1l4.8-2.6M6.6 9.9l4.8 2.6' stroke='%231a1a1a' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E");
    }
}
.carte-vignette {
    width: 100%;
    height: 90px;
    border-radius: 10px;
    background: var(--gris-panneau);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    margin-bottom: 8px;
}
.carte-vignette-image { object-fit: cover; display: block; }
.carte-categorie {
    font-size: 10px;
    font-weight: 600;
    color: var(--texte-attenue);
    text-transform: uppercase;
    margin-top: 3px;
}
.carte-titre { font-weight: 700; font-size: 14px; overflow-wrap: break-word; }
.carte-meta { font-size: 11px; color: var(--texte-attenue); margin-top: 6px; }

.vide {
    text-align: center;
    color: var(--texte-attenue);
    padding: 40px 20px;
    font-style: italic;
}

/* ---------- Fiche détail ---------- */
.fiche-entete { margin-bottom: 16px; }
.fiche-entete h1 { font-size: 22px; margin: 0 0 4px 0; }
.fiche-categorie-liste {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
}
.badge-categorie {
    font-size: 11px;
    font-weight: 700;
    color: var(--or-fonce);
    background: var(--or-clair);
    padding: 3px 10px;
    border-radius: 999px;
}
a.badge-categorie:hover { background: var(--or); color: var(--noir); }
.badge-tag {
    font-size: 11px;
    font-weight: 600;
    color: var(--texte);
    background: var(--gris-panneau);
    border: 1px solid var(--gris-bordure);
    padding: 3px 10px;
    border-radius: 999px;
}
.apercu-image-fiche { width: 100%; max-width: 320px; height: 160px; margin-bottom: 12px; }
/* line-height/font-size à 0 : sans ça, une cellule vide garde la hauteur de
   ligne normale du tableau même avec height:2px déclaré (la plupart des
   navigateurs traitent height sur un <td> comme un minimum, pas une valeur
   fixe — Théo l'a signalé trop épais). */
tr.ligne-separateur td { padding: 0; height: 2px; line-height: 0; font-size: 0; background: var(--noir); border: none; }

.bloc {
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
    border-radius: 14px;
    padding: 14px;
    margin-bottom: 14px;
}
.bloc h2 {
    font-size: 14px;
    font-weight: 700;
    color: var(--texte);
    margin: 0 0 10px 0;
}

table.tableau-ingredients {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}
table.tableau-ingredients th {
    text-align: left;
    font-size: 12px;
    font-weight: 600;
    color: var(--blanc);
    background: var(--noir);
    padding: 9px 8px;
}
table.tableau-ingredients td {
    padding: 4px 12px;
    border-bottom: 1px solid var(--gris-panneau);
}
table.tableau-ingredients tbody tr:nth-child(even):not(.ligne-total):not(.ligne-separateur) td {
    background: rgba(0, 0, 0, 0.035);
}
table.tableau-ingredients tbody tr[data-ingredient-id],
table.tableau-ingredients tbody tr[data-recipe-id] { cursor: pointer; }
table.tableau-ingredients tbody tr[data-ingredient-id]:hover td,
table.tableau-ingredients tbody tr[data-recipe-id]:hover td { background: var(--or-clair); }
table.tableau-ingredients tr.ligne-total td {
    background: var(--noir);
    color: var(--blanc);
    font-weight: 700;
}
table.tableau-ingredients td.num, table.tableau-ingredients th.num { text-align: right; white-space: nowrap; }

.grille-macros {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
}
.macro {
    background: var(--gris-panneau);
    border-radius: 10px;
    padding: 10px;
    text-align: center;
}
.macro-valeur { font-size: 17px; font-weight: 700; }
.macro-label { font-size: 11px; color: var(--texte-attenue); text-transform: uppercase; }

/* ---------- Fenêtre modale (fiche ingrédient en double-cliquant une ligne
   dans le tableau d'une recette/composition) ---------- */
.modale-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(33, 29, 22, 0.5);
    z-index: 1000;
    align-items: center;
    justify-content: center;
    padding: 20px;
}
.modale-overlay.visible { display: flex; }
.modale-carte {
    background: var(--blanc);
    border-radius: 16px;
    max-width: 420px;
    width: 100%;
    max-height: 85vh;
    overflow-y: auto;
    padding: 20px;
}
.modale-entete { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.modale-entete h2 { margin: 0; font-size: 18px; }
.modale-fermer {
    flex: 0 0 auto;
    background: var(--gris-panneau);
    border: none;
    border-radius: 50%;
    width: 28px;
    height: 28px;
    font-size: 15px;
    color: var(--texte-attenue);
    cursor: pointer;
}
.modale-prix { font-weight: 700; font-size: 15px; margin: 2px 0 12px 0; }
.modale-corps h3 { font-size: 13px; margin: 16px 0 8px 0; }

.liste-allergenes { display: flex; flex-wrap: wrap; gap: 10px; }
/* ---------- Cercles d'allergènes (comme AllergeneCercleAffichage sur le
   bureau) : icône ronde + nom en dessous, plutôt qu'une pastille de texte. ---------- */
.allergene-cercle { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 64px; }
.allergene-cercle-disque {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1.5px solid var(--gris-bordure);
    background: var(--blanc);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    color: var(--texte-attenue);
    flex: 0 0 auto;
}
.allergene-cercle-disque img { width: 74%; height: 74%; object-fit: contain; }
.allergene-cercle-label { font-size: 11px; color: var(--texte-attenue); text-align: center; line-height: 1.2; }
.badge-allergene {
    font-size: 12px;
    background: #fbe6e6;
    color: var(--brique);
    padding: 4px 10px;
    border-radius: 999px;
    font-weight: 600;
}

.liste-recettes-composition .item-recette {
    display: block;
    border-radius: 10px;
    border: 1px solid var(--gris-bordure);
    padding: 10px 12px;
    margin-bottom: 8px;
}
.liste-recettes-composition .item-recette:hover { border-color: var(--or); }
.item-recette-entete {
    display: flex;
    justify-content: space-between;
    font-weight: 700;
}
.item-recette-meta {
    font-size: 12px;
    color: var(--texte-attenue);
    margin-top: 2px;
}
.entete-detail-recette {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-bottom: 10px;
    flex-wrap: wrap;
}
.entete-detail-recette .bouton-secondaire-inline { width: auto; }

/* ---------- Formulaires (édition recette / composition / ingrédient) ---------- */
.message-erreur {
    background: #fbe6e6;
    color: var(--brique);
    padding: 10px 14px;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 14px;
}
.champ-texte, .champ-select, .champ-poids {
    width: 100%;
    /* max-width + min-width:0 — garde-fou : un input type="date" sur Safari
       iOS impose parfois une largeur interne (jj/mm/aaaa + icône) plus
       large que son width:100% déclaré, ce qui élargit ses parents en
       cascade (jusqu'au garde-fou overflow-x:hidden sur html) au lieu de
       rester dans le cadre — l'encadré débordait alors visuellement à
       droite plutôt que de simplement s'adapter. */
    max-width: 100%;
    min-width: 0;
    padding: 10px 12px;
    border: 1px solid var(--gris-bordure);
    border-radius: 10px;
    background: var(--gris-panneau);
    font-size: 15px;
    font-family: inherit;
    color: var(--texte);
}
.champ-texte:focus, .champ-select:focus, .champ-poids:focus {
    outline: none;
    border: 1.5px solid var(--or);
    background: var(--blanc);
}
textarea.champ-texte { resize: vertical; }
/* ---------- Zone de texte éditable riche (étiquette INCO) : contenteditable
   plutôt qu'un <textarea>, pour que le gras s'affiche réellement au lieu de
   montrer les balises <b> en toutes lettres — voir rafraichirApercuInco et
   le bouton "Gras" dans _etiquette_inco_modal_nouvelle.html. ---------- */
.barre-outils-editeur { display: flex; gap: 6px; margin-bottom: 6px; }
.barre-outils-editeur button {
    border: 1px solid var(--gris-bordure);
    background: var(--blanc);
    color: var(--texte);
    padding: 5px 10px;
    border-radius: 8px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}
.barre-outils-editeur button:hover { border-color: var(--or); color: var(--or-fonce); }
.champ-editeur-riche {
    min-height: 90px;
    line-height: 1.4;
    white-space: pre-wrap;
    overflow-wrap: break-word;
}
.champ-editeur-riche:focus { outline: none; border: 1.5px solid var(--or); background: var(--blanc); }
select.champ-select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%236b6558'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right: 30px;
}

.cases-categories {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
}
.case-categorie {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 14px;
}

.ligne-ingredient {
    display: grid;
    grid-template-columns: 1fr 80px auto;
    gap: 8px;
    margin-bottom: 8px;
    align-items: center;
}
.ligne-ingredient-recette { grid-template-columns: 18px 1fr 80px 46px 34px auto; }
/* Poignée de glisser-déposer pour réordonner les ingrédients (voir le
   pointerdown/pointermove/pointerup dans app.js — les événements HTML5
   dragstart/dragover ne se déclenchent jamais sur mobile/tactile, donc
   inutilisables ici vu l'usage principalement sur téléphone). */
.poignee-glisser-ingredient {
    cursor: grab;
    color: var(--texte-attenue);
    font-size: 15px;
    line-height: 1;
    text-align: center;
    user-select: none;
    touch-action: none;
}
.ligne-ingredient-recette.ligne-glissee { opacity: 0.4; }

/* ---------- Recherche avec suggestions (ingrédient dans une recette,
   recette dans une production) : liste maison plutôt que <datalist>, dont
   Safari (bureau ET iOS) n'affiche jamais les suggestions — sans ça,
   impossible de voir les résultats correspondants en tapant. ---------- */
.champ-recherche-wrapper { position: relative; min-width: 0; }
/* position:fixed + coordonnées calculées en JS (voir positionnerMenu dans
   app.js) plutôt qu'absolute/top:100% : un ancêtre avec overflow:hidden ou
   overflow-x:auto (ex. .bloc, très courant dans l'appli) aurait sinon rogné
   le menu par le bas au lieu de le laisser flotter par-dessus tout. */
.typeahead-menu {
    display: none;
    position: fixed;
    z-index: 500;
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
    border-radius: 10px;
    box-shadow: 0 8px 20px rgba(0,0,0,0.15);
    max-height: 220px;
    overflow-y: auto;
}
.typeahead-menu.visible { display: block; }
.typeahead-option {
    padding: 9px 12px;
    font-size: 14px;
    cursor: pointer;
}
.typeahead-option:hover, .typeahead-option.actif { background: var(--or-clair); }
.typeahead-option-vide { padding: 9px 12px; font-size: 13px; color: var(--texte-attenue); font-style: italic; }
.pourcentage-ligne-ingredient {
    font-size: 12px;
    font-weight: 600;
    color: var(--texte-attenue);
    text-align: right;
    white-space: nowrap;
}
.case-separateur {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    background: var(--gris-panneau);
    color: var(--texte-attenue);
    font-weight: 700;
    cursor: pointer;
}
.case-separateur input[type="checkbox"] { display: none; }
.case-separateur:has(input:checked) { background: var(--or); color: var(--noir); }

/* ---------- Image de recette (formulaire + vignette carnet) ---------- */
.ligne-image { display: flex; gap: 14px; align-items: flex-start; flex-wrap: wrap; }
.apercu-image {
    width: 160px;
    height: 90px;
    border-radius: 10px;
    object-fit: cover;
    background: var(--gris-panneau);
}
.apercu-image-vide {
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
}
.colonne-boutons-image { display: flex; flex-direction: column; gap: 8px; }
.bouton-fichier { position: relative; overflow: hidden; cursor: pointer; }
.champ-fichier-cache {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
    width: 100%;
}

/* ---------- Conservation (formulaire recette/composition) ---------- */
.colonnes-conservation { display: flex; gap: 28px; flex-wrap: wrap; }
.colonne-conservation { flex: 1 1 200px; }
.bouton-retirer-ligne {
    border: none;
    background: var(--gris-panneau);
    color: var(--texte-attenue);
    width: 34px;
    height: 34px;
    border-radius: 8px;
    font-size: 15px;
    cursor: pointer;
}
.bouton-retirer-ligne:hover { background: #fbe6e6; color: var(--brique); }

/* ---------- Lien d'invitation (panneau Admin) : champ + bouton copier ---------- */
.ligne-lien-invitation { display: flex; gap: 8px; align-items: center; }
.ligne-lien-invitation .champ-texte { flex: 1; margin-bottom: 0; }
.bouton-copier-lien {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border: none;
    border-radius: 8px;
    background: var(--gris-panneau);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.bouton-copier-lien:hover { background: var(--or-clair); }
.bouton-copier-lien.copie { background: var(--or); }
.bouton-copier-lien.echec { background: #fbe6e6; }
.bouton-secondaire.copie { background: var(--or); border-color: var(--or-fonce); }
.bouton-secondaire.echec { background: #fbe6e6; border-color: #e0a0a0; }

.champ-prompt {
    width: 100%;
    min-height: 220px;
    resize: vertical;
    font-family: ui-monospace, "SF Mono", Menlo, monospace;
    font-size: 12px;
    line-height: 1.5;
    color: var(--texte-attenue);
}

.bouton-secondaire {
    border: 1px dashed var(--gris-bordure);
    background: transparent;
    color: var(--texte-attenue);
    padding: 9px 14px;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    width: 100%;
}
.bouton-secondaire:hover { border-color: var(--or); color: var(--or-fonce); }

.barre-actions-form { margin: 18px 0; }
.bouton-principal {
    display: block;
    width: 100%;
    background: var(--noir);
    color: var(--blanc);
    border: none;
    padding: 13px;
    border-radius: 14px;
    font-size: 16px;
    font-weight: 700;
    cursor: pointer;
}
.bouton-principal:hover { background: var(--noir-clair); }
.bouton-danger {
    display: block;
    width: 100%;
    background: transparent;
    color: var(--brique);
    border: 1px solid #eab3b3;
    padding: 11px;
    border-radius: 14px;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    margin-bottom: 24px;
}
.lien-modifier {
    display: inline-block;
    margin-top: 10px;
    font-size: 13px;
    font-weight: 700;
    color: var(--or-fonce);
}

.aide-champ {
    font-size: 12px;
    color: var(--texte-attenue);
    margin: 4px 0 10px 0;
}

/* ---------- Poids éditables dans les tableaux (fiche recette/composition) ---------- */
.champ-poids-tableau {
    width: 84px;
    padding: 6px 8px;
    border: 1px solid var(--gris-bordure);
    border-radius: 8px;
    background: var(--or-clair);
    font-size: 14px;
    font-weight: 700;
    text-align: right;
    color: var(--texte);
    font-family: inherit;
}
.champ-poids-tableau:focus { outline: none; border: 1.5px solid var(--or); background: var(--blanc); }
.ligne-total .champ-poids-tableau-total {
    background: var(--noir-clair);
    border-color: var(--noir-clair);
    color: var(--blanc);
}
.item-recette-poids { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; }
.item-recette-poids .champ-poids-tableau { width: 72px; }

/* ---------- Ingrédients : liste compacte (pas de vignette) ---------- */
.grille-cartes-liste { display: flex; flex-direction: column; gap: 8px; }
.carte-ligne { padding: 10px 14px; }
.carte-ligne-fusionnable { display: flex; align-items: center; gap: 12px; padding: 6px 14px; }
.carte-ligne-fusionnable:has(.case-fusion input:checked) { border-color: var(--or); }
.carte-lien-fusion { flex: 1; min-width: 0; padding: 6px 0; }

/* ---------- Collaborateurs : un encadré par personne (comme les cartes
   catalogue), bulle de message à droite avec pastille "non lu" ---------- */
.carte-collaborateur { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.carte-collaborateur-texte { flex: 1; min-width: 0; }
.bouton-message-collaborateur {
    position: relative;
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    background: var(--gris-panneau);
    display: flex;
    align-items: center;
    justify-content: center;
}
.bouton-message-collaborateur:hover { background: var(--or-clair); }

/* ---------- Formulaire ingrédient : nutrition, allergènes, composition détaillée ---------- */
.grille-champs-nutrition {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px 14px;
}
.grille-options-calcul {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px 14px;
}
.grille-options-calcul .champ-ligne { margin-bottom: 0; }

/* ---------- Fiche détail (recette/composition) : 3 colonnes façon bureau
   (réglages à gauche, contenu au centre, catégorie/nutrition à droite) sur
   grand écran ; une seule colonne, empilée, sur mobile. Ordre du DOM pensé
   pour le mobile (le contenu principal d'abord) ; réordonné en CSS pour le
   bureau (voir la media query plus bas). ---------- */
.detail-col-gauche { order: 1; }
.detail-col-centre { order: 0; }
.detail-col-droite { order: 2; }
.detail-layout { display: flex; flex-direction: column; }
.detail-col { min-width: 0; }
.poignee-panneau-droit { display: none; }

/* Garde-fou générique : un tableau plus large que son bloc défile dans son
   propre cadre plutôt que de déborder sur le panneau voisin ou hors de
   l'écran, quelle que soit la largeur de fenêtre. */
.bloc { overflow-x: auto; }

/* ---------- Anneaux nutrition (Kcal/Glucides/Protéines/Lipides pour 100 g),
   comme NutritionRingLegende sur le bureau. ---------- */
.grille-anneaux { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.anneau-nutrition { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.anneau-nutrition-label { font-size: 11px; font-weight: 600; color: var(--texte-attenue); text-transform: uppercase; }

/* ---------- Équilibrage (formulation glaces/sorbets) ---------- */
.liste-equilibrage { display: grid; grid-template-columns: 1fr auto; gap: 6px 12px; margin: 0; }
.liste-equilibrage dt { color: var(--texte-attenue); font-size: 13px; }
.liste-equilibrage dd { margin: 0; font-weight: 600; text-align: right; font-size: 13px; }
.grille-champs-nutrition label {
    font-size: 12px;
    font-weight: 600;
    color: var(--texte-attenue);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.grille-allergenes {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}
.allergene-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border: 1.5px solid var(--gris-bordure);
    border-radius: 999px;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte);
    cursor: pointer;
}
.allergene-toggle input { display: none; }
.allergene-toggle.coche,
.allergene-toggle:has(input:checked) { background: var(--or); border-color: var(--or-fonce); color: var(--noir); }
.allergene-toggle-icone { width: 18px; height: 18px; border-radius: 50%; object-fit: cover; }

.groupe-repliable {
    border: 1px solid var(--gris-bordure);
    border-radius: 12px;
    padding: 4px 14px;
    margin-bottom: 8px;
}
.groupe-repliable summary {
    padding: 10px 0;
    font-weight: 700;
    font-size: 13px;
    cursor: pointer;
}
.groupe-repliable-contenu {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
    padding-bottom: 12px;
}
.champ-compo {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    color: var(--texte-attenue);
    position: relative;
}
.champ-compo-suffixe {
    position: absolute;
    right: 10px;
    top: 27px;
    font-size: 12px;
    color: var(--texte-attenue);
    font-weight: 400;
    pointer-events: none;
}

/* ---------- Champs de formulaire en ligne (production, paramètres) ---------- */
/* overflow-x:hidden en plus du display:block ci-dessous : un input
   type="date" sur Safari iOS peut réclamer, en interne, une largeur figée
   (jour/mois/année + icône) plus grande que le width:100% déclaré — ni
   max-width ni min-width posés directement sur l'input n'y changent rien
   (l'input garde alors sa propre boîte plus large que prévu). En posant le
   confinement sur .champ-ligne lui-même (un simple bloc, dont la largeur
   ne dépend jamais de celle de ses enfants), l'excédent est coupé au bon
   endroit au lieu de déborder visuellement à droite. */
.champ-ligne { display: block; overflow-x: hidden; font-size: 13px; font-weight: 600; color: var(--texte-attenue); margin-bottom: 14px; }
/* display:block indispensable ici : sans lui, l'input (inline-block par
   défaut) reste sur la même ligne que le texte du label qui le précède —
   son width:100% s'ajoute alors à la largeur de ce texte au lieu de la
   remplacer, et déborde à droite du bloc (repéré sur le champ date de
   l'étiquette de production, mais touchait tous les .champ-ligne). */
.champ-ligne .champ-texte, .champ-ligne .champ-select, .champ-ligne textarea {
    display: block;
    width: 100%;
    margin-top: 4px;
}
.champ-ligne-label { display: block; font-size: 13px; font-weight: 600; color: var(--texte-attenue); margin-bottom: 6px; }
.champ-ligne-inline {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--texte-attenue);
    margin-bottom: 12px;
}
.champ-ligne-inline .champ-texte, .champ-ligne-inline .champ-select { width: auto; flex: 0 0 auto; }
/* Champ désactivé (ex. "Hauteur des étiquettes" quand "Hauteur automatique"
   est cochée) : légèrement transparent, même traitement que les cartes non
   cochées en mode sélection — pour bien montrer que le champ est inerte
   plutôt que juste vaguement plus terne. */
.champ-ligne-inline:has(input:disabled) { opacity: 0.5; transition: opacity 0.15s ease; }
.champ-ligne-inline input[type="color"] {
    width: 44px;
    height: 32px;
    padding: 2px;
    border: 1px solid var(--gris-bordure);
    border-radius: 8px;
    background: var(--blanc);
}

/* ---------- Production ---------- */
.ligne-ajout-recette { display: flex; gap: 8px; margin-bottom: 14px; }
.ligne-ajout-recette .champ-select,
.ligne-ajout-recette .champ-recherche-wrapper { flex: 1; }
.ligne-ajout-recette .champ-texte { flex: 1; margin-bottom: 0; }

/* ---------- Messagerie entre collaborateurs ---------- */
.fil-messages { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.message-bulle {
    max-width: 75%;
    padding: 8px 12px;
    border-radius: 14px;
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
}
.message-bulle p { margin: 0; white-space: pre-wrap; word-break: break-word; }
.message-bulle .message-heure { display: block; margin-top: 2px; font-size: 10px; color: var(--texte-attenue); }
.message-recu { align-self: flex-start; }
.message-envoye { align-self: flex-end; background: var(--or-clair); border-color: var(--or-fonce); }
.bouton-secondaire-inline { width: auto; flex: 0 0 auto; white-space: nowrap; }
.bouton-mode-selection { display: inline-block; }
.bouton-secondaire-centre { display: inline-block; width: auto; text-align: center; }
.form-poids-inline { display: inline-flex; align-items: center; gap: 4px; justify-content: flex-end; }

/* ---------- Réglages enregistrés (presets de poids) ---------- */
.liste-chips-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.chip-preset {
    display: inline-flex;
    align-items: center;
    background: var(--blanc);
    border: 1.5px solid var(--or);
    border-radius: 999px;
    overflow: hidden;
}
.chip-preset button {
    border: none;
    background: transparent;
    cursor: pointer;
    font-weight: 600;
    font-size: 13px;
    color: var(--texte);
}
.chip-preset > button { padding: 6px 4px 6px 14px; }
.chip-preset > span { padding: 6px 4px 6px 14px; font-weight: 600; font-size: 13px; color: var(--texte); }
.chip-preset-suppr { padding: 6px 12px 6px 4px !important; color: var(--texte-attenue) !important; }
.chip-preset-suppr:hover { color: var(--brique) !important; }

/* ---------- Traçabilité ---------- */
.bloc-etiquette { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 14px; }
.etiquette-titre { font-weight: 700; flex: 1 1 160px; }
.etiquette-badges { display: flex; flex-wrap: wrap; gap: 6px; }
/* La croix de suppression devient une poubelle partout (mobile ET
   ordinateur) — classe dédiée (.bouton-supprimer-etiquette) plutôt que la
   classe générique partagée .bouton-retirer-ligne (utilisée ailleurs avec
   sa croix habituelle), pour ne changer que cette page. "Réimprimer", lui,
   ne devient une icône que sur mobile (voir plus bas) : sur ordinateur, où
   la place ne manque pas, il reste en texte. */
.bouton-supprimer-etiquette {
    color: transparent;
    text-indent: -9999px;
    overflow: hidden;
    background-repeat: no-repeat;
    background-position: center;
    background-size: 16px;
    background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Cpath d='M3 5h12M7 5V3.5a1 1 0 011-1h2a1 1 0 011 1V5M4.5 5l.7 9a1 1 0 001 .9h5.6a1 1 0 001-.9l.7-9' stroke='%231a1a1a' stroke-width='1.3' stroke-linecap='round' stroke-linejoin='round' fill='none'/%3E%3Cpath d='M7.5 8v4M10.5 8v4' stroke='%231a1a1a' stroke-width='1.1' stroke-linecap='round'/%3E%3C/svg%3E");
}
/* Sur mobile : encadrés plus compacts (moins de padding/marge) pour en
   voir davantage à l'écran, et "Réimprimer" passe en icône (imprimante)
   plutôt qu'en texte, pour gagner de la place. */
@media (max-width: 799px) {
    .bloc-etiquette { position: relative; padding: 8px 46px 8px 10px; gap: 6px; margin-bottom: 8px; }
    .etiquette-titre { font-size: 14px; }
    .etiquette-badges { gap: 4px; }
    .etiquette-badges .badge-categorie { padding: 2px 8px; font-size: 10px; }
    /* Les deux icônes groupées en colonne (imprimante au-dessus de la
       poubelle), épinglées en haut à droite de l'encadré via
       position:absolute plutôt que dans le flux flex : sinon, dès que la
       ligne titre+badges manquait de place, tout le groupe passait à la
       ligne EN DESSOUS (flex-wrap) et se retrouvait poussé à droite sur SA
       PROPRE ligne — d'où le vide à gauche sous le contenu. En absolute,
       il reste ancré au coin peu importe la hauteur du contenu à gauche ;
       le padding-right ajouté sur .bloc-etiquette réserve la place pour
       qu'aucun texte ne passe dessous. min-height sur .bloc-etiquette :
       sans ça, un encadré au contenu court (titre + badges tenant sur une
       ligne) restait plus bas (~57px) que les deux icônes empilées avec
       leur espacement (34+6+34=74px, +8px de marge en haut) — la poubelle
       débordait alors en dessous du bas de l'encadré. */
    .bloc-etiquette { min-height: 90px; }
    .etiquette-actions {
        position: absolute;
        top: 8px;
        right: 8px;
        display: flex;
        flex-direction: column;
        gap: 6px;
    }
    .bouton-reimprimer,
    .bouton-supprimer-etiquette {
        width: 34px;
        height: 34px;
        padding: 0;
        border: none;
        border-radius: 8px;
        background-color: var(--gris-panneau);
    }
    .bouton-reimprimer {
        color: transparent;
        text-indent: -9999px;
        overflow: hidden;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 16px;
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 18 18' width='18' height='18'%3E%3Crect x='5' y='1.2' width='8' height='5' rx='0.6' stroke='%231a1a1a' stroke-width='1.3' fill='none'/%3E%3Crect x='2' y='6' width='14' height='6.2' rx='1.6' stroke='%231a1a1a' stroke-width='1.3' fill='none' stroke-linejoin='round'/%3E%3Ccircle cx='12.3' cy='9.1' r='0.8' fill='%231a1a1a'/%3E%3Crect x='5' y='11' width='8' height='5.6' rx='0.6' stroke='%231a1a1a' stroke-width='1.3' fill='none'/%3E%3C/svg%3E");
    }
}
.titre-section { font-size: 14px; font-weight: 700; color: var(--texte); margin: 4px 0 12px 2px; }
/* Sélecteur de période : select natif gardé (fonctionnel), à droite de la
   recherche sur ordinateur (simple flux flex normal, .champ-select-periode
   ne grandit pas comme le ferait le champ de recherche) — réduit à une
   icône calendrier sur mobile, même principe que .champ-select-tri. */
/* width:auto en plus de flex:0 0 auto — sans lui, le width:100% déjà posé
   sur .champ-select (même spécificité, propriété non touchée par la règle
   ci-dessous) resurgit comme base du flex-basis "auto", et le select
   réclamait toute la largeur de la ligne, la faisant passer à la ligne
   suivante au lieu de rester à côté de la recherche. */
.champ-select-periode { flex: 0 0 auto; width: auto; }
@media (max-width: 799px) {
    .champ-select.champ-select-periode {
        flex: 0 0 40px;
        width: 40px;
        min-width: 40px;
        padding: 0;
        border: 1px solid var(--gris-bordure);
        border-radius: 10px;
        color: transparent;
        text-indent: -9999px;
        background-color: var(--gris-panneau);
        background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18'%3E%3Crect x='2' y='3' width='14' height='13' rx='2' fill='none' stroke='%236b6558' stroke-width='1.5'/%3E%3Cpath d='M2 7h14' stroke='%236b6558' stroke-width='1.5'/%3E%3Cpath d='M6 1.5v3M12 1.5v3' stroke='%236b6558' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: center;
        background-size: 18px;
    }
}

/* ---------- Paramètres : cartes de thème ---------- */
.grille-presets-theme {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 12px;
    margin-bottom: 10px;
}
.carte-preset-theme {
    display: block;
    background: var(--blanc);
    border: 1.5px solid var(--gris-bordure);
    border-radius: 14px;
    padding: 12px;
    cursor: pointer;
}
.carte-preset-theme.choisi { border: 2px solid var(--or); }
.carte-preset-theme input { display: none; }
.apercu-preset-theme {
    display: flex;
    align-items: center;
    height: 46px;
    padding: 0 10px;
    border-radius: 8px;
    margin-bottom: 8px;
}
.pastille-preset-theme { width: 22px; height: 22px; border-radius: 11px; }
.titre-preset { display: block; font-weight: 700; font-size: 13px; color: var(--texte); }
.description-preset { display: block; font-size: 11px; color: var(--texte-attenue); margin-top: 4px; }
.ligne-couleurs-perso { display: flex; flex-wrap: wrap; gap: 20px; }

/* ---------- Paramètres : chaque section repliée par défaut (menu
   déroulant natif <details>/<summary>) — seuls les titres sont visibles au
   premier coup d'œil, comme demandé, sans avoir à tout faire défiler. ---------- */
.panneau-parametre {
    background: var(--blanc);
    border: 1px solid var(--gris-bordure);
    border-radius: 14px;
    margin-bottom: 10px;
    overflow: hidden;
}
.panneau-parametre summary {
    padding: 14px 16px;
    font-weight: 700;
    font-size: 15px;
    cursor: pointer;
    list-style: none;
}
.panneau-parametre summary::-webkit-details-marker { display: none; }
.panneau-parametre summary::after {
    content: "›";
    float: right;
    color: var(--texte-attenue);
    font-size: 18px;
    transform: rotate(90deg);
    transition: transform 0.15s ease;
}
.panneau-parametre[open] summary::after { transform: rotate(270deg); }
.panneau-parametre .contenu-parametre { padding: 4px 16px 16px 16px; }
.form-deconnexion-mobile { margin-top: 4px; }
.form-deconnexion-desktop { display: none; }
@media (min-width: 800px) {
    .app-shell { display: flex; }

    /* Rail d'icônes doré, fixe à gauche, comme #railIcones dans main.py.
       Repliée à 64px (icônes seules) en permanence ici — l'agrandissement
       au survol (souris uniquement) est ajouté plus bas dans une media
       query séparée, gated sur (hover: hover). overflow-y:auto : en
       paysage sur téléphone (cette mise en page "ordinateur" s'active dès
       800px de large, y compris tactile), la hauteur d'écran est trop
       courte pour les ~9 icônes + Paramètres — sans ça, Paramètres était
       tout simplement inaccessible, coupé en bas sans aucun scroll
       possible. */
    .rail {
        position: fixed;
        top: 0; left: 0; bottom: 0; right: auto;
        width: 64px;
        flex-direction: column;
        align-items: stretch;
        background: var(--or);
        border-top: none;
        padding: 14px 4px;
        gap: 6px;
        overflow-x: hidden;
        overflow-y: auto;
        z-index: 30;
    }
    /* Repliée, chaque icône garde EXACTEMENT sa mise en page d'origine —
       carré centré, label caché : le passage en ligne pleine largeur
       (icône + texte côte à côte) n'a lieu qu'au survol réel, voir la
       media query (hover: hover) plus bas. align-self:center est
       nécessaire en plus de flex-direction:column : .rail a
       align-items:stretch (pour que le rail LUI-MÊME remplisse toute la
       largeur disponible), qui sinon étire aussi chaque .rail-lien à la
       largeur du rail — le carré actif/survolé devenait un rectangle
       56×48 même repliée à 64px. */
    .rail-lien {
        flex: 0 0 auto;
        align-self: center;
        min-width: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        color: var(--noir);
        padding: 12px;
        border-radius: 10px;
        white-space: nowrap;
    }
    .rail-lien:hover { background: rgba(0,0,0,0.08); }
    .rail-lien.actif { background: var(--blanc); color: var(--noir); }
    .rail-icone { width: 24px; height: 24px; flex: 0 0 auto; }
    /* La roue crantée "Paramètres" reste en bas du rail, séparée des
       sections principales — comme dans main.py. */
    .rail-lien-parametres { margin-top: auto; }
    /* Le nom de chaque section reste cependant accessible même replié via
       l'infobulle (attribut title posé dans base.html). display:none (et
       pas juste opacity:0) est important ici : un inline-block invisible
       occupe quand même sa largeur de texte dans la ligne flex (icône +
       libellé), ce qui élargissait la zone cliquable/le survol de chaque
       lien même repliée — display:none la retire complètement du flux,
       exactement comme avant l'ajout du survol. */
    .rail-libelle {
        display: none;
        font-size: 13px;
    }

    .colonne-principale { margin-left: 64px; width: calc(100% - 64px); }

    .barre-haut { padding: 0 20px; height: 52px; font-size: 15px; }
    .barre-haut-titre { font-size: 15px; }

    .contenu { padding: 28px 32px; padding-bottom: 32px; max-width: 1300px; }

    .bouton-flottant { display: none; } /* remplacé par le bouton du panneau latéral */
    .bouton-categories-mobile { display: none; } /* le panneau latéral fixe le remplace */
    /* Production : "+ Nouvelle production" est un <button> posé dans son
       propre <form> (action POST directe, pas un simple lien comme les
       autres catalogues) — masquer seulement le bouton laissait le <form>
       vide mais toujours présent dans la ligne flex, qui comptait quand
       même pour un "gap" et décalait "Sélectionner" de 10px vers la droite
       par rapport à la barre de recherche juste en dessous. */
    .ligne-boutons-catalogue form { display: none; }

    /* Le rail passe à gauche sur ordinateur (voir plus haut) : plus besoin
       de dégager de la place en bas pour la barre de pilules. */
    .barre-actions-groupe { bottom: 18px; }

    /* Catalogue : panneau de catégories (façon panneauLateral) + contenu,
       en pleine hauteur sous la barre du haut — .contenu perd son padding
       pour laisser le panneau toucher les bords, comme dans l'app. */
    body.page-catalogue .contenu {
        padding: 0;
        max-width: none;
        margin: 0;
    }
    /* Hauteur fixe (pas juste min-height) + overflow-y sur chaque colonne :
       le panneau de catégories et le contenu défilent chacun de leur côté,
       indépendamment l'un de l'autre — comme les colonnes de la fiche
       détail (.detail-col). Sans ça, scroller la liste faisait aussi
       défiler les catégories avec elle. */
    body.page-catalogue .catalogue {
        display: flex;
        align-items: stretch;
        height: calc(100vh - 52px);
    }
    .panneau-lateral {
        display: block;
        width: 230px;
        flex: 0 0 230px;
        background: var(--gris-panneau);
        padding: 16px;
        overflow-y: auto;
        scrollbar-width: none;
    }
    .panneau-lateral::-webkit-scrollbar { display: none; }
    .puces-categories { display: none; }
    .catalogue-contenu {
        flex: 1;
        min-width: 0;
        padding: 16px 20px;
        overflow-y: auto;
        scrollbar-width: none;
    }
    .catalogue-contenu::-webkit-scrollbar { display: none; }

    /* Moins large que le 200px mobile-first : laisse plus de place au champ
       de recherche, qui lui prend tout l'espace restant (flex:1). */
    .champ-select-tri { flex: 0 0 170px; }

    .grille-cartes { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 18px; }
    .grille-macros { grid-template-columns: repeat(4, 1fr); }

    /* Fiche détail / formulaires : contenu recentré, un peu plus large. */
    .fiche-entete h1 { font-size: 26px; }

    .grille-champs-nutrition { grid-template-columns: repeat(4, 1fr); }
    .groupe-repliable-contenu { grid-template-columns: repeat(3, 1fr); gap: 12px 20px; }

    /* Paramètres : les 4 sections passent en liste à gauche façon
       catégories (#nav-parametres réutilise .panneau-lateral/.categorie-lien)
       — cliquer un titre affiche son contenu à droite dans un encadré blanc
       (comme le tableau d'une fiche recette), une seule section visible à
       la fois (voir le JS qui bascule .actif/.open). */
    #nav-parametres { display: flex; flex-direction: column; height: 100%; }
    /* .categorie-lien-bouton (pas #nav-parametres) pour le reset de style
       natif <button> : une seule classe, comme .categorie-lien.actif
       ci-dessous (deux classes) — qui doit rester prioritaire pour que le
       fond doré de l'onglet sélectionné s'applique bien. Un sélecteur basé
       sur l'ID (comme avant) aurait battu .categorie-lien.actif quel que
       soit le nombre de classes en face, et effaçait ce fond doré. */
    .categorie-lien-bouton {
        display: block;
        width: 100%;
        text-align: left;
        background: none;
        border: none;
        font: inherit;
        cursor: pointer;
    }
    .form-deconnexion-desktop { display: block; margin-top: auto; }
    .form-deconnexion-mobile { display: none; }
    .panneau-parametre { display: none; border: none; border-radius: 0; margin-bottom: 0; }
    .panneau-parametre.actif {
        display: block;
        background: var(--blanc);
        border-radius: 14px;
        padding: 20px;
    }
    .panneau-parametre summary { display: none; }
    .panneau-parametre .contenu-parametre { padding: 0; }
}

/* ---------- Fiche détail : 3 colonnes façon bureau (ui/recipes_tab.py,
   ui/compositions_tab.py) — réglages à gauche, contenu au centre,
   catégorie/conservation/allergènes/nutrition/équilibrage à droite.
   Seuil séparé (plus large que les 800px du rail/panneau catalogue) : avec
   deux colonnes latérales fixes (260px + 280px), un tableau à 6 colonnes n'a
   plus assez de place pour respirer en dessous de ~1100px — en dessous de ce
   seuil, tout reste empilé en une seule colonne comme sur mobile plutôt que
   de laisser le tableau déborder sous le panneau. "order" plutôt qu'un autre
   ordre de DOM : le mobile/tablette garde le contenu principal en premier
   (voir règles hors media query). ---------- */
@media (min-width: 1100px) {
    /* .contenu perd son padding habituel : la fiche détail gère elle-même
       ses marges (voir .detail-layout) pour pouvoir calculer une hauteur
       exacte (100vh - barre du haut) sans compter en plus un padding déjà
       posé ailleurs — sinon soit ça dépasse d'un peu, soit il reste une
       marge morte en bas. Même truc que body.page-catalogue plus haut. */
    body.page-detail .contenu { padding: 0; max-width: none; margin: 0; }

    .detail-layout {
        flex-direction: row;
        /* stretch (pas flex-start) : gauche/droite prennent toute la hauteur
           de la ligne (comme panneauLateral sur le bureau, dont le fond gris
           remplit toute la fenêtre) — sans ça, la poignée du panneau droit se
           centre sur SA hauteur à lui (plus courte que la colonne centrale)
           et se retrouve isolée en plein milieu d'un vide. */
        align-items: stretch;
        gap: 12px;
        height: calc(100vh - 52px);
        padding: 12px 16px;
        overflow: hidden;
    }
    /* Chaque colonne défile indépendamment des deux autres (molette
       uniquement, pas de barre visible) — comme les QScrollArea séparées de
       chaque colonne sur le bureau, pour que réglages/tableau/nutrition ne
       bougent pas les uns les autres en défilant. */
    .detail-col { overflow-y: auto; scrollbar-width: none; }
    .detail-col::-webkit-scrollbar { display: none; }
    .detail-col-gauche, .detail-col-droite {
        background: var(--gris-bordure);
        border-radius: 14px;
        padding: 12px;
    }
    .detail-col-gauche { order: 0; flex: 0 0 250px; }
    .detail-col-centre { order: 1; flex: 1; min-width: 0; }
    .detail-col-droite { order: 2; flex: 0 0 270px; position: relative; transition: flex-basis 0.15s ease; }
    /* 270px de large ne laisse pas la place pour 4 colonnes de macros (elles
       débordaient, cf. capture) — repasse à 2 colonnes comme sur mobile. */
    .detail-col-droite .grille-macros { grid-template-columns: repeat(2, 1fr); }
    /* Espacement resserré au minimum entre les blocs empilés dans ces
       colonnes étroites — le but n'est pas de faire respirer chaque bloc
       comme sur une page pleine largeur, juste d'éviter qu'ils se touchent. */
    .detail-col .bloc { padding: 12px; margin-bottom: 10px; }
    .detail-col .bloc:last-child { margin-bottom: 0; }

    .poignee-panneau-droit {
        display: flex;
        position: absolute;
        top: 50%;
        left: -14px;
        transform: translateY(-50%);
        width: 20px;
        height: 44px;
        align-items: center;
        justify-content: center;
        background: var(--or);
        color: var(--noir);
        border: none;
        border-radius: 8px 0 0 8px;
        cursor: pointer;
        font-size: 15px;
        font-weight: 700;
        z-index: 3;
    }
    .poignee-panneau-droit:hover { background: var(--or-fonce); }
    /* Fermé, la colonne se réduit à une simple réglette grise : la poignée
       se recentre DEDANS (au lieu de rester calée sur le bord gauche, qui la
       faisait chevaucher le tableau du centre — cf. capture) et redevient un
       petit bouton symétrique plutôt qu'un onglet accroché à un bord. */
    .detail-col-droite.ferme { flex: 0 0 28px; overflow: visible; }
    .detail-col-droite.ferme .detail-col-droite-contenu { display: none; }
    .detail-col-droite.ferme .poignee-panneau-droit {
        left: 50%;
        transform: translate(-50%, -50%);
        border-radius: 8px;
    }
}

/* Agrandissement du rail au survol : uniquement pour un vrai pointeur de
   souris. Au doigt (tactile), le survol se déclenche puis se referme
   aussitôt au clic, ce qui ferait clignoter le menu de façon disgracieuse
   — rencontré en paysage sur iPhone, où cette mise en page "rail latéral"
   s'active dès 800px de large alors que la navigation y reste tactile. */
@media (min-width: 800px) and (hover: hover) and (pointer: fine) {
    .rail { transition: width 0.15s ease; }
    .rail:hover { width: 210px; }
    .rail:hover .rail-lien {
        align-self: stretch;
        flex-direction: row;
        justify-content: flex-start;
        gap: 12px;
    }
    .rail:hover .rail-libelle { display: inline-block; }
}
